Im trying to make a multi screen app and I want a date/time label on all of them.
I made a clock event that update the text value each second.
The clock event is working because on python console I can see it updating, but the label is not refreshing, it just works the first time.
How can I refresh the screen?
The main.py file:
import kivy
import time
from kivy.app import App
from kivy.uix.label import Label
from kivy.config import Config
from kivy.core.window import Window
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang import Builder
from kivy.clock import Clock
kivy.require("1.9.1")
Window.size = (1280,1024)
Config.set('graphics', 'fullscreen', '0')
class TimeLabel(Label):
def __init__(self, **kwargs):
super(TimeLabel, self).__init__(**kwargs)
self.text= str(time.asctime())
def update(self, *args):
self.text = str(time.asctime())
print self.text
class LoginScreen(Screen):
pass
class MainScreen(Screen):
pass
class ScreenManagement(ScreenManager):
pass
class My1App(App):
def build(self):
my1sm = Builder.load_file("main.kv")
crudeclock = TimeLabel()
Clock.schedule_interval(crudeclock.update, 1)
return my1sm
if __name__ == '__main__':
My1App().run()
and the main.kv file:
#: kivy 1.9
#: import ScreenManager kivy.uix.screenmanager.ScreenManager
#: import Screen kivy.uix.screenmanager.ScreenManager
#: import FadeTransition kivy.uix.screenmanager.FadeTransition
ScreenManagement:
transition: FadeTransition()
MainScreen:
LoginScreen:
<MainScreen>:
name: 'main'
size_hint: (1, 1)
FloatLayout:
Image:
source: './background.png'
size: 1280,1024
TimeLabel:
<LoginScreen>:
name: 'loginS'
FloatLayout:
Image:
source: './Login.png'
size: 1280,1024
TimeLabel:
<TimeLabel>:
x:-545
y:-475
color: (0,0,0,1)
You need to set the schedule_interval when the label is instantiated.
import kivy
import time
from kivy.app import App
from kivy.uix.label import Label
from kivy.config import Config
from kivy.core.window import Window
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang import Builder
from kivy.clock import Clock
kivy.require("1.9.1")
Window.size = (600,600)
Config.set('graphics', 'fullscreen', '0')
class TimeLabel(Label):
def __init__(self, **kwargs):
super(TimeLabel, self).__init__(**kwargs)
self.text= str(time.asctime())
Clock.schedule_interval(self.update,1)
def update(self, *args):
self.text = str(time.asctime())
print self.text
class LoginScreen(Screen):
pass
class MainScreen(Screen):
pass
class ScreenManagement(ScreenManager):
pass
class My1App(App):
def build(self):
my1sm = Builder.load_file("main.kv")
crudeclock = TimeLabel()
return my1sm
if __name__ == '__main__':
My1App().run()
Related
I don't understand what I'm doing wrong. I'm learning Kivy but when I want to load the new screen it tells me that it doesn't exist.
edited : [ how to work with screen in kivy correctly ]
Error :
raise ScreenManagerException('No Screen with name "%s".' % name)
kivy.uix.screenmanager.ScreenManagerException: No Screen with name "startup_screen".
main.py
from kivymd.app import MDApp
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.core.window import Window
from startup_screen import StartupScreen
class MyApp(MDApp):
def build(self):
Window.size = (300, 500)
sm = ScreenManager()
self.title = "App Test"
return sm
def on_start(self):
self.root.current = "startup_screen"
MyApp().run()
startup_screen.py
from kivymd.app import MDApp
from kivy.uix.screenmanager import Screen
from kivy.lang import Builder
kv = """
<StartupScreen>
name: 'startup_screen'
canvas:
Color:
rgb: (.93, .93, .93)
Rectangle:
size: self.size
pos: self.pos
Image:
id: img_logo
source: "resources/img/logo.png"
size_hint: (.8,.8)
pos_hint: {'center_x': .5, 'center_y': .6}
"""
class StartupScreen(Screen):
Builder.load_string(kv)
def __init__(self):
super(StartupScreen, self).__init__()
def on_enter(self, *args):
from kivy.clock import Clock
Clock.schedule_once(lambda dt: self.load_navigation(), 5)
#staticmethod
def load_navigation():
from kivymd.theming import ThemeManager
app = MDApp.get_running_app()
app.theme_cls = ThemeManager()
app.theme_cls.primary_hue = "600"
app.theme_cls.accent_palette = "Teal"
app.theme_cls.theme_style = "Light"
from navigation import NavigationScreen
app.root.current = "navigation"
The only problem with your code is that you are not creating a StartupScreen. Your kv string defines a rule for how a StartupScreen should be built, but it does not build one. A simple fix is to just add a line in your build() method to create the StartupScreen:
def build(self):
Window.size = (300, 500)
sm = ScreenManager()
sm.add_widget(StartupScreen()) # createe the StartupScreen
self.title = "App Test"
return sm
I am having troubles getting my code to work. The progressbar should move on when a button is pressed and jump to 0 when it is released. With the kivy built in functions on_touch_down and on_touch_up it works but with my own check input funtion some arguments are missing. How do I pass them in correctly?
Thanks in advance!
import kivy
from kivy.lang import Builder
from kivy.app import App
from kivy.properties import ObjectProperty
from kivy.properties import StringProperty
from kivy.uix.label import Label
from kivy.uix.widget import Widget
from kivy.uix.gridlayout import GridLayout
from kivy.uix.progressbar import ProgressBar
from kivy.clock import Clock
import RPi.GPIO as GPIO
buttonPin1 = 14
pbVal = 0
GPIO.setmode(GPIO.BCM)
GPIO.setup(buttonPin1, GPIO.IN, pull_up_down=GPIO.PUD_UP)
Builder.load_string('''
<MainGrid>
GridLayout:
cols:1
size: root.width, root.height
GridLayout:
cols:2
Label:
text: "Tipp"
Label:
text: "Vid"
GridLayout:
cols:3
ProgressBar:
id: pb
min: 0
max: 100
''')
class MainGrid(GridLayout):
def __init__(self, **kwargs):
super(MainGrid, self).__init__(**kwargs)
def update_bar(self,*args):
global pbVal
pbVal = pbVal+1
self.ids.pb.value=pbVal
def on_touch_down(self, touch):
print('ButtonPressed')
self.event=Clock.schedule_interval(self.update_bar, 1.0/10.0)
def on_touch_up(self, touch):
global pbVal
print('ButtonReleased')
self.event.cancel()
pbVal = 0
self.ids.pb.value=pbVal
def checkInput(self,*args):
global pbVal
if GPIO.input(buttonPin1) == True:
print("not pressed")
self.event.cancel()
pbVal = 0
self.ids.pb.value=pbVal
else:
print("pressed")
self.event=Clock.schedule_interval(self.update_bar, 1.0/10.0)
Clock.schedule_interval(checkInput, 1.0/10.0)
class MyApp(App):
def build(self):
return MainGrid()
if __name__ == '__main__':
MyApp().run()
Someone found out the solution for my problem in the kivy forum already!
The clock command must be called from within the init section, the the checkInputs functions receives all the necessary attributes:
def __init__(self, **kwargs):
super(MainGrid, self).__init__(**kwargs)
Clock.schedule_interval(checkInput, 1.0/10.0)
The following code permanently crashes with "Segmentation fault (core dumped)".
It seems that setting the text in the TextInput makes the application crash.
Can anyone confirm the error?
What does the error message mean?
I use threading, because I wanted the user to perform some actions on the screen and the application should wait for the result before going on.
Edit:
If there was something like a "goto" statement (o.k. sorry for that ;-)) I would like to jump to the line with comment # further commands and proceed there with the main flow.
import sys
import threading
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.button import Button
from kivy.uix.textinput import TextInput
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.properties import StringProperty, ObjectProperty, BooleanProperty
Builder.load_string("""
<MyWordRecorderScreen>:
id: TheWordRecorderScreen
my_text_input: TheTextInput
my_record_button: TheRecordButton
BoxLayout:
orientation: 'vertical'
spacing: 10
MyTextInput:
id: TheTextInput
my_screen: TheWordRecorderScreen
# removing the following statement makes the app work but useless
text: "Example"
RecordButton:
id: TheRecordButton
text: "Record"
my_screen: TheWordRecorderScreen
size_hint: (0.2, 0.15)
""")
class MyTextInput(TextInput):
pass
class RecordButton(Button):
my_screen = ObjectProperty(None)
class MyWordRecorderScreen(Screen):
my_record_button = ObjectProperty(None)
my_text_input = ObjectProperty(None)
class MyScreenSequence():
def __init__(self,my_screenmanager):
self.sm=my_screenmanager
def do_job(self):
self.CurrentScreen = MyWordRecorderScreen()
self.sm.switch_to(self.CurrentScreen)
def ApplicationFlow(MyScreenManager, *largs):
sm = MyScreenManager
screen_terminated=threading.Condition()
screen_terminated.acquire()
# subroutine will use screen_terminated.notify_all() once result is available
MSS=MyScreenSequence(my_screenmanager=sm)
t2 = threading.Thread(target=MSS.do_job)
t2.daemon = False
t2.start()
screen_terminated.wait()
screen_terminated.release()
# get result from subroutine
# futher commands
return
class TestApp(App):
def build(self):
self.sm = ScreenManager()
return self.sm
def on_start(self):
t1 = threading.Thread(target=ApplicationFlow, args=[self.sm])
t1.daemon = False
t1.start()
def main(args):
TestApp().run()
return 0
if __name__ == '__main__':
sys.exit(main(sys.argv))
I find it simpler to use Thread.Event(). Here is my version of your code that uses Kivy.clock:
import sys
import threading
import time
from kivy.app import App
from kivy.clock import Clock
from kivy.lang import Builder
from kivy.uix.button import Button
from kivy.uix.textinput import TextInput
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.properties import StringProperty, ObjectProperty, BooleanProperty
from functools import partial
Builder.load_string("""
<MyWordRecorderScreen>:
id: TheWordRecorderScreen
my_text_input: TheTextInput
my_record_button: TheRecordButton
BoxLayout:
orientation: 'vertical'
spacing: 10
MyTextInput:
id: TheTextInput
my_screen: TheWordRecorderScreen
# removing the following statement makes the app work but useless
text: "Example"
RecordButton:
id: TheRecordButton
text: "Record"
my_screen: TheWordRecorderScreen
size_hint: (0.2, 0.15)
""")
class MyTextInput(TextInput):
pass
class RecordButton(Button):
my_screen = ObjectProperty(None)
class MyWordRecorderScreen(Screen):
my_record_button = ObjectProperty(None)
my_text_input = ObjectProperty(None)
class MyScreenSequence():
def __init__(self,my_screenmanager):
self.sm=my_screenmanager
def do_job(self, theEvent, dt):
self.CurrentScreen = MyWordRecorderScreen()
self.sm.switch_to(self.CurrentScreen)
theEvent.set()
def ApplicationFlow(MyScreenManager, *largs):
sm = MyScreenManager
screen_terminated=threading.Event()
# subroutine will use screen_terminated.set() once result is available
MSS=MyScreenSequence(my_screenmanager=sm)
Clock.schedule_once(partial(MSS.do_job, screen_terminated), 0)
screen_terminated.wait()
# get result from subroutine
# futher commands
return
class TestApp(App):
def build(self):
self.sm = ScreenManager()
return self.sm
def on_start(self):
t1 = threading.Thread(target=ApplicationFlow, args=[self.sm])
t1.daemon = False
t1.start()
def main(args):
TestApp().run()
return 0
if __name__ == '__main__':
sys.exit(main(sys.argv))
I want to print out the position of the pointer (mouse) everytime it passes over a widget. Actually to be more specific I'd like to print the current position of the mouse everytime it moves. Is that at all possible?
Right now all I've managed to do is:
Print the position of the mouse whenever the mouse is clicked
(in two different ways)
Print the position of the widget when it's clicked
I'm sure it's easier than I think, thanks in advance.
from kivy.core.window import Window
from kivy.uix.widget import Widget
from kivy.app import App
from kivy.uix.button import Button
from kivy.lang import Builder
from kivy.base import runTouchApp
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.input.motionevent import MotionEvent
class MainScreen(Screen):
def __init__(self, **kwargs):
super(MainScreen, self).__init__(**kwargs)
def on_mouse_pos(self, pos):
if Window.mouse_pos == self.ids.example_button:
print("You've made it")
# With out this if statement it does print the position every time
# That the mouse is moved. But can I print "You've made it" every time
# that the mouse is moved over the position of a widget?
print(pos)
def test_print(self):
print(str(self.ids.example_button.pos))
Window.bind(mouse_pos = on_mouse_pos)
class MyScreenManager(Screen):
pass
root_widget = Builder.load_string('''
MyScreenManager:
MainScreen:
<MainScreen>:
name: 'Example'
Button:
id: example_button
text: 'Print position of widget when mouse touches it'
size_hint: 1, .05
pos_hint: {'x': 0, 'y': .5}
on_release: root.test_print()
''')
class TestApp(App):
def build(self):
self.title = 'Example Application'
return root_widget
if __name__ == '__main__':
TestApp().run()
A possible solution is to use mouse_pos:
from kivy.core.window import Window
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
class MainScreen(Screen):
def on_mouse_pos(self, pos):
print(pos)
Window.bind(mouse_pos = on_mouse_pos)
class MyScreenManager(Screen):
pass
root_widget = Builder.load_string('''
MyScreenManager:
MainScreen:
<MainScreen>:
name: 'Example'
Button:
id: example_button
text: 'I am button'
size_hint: 1, .05
pos_hint: {'x': 0, 'y': .5}
''')
class TestApp(App):
def build(self):
self.title = 'Example Application'
return root_widget
if __name__ == '__main__':
TestApp().run()
Update:
If you want to verify that the mouse point is inside a widget you must use collide_point.
from kivy.core.window import Window
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
class MainScreen(Screen):
def __init__(self, **kwargs):
super(MainScreen, self).__init__(**kwargs)
Window.bind(mouse_pos = self.on_mouse_pos)
def on_mouse_pos(self, instance, pos):
if self.ids.example_button.collide_point(*pos):
print("You've made it")
class MyScreenManager(Screen):
pass
root_widget = Builder.load_string('''
MyScreenManager:
MainScreen:
<MainScreen>:
name: 'Example'
Button:
id: example_button
text: 'I am button'
size_hint: 1, .05
pos_hint: {'x': 0, 'y': .5}
''')
class TestApp(App):
def build(self):
self.title = 'Example Application'
return root_widget
if __name__ == '__main__':
TestApp().run()
My Main class returns a instance of ScreenManager. This ScreenManager has a widget with a Screen class, and I want this Screen class to use a widget which is a Layout that I defined earlier.
When I execute the code, it only show a black screen with no more information. It should show a Button instead.
This is my file minimum.py:
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.app import App
class LayoutWithButton(BoxLayout):
def __init__(self, **kwargs):
super(LayoutWithButton, self).__init__(**kwargs)
class MainScreenApp(Screen):
def __init__(self, **kwargs):
super(MainScreenApp, self).__init__(**kwargs)
button_layout = LayoutWithButton()
self.add_widget(button_layout)
screen_manager = ScreenManager()
screen_manager.add_widget(MainScreenApp(name='main'))
class TestMainApp(App):
def build(self):
return screen_manager
if __name__ == '__main__':
TestMainApp().run()
And this is my file testmain.kv:
<LayoutWithButton>:
Button:
text: 'hello'
Even so, if I replace the line self.add_widget(button_layout) of the class MainScreenApp with the line self.add_widget(Button()) it works well.
What am I doing wrong?
The problem is that kv file is not loaded until TestMainApp is initialized. As you instanciate MainScreenApp before this, the rules defined within the kv have no effect.
A very simple solution is to move instanciation of MainScreenApp to App subclass:
class MainApp(App):
def build(self):
screen_manager = ScreenManager()
screen_manager.add_widget(MainScreenApp(name='main'))
return screen_manager
You can also force the kv load before instantiating:
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang.builder import Builder
from kivy.app import App
class LayoutWithButton(BoxLayout):
pass
class MainScreenApp(Screen):
def __init__(self, **kwargs):
super(MainScreenApp, self).__init__(**kwargs)
button_layout = LayoutWithButton()
self.add_widget(button_layout)
Builder.load_file("testmain.kv")
screen_manager = ScreenManager()
screen_manager.add_widget(MainScreenApp(name='main'))
class TestApp(App):
def build(self):
return screen_manager
if __name__ == '__main__':
TestApp().run()
Another option is do everything in your kv:
from kivy.uix.screenmanager import ScreenManager
from kivy.app import App
class RootWidget(ScreenManager):
pass
class MainTestApp(App):
def build(self):
return MainWindow()
if __name__ == '__main__':
MainTestApp().run()
maintest.kv:
<LayoutWithButton#BoxLayout>:
Button:
text: 'hello'
<MainScreenApp#Screen>:
LayoutWithButton:
<RootWidget>:
MainScreenApp:
name: "main"
From testmain.kv you should remove that < and > from the class name.
You only use it when you are styling it and will also be calling it many many times.
Example how your code should look like:
LayoutWithButton:
Button:
text: 'hello'
When to use < and >:
<LayoutWithButton>:
pos_hint: 0, .5
Button:
text: 'hello'
BoxLayout:
LayoutWithButton:
pos_hint: 0, .5
Good luck!