Python PyQt清除未完成的用户输入

Python PyQt清除未完成的用户输入,python,pyqt,Python,Pyqt,在我的程序中,我有一个按钮,它连接到一个运行大约需要15秒的函数。我希望在此运行时忽略所有用户输入。按下btn时,调用以下功能: def btn_call(self) : self.btn.setEnable(False) # disables the button and shows it greyed out fn() # some function that takes ~15 seconds to run self.btn.setEnable(True) # re

在我的程序中,我有一个按钮,它连接到一个运行大约需要15秒的函数。我希望在此运行时忽略所有用户输入。按下
btn
时,调用以下功能:

def btn_call(self) :
    self.btn.setEnable(False) # disables the button and shows it greyed out
    fn() # some function that takes ~15 seconds to run
    self.btn.setEnable(True) # re-enables the button
希望是在
fn()
运行时,防止程序对
btn
按下做出响应。当前,如果在运行
fn()
时按下
btn
,则每次按下
btn
时,
fn()
都将运行

是否有办法清除
fn()
运行时发生的所有用户输入

编辑: 增加了一个MWE。如果单击“运行函数”。函数将开始。如果单击“运行函数”。当函数仍在运行时,该函数将再次运行。这是我要制止的行为

from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *

import sys
from time import sleep

def fn(num) :
    for i in range(num) :
        sleep(0.5)
        yield i

class MainWindow(QWidget) :
    def __init__(self) :
        super().__init__()
        self.layout = QVBoxLayout(self)
        
        self.btn_fn = QPushButton("Run function.")
        self.btn_fn.clicked.connect(self.run_fn)
        self.layout.addWidget(self.btn_fn)
        
        self.prog_bar = QProgressBar()
        self.layout.addWidget(self.prog_bar)
        self.show()
    
    def run_fn(self) :
        self.btn_fn.setEnabled(False)
        num = 20
        self.prog_bar.setValue( 0 )
        for i in fn(num) :
            self.prog_bar.setValue( 100*(i+1)/num )
        self.btn_fn.setEnabled(True)
            
if __name__ == '__main__' :
    app = QApplication(sys.argv)
    window = MainWindow()
    sys.exit( app.exec_() )
当在主Qt线程中运行阻塞函数时,结果是所有事件都在该线程中排队所有事件,包括重新绘制和鼠标事件

QApplication仍将接收来自OS的传入事件,并将它们添加到事件队列中,直到阻塞函数返回

这导致以下重要方面:

  • Qt侧所有需要时间的操作将被阻止,直到控制返回到Qt事件循环
  • 如果涉及动画,则不会正确完成小部件重新绘制(因此,由于上一点的原因,即使按钮未启用,按钮仍可能看起来已启用)
  • 小部件可能接收到的所有键盘/鼠标事件都将排队,并且只有在控件返回到主Qt事件循环时才进行实际处理
从理论上讲,您可以做的是在计时功能即将启动时立即阻止按钮发出的所有信号,然后使用单次触发QTimer(这确保在Qt队列(包括定时事件)被清除后,立即处理其
超时
呼叫)解除对信号发射的封锁

这在以下示例中很清楚:

从PyQt5导入QtCore、QtWidgets
从时间上导入睡眠
def veryLongFunction():
睡眠(5)
def restoreButton():
label.setText('Idle'))
按钮。块信号(错误)
按钮。setEnabled(真)
def start():
label.setText('Working…'))
按钮。设置已启用(错误)
按钮。块信号(真)
qtwidts.QApplication.processEvents()
veryLongFunction()
qtwidts.QApplication.processEvents()
QtCore.QTimer.singleShot(0,restoreButton)
导入系统
app=qtwidts.QApplication(sys.argv)
widget=qtwidts.QWidget()
button=QtWidgets.QPushButton('启动长函数')
label=qtwidts.QLabel('Idle',alignment=QtCore.Qt.AlignCenter)
layout=qtwidts.QVBoxLayout(小部件)
layout.addWidget(按钮)
layout.addWidget(标签)
geo=QtCore.QRect(0,0,160,80)
geo.moveCenter(app.primaryScreen().availableGeometry().center())
widget.setGeometry(geo)
widget.show()
按钮。单击。连接(开始)
sys.exit(app.exec_())
在上面的示例中,发送到按钮的任何鼠标单击都将被忽略,直到再次启用该按钮,这从技术上解决了您的问题

但是,不,这不是一个好方法:这是一个非常糟糕的方法。
不仅在不支持对先前绘制的设备进行合成或双缓冲的平台上会出现更新问题(处理时移动窗口将显示图形工件),而且最重要的是,这不是正确的方法

只要“时间要求高”的进程只需单独完成(意味着不需要并发处理),QThread就是最好的选择:

#。。。
类VeryLongFunctionThread(QtCore.QThread):
def运行(自):
睡眠(5)
def start():
label.setText('Working…'))
按钮。设置已启用(错误)
veryLongFunction.start()
def end():
label.setText('Idle'))
按钮。setEnabled(真)
# ...
按钮。单击。连接(开始)
veryLongFunction=VeryLongFunctionThread()
veryLongFunction.finished.connect(结束)
# ...

如果处理需要同时运行多次,另一种方法是使用QRunnable,但请记住它不是从QObject继承的(因此它不支持信号),您需要找到一种方法来通知主线程它的状态。

请提供一个解决此问题的方法,谢谢。我对这个主题做了一些搜索,另一个stackoverflow也有帮助。对于将来希望在传递参数和更新进度条时实现此功能的用户,请访问: