31. Timers
In applications you often want to perform some tasks regularly or even just at some point in the future. In PyQt6 this is accomplished by using timers. The QTimer class gives you access to two different types of timer — recurring or interval timers, and single shot or one off timers. Both can be hooked up to functions and methods in your application to cause them to execute whenever you need. In this chapter we’ll look at these two types of timer and demonstrate how you can use them to automate your apps.
Interval timers
Using the QTimer class you can create interval timers for any duration in msecs. On each specified duration, the timer will time out. To trigger something to happen each time this occurs, you connect the timer’s timeout signal to whatever you want to do — just like you would with any other signal.
In the example below we setup up a timer, running every 100 msecs , which rotates a dial.
Listing 232. further/timers_1.py
import sys
from PyQt6.QtCore import QTimer
from PyQt6.QtWidgets import QApplication, QDial, QMainWindow
class MainWindow (QMainWindow):
def __init__ (self):
super().__init__()
self.dial = QDial()
self.dial.setRange( 0 , 100 )
self.dial.setValue( 0 )
self.timer = QTimer()
self.timer.setInterval( 10 )
self.timer.timeout.connect(self.update_dial)
self.timer.start()
self.setCentralWidget(self.dial)
def update_dial (self):
value = self.dial.value()
value += 1 # increment
if value > 100 :
value = 0
self.dial.setValue(value)
app = QApplication(sys.argv)
w = MainWindow()
w.show()
app. exec ()
This is just a simple example — you can do anything you want in the connected methods. However, the standard event loop rules apply and triggered tasks should return quickly to avoid blocking the GUI. If you need to perform regular long-running tasks, you can use the timer to trigger a separate thread or process.
ë
You must keep a reference to the created timer object, for the
duration that the timer is running. If you don’t then the timer
object will be deleted and the timer will stop — without
warning. If you create a timer and it doesn’t seem to be working,
check you’ve kept a reference to the object.
If the accuracy of the timer is important, you can adjust this by passing a Qt.QTimerType value to timer.setTimerType.
Listing 233. further/timers_1b.py
self.timer.setTimerType(Qt.TimerType.PreciseTimer)
The available options are shown below. Don’t make your timers more accurate than they need to be. You may block important UI updates.
Timer type Value Description
Qt.TimerType.PreciseTimer 0 Precise timers try to keep
millisecond accuracy
Qt.TimerType.CoarseTimer 1 Coarse timers try to keep
accuracy within 5% of the
desired interval
Qt.TimerType.VeryCoarseTimer 2 Very coarse timers only keep
full second accuracy
Note that even the most precise timer only tries to keep millisecond accuracy. Anything in the GUI thread risks being blocked by UI updates and your own Python code. If accuracy is that important, then put the work in another thread or process you control completely.
Single shot timers
If you want to trigger something, but only have it occur once, you can use a single
shot timer. These are constructed using static methods on the QTimer object. The simplest form just accepts a time in msecs and whatever callable you want to trigger when the timer fires — for example, the method you want to run.
In the following example we use a single shot timer to uncheck a toggleable push button after its pushed down.
Listing 234. further/timers_2.py
import sys
from PyQt6.QtCore import QTimer
from PyQt6.QtWidgets import QApplication, QMainWindow, QPushButton
class MainWindow (QMainWindow):
def __init__ (self):
super().__init__()
self.button = QPushButton("Press me!")
self.button.setCheckable(True)
self.button.setStyleSheet(
# Make the check state red so easier to see.
"QPushButton:checked { background-color: red; }"
)
self.button.toggled.connect(self.button_checked)
self.setCentralWidget(self.button)
def button_checked (self):
print ("Button checked")
QTimer.singleShot( 1000 , self.uncheck_button) ①
def uncheck_button (self):
print ("Button unchecked")
self.button.setChecked(False)
app = QApplication(sys.argv)
w = MainWindow()
w.show()
app. exec ()
①The uncheck_button method will be called after 1000 msecs.
If you run this example and press the button you’ll see it become checked and turn red — using custom styles. Then after a second the button will revert to its
unchecked state.
To achieve this we’ve chained together two custom methods using a single shot timer. First we connect the toggled signal from the button to a method button_checked. This fires off the single shot timer. When this timer times out, it calls uncheck_button which actually unchecks the button. This allows us to postpone the unchecking of the button by a configurable amount.
Unlike interval timers, you don’t need to keep a reference to the created timer — the QTimer.singleShot() method doesn’t return one.
Postponing-via the event queue
You can use zero-timed single shot timers to postpone operations via the event queue. When the timer is triggered the timer event goes to the back of the event queue (as it is a new event) and will only get processed once all existing events have been processed.
Remember that signals (and events) are only processed once you return control from Python to the event loop. If you trigger a series of signals in a method, and want to do something after they’ve occurred, you can’t do it directly in the same method. The code there will be executed before the signals take effect.
def my_method (self):
self.some_signal.emit()
self.some_other_signal.emit()
do_something_here() ①
①This function will be executed before the two signals take effect.
By using a single shot timer you can push the subsequent operation to the back of the event queue & ensure it occurs last.
def my_method (self):
self.some_signal.emit()
self.some_other_signal.emit()
QTimer.singleShot( 0 , do_something_here) ①
①This will be executed after the signal’s effects.
j
This technique only guarantees that the do_something_here
function executes after the preceding signals, not any
downstream effects of them. Don’t be tempted to increase the
value of msecs to work around this, as this makes your
application dependent on system timings.
'PyQt5_' 카테고리의 다른 글
Extending Signals (0) | 2023.03.13 |
---|---|
Extending Signals (0) | 2023.03.13 |
Plotting with Matplotlib. (0) | 2023.03.13 |
Plotting with PyQtGraph (0) | 2023.03.13 |
Running external commands & processes (0) | 2023.03.13 |
댓글