All the tutorials in the internet show how to use the ScreenManager and change different screens from the .kv files.
I am still learning and I can not manage to control it from the .py file.
I want my app first to perform some validation before starting. There fore i want Kivy app to start with a simple "Please wait" screen, and after that to go to the Main screen.
Please help!
Here is a short example of how one can change a screen from the python code
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang import Builder
Builder.load_string("""
<Screen1>:
name: 'screen1'
Button:
on_press: root.on_click()
<Screen2>:
name: 'screen2'
Label:
text: "Cool!"
<MyScreenManager>:
Screen1:
Screen2:
""")
class Screen1(Screen):
def on_click(self):
sm.current = 'screen2'
class Screen2(Screen):
pass
class MyScreenManager(ScreenManager):
pass
class SimpleApp(App):
def build(self):
global sm
sm = MyScreenManager()
return sm
SimpleApp().run()
I have used the button on_press event to call the python code but you can run your validations in another thread and when your are done you can call back to the kivy code using the Clock api
Related
I'm writing a python script with a kv script outside it for formatting.
I have 2 basic screens thus far. I can load 1 screen into a preview but things fall apart when I try 2 screens.
I get this error:
kivy.uix.screenmanager.ScreenManagerException: ScreenManager accepts only Screen widget.
Here's my python code:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen, ScreenManager
Builder.load_file("design.kv")
class Intro(Screen):
def go_settings(self):
self.manager.current = "settings"
pass
class Settings(Screen):
print("In settings")
pass
class RootWidget(ScreenManager):
pass
class MainApp(App):
def build(self):
return RootWidget()
if __name__ == "__main__":
MainApp().run()
My kv code, titled "design.kv":
<Intro>:
GridLayout:
cols: 1
Label:
text: "Intro"
Button:
text: "Settings"
on_press: root.go_settings()
<Settings>:
GridLayout:
cols: 1
Label:
text: "Settings"
<RootWidget>:
Intro:
name: "intro"
Settings: #will work if I comment this line and below
name: "settings"
Not sure what's going on. Thanks for the help!
Alright, I think I got it.
I need to change the names of
Intro
and
Settings
to
IntroScreen
and
SettingsScreen
It seemed to work for me. Never thought the name I'd give would matter.
How do I change the button's color when it's down? As in, when you press the button, replace the blue-ish colour into another one?
I've tried fiddling around with background_color_down, background_color_normal, I tried using canvas and the sort but nothing seems to have the effect I intend it to
Or you could try giving the path to a png file with your design of the pressed button to the background_down attribute. Here my file is called "pressed.png" and is located within the same folder as the python programm. this is a link to something i made within 30 seconds in inkscape.
#!/usr/bin/python3.5
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.lang.builder import Builder
from kivy.properties import ObjectProperty, ListProperty
from kivy.atlas import Atlas
kv= Builder.load_string('''
<MainScreen>
my_but:my_but
orientation: "vertical"
size_hint:1,1
# size_hint: 0.3,0.3
# pos_hint:{"center_x": 1, "center_y":1}
Label:
size_hint: 0.5,0.4
text: "Look at my Button go:"
Button:
id:my_but
size_hint: 0.5,0.4
text: "klick me"
# background_normal: ""
background_down: "pressed.png"
''')
class MainScreen(BoxLayout):
my_but = ObjectProperty(None)
def __init__(self, **kwargs):
super(MainScreen, self).__init__(**kwargs)
class myApp(App):
def build(self):
return MainScreen()
if __name__ == "__main__":
myApp().run()
it is a simple program but i can not find a way to make it work. i just want to add a widget in boxlayout2 when the user presses the button (and has not writed anything in textinput) which is located in boxlayout1 .The widget do not display in screen.What should i do?
main.py
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
class BoxLayout1(BoxLayout):
def Search(self):
if self.ids.textinput.text!='':
BoxLayout2()
class BoxLayout2(BoxLayout):
def Print(self):
self.add_widget(Button(text='hello'))
class TestApp(App):
pass
TestApp().run()
and here is my kivy code
test.kv
<BoxLayout1>:
BoxLayout:
Label:
text:'Hello'
TextInput:
id: textinput
Button:
text: 'write'
on_press: root.Search()
BoxLayout:
orientation: 'vertical'
BoxLayout1:
BoxLayout2:
I see the presentation layout i want but the button is nowhere to be found.
To make it clear let's follow the stream of the app you've written.
it creates a BoxLayout and puts BoxLayout1 and BoxLayout2 in it, the second one doesn't have any content. When you click on write, the app checks the content of the text box and if valid, calls the constructor of BoxLayout2! Now at this point it creates an instance of this class, but does not keep it's reference so it will be discarded immediately. Now what you want is to call a function of a currently existing instance, not to create another one. Here's the code:
python:
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from kivy.lang import Builder
class BoxLayout1(BoxLayout):
def Search(self):
if self.ids.textinput.text!='':
self.parent.ids.bxl2.addButton()
# BoxLayout2()
class BoxLayout2(BoxLayout):
def addButton(self):
button=Button(text='hello')
self.add_widget(button)
kivy language:
<BoxLayout1>:
BoxLayout:
Label:
text:'Hello'
TextInput:
id: textinput
Button:
text: 'write'
on_press: root.Search()
BoxLayout:
orientation: 'vertical'
BoxLayout1:
BoxLayout2:
id:bxl2
I'm doing a proyect using kivy but i have a problem with the checkboxes. At first I'm trying to do the program like python coding (I know it is'nt clean, but I understand more) And i have a first screen with this coding:
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.gridlayout import GridLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.textinput import TextInput
from kivy.uix.button import Button
from kivy.uix.screenmanager import ScreenManager, Screen, FadeTransition
from kivy.uix.checkbox import CheckBox
class MainScreen(GridLayout):
def __init__(self,**kwargs):
e=[]
super(MainScreen, self).__init__(**kwargs)
self.cols=2
def on_checkbox_active(checkbox, value):
if value:
e.append(value)
print e
else:
print('The checkbox', checkbox, 'is inactive')
self.add_widget(Label(text='Inserta assignatures desitjades',font_size=35))
self.add_widget(Label(text=''))
ch1 = CheckBox()
self.add_widget(ch1)
self.add_widget(Label(text='Termotecnia'))
ch2 = CheckBox()
self.add_widget(ch2)
self.add_widget(Label(text='Termotecnia'))
ch3 = CheckBox()
self.add_widget(ch3)
self.add_widget(Label(text='Termotecnia'))
ch4 = CheckBox()
self.add_widget(ch4)
self.add_widget(Label(text='Termotecnia'))
b1=Button(text='Exit',background_color=[0.7,0.7,1,1],font_size=24)
self.add_widget(b1)
b2=Button(text='Next',font_size=24,font_color=[1,3,4,0],background_color=[1,2,3,6])
self.add_widget(b2)
ch1.bind(active=on_checkbox_active)
ch2.bind(active=on_checkbox_active)
b1.bind(on_press=exit)
b2.bind(on_press=reloaded)
...
class SimpleKivy(App):
def build(self):
return MainScreen()
if __name__=='__main__':
SimpleKivy().run()
I want to select two or three options for example, and save it for the next screen, like a type of selection. If anyone knows how to do it and save information for the next screen it woul help me a lot, because i have the code of the next screen for all the options, but i want to preselect in the first screen and then only use which i have selected. Also if anyone can help me, i want to know hoy to do the transition to another class (screen) when the button "Next" is pressed. I know this question are pretty simple but I'm new in kivy programming and some concepts are pretty difficult. Thanks.
What you want is accessing variables in other classes. Sometimes this can be annoying and you can do it either hard way with all __init__() and stuff, or... a simplier way comes along: it's get_running_app().
You can create a dictionary or something else, where you can store any value your other classes need to access. It's similar to using globals and it costs you less lines of code. For example in your case you could use a dictionary(or nested dictionaries, json, ...) to store for example 'checkboxes':'<names of checked ones>' and in each init you can loop over these values to make checkboxes active
Basically all you need is a = App.get_running_app() somewhere and something to access in main - App - class.
Example:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
Builder.load_string('''
<Root>:
MainScreen:
name: 'main'
AnotherScreen:
name: 'another'
<MainScreen>:
BoxLayout:
Button:
text: 'next screen'
on_release: root.parent.current='another'
Button:
text: 'ping!'
on_release: root.ping()
<AnotherScreen>:
BoxLayout:
Button:
text: 'previous screen'
on_release: root.parent.current='main'
Button:
text: 'ping!'
on_release: root.ping()
''')
class MainScreen(Screen):
def __init__(self, **kw):
super(MainScreen, self).__init__(**kw)
self.a = App.get_running_app()
def ping(self):
print self.a.big_dict['hi']
class AnotherScreen(Screen):
def ping(self):
b = App.get_running_app()
print b.big_dict['hi']
class Root(ScreenManager):
pass
class SimpleKivy(App):
big_dict={'hi':'hi there!'}
def build(self):
return Root()
SimpleKivy().run()
You can see there's no need to call __init__(), no need to write more lines of code if you really don't need to.
I'm trying to get an application I've been building to stop at a certain point, and run a cleanup procedure on stop. This seems like it should be easy, but I keep encountering an error, and I haven't been able to track down a solution.
I use kivy 1.8.0 and Python 3.3. For ease, I've modified some code from the kivy documentation, since my code is based on the same framework, and both give me exactly the same error:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
# Create both screens. Please note the root.manager.current: this is how
# you can control the ScreenManager from kv. Each screen has by default a
# property manager that gives you the instance of the ScreenManager used.
Builder.load_string("""
<MenuScreen>:
BoxLayout:
Button:
text: 'Goto settings'
on_press: root.manager.current = 'settings'
Button:
text: 'Quit'
on_press: root.exit()
<SettingsScreen>:
BoxLayout:
Button:
text: 'My settings button'
Button:
text: 'Back to menu'
on_press: root.manager.current = 'menu'
""")
# Declare both screens
class MenuScreen(Screen):
def exit(self):
App.stop(self)
class SettingsScreen(Screen):
pass
# Create the screen manager
sm = ScreenManager()
sm.add_widget(MenuScreen(name='menu'))
sm.add_widget(SettingsScreen(name='settings'))
class TestApp(App):
def build(self):
return sm
def on_stop(self):
print('leaving now') # this is where I'd want to run the end of program procedure
if __name__ == '__main__':
TestApp().run()
When I run this and click on the Quit button, I get the following error:
builtins.KeyError: 'on_stop'
I should also note that if I comment out the on_stop function in class TestApp, the error persists. Any idea what is going wrong?
You're passing an instance of MenuScreen to App.stop(), which is causing your error as it expects an App instance. You can retrieve the running app and stop it like so:
class MenuScreen(Screen):
def exit(self):
App.get_running_app().stop()
Here is a solution for your problem:
# Declare both screens
class MenuScreen(Screen):
def exit(self):
self.manager.app.stop()
class TestApp(App):
def build(self):
# Create the screen manager
sm = ScreenManager()
# monkey patch the screen manager instance to add the app
sm.app = self
sm.add_widget(MenuScreen(name='menu'))
sm.add_widget(SettingsScreen(name='settings'))
return sm
though I do not think this is the canonical solution for your problem, as it is here involving monkey patching.
But what's wrong is that the stop() method you want to call exists only for an app instance, though the way you were handling it, you couldn't have access to the running App instance as it was declared after the code needing it.
So the solution is to move the ScreenManager build up inside the build method. And that's where I think my solution is not a good design: I'm basically adding a member to the ScreenManager by monkeypatching the instance, to give it a link to the App instance.
There you can call the close() function, and close your application gracefully.