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
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 trying to have a a screen inside a tabbed panels "space".
When i try to switch between screens, i get an AttributeError:
"AttributeError: 'TabbedTest' object has no attribute 'manager'"
I'm not sure how to add the screenmanager to the tabbedpanels attributes.
I'm unsure of how i create a reference to the tabbedpanel in the screenmanager or how i add the property.
Pythonfile
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.tabbedpanel import TabbedPanel
from kivy.uix.floatlayout import FloatLayout
class TabbedTest(TabbedPanel):
pass
class MainScreen(Screen):
pass
class Firstscreen(Screen):
pass
class myapp(App):
def build(self):
root = FloatLayout()
self.tabbedtest = TabbedTest()
root.add_widget(self.tabbedtest)
# Create the screen manager
self.sm = ScreenManager()
self.sm.add_widget(MainScreen(name='main'))
self.sm.add_widget(Firstscreen(name='first'))
root.add_widget(self.sm)
return root
if __name__ == '__main__':
myapp().run()
my.kv
<TabbedTest>
do_default_tab: False
TabbedPanelItem:
text:"test"
MainScreen:
Button:
text: 'first'
on_press: root.manager.current = 'first' #Problemarea
<Firstscreen>:
BoxLayout:
Label:
text:"first"
Button:
text: 'Back to main'
on_press: root.manager.current = 'main'
If you want the Screens inside the TabbedPanelItem, then you should put the ScreenManager inside the TabbedPanelItem. Here is a modified version of your kv that does that:
<TabbedTest>
do_default_tab: False
TabbedPanelItem:
text:"test"
ScreenManager:
id: manager # for easy access
MainScreen:
name: 'main'
Button:
text: 'first'
on_press: manager.current = 'first' # use manager id
Firstscreen:
name: 'first'
<Firstscreen>:
BoxLayout:
Label:
text:"first"
Button:
text: 'Back to main'
on_press: root.manager.current = 'main'
Then your build() method can be simply:
def build(self):
return TabbedTest()
I am learning Kivy, but I do not kown how to change a screen and running a funtion at the same time.
Where should I declare my funtion so the button have access to the code and can run the function?
from kivymd.app import MDApp
from kivy.lang.builder import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
screen_helper = """
ScreenManager:
MenuScreen:
FunctionScreen:
<MenuScreen>:
name: 'menu'
MDRectangleFlatButton:
text: 'Function'
pos_hint: {'center_x':0.5,'center_y':0.5}
on_press: root.manager.current = 'function screen'
<FunctionScreen>:
name: 'function screen'
MDRectangleFlatButton:
text: 'Back'
pos_hint: {'center_x':0.5,'center_y':0.1}
on_press: root.manager.current = 'menu'
"""
class MenuScreen(Screen):
pass
class FunctionScreen(Screen):
pass
# Create the screen manager
sm = ScreenManager()
sm.add_widget(MenuScreen(name='menu'))
sm.add_widget(FunctionScreen(name='function'))
class DemoApp(MDApp):
def build(self):
screen = Builder.load_string(screen_helper)
return screen
# def funtion(self):
# do stuff and then go to menu screen
DemoApp().run()
Should I try maybe, add the on_opress atribute in the build function?
Can you guys help me?
There are several convenient places to place the function(). One is in the MenuScreen, and in that case, it would be referenced in the kv files as:
root.function()
Another convenient place is in the DemoApp, and in that case, the reference would be:
app.function()
So, here is a version of your code tht puts the function() in the App:
from kivymd.app import MDApp
from kivy.lang.builder import Builder
from kivy.uix.screenmanager import Screen
screen_helper = """
ScreenManager:
MenuScreen:
FunctionScreen:
<MenuScreen>:
name: 'menu'
MDRectangleFlatButton:
text: 'Function'
pos_hint: {'center_x':0.5,'center_y':0.5}
on_press:
root.manager.current = 'function screen'
app.function()
<FunctionScreen>:
name: 'function screen'
MDRectangleFlatButton:
text: 'Back'
pos_hint: {'center_x':0.5,'center_y':0.1}
on_press: root.manager.current = 'menu'
"""
class MenuScreen(Screen):
pass
class FunctionScreen(Screen):
pass
class DemoApp(MDApp):
def build(self):
sm = Builder.load_string(screen_helper)
return sm
def function(self):
# do stuff and then go to menu screen
print('in function')
DemoApp().run()
Note that the lines of your code that built a ScreenManager have been deleted as they are unnecessary.
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 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()