Mor*_*itz 10 python multiprocessing kivy
我有几个屏幕.其中一个(DataScreen)包含8个标签,应显示当前传感器值.传感器由单独的进程读取(从MainScreen启动).过程本身就是一个例子multiprocessing.Process.
我可以参考标签 sensor_labels = self.manager.get_screen('data').l
但是,我无法弄清楚如何在子进程中更改它们.我可以从任何不是单独进程的函数中更改它们,只需执行以下操作:
for item in sensor_labels:
item.text = 'Update'
Run Code Online (Sandbox Code Playgroud)
不幸的是,将参考传递sensor_labels给工人似乎更加困难.如果我将它们作为参数传递,则两个进程(kivy和worker)似乎共享同一个对象(id是相同的).但是,如果我改变label.text = 'New Text'Kivy没有任何变化.
为什么两个对象的id都相同,但文本没有改变?我如何与另一个进程共享一个Kivy标签对象?
这是我工作的最小例子
#! /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()
Run Code Online (Sandbox Code Playgroud)
和.kv文件:
<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'
Run Code Online (Sandbox Code Playgroud)
看来您可能误解了多处理的工作原理。
Process当您使用多处理库启动新进程时,它会创建一个新进程并腌制运行目标函数所需的所有代码。您对传递的标签所做的任何更新都发生在工作进程中,并且不会反映在 UI 进程中。
要解决这个问题,您必须使用以下方法之一在工作进程和 UI 进程之间交换数据:https ://docs.python.org/2/library/multiprocessing.html#exchanging-objects- Between-processes 。由于您已经有一个队列,您可以执行以下操作:
将您的内容read_sensors放入worker.py传递一个tx队列rx,tx用于发送到 UI 并rx用于从 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)
Run Code Online (Sandbox Code Playgroud)
然后在您的应用程序中使用Clock调用更新处理程序来tx定期检查队列中的更新。退出时,UI 可以通过将消息放入队列来告诉工作线程停止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()
Run Code Online (Sandbox Code Playgroud)
此外,该类multiprocessing.Queue已经是“进程”安全的,您不需要在它周围使用锁。如果每个传感器都有一个单独的进程,您可以使用相同的想法,只是更多的队列。
| 归档时间: |
|
| 查看次数: |
512 次 |
| 最近记录: |