在python中用Qt实现一个取消按钮 [英] Implement a cancel button with Qt in python
问题描述
这个问题可能很愚蠢,但提供一些背景知识:我得到了一堆用 Qt 编写的 GUI 软件的代码,有人告诉我我应该通过实现多线程来改进它,这样界面就不会't 冻结(我有一堆这样写的代码,我用作模板).我一般不熟悉 Qt 或 GUI.
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 我正在尝试实现一个简单的取消按钮:由于后台进程可能需要很长时间,我希望能够任意停止它(例如,因为其中一个输入值不正确或拼写错误).令人惊讶的是,我找不到很多关于此的详细问题或教程.
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()
现在这里函数 Stop() 显然不起作用,我尝试了不同的实现,如从函数 CalcWidget._Stop()
退出线程或在 中定义一个变量Calc()
并在 Calc.upd
的 for 循环中添加条件语句.然而,在我看来,我遗漏了一些明显或简单的东西,我无法让它发挥作用.欢迎任何帮助或建议.
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.
通过调整这个问题,我重写了 Stop
函数如下:
By tweaking around the problem I rewrote the Stop
function as follows:
def Stop():
self.stop = True
其中 self.stop
是一个类变量,现在我可以在循环中添加一个条件:
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)
然而,更改已排队,因此线程完成循环,然后调用Stop
.使这无用.我想知道是否有办法解决这个问题.我不想重新定义线程对象,因为在我看来,它需要更多的工作来重新实现已经编写和工作的代码.
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.
推荐答案
这是一个关于如何实现它的简短示例.如果您希望我添加注释,请告诉我.
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_())
这篇关于在python中用Qt实现一个取消按钮的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!