Search code examples
pyqt5qt5focus

Understanding Qt5 / PyQt5 focus behavior


I have a very simple test application:

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())

I have two, possibly related, problems with this code:

  1. If I click on btn it dutifully starts QProgressBar updating and disables itself, but if I click on it twice I will see two iterations of the pbar; shouldn't clicks on a disabled widget be ignored?
  2. As soon as btn is disabled txt contents are selected, in spite of it being ReadOnly; is there some way to prevent this "focusing next" behavior? Leaving everything unfocused till next click would be best.

Is this behavior considered "normal"?


Solution

  • The first issue is due to the fact that you used a blocking function (which should never happen) in event-driven systems like ui frameworks. What happens is that the for loop with the sleep function prevents Qt to correctly process events, including input events: your second click gets "queued" and can only be processed as soon as the control is returned to the event manager (when the function finally returns), and since, at that point, you've re-enabled the button, only then the previously queud event gets finally processed (and the function is called again).

    The solution is simple: avoid any situation like this, if you need a time-based iteration, use a QTimer, if you need parallel and non blocking processing, use a QThread.