#python #multithreading #inheritance #pyqt #parent-child
#python #многопоточность #наследование #pyqt #родитель-потомок
Вопрос:
У меня есть программа, у которой есть графический интерфейс в PyQt в основном потоке. Он связывается с фотодетектором и получает показания мощности в другом потоке, который посылает сигнал в основной поток для обновления значения мощности графического интерфейса. Теперь я хочу использовать двигатель для автоматического выравнивания моего оптического волокна, получая обратную связь от фотоприемника.
Поэтому я создал класс, который управляет двигателями, но я должен каким-то образом передавать показания фотодетектора этому классу. Сначала я попытался получить доступ к родительской переменной power, но это не сработало. Затем я создал метод в своем графическом интерфейсе для возврата значения переменной и попытался получить к нему доступ из класса motor. У меня возникла проблема, в которой говорилось, что я не могу использовать родительский метод, не используя его __init__
первый. Есть ли способ обойти это? Я не могу __init__
снова вызвать графический интерфейс, я просто хочу использовать один из его методов из дочернего класса.
Если есть альтернативный способ сделать это, я тоже был бы счастлив.
PS: Я думаю, я не могу предоставить дочернему классу объект фотодетектора, потому что он находится в другом потоке, верно?
—Редактировать — Код графического интерфейса:
class MyApp(QtGui.QMainWindow, Ui_MainWindow):
self.PDvalue = 0 #initial PD value
self.PDState = 0 #control the PD state (on-off)
self.PDport = self.dialog.pm100d.itemText(self.dialog.pm100d.currentIndex()) #gets pot info
def __init__(self):
... #a lot of other stuff
self.nano = AlgoNanoMax.NanoMax('COM12') #creates the motor object
self.nano_maxX.clicked.connect(self.NanoMaximizeX) #connect its fun to a buttom
self.actionConnect_PM100D.triggered.connect(self.ActionConnect_PM100D) #PD buttom
def NanoMaximizeX(self):
self.nano.maximize_nano_x() #uses motor object function
def ActionConnect_PM100D(self):
if self.PDState == 0: #check if PD is on
self.PD = PDThread(self.PDState, self.PDport) #creates thread
self.PD.valueupdate.connect(self.PDHandler) #signal connect
self.PD.dialogSignal.connect(self.PDdialog) #create error dialog
self.threads = []
self.threads.append(self.PD)
self.PD.start() #start thread
else:
self.PDState = 0
self.PD.state = 0 #stop thread
self.startpd.setText('Start PD') #change buttom name
def PDHandler(self, value):
self.PDvalue = value #slot to get pow from thread
def ReturnPow(self):
return self.PDvalue #return pow (I tried to use this to pass to the motor class)
def PDdialog(self):
self.dialog.set_instrument('PM100D') #I have a dialog that says error and asks you to type the right port
if self.dialog.exec_() == QtGui.QDialog.Accepted: #if Ok buttom try again
ret = self.dialog.pm100d.itemText(self.dialog.pm100d.currentIndex()) #new port
self.PD.port = str(ret)
self.PD.flagWhile = False #change PD stop loop condition to try again
else: #pressed cancel, so it gives up
self.PD.photodetector.__del__() #delete objects
self.PD.terminate() #stop thread
self.PD.quit()
Теперь класс PD, который находится в другом потоке, но в том же файле, что и gui:
class PDThread(QtCore.QThread):
valueupdate = QtCore.pyqtSignal(float) #creating signals
dialogSignal = QtCore.pyqtSignal() #signal in case of error
state = 1 #used to stop thread
def __init__(self, state, port):
QtCore.QThread.__init__(self)
self.photodetector = PM100D() #creates the PD object
self.port = port
def run(self):
while True:
self.flagWhile = True #used to leave while
try:
self.photodetector.connect(self.port) #try to connect
except:
self.dialogSignal.emit() #emit error signal
while self.flagWhile == True:
time.sleep(0.5) #wait here until user press something in the dialog, which is in another thread
else:
break #leave loop when connected
window.PDState = 1 #change state of main gui buttom (change functionality to turn off if pressed again)
window.startpd.setText('Stop PD') #change buttom label
while self.state == 1:
time.sleep(0.016)
value = self.photodetector.get_pow() #get PD pow
self.valueupdate.emit(value) #emit it
Файл AlgoNanoMax:
import gui
from NanoMax import Nano
class NanoMax(gui.MyApp): #inheriting parent
def __init__(self, mcontroller_port):
self.mcontroller = Nano(mcontroller_port) #mcontroller is the communication to the motor
def maximize_nano_x(self, step=0.001, spiral_number=3):
''' Alignment procedure with the nano motor X'''
print 'Optimizing X'
power = super(NanoMax, self).ReturnPow() #here I try to read from the photodetector
xpos = self.mcontroller.initial_position_x
position = []
position = [[power, xpos]]
xsign = 1
self.mcontroller.move_relative(self.mcontroller.xaxis, (-1) * spiral_number * step)
print 'X nano move: ' str((-1) * spiral_number * step * 1000) ' micrometers'
time.sleep(4)
power = super(NanoMax, self).ReturnPow()
xpos = (-1) * spiral_number * step
position.append([power, xpos])
for _ in xrange(2*spiral_number):
self.mcontroller.move_relative(self.mcontroller.xaxis, xsign * step)
print 'X nano move: ' str(xsign * step * 1000) ' micrometers'
time.sleep(5)
power = super(NanoMax, self).ReturnPow()
xpos = xsign * step
position.append([power, xpos])
pospower = [position[i][0] for i in xrange(len(position))]
optimalpoint = pospower.index(max(pospower))
x_shift = (-1) * (xpos - position[optimalpoint][1])
print 'Maximum power: ' str(max(pospower)) ' dBm'
print 'Current power: ' str(super(NanoMax, self).ReturnPow()) ' dBm'
self.mcontroller.move_relative(self.mcontroller.xaxis, x_shift)
Комментарии:
1. Я попытался получить доступ к переменной мощности родителя … родительский? Этот класс двигателя наследуется от класса GUI? Это кажется неправильным или даже полезным. Экземпляр класса двигателя может получать данные от объекта фотодетектора в другом потоке, но может потребоваться блокировка, чтобы сделать его потокобезопасным. Предположим, что ваш фотоприемник имеет метод получения, который используется
threading.Lock
для считывания данных.2. Не могли бы вы поделиться своим кодом? В идеале не все, но, по крайней мере, классы и линии, где они общаются между собой?
Ответ №1:
Вызовы __init__
for NanoMax
и MyApp
should super().__init__()
гарантируют, что инициализация выполнена для всех уровней (если это Python 2, вы не можете использовать no-arg super
, поэтому это будет super(NanoMax, self).__init__()
и super(MyApp, self).__init__()
соответственно). Это предполагает PyQT
, что он был правильно написан с классами нового стиля и правильно используется super
сам по себе; вы используете super
в других местах, так что, по-видимому, по крайней мере, первое верно. super
Правильное использование во всех классах гарантирует, что все уровни будут __init__
изменены один раз, в то время как перечисление суперклассов вручную не будет работать в определенных шаблонах наследования или может вызывать некоторые __init__
s несколько раз или вообще не вызывать.
Если есть вероятность, что многие уровни могут принимать аргументы, вы также должны принять *args
/ **kwargs
и переслать их super().__init__
вызову, чтобы аргументы пересылались туда, куда нужно.
Объединяя эти два, ваш код должен выглядеть следующим образом:
class MyApp(QtGui.QMainWindow, Ui_MainWindow):
def __init__(self, *args, **kwargs):
super(MyApp, self).__init__(*args, **kwargs)
... rest of __init__ ...
class PDThread(QtCore.QThread):
def __init__(self, state, port, *args, **kwargs):
super(PDThread, self).__init__(*args, **kwargs)
...
class NanoMax(gui.MyApp): #inheriting parent
def __init__(self, mcontroller_port, *args, **kwargs):
super(NanoMax, self).__init__(*args, **kwargs)
self.mcontroller = Nano(mcontroller_port) #mcontroller is the communication to the motor
Примечание: Если вы перегрузили методы, которые может вызывать суперкласс в своем __init__
, и ваши перегрузки зависят от состояния, установленного в вашем собственном __init__
, вам нужно будет настроить это состояние до, а не после super().__init__(...)
вызова. Совместное множественное наследование может быть проблемой таким образом. Также обратите внимание, что использование позиционных аргументов для чего угодно, кроме класса самого низкого уровня, может быть уродливым при множественном наследовании, поэтому может иметь смысл передавать все аргументы по ключевому слову и только принимать и пересылать **kwargs
, not *args
, чтобы люди не передавали позиционные аргументы способами, которые ломаются, если иерархия наследования немного меняется.
Комментарии:
1. Извините, что я не скопировал код в своем сообщении, но я инициализирую класс MyApp. Теперь я изменил все
__init__
, как вы упомянули, т. Е.super(MyApp, self).__init__()
. Однако я получаю бесконечный цикл__inits
__` . MyApp запускает NanoMax, который снова запускает конструктор MyApp и так далее и тому подобное2.@Eduardo: либо вы злоупотребляете
super
(передаете неправильный тип в качестве первого аргумента), либо ваш код нарушен по дизайну. Вы увереныNanoMax
, что ‘s__init__
используетsuper(NanoMax, self)
иMyApp
‘s используетsuper(MyApp, self)
? Даже если это так, при повторном чтении, вероятно, неправильно, чтоMyApp
создало бы экземплярNanoMax
whenNanoMax
is aMyApp
; похоже, вы комбинируете наследование с композицией, нарушая оба; еслиNanoMax
это aMyApp
,MyApp
не нужно aNanoMax
; еслиMyApp
требуется aNanoMax
,NanoMax
не должно быть aMyApp
. В противном случае все по кругу.3. @ShadoRanger: Да, все они используют super. Более того, я согласен с тем, что вы только что сказали, в этом есть смысл. В моем случае происходит следующее: я просто хочу разрешить NanoMax считывать переменную моего объекта MyApp, на самом деле это не обязательно должно быть дочерним элементом MyApp. Есть ли другой способ сделать это?
4. @Eduardo: При
MyApp
созданииNanoMax
передавайтеself
вNanoMax
конструктор, чтобы у него была ссылка наMyApp
экземпляр? Вероятно, это должно быть последнее, что делается вMyApp
конструкторе, поэтомуMyApp
он полностью сконструирован; в качестве альтернативы,NanoMax
сохраняет ссылку для последующего использования, но не использует ее в__init__
(где она может быть не полностью инициализирована).5. Я сделал это, но это все равно не работает. Если я вызываю
super(NanoMax, self).__init__()
конструктор NanoMax, он получает бесконечный цикл, а если я этого не делаю, он говорит, что родительский__init__
объект никогда не вызывался
Ответ №2:
class MyApp(QtGui.QMainWindow, Ui_MainWindow):
self.PDvalue = 0 #initial PD value
self.PDState = 0 #control the PD state (on-off)
В приведенном выше коде он устанавливает переменную вне функции. Чтобы сделать это в классе, не ставьте перед ним ключевое слово self . Таким образом, вы можете просто иметь в определении класса
class MyApp(QtGui.QMainWindow, Ui_MainWindow):
PDvalue = 0 #initial PD value
PDState = 0 #control the PD state (on-off)
и в суперстроке
power = super(NanoMax, self).PDvalue
Например:
>>> class Hi:
H = 5
def __init__(self):
self.g = 6
>>> class Bye(Hi):
def H(self):
print(super(Bye, self).H)
>>> e = Bye()
>>> e.H()
5
>>>
Комментарии:
1. Это работает следующим образом, но он всегда считывает 0 для включения. Я думаю, это потому, что он читает переменную родительского класса, а не переменную экземпляра. Или я сделал что-то не так? Я полностью изменил себя. PDvalue в основном коде также соответствует MyApp.PDvalue