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

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

> [PySide2.QtCore.QObject.startTimer(interval[, timerType=Qt.CoarseTimer])](https://doc.qt.io/qtforpython-5/PySide2/QtCore/QObject.html#PySide2.QtCore.PySide2.QtCore.QObject.event)

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

```python: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処理が呼び出されない様子？とりあえず現状動作としてこうだったという事をメモしておく。

```bash
$ 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.
```




特に以下が気になりイベントループを２つ実行したところ、同じイベントループの他処理に影響しました。
イベントループ内で時間の掛かる処理は別スレッドにするか、別の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を２つにしたところ

```python: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のtimerEvent`、`Timer ID: 2のtimerEvent`、、が順番に返ってきました。

```bash
$ 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])](https://doc.qt.io/qtforpython-5/PySide2/QtCore/QObject.html#PySide2.QtCore.PySide2.QtCore.QObject.event)