Referencing ids within screenmanager object - python

I'm trying to get my head around screenmanager, in particular referencing objects within.
I used to use this to set a value:
class Widgets(Widget)
pass
w = Widgets()
w.ids.MyTitle.text = 'something'
Now I have this:
class Widgets(Screen)
pass
class SettingsScreen(Screen)
pass
sm = ScreenManager()
sm.add_widget(Widgets(name='main'))
sm.add_widget(SettingsScreen(name='settings'))
How do I reference MyTitle now? I've tried all sorts of combos such as:
sm.ids.main.MyTitle.text =
sm.main.MyTitle.text =
sm.main.ids.MyTitle.text =
.... but not getting it! Can someone put me out of my misery? Is there an easy way of browsing through the sm object or iterating through it maybe?
EDIT: Adding minimal running version:
minimal.kv:
# File name: minimal.py
#:kivy 1.8.0
<Widgets>
Button:
id: MyTitle
text: 'hello'
<SettingsScreen>:
Button:
id: Other
text: 'settings'
minimal.py:
from kivy.uix.widget import Widget
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.uix.button import Button
from kivy.lang import Builder
from kivy.app import App
class Widgets(Screen):
pass
class SettingsScreen(Screen):
pass
class myApp(App):
def build(self):
return sm
def on_start(self):
global sm
sm.ids['main'].ids['MyTitle'].text = 'changed' # <-- this fails
Builder.load_file("minimal.kv")
sm = ScreenManager()
sm.add_widget(Widgets(name='main'))
sm.add_widget(SettingsScreen(name='settings'))
if __name__ == '__main__':
myApp().run()

To get a screen from ScreenManager, use get_screen:
sm.get_screen('main').ids.MyTitle.text = 'changed'
Also, you may construct your app so:
kv file:
# File name: minimal.py
#:kivy 1.8.0
ScreenManager:
Widgets:
name: 'main'
SettingsScreen:
name: 'settings'
<Widgets>:
Button:
id: MyTitle
text: 'hello'
<SettingsScreen>:
Button:
id: Other
text: 'settings'
and in the python file:
sm=Builder.load_file(..)
class my12App(App):
def build(self):
return sm
def on_start(self):
self.root.get_screen('main').ids.MyTitle.text = 'changed'

According to the documentation you access an id like you would any dictionary key:
widget.ids['MyTitle']
Because ScreenManager in itself derives from Widget, and a given widget maintains a list of widgets it is aware of, you probably need something like:
sm.ids[0].ids['MyTitle'].text
However this is hard to say without a Minimal, Complete, and Verifiable Example. One thing you could do is:
for id in sm.ids: # Iterate through all widgets in ids
print(id) # Get the string representation of that widget
As a side note, this:
class Widgets(Screen)
pass
... will probably cause confusion, because you're extending Widget with Widgets (via an intermediate class Screen). OOP suggests that a subclass of a class should be a more specific form of the class. So, a Screen is a type of Widget. But Widgets is really some number of Widgets.

Unfortunately neither the code in the question, nor the one in the accepted answer worked for me, and I'm unsure as to how exactly it is supposed to work. My interpretation of it is, that OP wanted to change a property of a widget (a Button) inside of one Screen based on a callback from another Screen. The below code is a complete MWE doing that:
# File name: minimal.py
#:kivy 2.0.0
ScreenManager:
FoobarScreen:
name: 'foobar'
Widgets:
name: 'widgets'
<FoobarScreen>:
Button:
id: lalala
text: 'lalala'
<Widgets>:
Button:
id: lololo
text: 'lololo'
(minimal.kv)
import kivy
from kivy.uix.widget import Widget
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.uix.button import Button
from kivy.lang import Builder
from kivy.app import App
class FoobarScreen(Screen):
pass
class Widgets(Screen):
pass
class myApp(App):
def build(self):
return sm
def on_start(self):
self.root.get_screen('widgets').ids.lololo.text = 'changed'
self.root.current = 'widgets'
if __name__ == '__main__':
sm=Builder.load_file('minimal.kv')
myApp().run()
(minimal.py)

Related

Updating Kivy's display - Part two

