python - PyQt4 Espere en el hilo para la entrada del usuario desde la GUI
multithreading python-3.x (1)
Tengo una clase de hilo "MyThread" y mi aplicación principal que simplemente se llama "Gui". Quiero crear algunos objetos de la clase de hilo pero para este ejemplo creé solo un objeto. La clase de subproceso hace algo de trabajo, luego emite una señal a la clase Gui, indicando que se necesita una entrada del usuario (esta indicación por ahora simplemente está cambiando el texto de un botón). Luego, el hilo debe esperar una entrada del usuario (en este caso, un clic en el botón) y luego continuar haciendo lo que está haciendo ...
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_())
¿Cómo puedo esperar en (múltiples) hilos una entrada del usuario?
Por defecto, un
QThread
tiene un bucle de eventos que puede procesar señales y ranuras.
En su implementación actual, desafortunadamente ha eliminado este comportamiento al anular
QThread.run
.
Si lo restaura, puede obtener el comportamiento que desea.
Entonces, si no puede anular
QThread.run()
, ¿cómo lo hace enhebrar en Qt?
Un enfoque alternativo al subprocesamiento es colocar el código en una subclase de
QObject
y mover ese objeto a una instancia estándar de
QThread
.
Luego puede conectar señales y ranuras entre el hilo principal y el
QThread
para comunicarse en ambas direcciones.
Esto le permitirá implementar su comportamiento deseado.
En el siguiente ejemplo, comencé un subproceso de trabajo que imprime en el terminal, espera 2 segundos, imprime nuevamente y luego espera la entrada del usuario.
Cuando se hace clic en el botón, se ejecuta una segunda función separada en el subproceso de trabajo e imprime en el terminal con el mismo patrón que la primera vez.
Tenga en cuenta el orden en que uso
moveToThread()
y conecto las señales (según
this
).
Código:
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_()