Kivy widget in python to kv file, how control it - python

i have a simple test program:
main.py
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.screenmanager import Screen
from kivy.clock import mainthread
class TestScreen(Screen):
#mainthread
def on_pre_enter(self): #Is loaded before kv buttons etc? how make it work
pass
#mainthread
def on_enter(self): #Load after kv buttons etc?
button = Button(text="Work?")
#how now add it to display?
#how control where display it? on end or begin is just about on_pre and on?
class TestApp(App):
pass
if __name__ == '__main__':
TestApp().run()
And test.kv file
#:import NoTransition kivy.uix.screenmanager.NoTransition
<TestScreen>:
name:'try'
GridLayout:
id:'test'
cols:2
Button:
text:'Test'
on_press:app.root.current='Main'
ScreenManager:
transition: NoTransition()
Screen:
name: 'Main'
GridLayout:
cols:1
Button:
text:'1'
Button:
text:'2'
Button:
text:'Test'
on_press:root.current='try'
TestScreen:
Is simple to control kv and python widgets(but i dont know how but is more easy to writes widgets etc in kv file, but still need create some in python for automatic content) or better just create all in python withou kv file? I wanna make somehting like this: App with left menu always displayed and on the right side another screen with dynamic content based on screen(clicked from menu) is maybe another simple solution for this. Anyone can explain me step by step? :)

AttributeError
The solution to AttributeError, please replace "id: 'test'" with "id: test" in test.kv file.
Dynamic Content
It is possible to display screen with dynamic content based on clicked from menu. But remember to remove the widgets that were added when exiting the screen (TestScreen/SettingsScreen). If you do not remove the widgets, you will get duplicates/multiples of each widget added each time you enter the screen (TestScreen/SettingsScreen). I recommend using on_pre_enter and on_leave methods. Please refer to the example below for details.
Example
main.py
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.button import Button
class MyScreenManager(ScreenManager):
pass
class MenuScreen(Screen):
pass
class SettingsScreen(Screen):
def on_pre_enter(self, *args):
self.ids.test.add_widget(Button(text="Work?"))
def on_leave(self, *args):
self.ids.test.remove_widget(self.ids.test.children[0])
class TestApp(App):
title = "Add & Remove Widgets Dynamically"
def build(self):
return MyScreenManager()
if __name__ == "__main__":
TestApp().run()
test.kv
#:kivy 1.10.0
#:import NoTransition kivy.uix.screenmanager.NoTransition
<MyScreenManager>:
transition: NoTransition()
MenuScreen:
SettingsScreen:
<MenuScreen>:
name: 'menu'
GridLayout:
cols: 1
Button:
text: '1'
Button:
text: '2'
Button:
text: 'Test'
on_press: root.manager.current = 'settings'
<SettingsScreen>:
name:'settings'
GridLayout:
id: test
cols: 2
Button:
text: 'Test'
on_press: root.manager.current = 'menu'
Output

Related

How to make kivy app from different py files

I am trying to make an app out of different .py files. But I don't know how to add them together, I have one main file, and one login file with plans to add a lot more, but with these I'm experimenting right now. They are pretty basic for now until I figure out this "bonding" between them and then I will start adding some more complex stuff. I tried couple of things and they didn't work, but I left them in code for you to see (I tried to make the app to start with MainWindow, and on press of the first button it goes to login page*). Here's the code and please help me.
*Right now when I press the button it gives me this error: OSError: exception: access violation writing 0x0000000080006010
this is main.py:
from kivy.lang import Builder
from kivy.app import App
import login
from kivy.uix.screenmanager import Screen
kv = Builder.load_string('''
<MainWindow>:
GridLayout:
cols:1
GridLayout:
rows:5
Button:
text:"NOVA ROBA"
on_release:
root.call_login()
Button:
text:"KUPCI"
Button:
text:"PRODATO"
Button:
text: "AGRONOMI"
Button:
text: "STANJE U MAGACINU"
''')
class MainWindow(Screen):
def call_login(self):
login.app().run()
pass
class main_app(App):
def build(self):
return MainWindow()
if __name__ == '__main__':
main_app().run()
this is login.py:
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen
from kivy.app import App
class Login(Screen, App):
def build(self):
return
pass
kv = Builder.load_string('''
<Login>:
name:"login"
GridLayout:
rows:2
GridLayout:
cols:2
Label:
text:"Password: "
TextInput:
id:passwd
multiline: False
Button:
text: "Submit"
on_release:
passwd.text = ""
''')
class app(App):
def build(self):
return Login()
if __name__ == "__main__":
app().run()
You are creating 2 apps, which is not needed. Instead of inheriting from both Screen and App in the Loginscreen, inherit only from Screen. Then create a ScreenManager in your main.py's build method and then add the imported loginscreen as a widget, to switch to the new screen, use self.manager.current = "login" in the call_login method of MainWindow
class app(App):
def build(self):
sm = ScreenManager()
sm.add_widget(MainWindow())
sm.add_widget(Login())
return sm