The following code displays and updates the kivy display using kv strings loaded with Builder.load_string(). It works just fine. My question is how to modify this code to define the Example class in python rather than using dynamic class defined in the kv string?
import kivy
import threading
from kivy.app import App
from kivy.lang import Builder
from kivy.clock import Clock,mainthread
kivy.require('1.9.0')
KV1='''
Example:
<Example#BoxLayout>:
Label:
text:'hello'
Label:
text:'world'
'''
KV2='''
Example:
<Example#BoxLayout>:
Label:
text:'hello world'
Label:
text:'good morning'
'''
#mainthread
def update(dt):
try:
vRoot = App.get_running_app().root
vRoot.clear_widgets()
Builder.unload_file('file.kv')
vRoot.add_widget(Builder.load_string(KV2,filename='file.kv'))
except BaseException as e:
print (str(e))
def loop():
Clock.schedule_once(update,5)
class MyApp(App):
def build(self):
return Builder.load_string(KV1,filename='file.kv')
vThread=threading.Thread(target=loop)
vThread.start()
if __name__ == '__main__':
MyApp().run()
Thank you
You can always implement in pure python whatever you did using kvlang. However kvlang is recommended for faster, cleaner, effort less (compared to that in pure python) designing pattern.
Here, first you can define your widget in .py then design it using kvlang as,
Builder.load_string('''
<ExampleTwo>:
Label:
text:'hello world'
Label:
text:'good morning'
''')
class ExampleTwo(BoxLayout):
pass
Or can code the whole thing using pure python (without using kvlang).
With this your modified code should look like,
import kivy
import threading
from kivy.app import App
from kivy.lang import Builder
from kivy.clock import Clock,mainthread
from kivy.uix.boxlayout import BoxLayout
kivy.require('1.9.0')
KV1='''
Example:
<Example#BoxLayout>:
Label:
text:'hello'
Label:
text:'world'
'''
KV2='''
Example:
<Example#BoxLayout>:
Label:
text:'hello world'
Label:
text:'good morning'
'''
Builder.load_string('''
<ExampleTwo>:
Label:
text:'hello world'
Label:
text:'good morning everyone'
''')
class ExampleTwo(BoxLayout):
pass
#mainthread
def update(dt):
try:
vRoot = App.get_running_app().root
vRoot.clear_widgets()
Builder.unload_file('file.kv')
# vRoot.add_widget(Builder.load_string(KV2,filename='file.kv'))
vRoot.add_widget(ExampleTwo())
except BaseException as e:
print (str(e))
def loop():
Clock.schedule_once(update,5)
class MyApp(App):
def build(self):
return Builder.load_string(KV1,filename='file.kv')
vThread=threading.Thread(target=loop)
vThread.start()
if __name__ == '__main__':
MyApp().run()

Kivy Spinner adds a widget that is not codded

Can someone explain why below code adds this Close Button on the bottom of the window you can see on the pic? How to get rid of this?
CODE
from kivy.app import App
from kivy.lang import Builder
kv = '''
<MenuSpinner#Spinner>
<Menu#BoxLayout>
orientation: 'vertical'
MenuSpinner:
text: 'SPINNER'
values: ['1', '2']
BoxLayout:
Menu:
'''
sm = Builder.load_string(kv)
class TestApp(App):
def build(self):
return sm
if __name__ == '__main__':
TestApp().run()
The button is coming from the MenuSpinner, which is already present in Kivy (mostly internal, used by kivy.uix.settings). That class should probably be named differently on the Kivy side, but in any case renaming your version should fix the problem.

how do you change screens from python file in KIVY

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

Interaction between Kivy widgets in Python

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.

Understanding Kivy properities and binding methods

I am having problems understanding the usage of custom Properities and ways of binding methods to events.
Here's my code:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.properties import StringProperty
kivy_lang = '''
<MainWidget>:
on_my_property: my_label.text = 'from button bind method via StringProperty' + my_property
Label:
id: my_label
text: root.my_property
Button:
id: my_button
text: 'intro button'
'''
class MainWidget(BoxLayout):
# bind some properties
my_property = StringProperty('0')
def __init__(self, **kwargs):
super(MainWidget, self).__init__(**kwargs)
# if needed to do sth on widget construction
self.ids.my_button.bind(on_press=self.my_method)
def my_method(self,*args,**kwargs):
self.my_property = str(int(self.my_property)+1)
self.ids.my_button.text = 'new'
class MyApp(App):
def build(self):
Builder.load_string(kivy_lang)
return MainWidget()
if __name__ == '__main__':
MyApp().run()
When I run it it renders OK, but when I click a button, as a result I get
NameError: name 'my_property' is not defined
I tried binding method for Button in kv lang with (and removing whole 'init()' on python side):
on_press: root.my_method
and then when I press button the app doesn't crash but nothing happens
Can someone explain me how to adjust this code to work?
I understand the code is a little 'mixed techniques' but I did it that way to get to know different approaches, so I would appreciate if You don't turn it all around :)
1/ you are missing 'self' before 'my_property' in 'on_my_property' bindind, hence the crash
2/ in kv bindings. the python code is called as written, so you need '()' after 'root.my_method', or the statement has no effect.

Categories

Resources