问题
This question is probably dumb, but to give some background: I have been given a bunch of code of a software whose GUI is written in Qt, and I have been told that I should improve it by implement multithreading so the interface doesn't freeze (I have a bunch of code written like that that I am using as a template). I am not familiar with Qt or GUIs in general.
ATM I am trying to implement a simple cancel button: as the process in the background might take long, I want to be able to stop it arbitrarily (for example because one of the input values was incorrect or misspelled). Surprisingly I couldn't find many detailed questions or tutorials on this.
That said I tried to implement a simple widget to figure out how this works:
import time
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class Calc(QtCore.QObject):
new_val = QtCore.pyqtSignal(float)
def __init__(self):
super().__init__()
self.a = 0
def Stop(self, b=False):
if b:
QtCore.QThread.quit() # This obviously doesn't work
def upd(self, a):
self.a = a
a2 = a**2
for i in range(10): # To simulate a lengthy process
time.sleep(1)
self.new_val.emit(a2)
class CalcWidget(QtWidgets.QWidget):
go_sig = QtCore.pyqtSignal(float)
stop_sig = QtCore.pyqtSignal(bool)
def __init__(self, arg):
super().__init__()
self.arg = arg
self.arg_thread = QtCore.QThread()
self.arg.moveToThread(self.arg_thread)
self.stop = QtWidgets.QPushButton('Cancel')
self.a = QtWidgets.QSpinBox()
self.a2 = QtWidgets.QLabel('--')
self.a.valueChanged.connect(self._go)
self.go_sig.connect(self.arg.upd)
self.stop.clicked.connect(self._Stop)
self.stop_sig.connect(self.arg.Stop)
self.arg.new_val.connect(self.myupd)
# this is only the interface
hbox = QtWidgets.QHBoxLayout()
vbox1 = QtWidgets.QVBoxLayout()
vbox1.setAlignment(QtCore.Qt.AlignLeft)
vbox1.addWidget(self.a)
hbox.addLayout(vbox1)
vbox2 = QtWidgets.QVBoxLayout()
vbox2.setAlignment(QtCore.Qt.AlignLeft)
vbox2.addWidget(self.a2)
vbox2.addWidget(self.stop)
hbox.addLayout(vbox2)
self.setLayout(hbox)
self.arg_thread.start()
def _go(self):
self.go_sig.emit(self.a.value())
def _Stop(self):
self.stop_sig.emit(True)
def myupd(self, a2):
self.a2.setText(str(a2))
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
calc = Calc()
w = CalcWidget(calc)
w.show()
app.exec()
Now here the function Stop() obviously doesn't work, I have tried different implementations, as to quit the thread from the function CalcWidget._Stop()
or define a variable in Calc()
and add an conditional statement in the for loop in Calc.upd
.
However it seems to me I am missing something obvious or simple and I can't make it work. Any help or suggestion is welcome.
EDIT:
By tweaking around the problem I rewrote the Stop
function as follows:
def Stop():
self.stop = True
where self.stop
is a class variable, now I can add a condition in the loop:
for i in range(10):
if self.stop:
break
time.sleep(1)
however the changes are queued up so the thread finishes the loop and then calls Stop
. Making this useless. I wonder if there is a way to solve this.
I'd like not to redefine a thread object as it seems to me it would require more work in re-implementing par of the code that is already written and working.
回答1:
This is a short example of how you can implement it. Please let me know if you want me to add notes.
from PyQt5 import QtWidgets, QtCore
class WorkerThread(QtCore.QThread):
is_active = True
def run(self):
while self.is_active:
print("Processing...")
self.sleep(1)
class Window(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Window, self).__init__(parent)
self.start_button = QtWidgets.QPushButton("Start")
self.cancel_button = QtWidgets.QPushButton("Cancel")
self.start_button.clicked.connect(self.start)
self.cancel_button.clicked.connect(self.stop)
laytout = QtWidgets.QVBoxLayout()
laytout.addWidget(self.start_button)
laytout.addWidget(self.cancel_button)
self.setLayout(laytout)
self.worker = WorkerThread()
def start(self):
self.worker.is_active = True
self.worker.start()
def stop(self):
self.worker.is_active = False
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
win = Window()
win.show()
sys.exit(app.exec_())
来源:https://stackoverflow.com/questions/64660647/implement-a-cancel-button-with-qt-in-python