hetsch
hetsch

Reputation: 1578

PyQt5: QTimer out of sync after minimizing window (OSX)

I have a problem with the usage of the pyqt5 QTimer class and OSX.

If following simple application gets minimized by clicking on the yellow button on the top left of the window, the application moves as expected to the bottom right of the dock bar and the timer increases every second.

The strange thing is, that after circa 2:30 minutes the timer slows extremly down counting, lets say every 10 seconds. I suspect that this has something to do with a "freeze" or "sleep" mode under OSX?

What I want to have is a reliable timer wich counts every second. Is there a some trick under pyqt that I'm missing?

---- EDIT ----

This also happens if the window looses focus.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import time

from PyQt5.QtWidgets import *
from PyQt5.QtCore import Qt, QTimer, pyqtSlot


class Main(QWidget):

    def __init__(self, parent=None):
        super().__init__(parent)
        self.seconds = 0
        self.init_ui()

    def init_ui(self):
        self.timer = QTimer()
        self.timer.setInterval(1000)
        self.timer.setTimerType(Qt.PreciseTimer)
        self.timer.timeout.connect(self.on_timer)
        self.timer.start()

    @pyqtSlot()
    def on_timer(self):
        self.seconds += 1
        print(time.strftime("%H:%M:%S", time.gmtime(self.seconds)))


if __name__ == '__main__':
    import sys

    app = QApplication(sys.argv)

    screen = Main()
    screen.show()

    sys.exit(app.exec_())

Some informations of my system:

Python: 3.4.3
PyQt: 5.4.0
OSX: 10.9.5
uname -a: Darwin mac-pro 13.4.0 Darwin Kernel Version 13.4.0: Wed Mar 18 16:20:14 PDT 2015; root:xnu-2422.115.14~1/RELEASE_X86_64 x86_64

Upvotes: 1

Views: 2054

Answers (2)

hetsch
hetsch

Reputation: 1578

Additionally to @deets, this would be another possible solution with checking the starttime and using a delta for calculating the offset to that starttime. At least it works for me now:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import time
import os

from datetime import datetime

from PyQt5.QtWidgets import *
from PyQt5.QtCore import Qt, QObject, QTimer, QThread, pyqtSlot, pyqtSignal


class Main(QWidget):

    def __init__(self, parent=None):
        super().__init__(parent)
        self.ellapsed_sec = 0
        self.init_ui()

    def init_ui(self):
        self.timer = QTimer()
        self.timer.setInterval(1000)
        self.timer.setTimerType(Qt.PreciseTimer)
        self.timer.timeout.connect(self.on_timer)

        self.timestamp_start = datetime.now()

        self.timer.start(1000)

    @pyqtSlot()
    def on_timer(self):
        self.ellapsed_sec += 1

        delta = datetime.now() - self.timestamp_start
        delta = delta.total_seconds()
        if self.ellapsed_sec != delta:
            print('Using delta', delta)
            self.ellapsed_sec = delta

        print(time.strftime("%H:%M:%S", time.gmtime(self.ellapsed_sec)))


if __name__ == '__main__':
    import sys

    app = QApplication(sys.argv)

    screen = Main()
    screen.show()

    sys.exit(app.exec_())

Upvotes: 0

deets
deets

Reputation: 6395

This is probably related to a feature of OS X called "AppNap" which will aggressively reduces timer triggering, and syncs them even between apps, to conserve battery life.

You can turn it off via commandline on a per-app-basis using the command

 defaults write <domain> NSAppSleepDisabled -bool YES

where <domain> is your applications bundle-identifier. You can even invoke that within your app via subprocess - that's what I did.

It is not an ideal solution though, consider working around the timer. There might be other ways to solve this depending on your actual use-case.

Upvotes: 1

Related Questions