Mam kilka ekranów. Jeden z nich (DataScreen) zawiera 8 etykiet, które powinny pokazywać aktualne wartości czujników. Czujniki są odczytywane przez oddzielny proces (który rozpoczyna się z MainScreen). Sam proces jest instancją multiprocessing.Process
.Aktualizuj etykiety w osobnym module roboczym (instancja procesu)
mogę uzyskać odniesienie do etykiet przez sensor_labels = self.manager.get_screen('data').l
Jednak nie mogę dowiedzieć się, jak zmienić je w podproces. Mogę zmienić je z dowolnej funkcji, która nie jest oddzielny proces, po prostu robi coś takiego:
for item in sensor_labels:
item.text = 'Update'
Niestety, wydaje się być trudniejsze do zabicia odniesienie do sensor_labels
do pracownika. Jeśli przekazuję je jako argument, oba procesy (kivy i robotnik) wydają się dzielić tym samym obiektem (identyfikator jest taki sam). Jednak jeśli zmienię label.text = 'New Text'
nic się nie zmieni w Kivy.
Dlaczego identyfikator obu obiektów jest taki sam, ale tekst nie jest zmieniony? Jak mogę udostępnić obiekt etykiety Kivy innemu procesowi?
Oto moja praca minimalne Przykład
#! /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()
a plik .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'
Wiele procesów nie udostępnia danych (domyślnie). Zobacz np. [to pytanie] (https://stackoverflow.com/questions/2080660/python-multiprocessing-and-a-shared-counter). – syntonym
Ale dlaczego identyfikator jest taki sam? Spojrzałem na wieloprocesorowy.manager, ale o ile zrozumiałem, można go używać tylko z niektórymi typami obiektów. Jakieś pomysły na przekazanie pracownikowi obiektu Kivy? – Moritz
Myślę, że interpreter python jest "duplikowane", a więc internals takich jak id są takie same, ale nie wiem, internals, że dobrze. Czy możesz zamiast tego używać wątków? – syntonym