I have two screens.
On Box I have a button with a name. When I click the button, I want to switch the screen to ChangeText see the name there. Once I click the button the text of Box.name should be the TextInput-text.
I can't find a solution.
kv-file
<ChangeText>:
BoxLayout:
TextInput:
id: txt
multiline: False
text: Box.name
Button:
text: "Save"
on_release: Box.name = txt.text
<Box>:
BoxLayout:
Button:
text: root.name
on_press: root.change_text(root.name)
py-file
class ChangeText(Screen):
pass
class Box(Screen):
name = StringProperty("sometext")
def change_text(label):
sm.current = 'changetext'
### ?!?!? ###
Problem - attribute, name
When using Kivy ScreenManager, do not declare a variable/attribute called, name because it is a reserved word used for naming/identifying a screen.
ScreenManager » name
name
Name of the screen which must be unique within a ScreenManager. This
is the name used for ScreenManager.current.
name is a StringProperty and defaults to ‘’.
Solution
The solution is as follow:
Rename the class level property, name to home_name or anything except name
Add id to each screen.
Use root.manager.ids to reference widgets in declared in another screen.
Replace sm.current with self.manager.current because each screen has by default a property manager that gives you the instance of the ScreenManager used.
Example
main.py
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.properties import StringProperty
class ScreenManagement(ScreenManager):
pass
class ChangeText(Screen):
pass
class Box(Screen):
home_name = StringProperty("sometext")
def change_text(self):
self.manager.current = 'changetext'
class TestApp(App):
def build(self):
return ScreenManagement()
if __name__ == "__main__":
TestApp().run()
test.kv
#:kivy 1.11.0
<ScreenManagement>:
Box:
id: box
name: 'box'
ChangeText:
id: changeText
name: 'changetext'
<ChangeText>:
BoxLayout:
TextInput:
id: txt
multiline: False
text: root.manager.ids.box.home_name
Button:
text: "Save"
on_release: root.manager.ids.box.home_name = txt.text
<Box>:
BoxLayout:
Button:
text: root.home_name
on_press: root.change_text()
Output
So I found a workaround, what might be instable but works for now.
Took me hours.
I just declared eveyr property as global variable. Then I wrote a global export functio, that overwrite all global variables. So whenever I switch the screen, I execute export(props). Now all properties from my last view are exported. I need to update all properties of each screen. Therefore each screen has update(). Export() is extended by a for loop over all screens, calling the update().
home_name = ''
def export(hn):
home_name = hn
for screen in sm.screens:
screen.update()
class Team(BoxLayout):
home_name = StringProperty()
def update(self):
global home_name
self.home_name = home_name
class Box(BoxLayout):
home_name = StringProperty("Home")
def load_team(self):
export(self.home_name)
sm.current = 'team'
def update(self):
global home_name
self.home_name = home_name
#further screen specific actions
kv
<Team>:
id: team
TextInput:
id: team_name
text: root.home_name
multiline: False
Button:
text: "Save"
on_press: root.save(team_name.text)
<Box>:
Button:
id: btn_home_name
text: root.home_name
on_press: root.load_team()
Related
Sorry my english.
How can I send some text from popup to label in another class which is inherited from the Screen class?
I tried different options for accessing this object, but nothing happens.
It looks like text sends to another object, because these objects has different memory adresses. I checked it.
Comment indicating the problem is in the code:
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang import Builder
from kivy.uix.popup import Popup
Builder.load_string("""
#:import Factory kivy.factory.Factory
<Keyboard>:
text_input: text_input
BoxLayout:
orientation: 'vertical'
TextInput:
id: text_input
Button:
text: 'Send text'
on_release: root.send()
<Container>:
label: label
BoxLayout:
orientation: 'vertical'
Label:
id: label
text: 'Here must some be text from popup'
Button:
text: 'My popup'
on_release: Factory.Keyboard().open()
Button:
text: 'Goto options'
on_press: root.manager.current = 'Options'
<Options>:
BoxLayout:
Button:
text: 'Back to time'
on_press: root.manager.current = 'Time'
""")
class Options(Screen):
def show_kv(self, instance, value):
self.manager.current = value
class Container(Screen):
pass
class Keyboard(Popup):
def send(self):
try:
time = self.text_input.text
except:
time = ''
# Here is some problem
Container().label.text = time # Nothing happens
self.dismiss()
class KivyApp(App):
def build(self):
sm = ScreenManager()
sm.add_widget(Container(name='Time'))
sm.add_widget(Options(name='Options'))
sm.current = "Time"
return sm
if __name__ == '__main__':
KivyApp().run()
The line:
Container().label.text = time
is creating a new instance of Container, and sets the text of a Label in that new instance. However, that instance of Container is not the one that appears in your GUI. You must access the instance that is actually in your GUI. You can do that using the get_screen() method of the ScreenManager that is the root of your GUI. Like this:
container_instance = App.get_running_app().root.get_screen('Time')
container_instance.label.text = time
I'm quite new to python. I'm trying to enable a button of a kivy screen inside a popup but I failed to adress the related screen (and therefore button of that screen) as I am outside of its class. Can you show me how I can adress it properly ? Below I try to simulate the issue as plain as possible. (If you see any other areas of development, feel free to adress.) Thanks in advance...
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.popup import Popup
from kivy.uix.screenmanager import ScreenManager, Screen
popupWindow = ""
class S_Mananager(ScreenManager):
pass
class S_Primary(Screen):
def show_pop_up(self):
global popupWindow
popupWindow = Popup(
content = pup_Pass(),
size_hint = (None,None),
size = (250, 300)
)
popupWindow.open()
class pup_Pass(BoxLayout):
def btn_OK(self):
global popupWindow
popupWindow.dismiss()
S_Mananager.get_screen("scr_primary").ids.btn_enable_me.disabled = False
class S_Secondary(Screen):
pass
class Screens(App):
def build(self):
pass
if __name__ == '__main__':
Screens().run()
KV File:
<pup_Pass>:
orientation: "vertical"
size: root.width, root.height
Label:
text: "Label"
Button:
text: "OK"
on_release: root.btn_OK()
S_Mananager:
S_Primary:
S_Secondary:
<S_Primary>:
name: "win_Main"
BoxLayout:
id: "scr_primary"
orientation: "vertical"
size: root.width, root.height
Label:
text: 'Label'
Button:
text: 'Button'
on_release: root.show_pop_up()
Button:
id: "btn_enable_me"
text: 'Enable Me by Popup !'
on_release: root.show_pop_up()
disabled: True
<S_Secondary>:
Two problems:
You are trying to call get_screen() as though it were a class method, but it is an instance method. You need to call get_screen() through an instance of ScreenManager.
You should not use strings for ids.
The fix is to first change the id for the Button:
Button:
id: btn_enable_me # not a string
Then modify your python to use the instance of ScreenManager in your GUI:
def btn_OK(self):
global popupWindow
popupWindow.dismiss()
App.get_running_app().root.get_screen("win_Main").ids.btn_enable_me.disabled = False
Note that get_screen() requires the name of a Screen (which should be a string), not the id.
I have an application with multiple screens, and I am trying to call a method of my ScreenOne from within another screen. The code is a minimal example.
I would like to know the proper method(s) of referencing this method of ScreenOne when I am within my second screen.
cart_list = {}
class ScreenOne(Screen):
def update_cart(self):
cart_list.update({'Item one': 1.00})
for key, value in cart_list:
print(key, value)
class ScreenTwo(Screen):
pass
The associated kv code is as follows
ScreenTwo:
Button:
id: item_1
text: "Add to cart"
on_press: MainScreen.update_cart()
Solution
kv file
Add id to ScreenOne.
Each screen has by default a property manager that gives you the instance of the ScreenManager used.
Reference the method using root.manager.ids.screen_one.update_cart()
Below are some minor updates required in the Python script.
py file
Declare cart_list as global in method, update_cart
Append .items() to cart_list e.g. for key, value in cart_list.items():
Example
main.py
from kivy.app import App
from kivy.uix.screenmanager import Screen
from kivy.lang import Builder
cart_list = {}
class ScreenOne(Screen):
def update_cart(self):
global cart_list
cart_list.update({'Item one': 1.00})
for key, value in cart_list.items():
print(key, value)
class ScreenTwo(Screen):
pass
class TestApp(App):
def build(self):
return Builder.load_file('main.kv')
if __name__ == '__main__':
TestApp().run()
main.kv
#:kivy 1.11.0
ScreenManager:
ScreenOne:
id: screen_one
name: 'screen1'
ScreenTwo:
id: screen_two
name: 'screen2'
<ScreenOne>:
BoxLayout:
Button:
text: 'Goto Screen Two'
on_press: root.manager.current = 'screen2'
Button:
text: 'Quit'
<ScreenTwo>:
Button:
id: item_1
text: "Add to cart"
on_press: root.manager.ids.screen_one.update_cart()
Output
I believe that my problem is that the label_text tag in the ScreenManager: section is not being updated when the change_text() function is run.Because it just shows the original label_text value, which in this case is nothing.
Does anyone know how get the tag to update? My goal is to be able to pass strings between the 2 Screen classes. So when a user enters something like a zip code on the previous screen i can pass it to the new screen.
#:kivy 1.1.3
ScreenManager:
id: screen_manager
SearchScreen:
id: search_screen
name: 'SearchScreen'
manager: screen_manager
ForecastScreen:
id: forecast_screen
name: 'ForecastScreen'
manager: screen_manager
label_text: search_screen.text
<SearchScreen>:
display: entry
FloatLayout:
TextInput:
id: entry
on_text_validate:
root.change_text()
<ForecastScreen>:
BoxLayout:
FloatLayout:
Label:
text:root.label_text
Then the py code:
class SearchScreen(Screen):
text = StringProperty('')
def change_text(self):
self.text = "show this text"
self.manager.current = "ForecastScreen"
class ForecastScreen(Screen):
label_text = StringProperty()
Builder.load_file('weather.kv')
sm = ScreenManager()
sm.add_widget(SearchScreen(name='SearchScreen'))
sm.add_widget(ForecastScreen(name='ForecastScreen'))
class WeatherApp(App):
def build(self):
return sm
if __name__ == "__main__":
WeatherApp().run()
First, on_text_validate will only be called when you press enter if the TextInput has the multiline property to False, so set it.
On the other hand I see that you do not understand the difference between:
Foo:
and
<Foo>:
In the first case you are creating an instance of Foo (and there can only be one element of this type) and in the second you are implementing a component. When you call Builder.load_file() and having that first element without "<" ">" that instance is returned, that is, there is already a ScreenManager, but in your case you have created another with python code. The ScreenManager instantiated in the .kv already has the Screen where the texts are already linked, and in changes those of Python are not. And when you return the ScreenManager created in python without the linked elements you observe a correct behavior, nothing will be modified.
What you have to do is remove the ScreenManager from the .py and use the .kv:
*.py
class SearchScreen(Screen):
text = StringProperty('')
def change_text(self):
self.text = "show this text"
self.manager.current = "ForecastScreen"
class ForecastScreen(Screen):
label_text = StringProperty("")
sm = Builder.load_file('weather.kv')
class WeatherApp(App):
def build(self):
return sm
if __name__ == "__main__":
WeatherApp().run()
*.kv
ScreenManager:
id: screen_manager
SearchScreen:
id: search_screen
name: 'SearchScreen'
ForecastScreen:
id: forecast_screen
name: 'ForecastScreen'
label_text: search_screen.text
<SearchScreen>:
display: entry
FloatLayout:
TextInput:
id: entry
multiline: False # <----
on_text_validate:
root.change_text()
<ForecastScreen>:
BoxLayout:
FloatLayout:
Label:
text: root.label_text
I am using the Screen Manager to manage several different screens. One screen has two buttons that both lead to another screen, but depending on which button was pressed, I would like a label to display different text on the second screen. Here is my code:
.py
MY_GLOBAL = "test"
class ChooseProgScreen(Screen):
global MY_GLOBAL
def setTitle(self, newTitle):
MY_GLOBAL = newTitle
print(MY_GLOBAL)
class SwitchScreen(Screen):
global MY_GLOBAL
def getTitle(self):
return MY_GLOBAL
class ScreenManagement(ScreenManager):
pass
class MainApp(App):
def build(self):
presentation = Builder.load_file("kivy.kv")
return presentation
.kv
ScreenManagement:
transition: FadeTransition()
HomeScreen:
ChooseProgScreen:
SwitchScreen:
NewProgScreen:
<ChooseProgScreen>:
name: "chooseprog"
FloatLayout:
Button:
text: "test1"
on_release:
root.setTitle("test1")
app.root.current = "switch"
color: 1,1,1,1
font_size: 25
size_hint: 0.15,0.15
pos_hint: {"center_x":.1, "center_y":.9}
Button:
text: "test2"
on_release:
root.setTitle("test2")
app.root.current = "switch"
color: 1,1,1,1
font_size: 25
size_hint: 0.15,0.15
pos_hint: {"center_x":.3, "center_y":.9}
<SwitchScreen>:
name: "switch"
FloatLayout:
Label:
text: root.getTitle()
pos_hint: {"center_x":.1, "center_y":.1}
font_size: 25
In ChooseProgScreen in .kv, when the button is released, I call a method from the .py file that sets the global variable to a new screen and prints it. When you press one of the buttons, the print part works fine, and the global variable prints as the new string, but the SwitchScreen label still shows
"test"
and not
"test1" or "test2"
I think global variables is probably a terrible way of doing this, but I am at a loss for how else to do it using the Screen manager and the kivy language. If someone could help use global variables properly, or suggest a better way to do this, that would be greatly appreciated.
EDIT
Problem was that the screen only updated upon the first load. I added an update method to SwitchScreen:
def update(self):
self.ids.switchtitle.text = self.getTitle()
and updated the SwitchScreen in the .kv file:
<SwitchScreen>:
on_enter:
root.update()
Label:
id: switchtitle
text: root.getTitle()
pos_hint: {"center_x":.1, "center_y":.1}
font_size: 25
Solution
Define a class attribute, MY_GLOBAL of type StringProperty in your root widget, class ScreenManagement().
Update MY_GLOBAL in kv file.
Example
main.py
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang import Builder
from kivy.properties import StringProperty
class ChooseProgScreen(Screen):
pass
class SwitchScreen(Screen):
pass
class ScreenManagement(ScreenManager):
MY_GLOBAL = StringProperty('test')
class MainApp(App):
def build(self):
return Builder.load_file("kivy.kv")
if __name__ == "__main__":
MainApp().run()
kivy.kv
#:kivy 1.10.0
#:import FadeTransition kivy.uix.screenmanager.FadeTransition
ScreenManagement:
transition: FadeTransition()
ChooseProgScreen:
SwitchScreen:
<ChooseProgScreen>:
name: "chooseprog"
FloatLayout:
Button:
text: "test1"
on_release:
root.manager.MY_GLOBAL = self.text
root.manager.current = "switch"
color: 1,1,1,1
font_size: 25
size_hint: 0.15,0.15
pos_hint: {"center_x":.1, "center_y":.9}
Button:
text: "test2"
on_release:
root.manager.MY_GLOBAL = self.text
root.manager.current = "switch"
color: 1,1,1,1
font_size: 25
size_hint: 0.15,0.15
pos_hint: {"center_x":.3, "center_y":.9}
<SwitchScreen>:
name: "switch"
FloatLayout:
Label:
text: root.manager.MY_GLOBAL
pos_hint: {"center_x":.1, "center_y":.1}
font_size: 25
Output
class ChooseProgScreen(Screen):
def setTitle(self, newTitle):
global MY_GLOBAL
MY_GLOBAL = newTitle
print(MY_GLOBAL)
That should resolve your issue. MY_GLOBAL would technically be available via self.MY_GLOBAL, instead you assigned it to a new variable under the method but didn't assign it to the global variable object.