Python Kivy Screen manager with predefined Layout doesn't work - python

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!

Related

How to simulate user action on kivy widget ? (click on a button by example)

I want to simulate the user action and after looking in the doc, I saw the Clock.schedule_once(my_callback, 1).
.
Question: Is this the only way to do it ? I would have prefered something like my_button.press().
.
I want to press the button and then, it will call my_callback, I don't want to directly call my_callback.
(Because if I do this and my callback evolve, I lost the good reference)
PS: I saw that there was the possibility to use Clock.create_trigger(my_callback) but it didn't seems to be the good solution here.
from kivymd.app import MDApp
from kivy.lang import Builder
from kivy.clock import Clock
KV_APP = '''
BoxLayout:
Button:
id: my_button
text: "Click on me"
'''
class TestApp(MDApp):
def build(self):
self.screen = Builder.load_string(KV_APP)
self.screen.ids["my_button"].bind(on_press=self.on_button_click)
event = Clock.schedule_once(self.on_button_click, 1)
return self.screen
def on_button_click(self, instance):
print("hello there !")
if __name__ == '__main__':
TestApp().run()
You can create an Event and dispatch it. Here is an example:
from kivy.app import App
from kivy.clock import Clock
from kivy.input.providers.mouse import MouseMotionEvent
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
class MyBoxLayout(BoxLayout):
def mydispatchAnEvent(self, *args):
# create and dispatch a fake event
touch = MouseMotionEvent(None, 123, (123, 456)) # args are device, id, spos
touch.button = 'left'
touch.pos = (321, 654)
self.dispatch('on_touch_down', touch)
theRoot = Builder.load_string('''
MyBoxLayout:
orientation: 'vertical'
Label:
text: 'Button below should demonstrate receiving touch event'
Button:
text: 'Waiting for event'
on_press: print('touched')
''')
class EventDispatcherPlayApp(App):
def build(self):
Clock.schedule_once(theRoot.mydispatchAnEvent, 2)
return theRoot
EventDispatcherPlayApp().run()

Kivy app permanently crashes with "Segmentation fault (core dumped)"

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))

How can I print the position of the pointer every time the mouse is moved over a widget in kivy?

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()

update kivy label using screen manager

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()

Not displaying Time in label in kivy

I am kind of new to kivy and i am trying to display time in it
Here is the python code snippet(removed the unnecessary screens/parts for arbitrary reasons):
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
import time
from kivy.uix.label import Label
class MyScreen(Screen):
def update(self, *args):
self.timeb.text = time.asctime()
return time.asctime()
class MyApp(App):
def build(self):
x=MyScreen()
root = ScreenManager()
Clock.schedule_interval(x.update, 1)
root.add_widget(password(name='Screen0'))
root.add_widget(Correct(name='Screena'))
root.add_widget(MyScreen(name='Screen1'))
s=[x,root]
for i in s:
return i
if __name__ == '__main__':
MyApp().run()
.kv file(removed the unnecessary screens/parts for arbitrary reasons):
MyScreen:
<MyScreen>: #
timeb:time_box
BoxLayout:
orientation: "horizontal"
pos_hint: {'top':1}
height: "40dp"
size_hint_y: None
Label:
id:time_box
text:root.update()
size_hint_x: 6
font_size:30
font_name:"Roboto-Light.ttf"
As you can see in the code i have added a few screens but the My screen is the first to come up also if i change the
s=[x,root]
for i in s:
return i
to just
return root
then the time doesn't update itself.
Could anyone help?
Thanks!
Maybe you forgot to type something, most probably your kv is coded wrong. Why is there that hanging MyScreen: and what is it assigned to? You need a main rule somewhere, but I see none. I assigned your MyScreen: to <MyApp>: as a normal return MyScreen() or return some_scrmanager would do and it works.
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.clock import Clock
import time
from kivy.uix.label import Label
from kivy.lang import Builder
Builder.load_string('''
<MyApp>:
MyScreen:
<MyScreen>: #
timeb:time_box
BoxLayout:
orientation: "horizontal"
pos_hint: {'top':1}
height: "40dp"
size_hint_y: None
Label:
id:time_box
text:root.update()
size_hint_x: 6
font_size:30
''')
class MyScreen(Screen):
def update(self, *args):
self.timeb.text = time.asctime()
return time.asctime()
class MyApp(App):
def build(self):
x=MyScreen()
root = ScreenManager()
Clock.schedule_interval(x.update, 1)
root.add_widget(MyScreen(name='Screen1'))
s=[x,root]
for i in s:
return i
if __name__ == '__main__':
MyApp().run()
And the same kv, but more reasonable python code would looke like this:
<ScreenMgr>:
MyScreen:
<MyScreen>:
...
class MyScreen(Screen):
def __init__(self, **kw):
super(MyScreen, self).__init__(**kw)
Clock.schedule_interval(self.update, 1)
def update(self, *args):
self.timeb.text = time.asctime()
return time.asctime()
class ScreenMgr(ScreenManager):
pass
class MyApp(App):
def build(self):
x=MyScreen()
root = ScreenMgr()
root.add_widget(MyScreen(name='Screen1'))
return root
if __name__ == '__main__':
MyApp().run()
Because there's no need to call update() from Screen class in the building function and even outside a class where you want to use it presuming it'll run forever(Do you plan to stop time?).

Categories