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()
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 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?
So I wanted to read data from my Arduino through serial port, and update the data I read to a label text to display. It is working when I only have simple code to just read and update, but when I add in the ScreenManager and Screen, it stops updating the text.
Eventually I will need to have different animation according to the data I received, this is more on a testing if this function works
Thanks in Advance!
Here's my entire code
import os
os.environ['KIVY_GL_BACKEND'] ='gl'
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.widget import Widget
from kivy.properties import (NumericProperty, StringProperty, ReferenceListProperty, ObjectProperty, ListProperty)
from kivy.clock import Clock
from kivy.vector import Vector
from kivy.core.text import LabelBase
from kivy.uix.screenmanager import ScreenManager, Screen, FadeTransition
LabelBase.register(name='Sans',fn_regular="Sansation-Regular.ttf")
import serial
kivy = Builder.load_string("""
#:import FadeTransition kivy.uix.screenmanager.FadeTransition
<MyManager>:
transition: FadeTransition()
MainScreen:
OperationScreen:
<MainScreen>:
name: 'main'
Label:
text: 'Welcome'
font_size: 40
on_touch_up : app.root.current = 'operation'
Label:
text: 'dafault'
font_size: 20
pos: -200,-100
id: data_label
<OperationScreen>:
name: 'operation'
Label:
text: 'Youre in'
font_size: 40
""")
class OperationScreen(Screen):
pass
class MainScreen(Screen):
def __init__(self,**kwargs):
super(MainScreen,self).__init__(**kwargs)
def Read(self,dt):
Clock.unschedule(self.Read)
data = arduino.readline()
if data != '':
self.ids.data_label.text = data
Clock.schedule_once(self.Read)
pass
class MyManager(ScreenManager):
pass
class mainApp(App):
Main = MainScreen()
def build(self):
Clock.schedule_once(self.Main.Read)
return MyManager()
if __name__ == '__main__':
try:
arduino = serial.Serial('/dev/ttyACM0', 9600, timeout=1)
except:
print("failed to connect")
mainApp().run()
I expect the label with text 'default' to be changing accordingly but it just froze with the 'default'
Problem
When your app runs, there are two instances of class MainScreen. One was instantiated in kv file. The other one was instantiated manually, Main = MainScreen() in class mainApp.
The scheduling of method Read() is in the instance created manually, Main = MainScreen() and there is no modal view associated with this one.
Solution
In kv file, add id: main_screen for MainScreen:
Remove Main = MainScreen() in class mainApp
Implement a constructor for class MyManager()
Move the scheduling from class mainApp into the constructor of class MyManager()
In your case, it is better to use Clock.create_trigger() instead of Clock.schedule_once()
The correct way to cancel a Clock event is either event.cancel() or Clock.unschedule(event)
Example
main.py
import os
os.environ['KIVY_GL_BACKEND'] = 'gl'
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.widget import Widget
from kivy.properties import (NumericProperty, StringProperty, ReferenceListProperty, ObjectProperty, ListProperty)
from kivy.clock import Clock
from kivy.vector import Vector
from kivy.core.text import LabelBase
from kivy.uix.screenmanager import ScreenManager, Screen, FadeTransition
# LabelBase.register(name='Sans', fn_regular="Sansation-Regular.ttf")
import serial
kivy = Builder.load_string("""
#:import FadeTransition kivy.uix.screenmanager.FadeTransition
<MyManager>:
transition: FadeTransition()
MainScreen:
id: main_screen
OperationScreen:
<MainScreen>:
name: 'main'
Label:
text: 'Welcome'
font_size: 40
on_touch_up : app.root.current = 'operation'
Label:
text: 'dafault'
font_size: 20
pos: -200,-100
id: data_label
<OperationScreen>:
name: 'operation'
Label:
text: 'Youre in'
font_size: 40
""")
class OperationScreen(Screen):
pass
class MainScreen(Screen):
def Read(self, dt):
data = str(dt)
# data = arduino.readline()
if data != '':
self.ids.data_label.text = data
self.manager.event_trigger()
else:
self.manager.event_trigger.cancel()
class MyManager(ScreenManager):
event_trigger = ObjectProperty(None)
def __init__(self, **kwargs):
super(MyManager, self).__init__(**kwargs)
self.event_trigger = Clock.create_trigger(self.ids.main_screen.Read)
self.event_trigger()
class mainApp(App):
def build(self):
return MyManager()
if __name__ == '__main__':
# try:
# arduino = serial.Serial('/dev/ttyACM0', 9600, timeout=1)
# except:
# print("failed to connect")
mainApp().run()
Output
I am working on one simple Kivy GUI application. What i am trying to do is that i am want to access one object 'obj_random' of class 'CL_Random' from second screen(in which it is not created). I just don't know how to access declared in one screen to different screen.
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
#from kivy.uix.boxlayout import BoxLayout
#from kivy.uix.popup import Popup
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang import Builder
from kivy.properties import ObjectProperty
class CL_FirstWindow(Screen):
def change_screen(self):
obj_random = CL_Random()
self.parent.current = 'second_window'
class CL_SecondWindow(Screen):
scond_prop = ObjectProperty(None)
def change_screens(self):
self.parent.current = 'first_window'
class ScreenManagement(ScreenManager):
pass
class CL_Random:
def __init__(self):
self.x = 5
presentation = Builder.load_file("rough2.kv")
class app(App):
def build(self):
return presentation
obj_app = app()
obj_app.run()
rough2.kv
ScreenManagement:
CL_FirstWindow:
CL_SecondWindow:
<CL_FirstWindow>:
id: first_window_id
name: 'first_window'
FloatLayout:
Button:
text: 'Button1'
size_hint: 0.3, 0.12
pos_hint: {'x':0.1,'y':0.6}
on_release: root.change_screen()
<CL_SecondWindow>:
id: second_window_id
name: 'second_window'
FloatLayout:
Button:
text: 'Button2'
size_hint: 0.3, 0.12
pos_hint: {'x':0.1,'y':0.6}
on_release: root.change_screens()
Label:
id: second_label_id
text: 'SECOND SCREEN'
pos_hint: {'x': 0.20, 'y':0.8}
size_hint: 1, 0.1
font_size: 35
halign: 'left'
valign: 'middle'
text_size: self.size
Thnks
To access a variable that is declared within a class the first thing is that this variable must be an attribute of the class so that the scope is greater, but as in your case the variable is local and it will be eliminated when it finishes executing the function.
On the other the solution is based on accessing the other Screen via the ScreenManager, and having the object access to the attribute for it we use the get_screen() method:
CL_SecondWindow --> ScreenManager --> CL_FirstWindow --> obj_random
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang import Builder
from kivy.properties import ObjectProperty
class CL_FirstWindow(Screen):
def change_screen(self):
self.obj_random = CL_Random()
self.manager.current = 'second_window'
class CL_SecondWindow(Screen):
scond_prop = ObjectProperty(None)
def change_screens(self):
first_screen = self.manager.get_screen('first_window')
print(first_screen.obj_random.x)
self.parent.current = 'first_window'
class ScreenManagement(ScreenManager):
pass
class CL_Random:
def __init__(self):
self.x = 5
presentation = Builder.load_file("rough2.kv")
class app(App):
def build(self):
return presentation
obj_app = app()
obj_app.run()
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