Kivy label visual glitch when switching screens

I'm making a simple basic facts maths app in Kivy. I'm using ScreenManager to switch between different screens. When I switch between my screen, I'm getting an annoying visual glitch with my labels at the top of each page.
I've tried setting IDs to each label but that didn't do anything, I'm pretty new to kivy so have no idea what I'm doing.
The Program is in 2 files:
main.kv
Manager:
MainMenu:
GameMode:
DifficultyScreen:
Game:
<MainMenu>:
name: "menu"
GridLayout:
rows: 2
Label:
text: "More On Maths"
id: home_title
Button:
text: "Play"
on_release:
app.root.current = "gamemode_screen"
<GameMode>:
name: "gamemode_screen"
GridLayout:
rows: 5
Label:
text: "Select Game Mode"
id: gamemode_select
Button:
text: "Addition"
on_press: root.btn_add()
on_release:
app.root.current = "difficulty_screen"
main.py
import kivy
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
class MainMenu(Screen):
pass
class GameMode(Screen):
pass
class DifficultyScreen(Screen):
pass
class Game(Screen):
pass
class Manager(ScreenManager):
pass
class Main(App):
def build(self):
return
if __name__ == "__main__":
Main().run()
Anyone encountered this before? Any idea how to fix it?
Thanks

How to call a class from a function which is inside another class?

