[PyQt] wx.MilliSleep equivalent

Chris Pezley chris at pezley.net
Wed Oct 3 16:45:30 BST 2018


It means that whenever you are doing something which could take a lot of 
time, your program will become unresponsive unless you call 
processEvents. For example, let's say you have a for loop with a very 
heavy calculation in each iteration:

         for i in range(n):
             processEvents()
             expensive_operation(n)

If you do not processEvents, then in Windows the program will turn dark 
and you'll get the dialog saying that the program is not responding. If 
the expensive operation takes too long, even calling processEvents in 
each iteration step might not be enough. For that reason, you should use 
threading or multiprocessing for this kind of work.

I've created an example for you which shows the difference between doing 
an operation directly vs using threading.

         import time
         import threading
         from PyQt5 import QtCore, QtWidgets


         class Widget(QtWidgets.QWidget):
             # Define a signal which will transfer the data of our 
lengthy operation to some
             # function.
             WORK_DONE = QtCore.pyqtSignal(int)

             def __init__(self):
                 super().__init__()
                 # Create and connect the blocking button directly to 
the blocking work function.
                 # If we click this, then the UI should go grey and not 
react.
                 self.blocking_button = QtWidgets.QPushButton('Blocks')
self.blocking_button.clicked.connect(self.do_work_blocking)

                 # Create and connect the non-blocking button to our 
work function which uses
                 # threads to execute the lengthy operation.
                 self.nonblocking_button = QtWidgets.QPushButton('Does 
not block')
self.nonblocking_button.clicked.connect(self.do_work_threaded)

                 # Add the buttons to the widget.
                 layout = QtWidgets.QVBoxLayout(self)
                 layout.addWidget(self.blocking_button)
                 layout.addWidget(self.nonblocking_button)

                 # Connect our signal to our function which handles the 
finished results. This
                 # means that whenever we emit WORK_DONE, the 
self.work_done function is called
                 # with the value we give to emit.
                 self.WORK_DONE.connect(self.work_done)

                 # Here we save the input data we need for our lengthy 
blocking operation.
                 self.input_data_blocking = 1

                 # And here is the input data for the version which does 
not block.
                 self.input_data_threaded = 2

             def do_work_blocking(self):
                 print('Starting work (this will block)!')
                 self.heavy_work(self.input_data_blocking)

             def do_work_threaded(self, input_value):
                 """
                 This function creates a thread to execute our lengthy 
operation in parallel, so
                 that the Qt event loop can still run while we are doing 
this. This works for any
                 operations that stay in python. If you are calling some 
C functions that don't
                 release the GIL, then you need to use muiltiprocessing 
instead of threading.
                 """
                 # Right now, if you click the non-blocking button 
multiple times, it will start the
                 # work multiple times. If you want to block this, then 
do something like this:
                 #     if self._thread is not None and 
self._thread.running():
                 #          return
                 #     self._thread = 
threading.Thread(target=self.heavy_work, args=[self.input_data_threaded])
                 #     self._thread.start()
                 # Then in the work_done function set self._thread to None.
                 print('Starting work (this will NOT block)!')
                 thread = threading.Thread(target=self.heavy_work, 
args=[self.input_data_threaded])
                 thread.start()

             def heavy_work(self, input_value):
                 print('Processing value:', input_value)
                 time.sleep(10)
                 self.WORK_DONE.emit(input_value + 1)

             def work_done(self, value):
                 print('Work done! Got value:', value)


         def main():
             app = QtWidgets.QApplication([])

             widget = Widget()
             widget.show()

             app.exec_()


         if __name__ == '__main__':
             main()


On 03/10/2018 16.26, Tong Zhang wrote:
> Thanks Chris, it works! BTW, I read the doc of 'processEvents' 
> (http://doc.qt.io/qt-5/qcoreapplication.html#processEvents), which 
> says 'You can call this function occasionally when your program is 
> busy performing a long operation (e.g. copying a file).', what does 
> this mean? Why call this function helps, is it to indicate when the 
> busy task is finished?
>
> @Kyle, wx.MilliSleep does non-blocking wait, that is the app still can 
> respond other events during the period of millisleep time. While what 
> I wanna do is pretty much simple, that is just wait for some time 
> before going to next step while not block the app.
>
> @Barry time.sleep() does not help since it will block the app.
>
> 'qWait' in QTest actually provides another way to do, just like Jones, 
> Bryan mentioned in previous email.
>
> These are so far what I've learnt, thanks.
>
> Tong
>
>
> On 10/03/2018 05:44 AM, Chris Pezley wrote:
>> I misread the description of the wait argument in processEvents. It 
>> looks like it will return earlier if it can. You'll have to define 
>> your own blocking processEvents:
>>
>>
>> import time
>>
>> from PyQt5 import QtWidgets
>>
>>
>> def processWait(time_to_block):
>>      """
>>      :param time_to_block: The time to block in seconds.
>>      :type time_to_block:  float
>>      """
>>      start_time = time.time()
>>
>>      # Keep processing events until we've exceeded the specified time.
>>      while time.time() - start_time < time_to_block:
>>          QtWidgets.qApp.processEvents()
>>
>>
>>
>> On 10/01/2018 11:02 PM, Tong Zhang wrote:
>>> Hello Chris,
>>>
>>> Thanks, I've tried like this:
>>>
>>> from PyQt5.QtCore import QCoreApplication
>>> from PyQt5.QtCore import QEventLoop
>>>
>>> QCoreApplication.processEvents(QEventLoop.WaitForMoreEvents, msec)
>>>
>>> but had no luck, it does not behavior the same as qWait(msec). Did I 
>>> use this method correctly?
>>>
>>> Tong
>>>
>>> On 09/22/2018 02:02 AM, Chris Pezley wrote:
>>>> Hi Tong,
>>>>
>>>> maybe what you want is "QtWidgets.qApp.processEvents()"? (make sure 
>>>> to look at the one with the optional argument for maxtime)
>>>> http://doc.qt.io/qt-5/qcoreapplication.html#processEvents-1
>>>>
>>>> On 21/09/2018 20.22, Tong Zhang wrote:
>>>>> Hello,
>>>>>
>>>>> When I'm building applications by wxPython, there is a function 
>>>>> wx.MilliSleep could be used to do non-GUI-block sleep, while I 
>>>>> cannot find the similar thing in PyQt, is it available or not? 
>>>>> Otherwise, what I can do to is to use QThread. Any comment is 
>>>>> appreciated!
>>>>>
>>>>> Tong
>>>>>
>>>>>
>>>>> _______________________________________________
>>>>> PyQt mailing listPyQt at riverbankcomputing.com
>>>>> https://www.riverbankcomputing.com/mailman/listinfo/pyqt
>>>>
>>>>
>>>>
>>>>
>>>> _______________________________________________
>>>> PyQt mailing list    PyQt at riverbankcomputing.com
>>>> https://www.riverbankcomputing.com/mailman/listinfo/pyqt
>>>>
>>>
>>
>



More information about the PyQt mailing list