了解 Qt5 / PyQt5 焦点行为

Understanding Qt5 / PyQt5 focus behavior

我有一个非常简单的测试应用程序:

from PyQt5.QtWidgets import *
import sys
import time


class Example(QWidget):

    def __init__(self):
        super().__init__()

        self.pbar = QProgressBar(self)
        self.pbar.setGeometry(30, 40, 200, 25)
        self.btn = QPushButton('Start', self)
        self.btn.move(40, 80)
        self.btn.clicked.connect(self.do_action)
        self.txt = QLineEdit('Some info goes here', self)
        self.txt.setReadOnly(True)
        self.txt.move(40, 120)
        self.setGeometry(300, 300, 280, 170)
        self.setWindowTitle("Python")
        self.show()

    def do_action(self):
        # setting for loop to set value of progress bar
        self.btn.setDisabled(True)
        for i in range(101):
            time.sleep(0.05)
            self.pbar.setValue(i)
        self.btn.setEnabled(True)


if __name__ == '__main__':
    App = QApplication(sys.argv)
    window = Example()
    sys.exit(App.exec())

这段代码有两个可能相关的问题:

  1. 如果我点击 btn 它尽职尽责地开始 QProgressBar 更新并自行禁用,但是如果我点击它两次我将看到 两次 迭代pbar;不应该忽略对已禁用的小部件的点击吗?
  2. 一旦 btn 被禁用 txt 内容被选中,尽管它是只读的;有什么方法可以防止这种“关注下一个”行为吗?最好在下次点击之前让所有内容都不集中。

这种行为被认为是“正常”的吗?

第一个问题是由于您在 ui 框架等事件驱动系统中使用了阻塞函数(永远不会 发生)。发生的事情是带有睡眠函数的 for 循环阻止 Qt 正确处理事件,包括输入事件:你的第二次点击被“排队”并且只能在控制返回到事件管理器后立即处理(当函数最终returns),因为此时您已重新启用该按钮,只有到那时才最终处理之前的排队事件(并再次调用该函数)。

解决方法很简单:避免这种情况,如果需要基于时间的迭代,使用QTimer,如果需要并行和非阻塞处理,使用QThread。