from kivy.app import App
from kivy.uix.popup import Popup
from kivy.uix.widget import Widget
from kivy.uix.dropdown import DropDown
class MyPracticeApp(App):
def build(self):
return Debit()
class Debit(Widget):
def debit(self):
return Hello()
class Hello(Widget):
pass
if __name__ == "__main__":
MyPracticeApp().run()
kv file...
<Debit>
Button:
text : 'popup'
size_hint : 0.2,0.2
on_press : root.debit()
<Hello>:
Button:
text : 'popup2'
size_hint : 0.2,0.2
# on_press : root.debit()
There is a button popup, and when I press this button I am calling the debit() function. Inside the debit function I am calling Hello(), but I'm unable to get anything from this class. What am I doing wrong?
Whenever I click on a buttton I want a new screen but without screen navigation. How do I do this ?
You can use ScreenManager for this.
A little example:
from kivy.app import App
from kivy.lang import Builder
KV = """
#:import NoTransition kivy.uix.screenmanager.NoTransition
BoxLayout:
orientation: "vertical"
Label:
text: "top"
ScreenManager:
id: sm
transition: NoTransition()
Screen:
name: "screen1"
Button:
text: "screen 2"
on_release: sm.current = "screen2"
Screen:
name: "screen2"
Button:
text: "screen 1"
on_release: sm.current = "screen1"
Label:
text: "bottom"
"""
class TestApp(App):
def build(self):
return Builder.load_string(KV)
TestApp().run()
Question Kivy Factory
what is the concept of Factory in kivy
Answer
When the keyword Factory is used anywhere (e.g. kv file, or Python script) in your project, it will automatically register any class or module and instantiate them.
Example
The following example illustrates the use of Factory in the kv file to register and instantiate Popup widget, Hello. There is no class definition of Hello and no definition for method debit() in Python script.
main.py
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.lang import Builder
Builder.load_string("""
#:import Factory kivy.factory.Factory
<Debit>:
Button:
text : 'popup'
size_hint : 0.2,0.2
on_press : Factory.Hello().open()
<Hello#Popup>:
title: 'Popup2'
auto_dismiss: False
BoxLayout:
orientation: 'vertical'
Label:
text: 'Hello Kivy'
Button:
text : 'Close Popup'
size_hint : 1,0.2
on_press : root.dismiss()
""")
class Debit(Widget):
pass
class MyPracticeApp(App):
def build(self):
return Debit()
if __name__ == "__main__":
MyPracticeApp().run()
References: Kivy ยป Factory object
Question 1
There is a button popup, and when I press this button I am calling the
debit() function. Inside the debit function I am calling Hello(), but
I'm unable to get anything from this class. What am I doing wrong?
Explanation
The second button is not displayed because the app did not have instructions on what to do with the instantiated object, Hello which contains a child widget, Button.
Solution
One of the solution is to add the new object using add_widget() function.
Snippets - Py
def debit(self):
return self.add_widget(Hello())
Question 2
Whenever I click on a button I want a new screen but without screen
navigation. How do I do this ?
Solution
You could use Kivy Popup widget.
Example
main.py
from kivy.app import App
from kivy.uix.popup import Popup
from kivy.uix.widget import Widget
from kivy.lang import Builder
Builder.load_string("""
<Debit>:
Button:
text : 'popup'
size_hint : 0.2,0.2
on_press : root.debit()
<Hello>:
title: 'Hello Popup2'
auto_dismiss: False
Button:
text : 'Close Popup'
size_hint : 0.2,0.2
on_press : root.dismiss()
""")
class Debit(Widget):
def debit(self):
return Hello().open()
class Hello(Popup):
pass
class MyPracticeApp(App):
def build(self):
return Debit()
if __name__ == "__main__":
MyPracticeApp().run()
Output

Kivy's ScreenManager and Popups don't want to work together

