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

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

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

Python Kivy Screen manager with predefined Layout doesn't work

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!

Changing kivy widget properties within Class

I'm having difficulty figuring out how to change the text of a label within a kivy widget. For simplicity, I have a label set to 0 and I would like to change the text to read 30 in this example. However, I get the following error.
AttributeError: 'super' object has no attribute 'getattr'
I understand that I'm probably not properly targeting that widget and I am hoping someone can please explain how to specifically reference the text of this label (self.ids.mainel1temp.stuff_r.text = '30') to update (with more detail than fixing the code)
#!/usr/bin/kivy
import kivy
from random import random
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.gridlayout import GridLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.uix.switch import Switch
from kivy.uix.label import Label
from kivy.config import Config
Config.set('graphics', 'width', '800')
Config.set('graphics', 'height', '480')
Builder.load_string("""
<Menuscreen>:
#Handling the gesture event.
ScreenManager:
id: manager
Screen:
id: main_screen
name:'main_screen'
stuff_r: mainel1temp
FloatLayout:
Label:
id: mainel1temp
size: self.texture_size
text:'0'
size_hint: None, None
text_size: 75,75
pos: 295,308
font_size:'20sp'
halign: 'center'
""")
class Thermostuff(Screen):
stuff_r = ObjectProperty(None)
def starttherm(self):
Clock.schedule_interval((self.read_temp), 1)
def read_temp(self, dt):
self.ids.mainel1temp.stuff_r.text = '30'
Thermrun = Thermostuff()
Thermrun.starttherm()
class MenuScreen(Screen):
pass
sm = ScreenManager()
menu_screen = MenuScreen(name='menu')
sm.add_widget(menu_screen)
class TestApp(App):
def build(self):
return sm
if __name__ == '__main__':
TestApp().run()
You do a couple of things wrong here.
You dont want to put a ScreenManager inside a Screen
Only one ScreenManager is needed.
Then you can start the Clock in the __init__ of the Thermostuff(Screen)
Or if you want it to initiate on_enter you need to overrite that. In that case you might want to check somehow, if its allready started, so you wont have multiple clocks running.
Then when you create an ObjectProperty you dont need self.ids, because you allready created that property. So self.stuff_r is now the label.
I rewrote your example a bit, to demonstrate this.
Try this:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen
from kivy.properties import ObjectProperty
from kivy.clock import Clock
sm = """
#Handling the gesture event.
ScreenManager:
id: manager
MenuScreen:
Button:
text: "Go to Thermostuff"
on_release:
root.current = "main_screen"
Thermostuff:
name:'main_screen'
stuff_r: mainel1temp
FloatLayout:
Label:
id: mainel1temp
size: self.texture_size
text:'0'
size_hint: None, None
text_size: 75,75
pos: 295,308
font_size:'20sp'
halign: 'center'
"""
class Thermostuff(Screen):
stuff_r = ObjectProperty(None)
test_temp = 0
def __init__(self,**kwargs):
super(Thermostuff,self).__init__(**kwargs)
Clock.schedule_interval((self.read_temp), 1)
def read_temp(self, dt):
self.test_temp += 1
self.stuff_r.text = str(self.test_temp)
class MenuScreen(Screen):
pass
class TestApp(App):
def build(self):
return Builder.load_string(sm)
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()

Nothing is appearing in my TabbedPanel in Kivy

I am trying to code up a program using the TabbedPanel class. I am going to use ScreenManager and navbuttons to navigate different screens within each tab. Problem is nothing is showing up. At first at least the NavLayout was showing up but now all that appears are the tabs with their respective names. How do I get both Navigation buttons and Screen to show up in the tab.
ex1.kv
<MainPanel>:
id: mp
usb_tab_wid: usb_tab
remote_tab_wid: remote_tab
sd_tab_wid: sd_tab
size_hint: 1,1
tab_width: 65
do_default_tab: False
tab_pos: 'top_right'
TabbedPanelItem:
id: usb_tab
text: 'Usb'
BoxLayout:
orientation: 'vertical'
UsbScreenManager:
NavLayout:
TabbedPanelItem:
id: remote_tab
text: 'Remote'
TabbedPanelItem:
id: sd_tab
text: 'SD'
<NavLayout>:
id: grid1
spacing: 5,5
rows: 1
size_hint: 1, .2
LeftButton:
id: left_arrow_button
Image:
source: 'left_arrow_iconb.png'
center: left_arrow_button.center
size: grid1.width*.8, grid1.height*.8
BackButton:
id: back_button
Image:
source: 'back_iconb.png'
center: back_button.center
size: grid1.width*.8, grid1.height*.8
RightButton:
id: right_arrow_button
Image:
source: 'right_arrow_iconb.png'
center: right_arrow_button.center
size: grid1.width*.8, grid1.height*.8
usb.kv
<UsbScreenManager>:
id: usb_screen_manager
H2HScreen:
name: 'h2h'
V9validationScreen:
name: 'v9'
<H2HScreen>:
name: 'h2h'
BoxLayout:
TestIcon:
source: 'h2h.png'
<V9validationScreen>:
name: 'v9'
BoxLayout:
Label:
text: 'V9 tests'
ex1.py
import kivy
kivy.require('1.9.0')
import subprocess
import csv
from datetime import datetime
import usb.core
import usb.util
import threading
import time
from functools import partial
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.gridlayout import GridLayout
from kivy.uix.behaviors import ButtonBehavior
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.properties import ObjectProperty, StringProperty, NumericProperty
from kivy.clock import Clock
from kivy.uix.image import Image
from kivy.uix.screenmanager import ScreenManager, Screen, FadeTransition
from kivy.lang import Builder
from kivy.app import App
from kivy.uix.tabbedpanel import TabbedPanel
from kivy.uix.boxlayout import BoxLayout
class MainPanel(TabbedPanel):
usb_tab_wid = ObjectProperty()
remote_tab_wid = ObjectProperty()
sd_tab_wid = ObjectProperty()
usbman = ObjectProperty(None)
def switch_to(self, header):
pass
def print_wd(self):
for widget in self.walk():
print("[]->{}".format(widget, widget.id))
def on_current_tab(self, b, c):
print(self.current_tab)
#return super(MainPanel, self).on_current_tab()
#print(usb_tab_wid)
pass
class TestIcon(ButtonBehavior, Image):
pass
class UsbScreenManager(ScreenManager):
pass
class H2HScreen(Screen):
pass
class V9validationScreen(Screen):
pass
class RemoteScreenManager(ScreenManager):
pass
class SDcardScreenManager(ScreenManager):
pass
class NavLayout(GridLayout):
pass
class LeftButton(Button):
sm = ObjectProperty()
mp = MainPanel()
ct= mp.current_tab
#print (ct)
pass
class RightButton(Button):
sm = ObjectProperty()
panel = ObjectProperty()
pass
class BackButton(Button):
sm = ObjectProperty()
panel = ObjectProperty()
def active_panel():
#if current tab is __ then load corresponding screen manager
pass
pass
class Sam1App(App):
def build(self):
return MainPanel()
Builder.load_file("usb.kv")
if __name__ == '__main__':
Sam1App().run()
I found I overrode the switch_to method for TabbedPanel but did not define anything. So the TabbedPanel did not know what to switch to so it displayed nothing.

Categories