在python中用Qt实现一个取消按钮 [英] Implement a cancel button with Qt in python

查看:254
本文介绍了在python中用Qt实现一个取消按钮的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这个问题可能很愚蠢,但提供一些背景知识:我得到了一堆用 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