PyQt thread execution timeout in the background

Heads up! You've already completed this tutorial.

Alex1 | 2021-05-07 07:28:49 UTC | #1

When I click the minimize button to perform this operation in the background,Threads often execute beyond the limited time, Waking up the program in the background is back to normal. Please be patient, it will appear in about a minute.

83EABA78-8324-4105-9E6F-0ADD2998CBC5|652x238, 50%

EFB3D59A-54C5-4D3E-8128-C303C2774578|406x424, 50%

python
import sys, random, time, functools
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QMainWindow, QHBoxLayout
from PyQt5.QtCore import QThread, QObject

def clock(func):
    @functools.wraps(func)
    def clocked(*args, **kwargs):
        t0 = time.time()
        result = func(*args, **kwargs)
        elapsed = time.time() - t0
        name = func.__name__
        l_arg = []
        if args:
            l_arg.append(', '.join(repr(arg) for arg in args))
        arg_str = ', '.join(l_arg)
        print('[%0.5fs] %s(%s)' % (elapsed, name, arg_str))
        return result
    return clocked

@clock
def go_sleep(sleep_time):
    time.sleep(sleep_time)

def go_run():
    for i in range(100):
        go_sleep(random.randint(1, 3))

class WorkThread(QObject):
    def __int__(self):
        super(WorkThread, self).__init__()

    def run(self):
        go_run()

class WinForm(QMainWindow):
    def __init__(self, parent=None):
        super(WinForm, self).__init__(parent)
        self.button1 = QPushButton('Run')
        self.button1.clicked.connect(self.onButtonClick)
        self._thread = QThread(self)
        self.wt = WorkThread()
        self.wt.moveToThread(self._thread)
        layout = QHBoxLayout()
        layout.addWidget(self.button1)
        main_frame = QWidget()
        main_frame.setLayout(layout)
        self.setCentralWidget(main_frame)

    def onButtonClick(self):
        self.button1.setText('Running')
        self._thread.started.connect(self.wt.run)
        self._thread.start()

if __name__ == "__main__":
    app = QApplication(sys.argv)
    form = WinForm()
    form.show()
    sys.exit(app.exec_())

martin | 2020-10-23 08:30:58 UTC | #2

Do you mean that the execution of those threads lasts longer than the time given by the name? i.e. a time.sleep(1) lasts more than a single second?

That is actually expected! The time.sleep() method can sleep for longer than requested depending on system activity -- if the OS doesn't return control back to the thread, then the sleep won't finish. I guess that when you minimize the application the priority of the threads is being reduced.

From the Python documentation

time.sleep ( secs ) Suspend execution of the calling thread for the given number of seconds. The argument may be a floating point number to indicate a more precise sleep time. The actual suspension time may be less than that requested because any caught signal will terminate the sleep() following execution of that signal’s catching routine. Also, the suspension time may be longer than requested by an arbitrary amount because of the scheduling of other activity in the system.

If you don't want this to happen, you can set the priority on your threads. For a single QThread you can use .setPriority. It looks like the equivalent for a QRunnable is passing priority when starting it on your thread pool (although it says this determines run order so it may be subtley different)

python
threadpool.start(runnable, priority = 0)

Something worth covering in the book I think!


Alex1 | 2020-10-25 12:58:16 UTC | #3

Hi, martin ,Thank you for answering my question. I want to simulate I/O operation by Sleep(). I tried many types of priorities but none of them achieved the expected.Which priority should I use?

python
HighestPriority = 5
HighPriority = 4
IdlePriority = 0
InheritPriority = 7
LowestPriority = 1
LowPriority = 2
NormalPriority = 3
TimeCriticalPriority = 6

self._thread.setPriority(QThread.HighPriority)


Alex1 | 2020-10-28 06:09:23 UTC | #4

I am using macOS, this is related to the operating system, it can work normally in Windows. setPriority That doc included this line: "The effect of the priority parameter is dependent on the operating system's scheduling policy. In particular, the priority will be ignored on systems that do not support thread priorities."


martin | 2020-10-28 11:50:48 UTC | #5

this is related to the operating system, it can work normally in Windows.

Ah, good to know. I'm wondering if you can use another approach to simulate the IO delay -- a sleep is very explicitly saying "do nothing for X", and perhaps the GIL is getting involved here. An actual IO call (even if slow) might wake up more reliably, since the end-time isn't known in advanced.

What kind of IO are wanting to test? For API calls there are things like Slowwly for example.


Alex1 | 2020-10-28 16:55:40 UTC | #6

Network I/O with Socket communication, Can you give a small demonstration? thank you very much. :smiley:


Alex1 | 2020-11-17 05:54:52 UTC | #7

This is a promising solution:

python
 defaults write NSGlobalDomain NSAppSleepDisabled -bool YES

Over 10,000 developers have bought Create GUI Applications with Python & Qt!
Create GUI Applications with Python & Qt5
Take a look

Downloadable ebook (PDF, ePub) & Complete Source code

Also available from Leanpub and Amazon Paperback

[[ discount.discount_pc ]]% OFF for the next [[ discount.duration ]] [[discount.description ]] with the code [[ discount.coupon_code ]]

Purchasing Power Parity

Developers in [[ country ]] get [[ discount.discount_pc ]]% OFF on all books & courses with code [[ discount.coupon_code ]]
Well done, you've finished this tutorial! Mark As Complete
[[ user.completed.length ]] completed [[ user.streak+1 ]] day streak

PyQt thread execution timeout in the background was written by Martin Fitzpatrick .

Martin Fitzpatrick has been developing Python/Qt apps for 8 years. Building desktop applications to make data-analysis tools more user-friendly, Python was the obvious choice. Starting with Tk, later moving to wxWidgets and finally adopting PyQt.