Дочерний вызов родительского метода без вызова родительского __init__ в python

#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 when NanoMax is a MyApp ; похоже, вы комбинируете наследование с композицией, нарушая оба; если NanoMax это a MyApp , MyApp не нужно a NanoMax ; если MyApp требуется a NanoMax , NanoMax не должно быть a MyApp . В противном случае все по кругу.

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