I have the following code in which I am trying to send time to an Arduino to be displayed on an OLED. the Arduino side works well, if commands are sent individually it will display. However, I want the time to be updated every second. With some tweaking I am able to get it to update ever 2 seconds or so, with sometimes showing two times at once. I tried using threading but I'm not sure if its correct or not.
Here is the python script:
import serial
import kivy
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.widget import Widget
from datetime import datetime, timedelta
import time
from kivy.clock import Clock
import threading
ard_connected = False
try:
ard = serial.Serial(
port='COM10',
baudrate = 9600
)
ard.flush()
ard_connected = True
except:
print("Arduino Not Connected")
Builder.load_file('Layout.kv')
class GUILayout(Widget):
def ShowTime(self, state):
threading.Timer(1, self.ShowTime).start()
now = datetime.now()
a = timedelta(seconds=1)
while state == 'down':
if (ard_connected):
current_time = now.strftime("%H:%M:%S")
ard.write(current_time.encode())
ard.flush()
now += a
if (ard_connected):
ard.write(" ".encode())
ard.flush()
class GUI(App):
def build(self):
updateClock = GUILayout()
Clock.schedule_interval(updateClock.ShowTime, 0.5)
return updateClock
if __name__ == '__main__':
GUI().run()
and the .kv file:
<GUILayout>
BoxLayout:
orientation: "vertical"
size: root.width, root.height
GridLayout:
cols: 2
ToggleButton:
text: "Time"
on_state: root.ShowTime(self.state)
backgrund_normal: ""
background_color: (150/255,150/255,150/255,1)
You have too many things starting the ShowTime() method:
Clock.schedule_interval(updateClock.ShowTime, 0.5)
and
on_state: root.ShowTime(self.state)
and
threading.Timer(1, self.ShowTime).start()
And each of those has the possibility to start an infinite loop (while state == 'down':), since the state variable that is passed into ShowTime() will never change. The loop started by clicking on the ToggleButton will run in the main thread, freezing your GUI.
I believe a better approach would be to just start/stop the ShowTime() method at one location. perhaps using the ToggleButton.
Try changing the kv to accomplish that:
ToggleButton:
text: "Time"
on_state: root.startShowTime(self.state) if self.state == 'down' else root.stopShowTime()
backgrund_normal: ""
background_color: (150/255,150/255,150/255,1)
and add/change GUILayout and GUI methods to support that:
class GUILayout(Widget):
def startShowTime(self, state):
self.clock_event = Clock.schedule_interval(self.ShowTime, 0.5)
def stopShowTime(self):
self.clock_event.cancel()
if (ard_connected):
ard.write(" ".encode())
ard.flush()
def ShowTime(self, dt):
if (ard_connected):
now = datetime.now()
current_time = now.strftime("%H:%M:%S")
ard.write(current_time.encode())
ard.flush()
class GUI(App):
def build(self):
updateClock = GUILayout()
return updateClock
Related
I'm having trouble with correctly calling functions from different classes.
I am making a simple game which calculates the score using the amount of time it takes to clear a level. There's a stopwatch running in the background and I want to add a pause button that popup menu, and a resume button inside this popup menu.
The problem is that when calling the pause function from within the popup menu, it will also be returned inside the popup, instead of inside the main widget.
Here is a simplified version of the code:
import kivy
from kivy.app import App
from kivy.lang import Builder
from kivy.properties import NumericProperty
from kivy.uix.widget import Widget
from kivy.uix.popup import Popup
from kivy.clock import Clock
root_widget = Builder.load_file('app.kv')
class ExampleWidget(Widget):
time = NumericProperty(0)
paused = False
stop = False
# Keeping time
def increment_time(self, interval):
self.time += .1
print(self.time) # To check if stopwatch is running or not
# Stop should mean that the stopwatch must reset when it starts again.
# When paused it should resume when it starts again
def stop_start_or_pause(self):
# stop stopwatch
if self.stop:
Clock.unschedule(self.increment_time)
print('Stopped')
# Make sure time is 0 when restarting
elif not self.stop and not self.paused:
# Keeping time
self.time = 0
Clock.schedule_interval(self.increment_time, .1)
# Pause stopwatch
elif self.paused:
Clock.unschedule(self.increment_time)
print("!!", self.time) # To make it easier to see if stopwatch actually resumes where it left off
print('unscheduled') # Just to confirm and to make it a bit easier to see
# resume stopwatch
elif not self.paused:
Clock.schedule_interval(self.increment_time, .1)
class PopupMenu(Popup):
example = ExampleWidget()
class MyApp(App):
ExampleWidget = ExampleWidget()
def build(self):
return ExampleWidget()
MyApp().run()
.kv file:
#:import Factory kivy.factory.Factory
<PopupMenu#Popup>
auto_dismiss: False
size_hint_y: .8
size_hint_x: .9
title: 'Pause'
example: app.ExampleWidget
BoxLayout:
Button:
text: 'resume'
on_press: root.example.paused = False
on_release: root.dismiss(); root.example.stop_start_or_pause()
size: self.size
<ExampleWidget>:
GridLayout:
col: 2
rows: 3
size: root.size
Button:
text: 'start'
size: self.size
on_press: root.stop = False; root.stop_start_or_pause()
Button:
text: 'stop'
size: self.size
on_press: root.stop = True; root.stop_start_or_pause()
Button:
text: 'Pause menu'
size: self.size
on_press: root.paused = True
on_release: Factory.PopupMenu().open(); root.stop_start_or_pause()
Label:
text: str(round(root.time))
size: self.size
I tried making a function and using Clock.schedule.interval() to keep checking if paused == True, but it keeps returning:
AttributeError: 'float' object has no attribute 'stopped'
This didn't seem like efficient solution anyways, so I didn't want to spend too much time on this function. I also tried to find 'stupid' mistakes (I.e. ',' instead of '.') but that was before I realised that the resume button returned a 'second' stopwatch instead of updating the one I actually wanted to use.
I hope that someone can help, and that my question is clear. English is not my first language so I sometimes have a hard time finding the best way to explain/ask questions.
Thank you in advance!
If I understand your question, the problem is with your MyApp class:
class MyApp(App):
ExampleWidget = ExampleWidget()
def build(self):
return ExampleWidget()
This code is creating two instances of ExampleWidget. One is returned in the build() method, and one is saved as the ExampleWidget attribute of MyApp. Now, when you use the ExampleWidget attribute of MyApp, you are not referencing the ExampleWidget that is the root of your GUI, so it has no effect on what appears on the screen. The fix is to just creat a single instance of ExampleWidget, like this:
class MyApp(App):
ExampleWidget = ExampleWidget()
def build(self):
return self.ExampleWidget
I'm trying to get a live graph working in my code.
I can get a live graph working when everything is within the same python file, though when I try to separate the graph class into its own python file, the graph doesn't update. I have no real indication of why the graph isn't updating, but I think I may be creating a bunch of LogGraph objects as opposed to adding points to the actual LogGraph which is in my.kv, but I am not sure how to not do that.
My actual code is a bit involved and confusing, so I mocked up an example code here which should be behaving exactly the same:
main.py
from math import sin
import kivy
from kivy_garden.graph import Graph, MeshLinePlot
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.uix.boxlayout import BoxLayout
from kivy.clock import Clock
from data import startdata
class MainWindow(Screen):
def pressrun(self):
self.ids.runlbl.text = 'Started'
startdata()
pass
class MyApp(App):
def build(self):
wm.add_widget(MainWindow())
return wm
class WindowManager(ScreenManager):
def __init__(self, **kwargs):
super(WindowManager, self).__init__(**kwargs)
wm = WindowManager()
kv = Builder.load_file("my.kv")
MyApp().run()
my.kv
#: import LogGraph graphs.LogGraph
<MainWindow>:
id: main
BoxLayout:
orientation: "vertical"
LogGraph:
BoxLayout:
orientation: "horizontal"
Button:
text: "Start Data Gen"
on_release:
root.pressrun()
Label:
id: runlbl
text: "Not Started"
graphs.py
from kivy_garden.graph import Graph, MeshLinePlot
class LogGraph(Graph):
def __init__(self, **kwargs):
super(LogGraph, self).__init__(**kwargs)
self.xlabel = 'X'
self.ylabel = 'Y'
self.x_ticks_major = 25
self.x_ticks_minor = 5
self.x_grid_label = True
self.y_ticks_major = 1
self.y_grid_label = True
self.xmin = 0
self.xmax = 100
self.ymin = 0.1
self.ymax = 10
self.ylog = True
self.x_grid = True
self.y_grid = True
self.plot = MeshLinePlot(color=[1, 0, 0, 1])
self.add_plot(self.plot)
self.plot.points = [(1,1)]
def update_xaxis(self,xmin = 0):
self.xmin = xmin
self.xmax = xmin + 10
def update_yaxis(self,ymin = 0):
self.ymin = ymin
self.ymax = ymin + 10
def update_points(self, point, *args):
self.plot.points.append([point,point])
# x axis resize
if point > self.xmax:
self.update_xaxis(self.xmax)
# y axis resize
if point > self.ymax:
self.update_yaxis(self.ymax)
data.py
from kivy.clock import Clock
from functools import partial
from graphs import LogGraph
class DataStore():
def __init__(self):
self.i = 1
self.dataarray = []
def start(self):
self.clock = Clock.schedule_interval(self.getData, 1/60)
def cancel(self):
self.clock.cancel()
def wait(self):
print('Waited!')
def getData(self):
i = self.i + 1/60
LogGraph.update_points(LogGraph(), i)
pass
def startdata():
ds = DataStore()
ds.start()
Three main problems with your code:
Your code kv = Builder.load_file("my.kv") is loading the my.kv file a second time. It will be loaded automatically because the file is named correctly for that to happen. You should eliminate that code.
Your scheduled calls to DataStore.getData() will not work because your DataStore instance is not saved anywhere, and so it gets garbage collected.
The getData() method of DataStore creates a new instance of LogGraph each time it runs, but does not use the instance of LogGraph that is in your GUI.
To fix these problems, start by adding to your kv to allow access:
#: import LogGraph graphs.LogGraph
<MainWindow>:
id: main
name: 'main' # added to enable access
BoxLayout:
orientation: "vertical"
LogGraph:
id: graph # added to enable access
BoxLayout:
orientation: "horizontal"
Button:
text: "Start Data Gen"
on_release:
root.pressrun()
Label:
id: runlbl
text: "Not Started"
Then in the startdata() method, add a return
def startdata():
ds = DataStore()
ds.start()
# return the DataStore instance so it can be saved
return ds
Then save the returned DataStore in the pressrun() method:
def pressrun(self):
self.ids.runlbl.text = 'Started'
self.dataStore = startdata()
And the getData() method must be modified to access the LogGraph that is in the GUI:
def getData(self, dt): # time interval argument is required
self.i += dt
# access the LogGraph instance in the GUI
lg = App.get_running_app().root.get_screen('main').ids.graph
lg.update_points(self.i)
# LogGraph.update_points(LogGraph(), i)
New to kivy, and OOP.
I'm trying to update a label in kivy with data I pull from a temp sensor. The code that pulls in the sensor data is in labeltempmod. I created a function getTheTemp() that is called every second. In the function I try to assign the text of the label via Label(text=(format(thetemp)), font_size=80). The program ignores this. What am I doing wrong here?
#This is a test to see if I can write the temp to label
import labeltempmod
import kivy
from kivy.app import App
from kivy.clock import Clock
from kivy.uix.label import Label
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.textinput import TextInput
from kivy.uix.boxlayout import BoxLayout
def getTheTemp(dt):
thetemp = labeltempmod.readtemp()
Label(text=(format(thetemp)), font_size=80)
print thetemp
class LabelWidget(BoxLayout):
pass
class labeltestApp(App):
def build(self):
# call get_temp 0.5 seconds
Clock.schedule_interval(getTheTemp, 1)
return LabelWidget()
if __name__ == "__main__":
labeltestApp().run()
Here is the kivy language file:
<LabelWidget>:
orientation: 'vertical'
TextInput:
id: my_textinput
font_size: 80
size_hint_y: None
height: 100
text: 'default'
FloatLayout:
Label:
id: TempLabel
font_size: 150
text: 'Temp Test'
Thanks.
Sorry but you never update something You are just creating another label
Try this:
class LabelWidget(BoxLayout):
def __init__(self, **kwargs):
super(LabelWidget, self).__init__(**kwargs)
Clock.schedule_interval(self.getTheTemp, 1)
def getTheTemp(self, dt):
thetemp = labeltempmod.readtemp()
self.ids.TempLabel.text = thetemp
print thetemp
class labeltestApp(App):
def build(self):
return LabelWidget()
if __name__ == "__main__":
labeltestApp().run()
Update : for your last request, I think the best way to do that is:
...
class LabelWidget(BoxLayout):
def __init__(self, **kwargs):
super(LabelWidget, self).__init__(**kwargs)
self.Thetemp = None
Clock.schedule_interval(self.getTheTemp, 1)
def getTheTemp(self, dt):
if self.Thetemp is None:
self.thetemp = labeltempmod.readtemp()
else:
self.thetemp = labeltempmod.readtemp(self.theTemp)
self.ids.TempLabel.text = str(self.thetemp)
I am using kivy for UI. there is a Time_consuming function and when it runs, kivy ui will goes in black, so I used Threading.
I want to disable buttons until Time_consuming function finishes, and then enable button again. I have been used something like below:
from threading import Thread
from kivy.clock import Clock
from functools import partial
def run():
self.run_button.disabled=True
self.back_button.disabled=True
t=Thread(target=Time_consuming(), args=())
t.start()
Clock.schedule_interval(partial(disable, t.isAlive()), 8)
def disable(t, what):
print(t)
if not t:
self.run_button.disabled=False
self.back_button.disabled=False
but this dose not work, t.isAlive() in disable() even when Time_consuming() finishes, is True. where is the problem ?
question2: another problem is, Clock.schedule_interval will continue to run for ever. how can stop it when function finished?
I see you already answered your question. I was also building an example app to help you, while you were answering. I think it still can be of value to you and therefore I am posting it. One button shows you threading and the other one scheduling once. Happy coding :).
from kivy.app import App
from kivy.base import Builder
from kivy.properties import ObjectProperty
from kivy.uix.boxlayout import BoxLayout
from kivy.clock import Clock
import time
import threading
Builder.load_string("""
<rootwi>:
label_to_be_changed1: label_to_be_changed1
label_to_be_changed2: label_to_be_changed2
button1: button1
button2: button2
orientation: 'vertical'
Button:
id: button1
text:'Button1 - Threading'
on_press: root.change_Label_text1()
Button:
id: button2
text: 'Button2 - schedule'
on_press: root.change_Label_text2()
Label:
id: label_to_be_changed1
Label:
id: label_to_be_changed2
""")
class rootwi(BoxLayout):
label_to_be_changed1 = ObjectProperty()
label_to_be_changed2 = ObjectProperty()
button1 = ObjectProperty()
button2 = ObjectProperty()
def change_Label_text1(self):
self.button1.disabled = True
threading.Thread(target=self.timeconsuming).start()
def timeconsuming(self):
#do your stuff
time.sleep(5)
self.label_to_be_changed1.text = 'thread has ended'
self.button1.disabled = False
def change_Label_text2(self):
self.button2.disabled = True
Clock.schedule_once(self.change_Label_text2_callback, 4)
def change_Label_text2_callback(self, *largs):
self.label_to_be_changed2.text = 'schedule has ended'
self.button2.disabled = False
class MyApp(App):
def build(self):
return rootwi()
if __name__ == '__main__':
MyApp().run()
I have found that:
question1: pass t instead of t.isAlive().this :
Clock.schedule_interval(partial(disable, t.isAlive()), 8)
changed to :
Clock.schedule_interval(partial(disable, t), 8)
question2: If the disable() returns False, the schedule will be canceled and won’t repeat.
def run_model():
self.run_button.disabled=True
self.back_button.disabled=True
t=Thread(target=run, args=())
t.start()
Clock.schedule_interval(partial(disable, t), 8)
def disable(t, what):
if not t.isAlive():
self.run_button.disabled=False
self.back_button.disabled=False
return False
I do have several screens. One of them (DataScreen) contains 8 labels which should show the current sensor values. Sensors are read by a separate process (which is started from the MainScreen). The process itself is an instance of multiprocessing.Process.
I can get a reference to the labels by sensor_labels = self.manager.get_screen('data').l
However, I cannot figure out how to change them within the subprocess. I can change them from any function which is not a separate process, simply by doing something like:
for item in sensor_labels:
item.text = 'Update'
Unfortunately, it seems to be more difficult to pass the reference of the sensor_labels to the worker. If I pass them as argument both processes (kivy and the worker) seem to share the same object (the id is the same). However, if I change label.text = 'New Text' nothing changes in Kivy.
Why is the id of both objects the same, but the text is not changed ?
And how can I share a Kivy label object with another process ?
Here is my working minimal example
#! /usr/bin/env python
""" Reading sensor data
"""
from kivy.config import Config
Config.set('kivy', 'keyboard_mode', 'multi')
from kivy.app import App
from kivy.lang import Builder
from kivy.properties import StringProperty, ObjectProperty, NumericProperty
from kivy.uix.label import Label
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.stacklayout import StackLayout
from multiprocessing import Process, Queue, Array
# all other modules
import time
import numpy as np
from multiprocessing import Lock
class MainScreen(Screen):
def __init__(self, **kwargs):
super(MainScreen, self).__init__(**kwargs)
self.n_probes = 8
#staticmethod
def read_sensors(qu_rx, sensor_labels, lock):
while True:
if not qu_rx.empty():
message = qu_rx.get()
if message == 'STOP':
print('Worker: received poison pill')
break
data = np.random.random()
print('ID of labels in worker: {}'.format(id(sensor_labels)))
print('Text of labels in worker:')
lock.acquire()
for label in sensor_labels:
label.text = '{0:2f}'.format(data)
print(label.text)
lock.release()
time.sleep(5)
def run_worker(self, *args, **kwargs):
self.qu_tx_worker = Queue()
lock = Lock()
# this is a reference to the labels in the DataScreen class
self.sensor_labels = self.manager.get_screen('data').l
self.worker = Process(target=self.read_sensors,
args=(self.qu_tx_worker, self.sensor_labels, lock))
self.worker.daemon = True
self.worker.start()
def stop_worker(self, *args, **kwargs):
self.qu_tx_worker.put('STOP')
print('Send poison pill')
self.worker.join()
print('All worker dead')
print('ID of labels in Kivy: {}'.format(id(self.sensor_labels)))
print('Label text in Kivy:')
for label in self.sensor_labels:
print(label.text)
class DataScreen(Screen):
def __init__(self, **kwargs):
layout = StackLayout()
super(DataScreen, self).__init__(**kwargs)
self.n_probes = 8
self.label_text = []
for i in range(self.n_probes):
self.label_text.append(StringProperty())
self.label_text[i] = str(i)
self.l = []
for i in range(self.n_probes):
self.l.append(Label(id='l_{}'.format(i),
text='Start {}'.format(i),
font_size='60sp',
height=20,
width=20,
size_hint=(0.5, 0.2)))
self.ids.stack.add_widget(self.l[i])
def change_text(self):
for item in self.l:
item.text = 'Update'
Builder.load_file('phapp.kv')
class MyApp(App):
"""
The settings App is the main app of the pHBot application.
It is initiated by kivy and contains the functions defining the main interface.
"""
def build(self):
"""
This function initializes the app interface and has to be called "build(self)".
It returns the user interface defined by the Builder.
"""
sm = ScreenManager()
sm.add_widget(MainScreen())
sm.add_widget(DataScreen())
# returns the user interface defined by the Builder
return sm
if __name__ == '__main__':
MyApp().run()
And the .kv file:
<MainScreen>:
name: 'main'
BoxLayout:
orientation: 'vertical'
Button:
text: 'Start Application'
font_size: 40
on_release: root.run_worker()
Button:
text: 'Stop Application'
font_size: 40
on_release: root.stop_worker()
Button:
text: 'Go to data'
font_size: 40
on_release: app.root.current = 'data'
Button:
text: 'Exit'
font_size: 40
on_release: app.stop()
<DataScreen>:
name: 'data'
StackLayout:
id: stack
orientation: 'lr-tb'
BoxLayout:
Button:
size_hint: (0.5, 0.1)
text: 'Update'
font_size: 30
on_release: root.change_text()
Button:
size_hint: (0.5, 0.1)
text: 'Back to main menu'
font_size: 30
on_release: app.root.current = 'main'
It looks like you might misunderstand how multiprocessing works.
When you start a new Process with the multiprocessing library it creates a new process and pickles all the code needed to run the target function. Any updates you make to the labels passed are happening in the worker process and will NOT reflect in the UI process.
To get around this you have to use one of these methods to exchange data between the worker and UI processes: https://docs.python.org/2/library/multiprocessing.html#exchanging-objects-between-processes. Since you already have a queue you can do something like this:
Put your read_sensors into worker.py passing a tx and rx Queue where tx is used to send to the UI and rx is used to read from the UI.
#! /usr/bin/env python
""" Reading sensor data
"""
import time
import numpy as np
def read_sensors(rx,tx, n):
while True:
if not rx.empty():
message = rx.get()
if message == 'STOP':
print('Worker: received poison pill')
break
#: Sensor value for each label
data = [np.random.random() for i in range(n)]
#: Formatted data
new_labels = ['{0:2f}'.format(x) for x in data]
print('Text of labels in worker: {}'.format(new_labels))
#lock.acquire() # Queue is already safe, no need to lock
#: Put the formatted label in the tx queue
tx.put(new_labels)
# lock.release() # Queue is already safe, no need to unlock
time.sleep(5)
Then in your app use the Clock to call an update handler to check the tx Queue for updates periodically. When quitting, the UI can tell the worker to stop by putting a message in the rx queue.
#! /usr/bin/env python
""" Reading sensor data
"""
from kivy.config import Config
from kivy.clock import Clock
Config.set('kivy', 'keyboard_mode', 'multi')
from kivy.app import App
from kivy.lang import Builder
from kivy.properties import StringProperty, ObjectProperty, NumericProperty
from kivy.uix.label import Label
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.stacklayout import StackLayout
from multiprocessing import Process, Queue
#: Separate worker file so a separate app is not opened
import worker
class MainScreen(Screen):
def __init__(self, **kwargs):
super(MainScreen, self).__init__(**kwargs)
self.n_probes = 8
#: Hold the update event
self._event = None
def read_worker(self,dt):
""" Read the data from the worker process queue"""
#: Get the data from the worker (if given) without blocking
if self.tx.empty():
return # No data, try again later
#: The worker put data in the queue, update the labels
new_labels = self.tx.get()
for label,text in zip(self.sensor_labels,new_labels):
label.text = text
def run_worker(self, *args, **kwargs):
self.rx = Queue() #: Queue to send data to worker process
self.tx = Queue() #: Queue to recv from worker process
self.sensor_labels = self.manager.get_screen('data').l
self.worker = Process(target=worker.read_sensors,
args=(self.rx,self.tx,self.n_probes))
self.worker.daemon = True
self.worker.start()
# Check the tx queue for updates every 0.5 seconds
self._event = Clock.schedule_interval(self.read_worker, 0.5)
def stop_worker(self, *args, **kwargs):
self.rx.put('STOP')
print('Send poison pill')
self.worker.join()
print('All worker dead')
#: Stop update loop
if self._event:
self._event.cancel()
print('ID of labels in Kivy: {}'.format(id(self.sensor_labels)))
print('Label text in Kivy:')
for label in self.sensor_labels:
print(label.text)
class DataScreen(Screen):
def __init__(self, **kwargs):
layout = StackLayout()
super(DataScreen, self).__init__(**kwargs)
self.n_probes = 8
self.label_text = []
for i in range(self.n_probes):
self.label_text.append(StringProperty())
self.label_text[i] = str(i)
self.l = []
for i in range(self.n_probes):
self.l.append(Label(id='l_{}'.format(i),
text='Start {}'.format(i),
font_size='60sp',
height=20,
width=20,
size_hint=(0.5, 0.2)))
self.ids.stack.add_widget(self.l[i])
def change_text(self):
for item in self.l:
item.text = 'Update'
Builder.load_file('phapp.kv')
class MyApp(App):
"""
The settings App is the main app of the pHBot application.
It is initiated by kivy and contains the functions defining the main interface.
"""
def build(self):
"""
This function initializes the app interface and has to be called "build(self)".
It returns the user interface defined by the Builder.
"""
sm = ScreenManager()
sm.add_widget(MainScreen())
sm.add_widget(DataScreen())
# returns the user interface defined by the Builder
return sm
if __name__ == '__main__':
MyApp().run()
Also the multiprocessing.Queue class is already 'process' safe, you don't need to use a lock around it. If you have a separate process for each sensor you can use the same idea just more queues.
Kivy doesn't provide IPC, and the GUI elements should be updated only in the main thread. To implements IPC you could use OSC to facilitate that, see this. If you move reading the sensors inside threads, then read this and this if you haven't done so yet.