PyQt — диалог в другом потоке

моя программа имеет главное окно, и в этом окне она запускает поток для считывания мощности с фотодетектора, затем отправляет сигнал, который захватывается слотом в главном окне, который обновляет графический интерфейс главного окна.

Затем у меня есть еще один виджет (назовем его программным виджетом), который появляется из главного окна и представляет собой обычный текст, в который пользователь может вставлять команды, чтобы программа выполняла их. Теперь возникает проблема:

Когда я просто открываю виджет программирования с помощью «показать», главное окно продолжает обновлять фотодетектор. Однако, когда я начинаю выполнять последовательность из виджета программирования, главное окно зависает, и показания фотодетектора останавливаются вместе с ним во время выполнения команд (я думаю, он продолжает читать, потому что находится в другом потоке, но перестает обновлять графический интерфейс главного окна).

Мой вопрос: виджет программирования по умолчанию уже находится в другом потоке, поскольку главное окно продолжает работать, когда оно открывается. Итак, почему оно (главное окно) зависает, когда виджет программирования находится в цикле?

Вот код темы:

class PDThread(QtCore.QThread):

valueupdate = QtCore.pyqtSignal(float)
state = 1

def __init__(self, state, port):
    QtCore.QThread.__init__(self)
    self.photodetector = PM100D()
    self.port = port

def run(self):
        while True:
            try:
                self.photodetector.connect(self.port)
                break
            except:
                self.dialog = dialog_classes.DialogPort()
                self.dialog.set_instrument('PM100D')
                self.dialog.exec_()
                ret = self.dialog.pm100d.itemText(self.dialog.pm100d.currentIndex())
                if ret == QtGui.QDialog.Accepted:
                    self.port = str(ret)
                else:
                    self.photodetector.__del__()
                    self.quit()
                    return

        window.PDState = 1
        window.startpd.setText('Stop PD')     
        while self.state == 1:
            time.sleep(0.1)
            value = self.photodetector.get_pow()
            self.valueupdate.emit(value)

Вот функция, которая создает поток и слот в главном окне:

def ActionConnect_PM100D(self):
    if self.PDState == 0:
        self.PD = PDThread(self.PDState, self.PDport)
        self.PD.valueupdate.connect(self.PDHandler)
        self.threads = []
        self.threads.append(self.PD)
        self.PD.start()
    else:
        self.PDState = 0
        self.PD.state = 0
        self.startpd.setText('Start PD')


def PDHandler(self, value):
    ref = float(self.refpd.value())
    self.outputpd.setText(str(value-ref))

Здесь также в главном окне функция, создающая виджет:

    def ActionSetup(self):
    self.program = dialog_classes.Programming(self.newport, self.output, self.outputpd)
    self.program.show()

Наконец, код виджета:

class Programming(QtGui.QDialog, Ui_Programming_Dialog, gui.MyApp):

def __init__(self, ESP300, output, outputpd):
    QtGui.QDialog.__init__(self)
    self.setupUi(self)
    self.setWindowTitle('Programming Setup')
    self.openbuttom.clicked.connect(self.Open)
    self.save.clicked.connect(self.Save)
    self.execute.clicked.connect(self.Execute)
    self.newport = ESP300
    self.output = output
    self.outputpd = outputpd

def Open(self):
    self.fileName = QtGui.QFileDialog.getOpenFileName(self, 'OpenFile', '', '*.txt')
    try:
        text = open(self.fileName).read()
    except:
        None
    else:
        self.program.setPlainText(text)

def Save(self):
    self.fileName = QtGui.QFileDialog.getSaveFileName(self, 'Save File', '', '*.txt')
    try:
        open(self.fileName, 'w').write(str(self.program.toPlainText()))
    except:
        None

def Execute(self):
    text = str(self.program.toPlainText())
    lines = text.split('\n')
    for line in lines:
        arg = []
        List = line.split(',')
        for word in List:
            arg.append(word.strip())

        if arg[0].lower() == 'move':
            if arg[1].lower() == 'x':
                self.newport.move_x(float(arg[2]))
            elif arg[1].lower() == 'y':
                self.newport.move_y(float(arg[2]))
            elif arg[1].lower() == 'z':
                self.newport.move_z(float(arg[2]))
        elif arg[0].lower() == 'wait':
            self.newport.wait()
        elif arg[0].lower() == 'home':
            self.newport.home()
            while True:
                try:
                    self.GetPosition()
                except:
                    time.sleep(0.5)
                else:
                    if self.newport.x == 0 and self.newport.y == 0 and self.newport.z == 0:
                        break
        elif arg[0].lower() == 'power on':
            self.newport.power_on(arg[1])
        elif arg[0].lower() == 'power off':
            self.newport.power_off(arg[1])
        elif arg[0].lower() == 'pos':
            self.newport.pos_x(arg[1])
            self.newport.pos_y(arg[2])
            self.newport.pos_z(arg[3])
            while True:
                try:
                    self.GetPosition()
                except:
                    time.sleep(0.5)
                else:
                    time.sleep(1)
                    break
        elif arg[0].lower() == 'get pos':
            self.GetPosition()
        elif arg[0].lower() == 'get error':
            self.GetError()
        elif arg[0].lower() == 'get pow':
            print(self.outputpd.toPlainText())

        time.sleep(2)


