1

So I'm trying to create a timer or sorts, the start button starts the timer, i need to stop the timer with the stop button and then record the time... I can't seem to figure out how to stop the timer function once its started. Ive tried if statements, disconnect(), and many others... I realize that there is a timer in qt already but I'm trying to figure it out this way. thanks.

import sys
import time
from PyQt4 import QtCore, QtGui, uic

form_class = uic.loadUiType("/Users/Home/Desktop/Timer/timer.ui")[0]  


class MyWindowClass(QtGui.QMainWindow, form_class):
    def __init__(self, parent=None):
        QtGui.QMainWindow.__init__(self, parent)
        self.setupUi(self)
        self.startButton.clicked.connect(self.timer)

    def timer(self):
        timeL = 0
        self.stopButton.clicked.connect(False)
        while True:
            self.timeView.display(timeL)
            timeL = timeL + 1
            time.sleep(1)
            app.processEvents()


app = QtGui.QApplication(sys.argv)
myWindow = MyWindowClass(None)
myWindow.show()
app.exec_()
Kyle Joeckel
  • 469
  • 2
  • 9

3 Answers3

2

TL;DR: You're after QElapsedTimer, not QTimer.

The operating system is already measuring the passage of time for you. You won't do any better job at it yourself.

"I'm trying to figure it out this way" - it will be less accurate than using QElapsedTimer because you're assuming that exactly as much time has passed as you wished to sleep for. This is almost never true: the actual amount of time that passed is different than the argument to sleep. Worse yet, the errors are usually of a systematic kind too, so your time accumulation will have a bias and will get less accurate as time passes. So, don't do it. It makes no sense. Perhaps you're not telling us exactly what you're trying to do: if you're asking about a particular solution that doesn't work, it helps to say what problem the solution is supposedly to. Why are you trying to figure it out this (wrong) way?

There are conceptually three different kinds of timers in Qt:

  1. QElapsedTimer is like a stopwatch: it is an interface to the operating system's way of measuring the passage of time. That's the class you should be using to measure how much time has passed between the button clicks.

  2. QTime is like a wall clock: you can ask it what time it is through currentTime(), and take difference between two readings of time to obtain elapsed time. Use this class only if you need to know the absolute time, otherwise QElapsedTimer will offer better resolution for elapsed time measurements.

  3. QTimer is a source of timeouts: it is a way to periodically call your code. This is not meant to be used in measuring time, but merely to let your code run periodically, e.g. when you wish to refresh screen display, or implement a metronome that beeps periodically. There are no guarantees that your code will be called on time, and no guarantees that some ticks won't be missed. You want it guaranteed, you need to write a kernel driver, no way around that.

Below is a complete example using PyQt4, for Python 3.5. It uses QElapsedTimer to measure the passage of time between button presses, and QTimer to keep the time display refreshed.

#!/usr/bin/env python
#https://github.com/KubaO/stackoverflown/tree/master/questions/interval-timer-38036583
# -*- coding: utf-8 -*-
import sys
from PyQt4 import QtCore, QtGui

if __name__ == "__main__":
    running = False
    app = QtGui.QApplication(sys.argv)
    w = QtGui.QWidget()
    layout = QtGui.QVBoxLayout(w)
    label = QtGui.QLabel()
    button = QtGui.QPushButton('Start')
    timer = QtCore.QElapsedTimer()
    updateTimer = QtCore.QTimer()

    layout.addWidget(label)
    layout.addWidget(button)

    def onTimeout():
        label.setText('Elapsed: {0}ms'.format(timer.elapsed()))

    def onClicked():
        global running
        if running:
            onTimeout()
            updateTimer.stop()
            button.setText('Start')
        else:
            timer.start()
            updateTimer.start()
            button.setText('Stop')
        running = not running

    updateTimer.setInterval(1000/25) # ~25fps update rate
    updateTimer.timeout.connect(onTimeout)
    button.clicked.connect(onClicked)

    w.show()
    sys.exit(app.exec_())
Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313
  • Thanks a lot this is very helpful. I am new to qt and am just experimenting with a couple things that I may be able to implement in my current job(not yet programming) so the good and the bad has all been helpfull. Thanks again. – Kyle Joeckel Jun 28 '16 at 18:21
0

Use a flag to control the loop. Then reset the flag in the slot connected to the stop button:

        self.startButton.clicked.connect(self.timer)
        self.stopButton.clicked.connect(self.stop)

    def stop(self):
        self._timer_flag = False

    def timer(self):
        timeL = 0
        self._timer_flag = True
        while self._timer_flag:
            self.timeView.display(timeL)
            timeL = timeL + 1
            time.sleep(1)
            app.processEvents()

A QTimer is better, because there is no lag in updating the ui. But you can improve your example by using an inner loop to call processEvents more frequently:

    def timer(self):
        timeL = 0
        self._timer_flag = True
        while self._timer_flag:
            self.timeView.display(timeL)
            timeL = timeL + 1
            for _ in range(10):
                # process events for up to 50 milliseconds
                app.processEvents(QtCore.QEventLoop.AllEvents, 50)
                time.sleep(0.1)
ekhumoro
  • 115,249
  • 20
  • 229
  • 336
  • This is absolutely horrible advice that completely misses the point. The OP wants to measure the passage of time. Event loops and timers (`QTimer`) don't figure anywhere in it. The OS is already measuring the progression of time. Just ask it! `QElapsedTimer` is the api you want to use. – Kuba hasn't forgotten Monica Jun 27 '16 at 19:11
  • This works, but is a kludge so abominable that the only utility of this answer is in showing precisely how **not** to do it. If you see any such code, run away (or perhaps better fix it). – Kuba hasn't forgotten Monica Jun 27 '16 at 20:07
  • @KubaOber. I think the OP already understands that better alternatives are available. The way I read the question was that they just wanted to know how to fix their example code. And as you say, it **does** work: so I don't see any harm in answering it on that basis. What little advice I gave was **entirely** specific to the OP's example code, so I'm happy to stand by it. (Frankly, it never crossed my mind that anyone would take such example code as seriously as you appear to have done). – ekhumoro Jun 28 '16 at 01:24
-2

Sounds like you want to use QTimer on a QThread. This answer should give you everything you need. https://stackoverflow.com/a/18960953/5757280

Community
  • 1
  • 1
Lee Dunham
  • 117
  • 3