PyQt5系列教程(54):开局5农民,从荒岛到建立帝国1!

PyQt5系列教程(54):开局5农民,从荒岛到建立帝国1!

前几期我们学习了QMdiArea类,在一个窗口实现了多个窗口部件的呈现,今天我们再来一个综合性的例子,讲解一下QTimer与QThread的综合使用。

总体介绍

QTimer类

还记得我们在

学点编程吧:PyQt5系列教程(20):液晶显示屏zhuanlan.zhihu.com图标

倒计时的例子吗?在那个例子中我们使用了QTimer类实现了定时触发槽函数的功能。

QTimer类提供重复和单次定时器。

QTimer类为定时器提供高级编程接口。要使用它,请创建一个QTimer,将其timeout()信号连接到相应的插槽,然后调用start()。从那时起,它将以恒定的间隔发出timeout()信号。

一秒(1000毫秒)定时器的示例(来自模拟时钟示例):

timer = QTimer()
timer.timeout.connect(self.update)
timer.start(1000)

从那时起,每隔一秒调用update()这个槽函数。

您可以通过调用setSingleShot(True)将计时器设置为仅超时一次。您还可以使用静态QTimer.singleShot()函数在指定的时间间隔后调用槽函数:

   QTimer.singleShot(2000, self.start)
    
def start(self):
    print('start')

精度

定时器的准确性取决于底层操作系统和硬件。大多数平台支持1毫秒的分辨率,但在许多实际情况下,定时器的精度将不等于此分辨率。

精度还取决于计时器类型。对于Qt.PreciseTimer,QTimer将尝试将精度保持在1毫秒。精确的计时器也永远不会超出预期的时间。

对于Qt.CoarseTimer和Qt.VeryCoarseTimer类型,QTimer可能会在这些类型的边距内提前唤醒:Qt.CoarseTimer的间隔为5%,Qt.VeryCoarseTimer为500 ms。

如果系统繁忙或无法提供所要求的准确度,则所有计时器类型可能会超出预期的时间。在超时超限的情况下,Qt将仅发出一次activate(),即使多个超时已到期,然后将恢复原始时间间隔。

QTimer的替代品

使用QTimer的另一种方法是为对象调用QObject.startTimer()并重新实现类中的QObject. timerEvent()事件处理程序(必须继承QObject)。缺点是timerEvent()不支持单次定时器或信号等高级功能。

另一种选择是QBasicTimer。它通常不如直接使用QObject.startTimer()那么麻烦。有关所有三种方法的概述,请参见计时器。

更多内容请参见:

QTimer Class | Qt Core 5.11doc.qt.io

QThread类

QThread类提供了一种独立于平台的管理线程的方法。

QThread对象管理程序中的一个控制线程。 QThreads开始在run()中执行。默认情况下,run()通过调用exec()启动事件循环并在线程内运行Qt事件循环。

您可以使用QObject.moveToThread()将worker对象移动到线程来使用它们(此种方式这里不做介绍)。

使代码在单独的线程中运行的另一种方法是继承QThread并重新实现run()。例如:

class  WorkerThread(QThread):
    finish = pyqtSignal(int)
    def __init__(self, parent = None):
        super().__init__(parent)
    
    def run(self):
        #长时间的工作放到这里

self.work = WorkerThread()
self.work.finish.connect(self.showResult)
self.work.start()

在该示例中,线程将在运行函数返回后退出。除非你调用exec(),否则线程中不会运行任何事件循环。

重要的是要记住,QThread实例存在于实例化它的旧线程中,而不是在调用run()的新线程中。这意味着所有QThread的排列函数和调用的方法都将在旧线程中执行。因此,希望在新线程中调用槽函数的开发人员必须使用worker-object方法。

直接在QThread对象上调用的方法将在调用该方法的线程中执行。在继承QThread时,请记住构造函数在旧线程中执行,而run()在新线程中执行。如果从两个函数访问成员变量,则从两个不同的线程访问该变量。

注意:在跨不同线程的对象交互时必须小心。

管理线程

当线程started()和finished()时,QThread将通过信号通知您,或者您可以使用isFinished()和isRunning()来查询线程的状态。

您可以通过调用exit()或quit()来停止该线程。在极端情况下,您可能希望强制terminate()正在执行的线程。但是,这样做是危险和沮丧的。

使用wait()来阻塞调用线程,直到另一个线程完成执行(或直到指定的时间过去)。

QThread还提供静态的,独立于平台的睡眠功能:sleep(),msleep()和usleep()分别允许秒,毫秒和微秒的睡眠级别。

注意:一般来说,wait()和sleep()函数是不必要的,因为Qt是一个事件驱动的框架。考虑监听finished()信号,而不是wait()。请考虑使用QTimer,而不是sleep()函数。

更多内容请参见:

QThread Class | Qt Core 5.11doc.qt.io

类归属

PyQt5->QtCore->QTimer

PyQt5->QtCore->QThread

继承关系

PyQt5->QObject->QTimer

PyQt5->QObject->QThread

小例子

本期我模拟了魔兽世界游戏中人族挖金矿这个环节。我们设定了挖矿的时间10-1000秒之间,最后给你一个你挖到金矿的结果。

https://www.zhihu.com/video/998629852639014912

主要是通过QTimer动态的加载进度条,告知我们现在的挖矿进度。挖矿这个过程不在主线程(否则就是未响应),参考这里:

学点编程吧:PyQt5番外篇(2-5):冲顶大会语音答题辅助小工具之解析篇——问题搜索及程序整合zhuanlan.zhihu.com图标

挖矿我们怎么模拟的呢?我们知道ASCII码可以表示256种可能的字符,我们选取一种作为金矿。同时我们知道金矿在自然界中是非常稀有的,我们在随机选择ASCII字符的时候,降低作为金矿的ASCII码字符被选中的概率,让其更加珍惜。

具体的代码说明在下期吧!代码我还想再斟酌一下!

最后

好的,今天这期就这样结束吧。如果你喜欢本篇文章,请给我点赞


赞赏(推荐)


分享给你的好友们吧!


欢迎关注微信公众号:学点编程吧。加油!(ง •̀_•́)ง (*•̀ㅂ•́)

实操中有问题?来讨论吧!

学点编程吧-百度贴吧--计算机程序学习的园地!--学点编程吧,让我们的生活更简单,更高效!能用计算机解决的事情,尽量不要让人解决。如果你在学习当中有任何疑问、学习心得、职业发展等内容欢迎在贴吧中分享,让我tieba.baidu.com图标

编辑于 2018-09-18

文章被以下专栏收录

    承蒙各位学友们的支持,《Python图形界面编程》课程后推出后得到了1万多的学习量。课程推出后通过与广大学友的交流,本着对先前的课程进行进一步改进的想法(如:数据库操作、网络传输等均没有涉及),并结合当下Python3学习人员的日渐增多的情形,决定再推出一个PyQt5的教程,希望大家一起交流共同进步。