def closeIt(self):
    self.close()

Спасибо за поддержку.


person Eduardo    schedule 19.09.2016    source источник
comment
Ни один объект, производный от QWidget, не может находиться ни в одном потоке, кроме основного потока. Ваш код не является многопоточным, он просто блокирует пользовательский интерфейс, когда вы находитесь в цикле. Код пользовательского интерфейса работает, выполняя цикл событий, который отправляет события, когда вы застряли в каких-либо слотах или обработчиках событий, цикл событий не запускается. Инвертируйте поток управления: вместо того, чтобы ждать чего-то в цикле, используйте таймер, который время от времени вызывает ваш код, чтобы прочитать что-то, или реагировать на новые данные, когда они становятся доступными, если вы используете, например. QSerialPort.   -  person Kuba hasn't forgotten Monica    schedule 20.09.2016
comment
Вы можете посмотреть страницу документации по потокам на Stack Overflow PyQt. слишком.   -  person three_pineapples    schedule 20.09.2016


Ответы (2)


Как правило, это делается для создания класса, производного от QObject, который обрабатывает весь сбор данных, отличных от Qt, и перемещения его в отдельный поток с помощью рабочая модель.

Затем вы можете использовать сигналы для передачи данных туда и обратно между основным потоком (GUI) и рабочим потоком, а также для запуска событий (например, всплывающее диалоговое окно в основном потоке из-за события в рабочем потоке).

person Brendan Abel    schedule 19.09.2016
comment
Я попытался переместить метод Execute в другой поток, используя рабочую модель, как вы предложили, но он по-прежнему зависает в графическом интерфейсе. Мне непонятно, почему сейчас это не работает. - person Eduardo; 21.09.2016

Я попытался переместить метод Execute в другой поток, используя рабочую модель, как было предложено, но это также зависло в графическом интерфейсе, я не знаю почему. Наверное, я сделал что-то не так.

Однако, когда я создал другой поток непосредственно в классе, реализованном для выполнения цикла, и это сработало. Я следовал этому примеру: https://nikolak.com/pyqt-threading-tutorial/

Кроме того, вот мой код. Я надеюсь, что это может помочь и другим, спасибо.

self.worker_thread = []

def Execute(self):
    self.execution = ProgramExecution(self.newport, self.output, self.outputpd, self.program)
    self.worker_thread.append(self.execution)
    self.execution.start()

И вот класс потока:

class ProgramExecution(QtCore.QThread):

_signalStatus = QtCore.pyqtSignal(str)

def __init__(self, ESP300, output, outputpd, program):
    QtCore.QThread.__init__(self)
    self.newport = ESP300
    self.output = output
    self.outputpd = outputpd
    self.program = program

def __del__(self):
    self.wait()

def run(self):
    text = str(self.program.toPlainText())
    lines = text.split('\n')
    for line in lines:
        arg = []
        List = line.split(',')
        for word in List:
            arg.append(word.strip())

        if arg[0].lower() == 'move':
            if arg[1].lower() == 'x':
                self.newport.move_x(float(arg[2]))
            elif arg[1].lower() == 'y':
                self.newport.move_y(float(arg[2]))
            elif arg[1].lower() == 'z':
                self.newport.move_z(float(arg[2]))
        elif arg[0].lower() == 'wait':
            self.newport.wait()
        elif arg[0].lower() == 'home':
            self.newport.home()
            while True:
                try:
                    self.GetPosition()
                except:
                    time.sleep(0.5)
                else:
                    if self.newport.x == 0 and self.newport.y == 0 and self.newport.z == 0:
                        break
        elif arg[0].lower() == 'power on':
            self.newport.power_on(arg[1])
        elif arg[0].lower() == 'power off':
            self.newport.power_off(arg[1])
        elif arg[0].lower() == 'pos':
            self.newport.pos_x(arg[1])
            self.newport.pos_y(arg[2])
            self.newport.pos_z(arg[3])
            while True:
                try:
                    self.GetPosition()
                except:
                    time.sleep(0.5)
                else:
                    time.sleep(1)
                    break
        elif arg[0].lower() == 'get pos':
            self.GetPosition()
        elif arg[0].lower() == 'get error':
            self.GetError()
        elif arg[0].lower() == 'get pow':
            print(self.outputpd.toPlainText())

        time.sleep(2)
person Eduardo    schedule 21.09.2016