I have a two screen kivy app and I want to pass a variable from one screen to a second screen using python.
python file
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.properties import StringProperty,ObjectProperty
from kivy.uix.label import Label
class MenuScreen(Screen):
label1=StringProperty()
label2=StringProperty()
def __init__(self,**kwargs):
super(MenuScreen, self).__init__(**kwargs)
self.label1="hello"
self.label2="world"
def change_text(self):
lbl1=self.label1+ " and "
lbl2= "A beautiful "+self.label2
chg=SettingsScreen()
chg.UpdateSettings(lbl1,lbl2)
# HERE: something like
class SettingsScreen(Screen):
label3=StringProperty()
label4=StringProperty()
def __init__(self,**kwargs):
super(SettingsScreen, self).__init__(**kwargs)
#some default texts
self.label3="Nothing"
self.label4="Nothing"
def UpdateSettings(self,lbl1,lbl2):
print(lbl1,lbl2)
self.label3=lbl1
self.label4=lbl2
class TestScreenManager(ScreenManager):
menu_screen=ObjectProperty(None)
settings_screen=ObjectProperty(None)
class TestApp(App):
def build(self):
return TestScreenManager()
TestApp().run()
kvfile
#: import ScreenManager kivy.uix.screenmanager.ScreenManager
#: import Screen kivy.uix.screenmanager.ScreenManager
#: import SettingsScreen screen
<TestScreenManager>:
id: screen_manager
menu_screen: menu_screen
settings_screen: settings_screen
MenuScreen:
id: menu_screen
name: 'menu'
manager: screen_manager
SettingsScreen:
id: settings_screen
name: 'settings_screen'
manager: screen_manager
<MenuScreen>:
name: 'MenuScreen'
BoxLayout:
Label:
text:root.label1
Label:
text:root.label2
Button:
text: 'Goto nn'
size_hint_y:0.2
on_press:
root.manager.current = 'settings_screen'
root.change_text()
<SettingsScreen>:
#name: 'SettingsScreen'
label_id: label_field
BoxLayout:
Label:
text:root.label3
Label:
text:root.label4
Label:
id: label_field
text: "some text"
It runs without an error but it is not changing the variables of the second screen. For debugging I added print(lbl1,lbl2) inside UpdateSetting functions and it prints out passing lbl1 and lbl2 variables but its not updating the labels.
When you do chg=SettingsScreen(), you are creating a new instance of the SettingsScreen class and you modify the labels of that instance. You need to access the object used by your ScreenManager, not create a new one.
You can use the manager property of your current Screen to get the instance reference of the other screen:
def change_text(self):
lbl1=self.label1 + " and "
lbl2= "A beautiful "+ self.label2
chg = self.manager.settings_screen #<<<<<<<<<<<<<<
chg.UpdateSettings(lbl1,lbl2)
Related
I want to make a variable that can be modified by one screen, then the variable can change the text of different screen.
Here is the python file
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen,ScreenManager
from kivy.properties import NumericProperty
import random
value = NumericProperty(0)
class MainWindow(Screen):
def vval(self, root):
root.value = 1
def vvval(self, root):
root.value = 2
class SecondWindow(Screen):
pass
class WindowManager(ScreenManager):
pass
kv = Builder.load_file("testing.kv")
class testing(App):
def build(self):
return kv
if __name__ == "__main__":
testing().run()
and here is the kivy file
WindowManager:
MainWindow:
SecondWindow:
<MainWindow>:
name: "main"
GridLayout:
size: root.width, root.height
rows: 2
Button:
text: "print 1"
on_release:
root.vval(root)
app.root.current = "second"
Button:
text: "print 2"
on_release:
root.vvval(root)
app.root.current = "second"
<SecondWindow>:
name: "second"
Label:
text: "successfully printed ", root.value
What I expected to happens is when I click one of the button in the the MainWindow, the variable, which I named it "value" will be modified to 1 or 2 depending on what button i click, then the screen changed to the SecondWindow and display the text "successfully printed x", the value of x is depends to the "value" variable.
I'm still new on kivy, if there is some error or ambiguity, I am sorry. Please share me your knowledge about this, it will be appreciated.
Generally, one would do this with a StringProperty object. The code below shows a working example, although I did take liberties in restructuring some things about your code. I admit I had some challenges with .bind of the StringProperty which I ended up resolving by building the ScreenManager object in the Python code instead of the .kv lines.
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.properties import NumericProperty, StringProperty
value = NumericProperty(0)
Builder.load_string('''
<MainWindow>
name: "main"
GridLayout:
size: root.width, root.height
rows: 2
Button:
text: "print 1"
on_release:
# arguments can be passed, including a 'self' of the button object
app.kv_button("1", "second", self)
# app.root.current = "second"
Button:
text: "print 2"
on_release:
# arguments can be passed, including a 'self' of the button object
app.kv_button("2", "second", self)
# app.root.current = "second"
<SecondWindow>
name: "second"
Label:
# maps to a StringProperty, root. would be the widget, this is app. which is the App()
text: app.result_text
''')
class MainWindow(Screen):
pass
class SecondWindow(Screen):
value: str
pass
class WindowManager(ScreenManager):
pass
# Builder.load_file("testing.kv")
class testing(App):
result_text = StringProperty()
def kv_button(self, _value: str, next_screen: str, *args):
print(f"{self} {_value} {args}")
self.result_text = _value
# change the screen using the WindowManager object that has been kept as a reference
self._main.current=next_screen
def __init__(self, **kwargs):
super().__init__(**kwargs)
self._main = WindowManager()
self._main.add_widget(MainWindow())
self._main.add_widget(SecondWindow())
def build(self):
return self._main
if __name__ == "__main__":
testing().run()
I have tried to minimize the example as much as possible but it seems empty and the problem is not exactly the same after.
I think i misunderstand things about class variables and instance variables, but Kivy doesn't help me on this : we create a lot of class but never instantiate them !
Of course I want the "Screenx instance has overwritten question" message appears and I just see "Not overwritten".
calendrier.py
from kivy.properties import StringProperty
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.clock import Clock
class Menu(Screen):
pass
class CalculGenerique(Screen):
question = StringProperty()
def __init__(self, **kwargs):
super(CalculGenerique, self).__init__(**kwargs)
Clock.schedule_once(self.late_init, 0)
def late_init(self, *largs):
self.renew_question()
def renew_question(self):
self.question = "Not overwritten"
class Screen1(CalculGenerique):
def renew_question(self):
self.question = "Screen1 instance has overwritten question"
class Screen2(CalculGenerique):
def renew_question(self):
self.question = "Screen2 instance has overwritten question"
class MyScreenManager(ScreenManager):
pass
class CalendarApp(App):
def build(self):
root_widget = Builder.load_file("calendrier.kv")
return root_widget
if __name__ == "__main__":
app = CalendarApp()
app.run()
calendrier.kv
#:import App kivy.app.App
MyScreenManager:
Menu:
CalculGenerique:
name: 'Screen1'
CalculGenerique:
name: 'Screen2'
<Menu>:
name: 'Menu'
BoxLayout:
orientation: 'vertical'
Button:
text: 'Screen1'
on_press : app.root.current = 'Screen1'
Button:
text: 'Screen2'
on_press : app.root.current = 'Screen2'
Button:
text: 'Quitter l app'
on_press : App.get_running_app().stop()
<CalculGenerique#Screen>:
BoxLayout:
orientation: 'vertical'
Button:
text: 'Back to Menu'
on_press : app.root.current = 'Menu'
Label:
text: root.question
Your 'kv' rule:
MyScreenManager:
Menu:
CalculGenerique:
name: 'Screen1'
CalculGenerique:
name: 'Screen2'
creates two instances of the CalculGenerique class, but no instances of Screen1 or Screen2. I think you just need to change that rule to:
MyScreenManager:
Menu:
Screen1:
name: 'Screen1'
Screen2:
name: 'Screen2'
The name property of a Screen is just used to identify the Screen, and does not specify the class.
I have some buttons, if I press the button - then the screen changes and on the new screen there is a label that shows the text that was on the button that has been pressed.
It does not work when everything looks right.
Python
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen, FadeTransition
from kivy.lang import Builder
from kivy.uix.button import Button
class ScreenOne(Screen):
pass
class ScreenTwo(Screen):
def on_pre_enter(self, *args):
btn = Button(text = "word is here", on_release =self.pressedFunction)
self.ids.container.add_widget(btn)
btn1 = Button(text = "another word is here", on_release =self.pressedFunction)
self.ids.container.add_widget(btn1)
def pressedFunction(self, instance, *args):
self.manager.current= "three"
screenThree = ScreenThree()
text = str(instance.text)
screenThree.changing_label(text)
class ScreenThree(Screen):
def changing_label(self, text):
self.ids.my_label.text = text
class ScreenManagement(ScreenManager):
pass
presentation = Builder.load_file("example.kv")
class MainApp(App):
def build(self):
return presentation
if __name__ == "__main__":
MainApp().run()
Kivy
ScreenManagement:
ScreenOne:
ScreenTwo:
ScreenThree:
<ScreenOne>:
BoxLayout:
Button:
text: "press me"
on_release: app.root.current = "two"
<ScreenTwo>:
name: "two"
BoxLayout:
id: container
<ScreenThree>:
name: "three"
BoxLayout:
id: labelContainer
Label:
text: ""
Output
File "example.py", line 28, in changing_label
self.ids.my_label.text = text
File "kivy\properties.pyx", line 841, in kivy.properties.ObservableDict.__getattr__
AttributeError: 'super' object has no attribute '__getattr__'
"my_label" does not exist in your .kv file, add an id on the label where you wanna make these changes.
<ScreenThree>:
name: "three"
BoxLayout:
id: labelContainer
Label:
id: my_label
text: ""
Problems
1. AttributeError - my_label
In kv file, it is missing id: my_label.
2. Switching Screen before populating my_label.text
Populating of my_label.text was done after screen is switched, self.manager.current = "three".
3. ScreenThree - Blank/Black Window
ScreenThree instantiated twice. The first instance is created in kv file by ScreenThree: (this is equivalent to ScreenThree() in Python Code). The second instance is created in Python code, screenThree = ScreenThree().
The populating of the my_label.text is in the second instance/object and not in the first instance. Therefore, the ScreenThree is blank/black window because app is using the view as per kv file i.e. the first instance of ScreenThree.
Note:
If you add id() function, it will shows different memory locations for the screens.
def pressedFunction(self, instance, *args):
self.manager.current = "three"
self.debug()
screenThree = ScreenThree()
print("screenThree={0}, id(screenThree)={1}".format(screenThree, id(screenThree)))
self.debug()
text = str(instance.text)
screenThree.changing_label(text)
def debug(self):
print("\ndebug:")
print("\tself.manager.screen_names=", self.manager.screen_names)
print("\tself.manager.screens=", self.manager.screens)
for x in self.manager.screens:
print("\t\tscreen={0}, id(screen)={1}".format(x, id(x)))
Solution
kv file
Add id: screen_two under ScreenTwo:. This will be used to reference class attributes / methods in ScreenTwo.
Replace app.root.current = "two" with root.manager.current = "two" because every screen has by default a property manager
Add id: my_label under Label:
Python code
Add import statement, from kivy.properties import StringProperty
Declare StringProperty inside class ScreenTwo(), text = StringProperty('') so there is no need to pass parameters around and also encapsulation.
In pressedFunction() method, replace text = str(instance.text) with self.text = str(instance.text)
Populate text before switching screen
In class ScreenThree(), rename changing_label() method to on_pre_enter() method, and remove text from the argument list.
Replace self.ids.my_label.text = text' withself.ids.my_label.text = self.manager.ids.screen_two.text`
Optional: Reduce memory used, replace return presentation with return Builder.load_file("example.kv") and remove presentation = Builder.load_file("example.kv")
Example
main.py
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang import Builder
from kivy.uix.button import Button
from kivy.properties import StringProperty
class ScreenOne(Screen):
pass
class ScreenTwo(Screen):
text = StringProperty('')
def on_pre_enter(self, *args):
print("\nScreenTwo.on_pre_enter:")
btn = Button(text = "word is here", on_release =self.pressedFunction)
self.ids.container.add_widget(btn)
btn1 = Button(text = "another word is here", on_release =self.pressedFunction)
self.ids.container.add_widget(btn1)
def pressedFunction(self, instance, *args):
self.text = str(instance.text) # populate before switching screen
self.manager.current = "three" # switch screen
class ScreenThree(Screen):
def on_pre_enter(self, *args):
self.ids.my_label.text = self.manager.ids.screen_two.text
class ScreenManagement(ScreenManager):
pass
class MainApp(App):
def build(self):
return Builder.load_file("example.kv")
if __name__ == "__main__":
MainApp().run()
example.kv
#:kivy 1.11.0
ScreenManagement:
ScreenOne:
ScreenTwo:
id: screen_two
ScreenThree:
<ScreenOne>:
BoxLayout:
Button:
text: "press me"
on_release: root.manager.current = "two" # every screen has a default property manager
<ScreenTwo>:
name: "two"
BoxLayout:
id: container
<ScreenThree>:
name: "three"
BoxLayout:
id: labelContainer
Label:
id: my_label
text: ""
Output
I have been started to work in kivy recently. The thing what I am doing now is, i have a blank page with a button, when I click that button it navigates to an user input screen. It works fine, but the content comes in a very small input boxes and text as in the picture.
My question is that I want it bigger and centred.
Here is my code:
In python:
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.gridlayout import GridLayout
from kivy.uix.textinput import TextInput
from kivy.uix.screenmanager import ScreenManager, Screen, FadeTransition
from kivy.uix.widget import Widget
from kivy.lang import Builder
class LoginScreen(GridLayout):
def __init__(self, **kwargs):
super(LoginScreen, self).__init__(**kwargs)
self.cols = 2
self.add_widget(Label(text="Username:"))
self.username = TextInput(multiline=False)
self.add_widget(self.username)
self.add_widget(Label(text="Password:"))
self.password = TextInput(multiline=False, password=True)
self.add_widget(self.password)
self.add_widget(Label(text="Two Factor Auth:"))
self.tfa = TextInput(multiline=False)
self.add_widget(self.tfa)
class MainScreen(Screen):
pass
class AnotherScreen(Screen):
pass
class ScreenManagement(ScreenManager):
pass
presentation = Builder.load_file("screen.kv")
class SimpleKivy(App):
def build(self):
return presentation
if __name__ == "__main__":
SimpleKivy().run()
In kv:
#: import FadeTransition kivy.uix.screenmanager.FadeTransition
ScreenManagement:
transition: FadeTransition()
MainScreen:
AnotherScreen:
<MainScreen>:
name: "main"
Button:
color: 0,1,0,1
font_size: 25
size_hint: 0.3,0.2
text: "Click"
on_release: app.root.current = "other"
pos_hint: {"right":1, "top":1}
<AnotherScreen>:
name: "other"
GridLayout:
LoginScreen
In your screen.kv, you have the LoginScreen inside a GridLayout. Since the LoginSCreen is a GridLayout, you do not need that extra GridLayout.
Just change:
<AnotherScreen>:
name: "other"
GridLayout:
LoginScreen
to:
<AnotherScreen>:
name: "other"
LoginScreen:
Using the Kivy Screen Manager, I create two Screens. Whilst being in screen 1, i want to change a label in screen two. I highlight the problematic area in my code:
my test.ky:
#: import ScreenManager kivy.uix.screenmanager.ScreenManager
#: import Screen kivy.uix.screenmanager.ScreenManager
#: import SettingsScreen screen
ScreenManager:
MenuScreen:
SettingsScreen:
<MenuScreen>:
name: 'MenuScreen'
BoxLayout:
Button:
text: 'Goto nn'
on_press:
root.manager.current = 'SettingsScreen'
root.change_text()
<SettingsScreen>:
name: 'SettingsScreen'
label_id: label_field
BoxLayout:
Label:
id: label_field
text: "to_be_changed"
and my screen.py
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.screenmanager import ScreenManager, Screen
class MenuScreen(Screen):
def change_text(self):
pass
# HERE: something like
# root.SettingsScreen.label_field.text = 'new text'
class SettingsScreen(Screen):
pass
class TestApp(App):
pass
TestApp().run()
Any help is greatly appreciated!
Thanks, Nico
How about this:
When you press the button on MenuScreen, it sets an attribute on itself containing the text you want to put in the SettingsScreen Label. Then the MenuScreen is assigned an id value in the kv file, which is used to reference this attribute. Example:
main.py
class MenuScreen(Screen):
text = StringProperty('')
def change_text(self):
self.text = "The text you want to set"
self.manager.current = "SettingsScreen"
class SettingsScreen(Screen):
label_text = StringProperty('')
kv file
ScreenManager:
id: screen_manager
MenuScreen:
id: menu_screen
name: 'MenuScreen'
manager: screen_manager
SettingsScreen:
name: 'SettingsScreen'
manager: screen_manager
label_text: menu_screen.text
<MenuScreen>:
BoxLayout:
Button:
text: 'Goto nn'
on_press:
root.change_text()
<SettingsScreen>:
BoxLayout:
Label:
text: root.label_text
As you can see, I set the names and id of the screens under ScreenManager itself in the kv file, as this is what I would usually do to make this work.