Ответ 1
Похоже, вы можете неправильно понять, как работает многопроцессорность.
Когда вы начинаете новый Process
с помощью библиотеки многопроцессорности, он создает новый процесс и рассосает весь код, необходимый для запуска целевой функции. Любые обновления, которые вы делаете для пропущенных меток, происходят в рабочем процессе и НЕ отразятся в процессе пользовательского интерфейса.
Чтобы обойти это, вы должны использовать один из этих методов для обмена данными между рабочими и процессами пользовательского интерфейса: https://docs.python.org/2/library/multiprocessing.html#exchanging-objects-between-processes. Поскольку у вас уже есть очередь, вы можете сделать что-то вроде этого:
Поместите ваш read_sensors
в worker.py
, передавая tx
и rx
Queue, где tx
используется для отправки в пользовательский интерфейс, а rx
используется для чтения из пользовательского интерфейса.
#! /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)
Затем в вашем приложении используйте Clock
, чтобы вызвать обработчик обновлений для периодического обновления очереди tx
для обновлений. При выходе из пользовательского интерфейса пользователь может прекратить работу, помещая сообщение в очередь rx
.
#! /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()
Также класс multiprocessing.Queue
уже является "процессом" безопасным, вам не нужно использовать блокировку вокруг него. Если у вас есть отдельный процесс для каждого датчика, вы можете использовать ту же идею, что и больше очередей.