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:
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?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"?
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.