У меня есть несколько экранов. Один из них (DataScreen) содержит 8 меток, которые должны показывать текущие значения датчика. Датчики считываются отдельным процессом (который запускается с MainScreen). Сам процесс является экземпляром multiprocessing.Process
.
Я могу получить ссылку на метки sensor_labels = self.manager.get_screen('data').l
Однако я не могу понять, как изменить их в подпроцессе. Я могу изменить их из любой функции, которая не является отдельным процессом, просто сделав что-то вроде:
for item in sensor_labels:
item.text = 'Update'
К сожалению, сложнее передать ссылку sensor_labels
работнику. Если я передам их как аргумент, то оба процесса (kivy и рабочий), похоже, используют один и тот же объект (id одинаковый). Однако, если я изменю label.text = 'New Text'
, ничего не изменится в Kivy.
Почему идентификатор обоих объектов одинаковый, но текст не изменяется? И как я могу совместно использовать объект метки 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()
И файл .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'