最近在测试中,有一个按钮的切换功能,用户点击开始后,会显示停止,用户点击停止,会恢复为开始按钮。
但是,在用户点击停止时,因为要等待子线程完成任务,才会停止。
这个时候如果继续疯狂点击停止,那么子线程完成后,恢复为开始按钮后又立刻被点击,然后又会被点击停止……导致出现不期待的结果。
那么该如何避免呢?
如果你只是想在停止过程中,不再单独接收点击事件,你可以通过几种方式控制它,并且在线程恢复后不会响应阻塞期间积累的点击操作。
下面是几种实现方式:
需要注意的是,以下方式都需要执行的任务为一个单独的线程时才有效。
因为当主线程被阻塞时,事件循环无法处理任何事件。
1. 在后台线程中执行任务
为了防止主界面阻塞,你可以考虑将耗时操作放到后台线程中执行,这样不会导致主线程无响应,同时你可以禁用主界面的交互:
from PyQt5.QtCore import QThread, pyqtSignal
class Worker(QThread):
finished = pyqtSignal()
def run(self):
# 执行耗时任务
# ...
self.finished.emit()
def start_long_task():
main_window.setEnabled(False) # 禁用主窗口
worker = Worker()
worker.finished.connect(lambda: main_window.setEnabled(True)) # 恢复主窗口
worker.start()
2. 使用 setEnabled(False)
禁用窗口
在执行阻塞操作前,你可以禁用整个窗口(或者特定控件)来防止用户的点击操作。
通过设置窗口的 setEnabled(False)
属性,可以有效阻止所有点击操作:
# 假设你有一个主窗口对象 main_window
main_window.setEnabled(False)
# 执行阻塞操作或长时间任务
# ...
# 完成后恢复
main_window.setEnabled(True)
这样在窗口被禁用时,所有点击事件都不会被处理。
3. 使用事件过滤器过滤点击事件
你可以通过事件过滤器来拦截并忽略所有鼠标事件,这样即使用户点击了界面也不会触发任何操作:
class BlockMouseEventFilter(QObject):
def eventFilter(self, obj, event):
if event.type() in (QEvent.MouseButtonPress, QEvent.MouseButtonRelease):
return True # 阻止鼠标事件传播
return super().eventFilter(obj, event)
# 在需要阻塞的时候安装过滤器
event_filter = BlockMouseEventFilter()
main_window.installEventFilter(event_filter)
# 解除阻塞后移除过滤器
main_window.removeEventFilter(event_filter)
4. 使用 QApplication.setOverrideCursor
设置等待光标
当程序在执行阻塞操作时,你可以显示等待光标,并忽略所有用户输入:
from PyQt5.QtWidgets import QApplication
from PyQt5.QtCore import Qt
# 设置等待光标
QApplication.setOverrideCursor(Qt.WaitCursor)
# 执行阻塞操作或长时间任务
# ...
# 恢复光标
QApplication.restoreOverrideCursor()
总结
无论是禁用窗口、过滤事件、还是使用等待光标,都可以达到在界面阻塞期间防止点击操作的效果,并且在任务完成后确保不会响应积累的点击操作。
你可以根据具体的应用场景选择合适的解决方案。