2016-02-20 2 views
2

У меня есть класс потоков «MyThread» и мое основное приложение, которое просто называется «Gui». Я хочу создать несколько объектов из класса потоков, но для этого примера я создал только один объект. Класс thread выполняет некоторую работу, затем излучает сигнал в класс Gui, указывая на необходимость ввода пользователя (это указание на данный момент просто изменяет текст кнопки). Тогда поток должен ждать пользовательского ввода (в данном случае кнопка мыши), а затем продолжать делать то, что он делает ...PyQt4 Wait in thread для пользовательского ввода из GUI

from PyQt4 import QtGui, QtCore 
class MyTrhead(QtCore.QThread): 
    trigger = QtCore.pyqtSignal(str) 

    def run(self): 
     print(self.currentThreadId()) 
     for i in range(0,10): 
      print("working ") 
      self.trigger.emit("3 + {} = ?".format(i)) 
      #### WAIT FOR RESULT 
      time.sleep(1) 


class Gui(QMainWindow, Ui_MainWindow): 
    def __init__(self, parent=None): 
     super(Gui, self).__init__(parent) 
     self.setupUi(self) 
     self.pushButton.clicked.connect(self.btn) 

     self.t1 = MyTrhead() 
     self.t1.trigger.connect(self.dispaly_message) 
     self.t1.start() 
     print("thread: {}".format(self.t1.isRunning())) 


    @QtCore.pyqtSlot(str) 
    def dispaly_message(self, mystr): 
     self.pushButton.setText(mystr) 

    def btn(self): 
     print("Return result to corresponding thread") 



if "__main__" == __name__: 
    import sys 
    app = QtGui.QApplication(sys.argv) 
    m = Gui() 
    m.show() 
    sys.exit(app.exec_()) 

Как я могу ждать (несколько) потоков для пользовательского ввода?

ответ

6

По умолчанию QThread имеет цикл событий, который может обрабатывать сигналы и слоты. В вашей текущей реализации вы, к сожалению, удалили это поведение, переопределив QThread.run. Если вы его восстановите, вы сможете получить желаемое поведение.

Так что, если вы не можете переопределить QThread.run(), как вы выполняете резьбу в Qt? Альтернативный подход к потоковой передаче заключается в том, чтобы поместить ваш код в подкласс QObject и перенести этот объект на стандартный экземпляр QThread. Затем вы можете подключать сигналы и слоты между основным потоком и QThread для связи в обоих направлениях. Это позволит вам реализовать желаемое поведение.

В приведенном ниже примере я начал рабочий поток, который печатает на терминал, ждет 2 секунды, снова распечатывает и ждет ввода пользователя. Когда кнопка нажата, выполняется вторая отдельная функция в рабочем потоке и печатает на терминал в том же шаблоне, что и в первый раз. Обратите внимание на порядок, в котором я использую moveToThread() и подключаю сигналы (согласно this).

Код:

from PyQt4.QtCore import * 
from PyQt4.QtGui import * 
import time 

class MyWorker(QObject): 

    wait_for_input = pyqtSignal() 
    done = pyqtSignal() 


    @pyqtSlot() 
    def firstWork(self): 
     print 'doing first work' 
     time.sleep(2) 
     print 'first work done' 
     self.wait_for_input.emit() 

    @pyqtSlot() 
    def secondWork(self): 
     print 'doing second work' 
     time.sleep(2) 
     print 'second work done' 
     self.done.emit() 


class Window(QWidget): 
    def __init__(self, parent = None): 
     super(Window, self).__init__() 

     self.initUi() 
     self.setupThread() 

    def initUi(self): 
     layout = QVBoxLayout() 
     self.button = QPushButton('User input') 
     self.button.setEnabled(False) 
     layout.addWidget(self.button) 
     self.setLayout(layout) 
     self.show() 

    @pyqtSlot() 
    def enableButton(self): 
     self.button.setEnabled(True) 

    @pyqtSlot()  
    def done(self): 
     self.button.setEnabled(False) 

    def setupThread(self): 
     self.thread = QThread() 
     self.worker = MyWorker() 

     self.worker.moveToThread(self.thread) 

     self.thread.started.connect(self.worker.firstWork) 
     self.button.clicked.connect(self.worker.secondWork) 
     self.worker.wait_for_input.connect(self.enableButton) 
     self.worker.done.connect(self.done) 

     # Start thread 
     self.thread.start()  

if __name__ == "__main__": 
    app = QApplication([]) 
    w = Window() 
    app.exec_() 
+0

Спасибо за вашу помощь, еще один вопрос: Как я могу передать аргументы функции firstWork к GUI и передать результат обратно secondWork в случае, если у меня есть более чем один из этих работника потоки? Подумайте, функция firstWork задает вопрос, передает это пользователю, а вторая работа получает ответ? – Maecky

+1

Измените декоратор слота на '@pyqtSlot ()', за которым следует одна и та же подпись функции, которая представляет слот 'def firstwork (self, )'. Например, '@pyqtSlot (int, str)' означает, что для слота требуются 2 аргумента (один из 'int' и другой типа' str'). Функция, которой принадлежит декоратор слота, должна выглядеть так: 'def firstWork (self, x, y)', где 'x' является' int' и 'y', являющимся« str' частями данных, передаваемых сигналом. Вы также должны изменить сигнал, полученный этим слотом: mySignal = pyqtSignal (int, str) '. – rbaleksandar

+0

@three_pineapples не забудьте подключить сигнал 'QThread.finished' к слову' self.worker.deleteLater'. – rbaleksandar