1
Help us understand the problem. What are the problem?

posted at

updated at

QtのstartTimerによるEventCallbackは、処理がcallback周期から遅れるとどうなるのか確認した際のメモ

以下のQtのstartTimerによるEventCallback挙動確認時のメモ

PySide2.QtCore.QObject.startTimer(interval[, timerType=Qt.CoarseTimer])

仕様には特に本件について何か書いているわけではなさそうだ、、
実動作として、timer event callback頻度は1msec周期にしつつ、event処理の中で1000msec sleepを入れてみる。

test.py
#!/usr/bin/python3
# -*- coding: utf-8 -*-

import sys
import time
import datetime
from PyQt5.QtWidgets import QMainWindow, QApplication
from PyQt5.QtCore import Qt, QObject

class MyObject(QObject):

    def __init__(self, parent):
        QObject.__init__(self, parent)

        self.startTimer(1)           # 1-millisecond timer
        #self.startTimer(50)          # 50-millisecond timer
        #self.startTimer(100)         # 1000-millisecond timer
        #self.startTimer(1000)        # 1-second timer
        #self.startTimer(60000)       # 1-minute timer

    def timerEvent(self, event):
        print("Timer ID:", event.timerId())
        print("datetime now:", datetime.datetime.now())
        time.sleep(1)

class Example(QMainWindow):
    def __init__(self):
        super().__init__()
        self.myobject = MyObject(self) 

if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec_())

実行したところ、1000msec間隔でevent処理が実行されているようにみえる。
どうやらevent処理中(今回の場合だと、1000msec sleep処理中)は、次のevent callback処理が呼び出されない様子?とりあえず現状動作としてこうだったという事をメモしておく。

$ python test.py
Timer ID: 1
datetime now: 2022-01-09 18:00:46.863970
Timer ID: 1
datetime now: 2022-01-09 18:00:47.866091
Timer ID: 1
datetime now: 2022-01-09 18:00:48.875696
Timer ID: 1
datetime now: 2022-01-09 18:00:49.914547
Timer ID: 1
datetime now: 2022-01-09 18:00:50.921863
Timer ID: 1
datetime now: 2022-01-09 18:00:51.922785
Timer ID: 1
datetime now: 2022-01-09 18:00:52.924472

追記

@tenmyoさんコメントの通り、以下にTimersに関する記載がありました。

Timers | Qt Core 6.2.2
https://doc.qt.io/qt-6/timers.html

For this mechanism to work, the application must run in an event loop. 
You start an event loop with QApplication::exec(). When a timer fires, 
the application sends a QTimerEvent, and the flow of control leaves 
the event loop until the timer event is processed. This implies that a 
timer cannot fire while your application is busy doing something else. 
In other words: the accuracy of timers depends on the granularity of 
your application.

特に以下が気になりイベントループを2つ実行したところ、同じイベントループの他処理に影響しました。
イベントループ内で時間の掛かる処理は別スレッドにするか、別のtimer処理に分ける等、設計する場合は考慮が必要だと思いました。

This implies that a 
timer cannot fire while your application is busy doing something else. 
In other words: the accuracy of timers depends on the granularity of 
your application.

以下の通りMyObjectを2つにしたところ

test.py
#!/usr/bin/python3                                                                                                                                                                                          
# -*- coding: utf-8 -*-                                                                                                                                                                                     

import sys
import time
import datetime
from PyQt5.QtWidgets import QMainWindow, QApplication
from PyQt5.QtCore import Qt, QObject

class MyObject(QObject):

    def __init__(self, parent):
        QObject.__init__(self, parent)

        self.startTimer(1)           # 1-millisecond timer                                                                                                                                                  
        #self.startTimer(50)          # 50-millisecond timer                                                                                                                                                
        #self.startTimer(100)         # 1000-millisecond timer                                                                                                                                              
        #self.startTimer(1000)        # 1-second timer                                                                                                                                                      
        #self.startTimer(60000)       # 1-minute timer                                                                                                                                                      

    def timerEvent(self, event):
        print("Timer ID:", event.timerId(),"  datetime now:", datetime.datetime.now())
        time.sleep(1)

class Example(QMainWindow):
    def __init__(self):
        super().__init__()
        self.myobject = MyObject(self)
        self.myobject2 = MyObject(self)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec_())

1000msec毎にTimer ID: 1のtimerEventTimer ID: 2のtimerEvent、、が順番に返ってきました。

$ python qt.py
Timer ID: 1   datetime now: 2022-01-09 23:14:21.350370
Timer ID: 2   datetime now: 2022-01-09 23:14:22.351786
Timer ID: 1   datetime now: 2022-01-09 23:14:23.353236
Timer ID: 2   datetime now: 2022-01-09 23:14:24.354172
Timer ID: 1   datetime now: 2022-01-09 23:14:25.367485
Timer ID: 2   datetime now: 2022-01-09 23:14:26.367732

参考

PySide2.QtCore.QObject.startTimer(interval[, timerType=Qt.CoarseTimer])

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
1
Help us understand the problem. What are the problem?