【没钱买ps,PyQt自己写】Day 18 / Project 使用 QTimer,自制码表(计时器) PyQt5 stopwatch DIY

看完这篇文章你会得到的成果图

前言

这篇我们要来学一个新的东西 QTimer!
QTimer 是独立於程序运作的计时器,
你可能会想什麽时候会用到?

其实很意外的,真正的程序反而非常经常会需要这个,功能来作为「自动更新」,
我们仔细想想,一般的程序一行接一行执行下去,但万一有个背景任务,
是随着时间要定期更新的,我们主程序的流程哪有「突然切换至时间功能,并更新」,这样的设计
(如果是 step by step 的设计程序,这样也太难。)

因此我们有 QTimer 的设计,来帮助我们定期更新一些东西,或做一些其他事情。

之後也会介绍 QThread,也同样的是让主程序在执行的同时,能够同时有其他的支线任务可以独立运行!

之前内容的重点复习 (前情提要)

我们接下来的讨论,会基於读者已经先读过我 day5 文章 的架构下去进行程序设计
如果还不清楚我程序设计的逻辑 (UI.py、controller.py、start.py 分别在干麻)
建议先阅读 day5 文章後再来阅读此文。

https://www.wongwonggoods.com/python/pyqt5-5/

此篇文章的范例程序码 github

https://github.com/howarder3/ironman2021_PyQt5_photoshop/tree/main/day18_qtimer

UI 设计部份 (UI.py)

这次我们的重点不是放在 UI 设计,我们简单拉一个作为结果显示用的 Qlabel 即可。

转换成 UI.py

一样的编译指令,我们加上 -x (也可不加),
我们就可以先检视看看转换後的视窗是不是跟我们想像的一样。

转换 day18.ui -> UI.py

pyuic5 -x day18.ui -o UI.py

执行看看 UI.py 画面是否如同我们想像

一样,这程序只有介面 (视觉上的呈现),没有任何互动功能

  • 看看我们制作出来的介面
python UI.py

controller 设计部份 (controller.py)

QTimer 的使用方式很简单,主要我们需要设定一个 timeout 时间,
每经过一次 timeout,我们的程序就会做一次指定的事情

from PyQt5 import QtCore 
from PyQt5.QtWidgets import QMainWindow, QFileDialog
from PyQt5.QtCore import QTimer

import time
import os

from UI import Ui_MainWindow

class MainWindow_controller(QMainWindow):
    def __init__(self):
        super().__init__() # in python3, super(Class, self).xxx = super().xxx
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)
        self.setup_control()

    def setup_control(self):
        self.timer=QTimer() # init QTimer
        self.timer.timeout.connect(self.run) # when timeout, do run one
        self.timer.start(1) # start Timer, here we set '1ms' while timeout one time
        self.time_counter = 0  # init time counter # for testing: 3599000

    def run(self):
        self.ui.label.setText(str(self.set_time_counter_format(self.time_counter))) # show time_counter (by format)
        self.time_counter += 1 # time_counter + 1


    def set_time_counter_format(self, time_counter):
        ms = time_counter % 1000
        total_sec = max(0, (time_counter - ms)//1000)
        hour = max(0, total_sec//3600)
        minute = max(0, total_sec//60 - hour * 60)
        sec = max(0, (total_sec - (hour * 3600) - (minute * 60)))
        return f"{hour}:{minute:0>2}:{sec:0>2}.{ms:0>3}"

我们先来看一下,最主要的设定 QTimer 部分

设定主程序 setup_control()

  • self.timer=QTimer():初始化 QTimer
  • self.timer.timeout.connect(self.run):当 QTimer 每经过一次 timeout,执行 run 的动作
  • self.timer.start(1):开始 QTimer,这里的"1" 代表的是 1ms,所以我们每 1ms 就会 timeout 一次
  • self.time_counter = 0:我们初始化计数器,从 0 开始计数。

设定 run(),每 timeout 就执行一次

  • self.ui.label.setText(str(self.set_time_counter_format(self.time_counter)))):设定格式并显示时间
  • self.time_counter += 1:计数器 +1 (因为 timeout 一次)

设定时间格式 set_time_counter_format()

  • 这边就是我自己计算的公式了,有几个计算上的重点
  • max(0, x):避免计算结果 < 0 导致显示负数,我们直接给予 0 这个值
  • f"{hour}:{minute:0>2}:{sec:0>2}.{ms:0>3}": f-string 的 格式设定,我们抓住「:」冒号後的就是格式设定内容,「0」表示补0,「>」表示靠右,「2(3)」表示总共2(3)位。

执行结果

照我们 day5 的程序架构,我们执行

python start.py

於是我们就这样完成了我们的码表 (stopwatch)。

此外,我们可以透过更改 counter 的初始值来修正程序错误
例如依照我们上面的算式,我们可以设定初始值为 "3599000",我们可以同时验证「小时」、「分钟」、「秒」、能不能正确增加 (因为从约 0:59:50开始)

Reference


★ 本文也同步发於我的个人网站(会有内容目录与显示各个小节,阅读起来更流畅):【PyQt5】Day 18 / Project 使用 QTimer,自制码表(计时器) PyQt5 stopwatch DIY


<<:  [Day18]ISO 27001 附录 A.6 资讯安全之组织

>>:  JavaScript IIFE (立即函数)

[Day 31] 番外篇─如何将OV7670 + BLE Sense连到Edge Impulse取像

虽然铁人赛已暂告一个段落,但在[Day 27] Edge Impulse + BLE Sense实现...

Day 23 Git → Gitlab-CI 超简单

会讲这个题目其实有点微妙,但是都讲完了测试,却不讲 CI 我觉得好像少了一点什麽。 不知道 Andr...

大共享时代系列_002_共享美食资讯

没有什麽事情,是一顿美食解决不了的,如果有...那就再吃第二顿吧~XD 怎麽分享美食的资讯? 大家现...

软件工程师(ASP.NET)面试心得分享

这是我自己面谈後的反省心得,有些要注意的地方真的是讲到烂了,网路上应该也很多面谈教学,但还是想整理...

Day 25: 准备假的Coroutine,让外面世界不会影响我!

Keyword: Coroutine mock 直到27日,完成KMM的测试功能放在 KMMDay2...