I am trying to collect data from several screens and display on last 'result' screen. For that I want to store values of different widgets in dict property and display them on the screen. The minimum demonstration:
.py:
from kivy.app import App
from collections import defaultdict
from kivy.uix.widget import Widget
from kivy.properties import (
NumericProperty, ReferenceListProperty, ObjectProperty, DictProperty
)
from kivy.vector import Vector
from kivy.clock import Clock
from kivy.uix.screenmanager import ScreenManager, Screen
import pickle
import kivy
class ResultScreen(Screen):
results = DictProperty({})
def get_values(self):
self.results = {1 : -1, 2 : -2}
class PongScreen(Screen):
pass
class TestApp(App):
def build(self):
sm = ScreenManager()
# print(f'------------{sm.manager}--------------')
sm.add_widget(PongScreen(name='menu'))
sm.add_widget(ResultScreen(name='result'))
return sm
TestApp().run()
.kv:
#:kivy 1.0.9
#:import Factory kivy.factory.Factory
<ResultScreen>:
on_enter: self.get_values()
BoxLayout:
orientation : 'vertical'
BoxLayout:
orientation : 'horizontal'
BoxLayout:
orientation : 'vertical'
Label:
text: str(root.results.keys())
<PongScreen>:
BoxLayout:
Button:
text: 'To results'
on_press: root.manager.current = 'result'
Button:
text: 'Exit'
on_press: app.stop()
I expect to get [1, 2] on the text of Label on ResultScreen. However I am getting empty list. As far as I understand it happens because initialization takes place before execution of get_values function. How can I display desired values?
Related
I am writing a program that requires a label, a text_input, and a checkbox to be aligned on screen in that order. They should span the width of the screen and be at the same y level.
I have written this code in my .kv file which uses identical pos_hint values, but only the text_input box moves
Below is my .kv code:
<Union>:
BoxLayout:
orientation: "vertical"
BoxLayout:
orientation: "horizontal"
pos_hint_y: {"top":.85}
Label:
text: 'Session Length'
pos_hint: {"x":.3, "top":.85}
TextInput:
id: input
text: "test"
pos_hint: {"x":.5, "top":.85}
size_hint: 0.3, 0.05
background_disabled_normal: ""
disabled: not checkbox.active
on_text: app.return_text()
CheckBox:
id: checkbox
pos_hint: {"x":.7, "top":.85}
and here is my main.py
from kivy.core.window import Window
from kivy.lang import Builder
from kivy.uix.button import Button
from kivy.uix.dropdown import DropDown
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.gridlayout import GridLayout
from kivy.uix.screenmanager import Screen, ScreenManager
Window.size = (309, 555)
class MenuScreen(Screen):
pass
class SetupScreen(Screen):
pass
class drop_content(DropDown):
pass
class UnionScreen(Screen):
class Checkbox_Setup(FloatLayout):
pass
class TheApp(App):
def build(self):
# Create the screen manager
sm = ScreenManager()
sm.add_widget(MenuScreen(name='menu'))
sm.add_widget(SetupScreen(name='setup'))
sm.add_widget(UnionScreen(name='union'))
return sm
def return_text(self):
text = self.root.get_screen('union').ids.input.text
print(text)
def main():
Builder.load_file('menu.kv')
app = TheApp()
app.run()
if __name__ == '__main__':
main()
Finally, here is the output I am currently getting, with the label and checkbox in line but underneath the text_input which is in the right place.
I am relatively new to kivy so any help would be greatly appreciated. Thanks in advance!
i cannot remove widget using the screen with kivy python, i dont know why it just does not do anything
the code was suppose to remove textinput with id:name on the first screen but it just does not do anything and no error message.
here is all of the code it is tested on python 3.7.4, kivy 1.11.1 on window.
module_media_player.py
import kivy
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.properties import ObjectProperty
from kivy.lang import Builder
from kivy.core.audio import SoundLoader
from kivy.uix.videoplayer import VideoPlayer
from kivy.uix.screenmanager import ScreenManager,Screen
class Player(Screen):
def press(self):
self.ids.name.text = 'nice'
def remove(self):
self.remove_widget(self.ids.name)
class MediaPlayer(Screen):
pass
class WindowManager(ScreenManager):
pass
kv = Builder.load_file('my.kv')
class GoodApp(App):
def build(self):
return kv
if __name__ == '__main__':
GoodApp().run()
my.kv
WindowManager:
Player:
MediaPlayer:
<Player>:
name:"player"
BoxLayout:
orientation:"vertical"
size:root.width,root.height
cols:2
TextInput:
id:name
multiline:False
text:"first"
font_size:12
size_hint_y:0.3
Button:
text:"click me"
on_press:root.remove()
Button:
text:"next window"
font_size:14
size_hint_y:0.7
on_release:
app.root.current = "mediaplayer"
root.manager.transition.direction = "left"
<MediaPlayer>:
name:"mediaplayer"
BoxLayout:
orientation:"vertical"
size:root.width,root.height
Label:
text:"second"
font_size:12
Button:
text:"previous window"
font_size:14
on_release:
app.root.current = "player"
root.manager.transition.direction = "right"
Your code:
def remove(self):
self.remove_widget(self.ids.name)
is trying to remove the TextInput from the Player Screen, but that Textinput is not a child of Player. It is actually a child of the BoxLayout. You can fix this by modifying your remove() method:
def remove(self):
textinput = self.ids.name
textinput.parent.remove_widget(textinput) # remove widget from its parent
I'm new to kivy and python so my code isn't perfect.
I'm trying to make a program with 2 screens, a first screen where there is a label with a text that is not defined and that can change and a second screen that keeps the same text as the first screen.
I've been searching for a week and I tried to make a global variable that I edit and that becomes the text of the second label but it doesn't work.
I also tried with String. property () or object. property () but I didn't get any more results and I didn't really understand how to use it.
Any help would be welcome <3
(sorry for my english)
Here is a part of my code that I have simplified as much as possible:
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.label import Label
from kivy.core.window import Window
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
Builder.load_string("""
<MenuScreen>:
label_wid : ratio
FloatLayout:
Button:
text: "options"
pos: 270, 240
size_hint: .30, .10
background_color: 0,1,0,0.75
on_press: root.manager.current = 'settings'
Label:
id:ratio
text: ""
pos: 0,90
font_size:30
color:1,0,0,1
<SettingsScreen>:
label_wid2 : ratio
FloatLayout:
Label:
id:ratio
text: str(root.texte2())
pos: 0,90
font_size:30
color:1,0,0,1
""")
u=""
class MenuScreen(Screen):
def texte(self):
global u
u= self.label_wid.text = 'exemple'
pass
class SettingsScreen(Screen):
def texte2(self):
self.label_wid2.text=u
pass
class Quizz(App):
def build(self):
self.title = 'Quizz'
Window.clearcolor = (0, 1, 1, 0.25)
return sm
sm = ScreenManager()
sm.add_widget(MenuScreen(name='menu'))
sm.add_widget(SettingsScreen(name='settings'))
if __name__ == '__main__':
Quizz().run()
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.label import Label
from kivy.core.window import Window
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
Builder.load_string("""
<MenuScreen>:
label_wid : ratio
FloatLayout:
Button:
text: "options"
pos: 270, 240
size_hint: .30, .10
background_color: 0,1,0,0.75
on_press: root.manager.current = 'settings'
Label:
id:ratio
text: ""
pos: 0,90
font_size:30
color:1,0,0,1
<SettingsScreen>:
label_wid : ratio
FloatLayout:
Label:
id:ratio
text: root.texte2()
pos: 0,90
font_size:30
color:1,0,0,1
""")
u=""
class MenuScreen(Screen):
def texte(self):
global u
u= self.label_wid.text = 'exemple'
pass
class SettingsScreen(Screen):
def texte2(self, text):
u
pass
class Quizz(App):
def build(self):
self.title = 'Quizz'
Window.clearcolor = (0, 1, 1, 0.25)
return sm
sm = ScreenManager()
sm.add_widget(MenuScreen(name='menu'))
sm.add_widget(SettingsScreen(name='settings'))
if __name__ == '__main__':
Quizz().run() ```
Using a StringProperty in the ScreenManager and another in SettingsScreen provide a source of text for both Labels in the different Screens. And changing the StringProperty can change both Labels:
from kivy.app import App
from kivy.clock import Clock
from kivy.properties import StringProperty
from kivy.core.window import Window
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
class MenuScreen(Screen):
pass
class SettingsScreen(Screen):
# this is the text to be used when the first Label text is not '1'
theOtherText = StringProperty('Not One')
class MyScreenManager(ScreenManager):
# This is the text that both Labels may display
theText = StringProperty('0')
kv = """
MyScreenManager:
MenuScreen:
name: 'menu'
SettingsScreen:
name: 'settings'
<MenuScreen>:
label_wid : ratio
FloatLayout:
Button:
text: "options"
pos: 270, 240
size_hint: .30, .10
background_color: 0,1,0,0.75
on_press: root.manager.current = 'settings'
Label:
id:ratio
# Use theText Property from MyScreenManager
text: root.manager.theText
pos: 0,90
font_size:30
color:1,0,0,1
<SettingsScreen>:
label_wid : ratio
FloatLayout:
Label:
id:ratio
# Use theText Property from MyScreenManager
text: '1' if root.manager.theText == '1' else root.theOtherText
pos: 0,90
font_size:30
color:1,0,0,1
"""
class Quizz(App):
def build(self):
self.title = 'Quizz'
Window.clearcolor = (0, 1, 1, 0.25)
Clock.schedule_once(self.change_text, 4)
return Builder.load_string(kv)
def change_text(self, dt):
print('changing text')
self.root.theText = '1'
if __name__ == '__main__':
Quizz().run()
Building the MyScreenManager in the kv eliminates the need to check if the manager attribute of the Screens is set. That simplifies the logic in the kv. Then the line in kv:
text: '1' if root.manager.theText == '1' else root.theOtherText
sets the text of the second Label to 1 if the first Label text is 1, otherwise it is set to the value of theOtherText in the SettingsScreen.
I have added a Clock.schedule_once() just for testing.
I asked a question regarding updating label text (Kivy change label text with python) - it was answered and was working but I've been trying for hours trying to adapt my running code to ScreenManager so I can have multiple screens. I've cut down the code to the basic stuff I can't get working.
ScreenManager code breaks the solution, I know what the issue is I just can't resolve it. The code is working, text and time updating in the shell but not the labels, I couldn't add return self.first_screen to the Kivy def build so its not binding to the_time: _id_lbl_time - Any help/pointers would be really appreciated.
from kivy.app import App
from kivy.base import runTouchApp
from kivy.lang import Builder
from kivy.properties import ListProperty, StringProperty, ObjectProperty
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.screenmanager import ScreenManager, Screen, WipeTransition, FadeTransition
from kivy.uix.anchorlayout import AnchorLayout
from kivy.clock import Clock
import time
from datetime import datetime, timedelta
class FirstScreen(Screen):
def update_time(self, sec):
MyTime = time.strftime("%H:%M:%S")
print MyTime # to test update_time is called etc
self.the_time.text = MyTime #<---- self.the_time isn't working
class SecondScreen(Screen):
def update_text(self, sec):
MyText = 'New Text'
print MyText # to test update_text is called etc
self.the_set.text = MyText #<---- self.the_set isn't working
class MyScreenManager(ScreenManager):
pass
Builder.load_string('''
<FirstScreen>:
name: 'first'
the_time: _id_lbl_time
BoxLayout:
orientation: 'vertical'
Label
id: _id_lbl_time
text: 'Clock'
font_size: 30
BoxLayout:
Button:
text: 'New Here'
font_size: 20
size: 200, 50
size_hint: None, None
on_release: app.root.current = 'second'
<SecondScreen>:
name: 'second'
the_set: _id_lbl_set
BoxLayout:
orientation: 'vertical'
Label:
id: _id_lbl_set
text: 'New Here'
font_size: 30
BoxLayout:
Button:
text: 'Main'
font_size: 20
size: 200, 50
size_hint: None, None
on_release: app.root.current = 'first'
''')
class ScreenManagerApp(App):
def build(self):
sm = ScreenManager()
sm.add_widget(FirstScreen(name='first'))
sm.add_widget(SecondScreen(name='second'))
self.first_screen = FirstScreen()
self.sec_screen = SecondScreen()
return sm #<---- I can't return self.first_screen etc here?
def on_start(self):
Clock.schedule_interval(self.first_screen.update_time, 1) # 1 second
Clock.schedule_interval(self.sec_screen.update_text, 2)
ScreenManagerApp().run()
I can't return self.first_screen etc here?
No. There has to be a ScreenManager on top of the Screen widgets, otherwise it's just a RelativeLayout as is its definition in the source code.
You experienced a really common mistake by beginners. You used one instance FirstScreen() and SecondScreen() for ScreenManager(those are visible) and then you created another instances with:
self.first_screen = FirstScreen()
self.sec_screen = SecondScreen()
which you then used for Clock:
Clock.schedule_interval(self.first_screen.update_time, 1)
Clock.schedule_interval(self.sec_screen.update_text, 2)
and that means text properties in instances that actually aren't added anywhere as widgets were used for updating. The instances exist, so no error to trigger except the visual one → you don't see the values, because you used wrong instance.
Correct build():
def build(self):
sm = ScreenManager()
self.first_screen = FirstScreen(name='first')
self.sec_screen = SecondScreen(name='second')
sm.add_widget(self.first_screen)
sm.add_widget(self.sec_screen)
return sm
I'm semi-OK with Python but brand new to Kivy, I know my problem is referencing the label ID but I can't seem to solve it and searching doesn't seem to give me what I need.
I'm trying to get a label to display the current time, so I know I have the right framework in terms of updating etc but I'm sure its all down to referencing the label ID somehow and its that I'm struggling with?
The following code runs fine, displays the labels etc until I try to update the label_text.text at which point i get an error: AttributeError: 'float' object has no attribute 'lbl_time'. I've tried str(time.strftime("%H:%M:%S")) but that doesn't solve it.
from kivy.app import App
from kivy.base import runTouchApp
from kivy.lang import Builder
from kivy.properties import ListProperty
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import ObjectProperty
from kivy.uix.screenmanager import ScreenManager, Screen, WipeTransition, FadeTransition
from kivy.uix.anchorlayout import AnchorLayout
from kivy.properties import ObjectProperty, StringProperty
from kivy.clock import Clock
import time
from datetime import datetime
class MainScreen(Screen):
def update_time(self):
lbl_time = ObjectProperty()
MyTime = time.strftime("%H:%M:%S")
self.lbl_time.text = MyTime
class DetailScreen(Screen):
pass
class SettingsScreen(Screen):
pass
class MyScreenManager(ScreenManager):
pass
root_widget = Builder.load_string('''
MyScreenManager:
MainScreen:
DetailScreen:
SettingsScreen:
<MainScreen>:
name: 'main'
BoxLayout:
orientation: 'vertical'
Label:
id: lbl_time
text: 'Time'
font_size: 60
Label:
text: 'Main2'
font_size: 30
GridLayout:
cols: 2
Label:
text: 'Bottom'
font_size: 30
Label:
text: 'Bottom1'
font_size: 30
<DetailScreen>:
name: 'details'
<SettingsScreen>:
name: 'settings'
''')
class ScreenManagerApp(App):
def build(self):
return root_widget
def on_start(self):
Clock.schedule_interval(MainScreen.update_time, 1)
ScreenManagerApp().run()
This was more of a Python problem rather than a Kivy one. You were calling the update_time of the class MainScreen class, not of the object/instance of the MainScreen. Basically, you would need to save a reference to the object (self.main_screen) in the build method, and then use it in the on_start.
class ScreenManagerApp(App):
def build(self):
self.main_screen = MainScreen()
return self.main_screen
def on_start(self):
Clock.schedule_interval(self.main_screen.update_time, 1)
Also you cannot access id outside of the kv language, i.e. in the python. You have to reference the id by adding a property, e.g. the_time:
<MainScreen>:
name: 'main'
the_time: _id_lbl_time
BoxLayout:
orientation: 'vertical'
Label:
id: _id_lbl_time
A minor problem is that the update_time() receives to parameters. Also, there were quite a few weird things in your code, so I couldn't run the code. I fixed all the above in the code below:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen
from kivy.properties import ObjectProperty, StringProperty
from kivy.clock import Clock
import time
from datetime import datetime
Builder.load_string('''
<MainScreen>:
name: 'main'
the_time: _id_lbl_time
BoxLayout:
orientation: 'vertical'
Label:
id: _id_lbl_time
text: 'Time'
font_size: 60
''')
class MainScreen(Screen):
def update_time(self, sec):
MyTime = time.strftime("%H:%M:%S")
self.the_time.text = MyTime
class ScreenManagerApp(App):
def build(self):
self.main_screen = MainScreen()
return self.main_screen
def on_start(self):
Clock.schedule_interval(self.main_screen.update_time, 1)
ScreenManagerApp().run()