as stated in the title - I'm stuck. I've been playing with the code around and everything works as long as I keep ScreenManager and Popup separate. Once combined - they refuse to cooperate. Anyway, here is the simple app that shows the problem I'm having.
from kivy.lang import Builder
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.uix.popup import Popup
from kivy.uix.screenmanager import Screen, ScreenManager
class First(GridLayout,Screen):
def show_popup(self):
Popp().open()
pass
class Second(Screen):
pass
class Popp(Popup):
pass
class ScreenManagement(ScreenManager):
pass
app = Builder.load_file("main.kv")
class MainApp(App):
def build(self):
return app
if __name__ == "__main__":
MainApp().run()
And main.kv file
ScreenManagement:
First:
Second:
<First>:
name:"First"
rows: 2
Button:
text: "FirstButton"
on_release: app.root.current = "Second"
Button:
text: "Show popup"
on_release: root.show_popup()
<Second>:
name:"Second"
Button:
text: "BUTTON"
on_release: app.root.current = "First"
<Popp>:
title: "testing"
text: "Hello world"
size_hint: None,None
size: 400,400
auto_dismiss: False
Button:
text: "Okay"
on_press: root.dismiss()
App starts, first and second screen are working but when trying to get popup up I end up with:
kivy.uix.popup.PopupException: Popup can have only one widget as content
Somehow Screen is seen as a widget inside of Popp? Or am I terribly misinterpreting kivy docs?
It's a bug with loading kv file, it should throw an exception in this case.
What you are doing in the code is loading the kv file twice, what causes some weird behavior. Just delete the Builder.load_file(..) and it will work. The file is going to be loaded automatically.
Also, never do double subclassing of widgets like class First(GridLayout, Screen) as it might lead to some problems. Instead, create a grid layout inside the screen.
Put the elements in the Popup inside a layout, for example: Boxlayout.
Here's what I mean:
<Popp>:
title: "testing"
BoxLayout:
orientation: 'vertical'
size_hint: None,None
size: 400,400
Label:
text: "Hello world"
Button:
text: "Okay"
on_press: root.dismiss()
I have same problem with using kivy Builder.load_file and Popup, they dont work together.
the solution is simple, build popup in python code side. this is a loading popup example:
python:
from kivy.app import App
from kivy.uix.popup import Popup
from kivy.factory import Factory
from kivy.properties import ObjectProperty
from kivy.clock import Clock
from kivy.lang import Builder
from kivy.uix.popup import Popup
from kivy.uix.image import Image
from kivy.uix.label import Label
from kivy.uix.boxlayout import BoxLayout
import time, threading
buildKV = Builder.load_file("example.kv")
class ExampleApp(App):
def show_popup(self):
content = BoxLayout(orientation= "vertical")
image=Image(source= 'files/loading.gif', anim_delay= 0)
label=Label(text= 'Model is Running.\nBe Patient Please.')
content.add_widget(image)
content.add_widget(label)
self.popup = Popup(title='Model is Running.',
size_hint=(.250, .785),
content=content, auto_dismiss=False)
self.popup.open()
def process_button_click(self):
# Open the pop up
self.show_popup()
# Call some method that may take a while to run.
# I'm using a thread to simulate this
mythread = threading.Thread(target=self.something_that_takes_5_seconds_to_run)
mythread.start()
def something_that_takes_5_seconds_to_run(self):
thistime = time.time()
while thistime + 10 > time.time(): # 5 seconds
time.sleep(1)
# Once the long running task is done, close the pop up.
self.pop_up.dismiss()
if __name__ == "__main__":
ExampleApp().run()
kivy:
BoxLayout:
Button:
height: 40
width: 100
size_hint: (None, None)
text: 'Click Me'
on_press: app.process_button_click()

Kivy language function call on button press using screens

I have been fiddling with the Kivy language and have done some searching, but haven't found a solution that works for my situation. I assume I am missing a little fundamental thing here.
I am trying to call a function within a screen from a button press and I want to facilitate this with a kv file. I simplified the code and left out some formatting in kv file, such as layout and button size, etc.
The 'entered' variable is showing up on the screen as "Not Entered", but when I press the button, the label does not change and the function is not entered, nothing happens.
Python:
import kivy
kivy.require('1.9.1')
from kivy.app import App
from kivy.uix.screenmanager import Screenmanager, Screen
from kivy.properties import StringProperty
class ScreenManager(ScreenManager):
pass
class StartMenu(Screen):
pass
class MyScreen(Screen):
entered = StringProperty()
entered = "Not Entered"
def my_function(self, *args):
self.entered = "Entered"
class MyApp(App):
def build(self):
return ScreenManager()
if __name__ == "__main__":
MyApp().run()
Kivy: my.kv
#:kivy 1.9.1
<ScreenManager>:
StartMenu:
MyScreen:
<StartMenu>:
name: 'StartMenu'
Button:
on_release:
root.manager.current = 'MyScreen'
<MyScreen>:
name: 'MyScreen'
Label:
text: root.entered
Button:
on_release:
root.my_function()
Thanks for your time!
The problem is here:
class MyScreen(Screen):
entered = StringProperty()
entered = "Not Entered"
entered gets overwritten immediately (and is a standard class property, losing all the event magic). Instead initialize it as entered = StringProperty("Not Entered"), or in the kv file as
<MyScreen>:
entered: "Not Entered"
Btw, to make your example work, there should be some kind of layout:
#:kivy 1.9.1
<ScreenManager>:
StartMenu:
MyScreen:
<StartMenu>:
name: 'StartMenu'
Button:
on_release:
root.manager.current = 'MyScreen'
<MyScreen>:
entered:"Not Entered"
name: 'MyScreen'
GridLayout:
cols: 2
Label:
text: root.entered
Button:
on_release:
root.my_function()

Categories