九、PyQt5多线程编程

2023-05-16

(三)多线程编程

      如果一次只完成一件事情,那是一个不错的想法,但事实上很多事情都是同时进行的,所以在Python中为了模拟这种状态,引入了线程机制,简单地说,当程序同时完成多件事情时,就是所谓的多线程程序。多线程应用广泛,开发人员可以使用多线程程序对要执行的操作分段执行,这样可以大大提高程序的运行速度和性能
      我们先对线程的分类及概述做简单介绍,然后详细讲解Python中进行线程编程的主要两个类——QTimer计时器类、QThread线程类,并对线程的具体实现进行详细讲解。之后,我们就可以熟悉使用Python进行线程编程的基础知识,并在实际开发中应用线程处理多任务问题。

1. 线程概述

      世间万物都会同时完成很多工作,例如,人体同时进行呼吸、血液循环、思考问题等活动,用户既可以使用计算机听歌,又可以使用它打印文件,而这些活动完全可以同时进行,这种思想放在Python中被称为并发,而将并发完成的每一件事情称为线程。

1. 线程的定义与分类

      先了解一个概念——进程。系统中资源分配和资源调度的基本单位,叫做进程。其实进程很常见,我们使用的QQ、Word、甚至是输入法等,每个独立执行的程序在系统中都是一个进程
      每个进程中都可以同时包含多个线程,例如,QQ是一个聊天软件,但它的功能有很多,如收发信息、播放音乐、查看网页和下载文件等,这些工作可以同时运行并且互不干扰,这就是使用了线程的并发机制,我们把QQ这个软件看作一个进程,而它的每一个功能都是一个可以独立运行的线程。
在这里插入图片描述
      上面介绍了一个进程可以包括多个线程,但计算机的CPU只有一个,那么这些线程是怎么做到并发运行的呢?Windows操作系统是多任务操作系统,它以进程为单位,每个独立执行的程序称为进程,在系统中可以分配给每个进程一段有限的使用CPU的时间(也可以称为CPU时间片),CPU在片段时间中执行某个进程,然后下一个时间片又跳至另一个进程中去执行。由于CPU转换较快,所以使得每个进程好像是同时执行一样。
在这里插入图片描述
      一个线程则是进程中的执行流程,一个进程中可以同时包括多个线程,每个线程也可以得到一小段程序的执行时间,这样一个进程就可以具有多个并发执行的线程。

2. 多线程的优缺点

      一般情况下,需要用户交互的软件都必须尽可能快地对用户的操作做出反应,以便提供良好的用户体验,但同时它又必须执行必要的计算以便尽可能快地将数据呈现给用户,这时可以使用多线程来实现。

1. 多线程的优点

      要提高对用户的相应速度,使用多线程是一种最有效的方式,在具有一个处理器的计算机上,多线程可以通过利用用户事件之间很小的时间段在后台处理数据来达到这种效果。多线程的优点如下:

  • 通过网络与Web服务器和数据库进行通信。
  • 执行占用大量时间的操作。
  • 区分具有不同优先级的任务。
  • 使用户界面可以在将时间分配给后台任务时仍能快速做出响应。

2. 多线程的缺点

多线程有好处,同时也有坏处,建议一般不要在程序中使用太多的线程,这样可以最大限度地减少操作系统资源的使用,并提高性能。使用多线程可能对程序造成的负面影响如下:

  • 系统将为进程和线程所需的上下文信息使用内存。因此,可以创建的进程和线程的数目会受到可用内存的限制。
  • 跟踪大量的线程将占用大量的处理器时间。如果线程过多,则其中大多数线程都不会产生明显的进度。如果大多数线程处于一个进程中,则其他进程中的线程的调度频率就会很低。
  • 使用多个线程控制代码执行非常复杂,并可能产生许多Bug。
  • 销毁线程需要了解可能发生的问题并进行处理。

在PyQt5中实现多线程主要有两种方法,一种是使用QTimer计时器模块;另一种是使用QThread线程模块

2. QTimer:计时器

      在PyQt5程序中,如果需要周期性地执行某项操作,就可以使用QTimer类实现,QTimer类表示计时器,它可以定期发射timeout信号,时间间隔的长度在start()方法中指定,以毫秒为单位,如果要停止计时器,则需要使用stop()方法。
在使用QTimer类时,首先需要进行导入:

from PyQt5.QtCore import QTimer

示例:双色球彩票选号器

      使用PyQt5实现模拟双色球选号的功能。
      (1)在PyQt5的Qt Designer设计器中创建一个窗口,设置背景,并添加7个Label标签和两个PushButton按钮。
      (2)将设计的窗口保存为.ui文件,并使用PyUIC工具将其转换为.py文件,同时使用qrcTOpy工具将用到的存储图片的资源文件转换为.py文件。这部分的代码如下:

from PyQt5 import QtCore, QtGui, QtWidgets


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(435, 294)
        MainWindow.setWindowTitle("双色球彩票选号器")    # 设置窗口标题
        # 设置窗口背景图片
        MainWindow.setStyleSheet("border-image: url(./image/双色球彩票选号器.png)")
        self.centralwidget=QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        # 创建第一个红球数字的标签
        self.label = QtWidgets.QLabel(self.centralwidget)
        self.label.setGeometry(QtCore.QRect(97, 178,31, 31))
        # 设置标签的字体
        font = QtGui.QFont()    # 创建字体对象
        font.setPointSize(16)    # 设置字体大小
        font.setBold(True)      # 设置粗体
        font.setWeight(75)    # 设置字体
        self.label.setFont(font)    # 为标签设置字体
        # 设置标签的文字颜色
        self.label.setStyleSheet("color:rgb(255,255,255);")
        self.label.setObjectName("label")

        # 第2、3、4、5、6个红球和一个蓝球标签的代码的创建及设置代码与第一个红球标签的代码一样
        # 创建第2个红球
        self.label_2 = QtWidgets.QLabel(self.centralwidget)
        self.label_2.setGeometry(QtCore.QRect(128, 178, 31, 31))
        self.label_2.setFont(font)  # 为标签设置字体
        self.label_2.setStyleSheet("color:rgb(255,255,255);")  # 设置标签的文字颜色
        self.label_2.setObjectName("label_2")
        # 创建第3个红球
        self.label_3 = QtWidgets.QLabel(self.centralwidget)
        self.label_3.setGeometry(QtCore.QRect(159, 178, 31, 31))
        self.label_3.setFont(font)  # 为标签设置字体
        self.label_3.setStyleSheet("color:rgb(255,255,255);")  # 设置标签的文字颜色
        self.label_3.setObjectName("label_3")
        # 创建第4个红球
        self.label_4 = QtWidgets.QLabel(self.centralwidget)
        self.label_4.setGeometry(QtCore.QRect(190, 178, 31, 31))
        self.label_4.setFont(font)  # 为标签设置字体
        self.label_4.setStyleSheet("color:rgb(255,255,255);")  # 设置标签的文字颜色
        self.label_4.setObjectName("label_4")
        # 创建第个5红球
        self.label_5 = QtWidgets.QLabel(self.centralwidget)
        self.label_5.setGeometry(QtCore.QRect(221, 178, 31, 31))
        self.label_5.setFont(font)  # 为标签设置字体
        self.label_5.setStyleSheet("color:rgb(255,255,255);")  # 设置标签的文字颜色
        self.label_5.setObjectName("label_5")
        # 创建第6个红球
        self.label_6 = QtWidgets.QLabel(self.centralwidget)
        self.label_6.setGeometry(QtCore.QRect(252, 178, 31, 31))
        self.label_6.setFont(font)  # 为标签设置字体
        self.label_6.setStyleSheet("color:rgb(255,255,255);")  # 设置标签的文字颜色
        self.label_6.setObjectName("label_6")
        # 创建第7个红球
        self.label_7 = QtWidgets.QLabel(self.centralwidget)
        self.label_7.setGeometry(QtCore.QRect(283, 178, 31, 31))
        self.label_7.setFont(font)  # 为标签设置字体
        self.label_7.setStyleSheet("color:rgb(255,255,255);")  # 设置标签的文字颜色
        self.label_7.setObjectName("label_7")

        # 创建“开始”按钮
        self.pushButton = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton.setGeometry(QtCore.QRect(310, 235, 51, 51))
        # 设置按钮的背景图片
        self.pushButton.setStyleSheet("border-image: url(./image/开始.png);")
        self.pushButton.setText("")
        self.pushButton.setObjectName("pushButton")
        # 创建“停止”按钮
        self.pushButton_2 = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton_2.setGeometry(QtCore.QRect(370, 235, 51, 51))
        # 设置按钮的背景图片
        self.pushButton_2.setStyleSheet("border-image: url(./image/停止.png);")
        self.pushButton_2.setText("")
        self.pushButton_2.setObjectName("pushButton_2")
        MainWindow.setCentralWidget(self.centralwidget)
        # 初始化双色球数字的Label标签的默认文本
        self.label.setText("00")
        self.label_2.setText("00")
        self.label_3.setText("00")
        self.label_4.setText("00")
        self.label_5.setText("00")
        self.label_6.setText("00")
        self.label_7.setText("00")
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

      (3)由于使用Qt Designer设计器设置窗口时,控件的背景默认会跟随窗口的背景,所以在.py文件的setupUi()方法中将7个Label标签的背景设置透明。

        # 设置显示双色球数字的Label标签背景透明
        self.label.setAtrribute(QtCore.Qt.WA_TranslucentBackground)
        self.label_2.setAtrribute(QtCore.Qt.WA_TranslucentBackground)
        self.label_3.setAtrribute(QtCore.Qt.WA_TranslucentBackground)
        self.label_4.setAtrribute(QtCore.Qt.WA_TranslucentBackground)
        self.label_5.setAtrribute(QtCore.Qt.WA_TranslucentBackground)
        self.label_6.setAtrribute(QtCore.Qt.WA_TranslucentBackground)
        self.label_7.setAtrribute(QtCore.Qt.WA_TranslucentBackground)

      (4)然后定义3个槽函数strat()、num()和stop(),分别用来开始计时器、随机生成双色球数字、停止计时器的功能。

# 自定义槽函数,用来开始计时器
    def start(self):
        self.timer = QTimer(MainWindow)    # 创建计时器对象
        self.timer.start()     # 开始计时器
        self.timer.timeout.connect(self.num)      # 设置计时器要执行的槽函数

    # 定义槽函数,用来设置7个Label标签中的数字
    def num(self):
        import random
        self.label.setText("{0:02d}".format(random.randint(1, 33)))     # 随机生成第一个红球数字
        self.label_2.setText("{0:02d}".format(random.randint(1, 33)))     # 随机生成第二个红球数字
        self.label_3.setText("{0:02d}".format(random.randint(1, 33)))     # 随机生成第三个红球数字
        self.label_4.setText("{0:02d}".format(random.randint(1, 33)))     # 随机生成第四个红球数字
        self.label_5.setText("{0:02d}".format(random.randint(1, 33)))     # 随机生成第五个红球数字
        self.label_6.setText("{0:02d}".format(random.randint(1, 33)))     # 随机生成第六个红球数字
        self.label_7.setText("{0:02d}".format(random.randint(1, 16)))     # 随机生成蓝球数字

    # 定义槽函数,用来停止计时器
    def stop(self):
        self.timer.stop()

      由于用到了random随机数类和QTimer类,所以需要导入相应的模块。

from PyQt5.QtCore import QTimer
import random

      (5)在.py文件的setupUi()方法中为“开始”和“停止”按钮的clicked信号绑定自定义的槽函数,以便在单击按钮时执行相应的操作。

		# 为“开始”按钮绑定单击信号
        self.pushButton.clicked.connect(self.start)
        # 为“停止”按钮绑定单击信号
        self.pushButton_2.clicked.connect(self.stop)

      (6)为.py文件添加__main__方法。

# 主函数
if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    global MainWindow
    MainWindow = QtWidgets.QMainWindow()     # 创建窗体对象
    ui = Ui_MainWindow()      # 创建PyQt5设计的窗体对象
    MainWindow.show()      # 调用PyQt5窗体的方法对窗体对象进行初始化设置
    ui.setupUi(MainWindow)     # 显示窗体
    sys.exit(app.exec_())     # 程序关闭时退出进程

完整的代码如下:

from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import QTimer


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(435, 294)
        MainWindow.setWindowTitle("双色球彩票选号器")    # 设置窗口标题
        # 设置窗口背景图片
        MainWindow.setStyleSheet("border-image: url(./image/双色球彩票选号器.png)")
        self.centralwidget=QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        # 创建第一个红球数字的标签
        self.label = QtWidgets.QLabel(self.centralwidget)
        self.label.setGeometry(QtCore.QRect(97, 178,31, 31))
        # 设置标签的字体
        font = QtGui.QFont()    # 创建字体对象
        font.setPointSize(16)    # 设置字体大小
        font.setBold(True)      # 设置粗体
        font.setWeight(75)    # 设置字体
        self.label.setFont(font)    # 为标签设置字体
        # 设置标签的文字颜色
        self.label.setStyleSheet("color:rgb(255,0,0);")
        self.label.setObjectName("label")

        # 第2、3、4、5、6个红球和一个蓝球标签的代码的创建及设置代码与第一个红球标签的代码一样
        # 创建第2个红球
        self.label_2 = QtWidgets.QLabel(self.centralwidget)
        # self.label_2.setGeometry(QtCore.QRect(128, 178, 31, 31))
        self.label_2.setGeometry(QtCore.QRect(134, 178, 31, 31))
        self.label_2.setFont(font)  # 为标签设置字体
        self.label_2.setStyleSheet("color:rgb(255,0,0);")  # 设置标签的文字颜色
        self.label_2.setObjectName("label_2")
        # 创建第3个红球
        self.label_3 = QtWidgets.QLabel(self.centralwidget)
        # self.label_3.setGeometry(QtCore.QRect(159, 178, 31, 31))
        self.label_3.setGeometry(QtCore.QRect(171, 178, 31, 31))
        self.label_3.setFont(font)  # 为标签设置字体
        self.label_3.setStyleSheet("color:rgb(255,0,0);")  # 设置标签的文字颜色
        self.label_3.setObjectName("label_3")
        # 创建第4个红球
        self.label_4 = QtWidgets.QLabel(self.centralwidget)
        # self.label_4.setGeometry(QtCore.QRect(190, 178, 31, 31))
        self.label_4.setGeometry(QtCore.QRect(205, 178, 31, 31))
        self.label_4.setFont(font)  # 为标签设置字体
        self.label_4.setStyleSheet("color:rgb(255,0,0);")  # 设置标签的文字颜色
        self.label_4.setObjectName("label_4")
        # 创建第个5红球
        self.label_5 = QtWidgets.QLabel(self.centralwidget)
        # self.label_5.setGeometry(QtCore.QRect(221, 178, 31, 31))
        self.label_5.setGeometry(QtCore.QRect(239, 178, 31, 31))
        self.label_5.setFont(font)  # 为标签设置字体
        self.label_5.setStyleSheet("color:rgb(255,0,0);")  # 设置标签的文字颜色
        self.label_5.setObjectName("label_5")
        # 创建第6个红球
        self.label_6 = QtWidgets.QLabel(self.centralwidget)
        # self.label_6.setGeometry(QtCore.QRect(252, 178, 31, 31))
        self.label_6.setGeometry(QtCore.QRect(273, 178, 31, 31))
        self.label_6.setFont(font)  # 为标签设置字体
        self.label_6.setStyleSheet("color:rgb(255,0,0);")  # 设置标签的文字颜色
        self.label_6.setObjectName("label_6")
        # 创建第7个红球
        self.label_7 = QtWidgets.QLabel(self.centralwidget)
        self.label_7.setGeometry(QtCore.QRect(283, 178, 31, 31))
        self.label_7.setGeometry(QtCore.QRect(307, 178, 31, 31))
        self.label_7.setFont(font)  # 为标签设置字体
        self.label_7.setStyleSheet("color:rgb(0,0,255);")  # 设置标签的文字颜色
        self.label_7.setObjectName("label_7")

        # 创建“开始”按钮
        self.pushButton = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton.setGeometry(QtCore.QRect(310, 235, 51, 51))
        # 设置按钮的背景图片
        self.pushButton.setStyleSheet("border-image: url(./image/开始.png);")
        self.pushButton.setText("")
        self.pushButton.setObjectName("pushButton")
        # 创建“停止”按钮
        self.pushButton_2 = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton_2.setGeometry(QtCore.QRect(370, 235, 51, 51))
        # 设置按钮的背景图片
        self.pushButton_2.setStyleSheet("border-image: url(./image/停止.png);")
        self.pushButton_2.setText("")
        self.pushButton_2.setObjectName("pushButton_2")
        MainWindow.setCentralWidget(self.centralwidget)
        # 初始化双色球数字的Label标签的默认文本
        self.label.setText("00")
        self.label_2.setText("00")
        self.label_3.setText("00")
        self.label_4.setText("00")
        self.label_5.setText("00")
        self.label_6.setText("00")
        self.label_7.setText("00")
        QtCore.QMetaObject.connectSlotsByName(MainWindow)
        # 设置显示双色球数字的Label标签背景透明
        self.label.setAttribute(QtCore.Qt.WA_TranslucentBackground)
        self.label_2.setAttribute(QtCore.Qt.WA_TranslucentBackground)
        self.label_3.setAttribute(QtCore.Qt.WA_TranslucentBackground)
        self.label_4.setAttribute(QtCore.Qt.WA_TranslucentBackground)
        self.label_5.setAttribute(QtCore.Qt.WA_TranslucentBackground)
        self.label_6.setAttribute(QtCore.Qt.WA_TranslucentBackground)
        self.label_7.setAttribute(QtCore.Qt.WA_TranslucentBackground)
        # 为“开始”按钮绑定单击信号
        self.pushButton.clicked.connect(self.start)
        # 为“停止”按钮绑定单击信号
        self.pushButton_2.clicked.connect(self.stop)

    # 自定义槽函数,用来开始计时器
    def start(self):
        self.timer = QTimer(MainWindow)    # 创建计时器对象
        self.timer.start()     # 开始计时器
        self.timer.timeout.connect(self.num)      # 设置计时器要执行的槽函数

    # 定义槽函数,用来设置7个Label标签中的数字
    def num(self):
        import random
        self.label.setText("{0:02d}".format(random.randint(1, 33)))     # 随机生成第一个红球数字
        self.label_2.setText("{0:02d}".format(random.randint(1, 33)))     # 随机生成第二个红球数字
        self.label_3.setText("{0:02d}".format(random.randint(1, 33)))     # 随机生成第三个红球数字
        self.label_4.setText("{0:02d}".format(random.randint(1, 33)))     # 随机生成第四个红球数字
        self.label_5.setText("{0:02d}".format(random.randint(1, 33)))     # 随机生成第五个红球数字
        self.label_6.setText("{0:02d}".format(random.randint(1, 33)))     # 随机生成第六个红球数字
        self.label_7.setText("{0:02d}".format(random.randint(1, 16)))     # 随机生成蓝球数字

    # 定义槽函数,用来停止计时器
    def stop(self):
        self.timer.stop()


# 主函数
if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    global MainWindow
    MainWindow = QtWidgets.QMainWindow()     # 创建窗体对象
    ui = Ui_MainWindow()      # 创建PyQt5设计的窗体对象
    MainWindow.show()      # 调用PyQt5窗体的方法对窗体对象进行初始化设置
    ui.setupUi(MainWindow)     # 显示窗体
    sys.exit(app.exec_())     # 程序关闭时退出进程

运行程序,单击“开始”按钮,红球和蓝球同时滚动,单击“停止”按钮,则红球和蓝球停止滚动,当前显示的数字就是程序选中的号码。
在这里插入图片描述

在这里插入图片描述

1. QThread:线程类

      PyQt5通过使用QThread类实现线程。本节介绍如何使用QThread类实现线程,并且介绍线程的生命周期。

1. 线程的实现

      QThread类是PyQt5中的核心线程类,要实现一个线程,需要创建QThread类的一个子类,并且实现其run()方法。

QThread类的常用方法及说明

方法说明
run()线程的起点,在调用start()之后,新创建的线程将调用该方法。
start()启动线程。
wait()阻塞线程。
sleep()以秒为单位休眠线程。
msleep()以毫秒为单位休眠线程。
usleep()以微秒为单位休眠线程。
quit()退出线程的事件循环并返回代码0(成功),相当于exit(0)。
exit()退出线程的事件循环,并返回代码,如果返回0则表示成功,任何非0值都表示错误。
terminate()强制终止线程,在terminate()之后应该使用wait()方法,以确保当线程终止时,等待线程完成的所有线程都将被唤醒;另外,不建议使用这种方法终止线程。
setPriority()
  • 设置线程优先级,取值如下:
  • QThread.IdlePriority:空闲优先级;
  • QThread.LowestPriority:最低优先级;
  • QThread.LowPriority:低优先级;
  • QThread.NormalPriority:系统默认优先级;
  • QThread.HighPriority:高优先级;
  • QThread.HighestPriority:最高优先级;
  • QThread.TimeCriticalPriority:尽可能频繁地分配执行;
  • QThread.InheritPriority:默认值,使用与创建线程相同的优先级。
isFinished()是否完成。
isRunning()是否正在运行。

QThread类的常用信号及说明

信号说明
started在调用run()方法之前,在相关线程开始执行时从该线程发射。
finished在相关线程完成执行之前从该线程发射。

示例:在线程中叠加数数

      在PyCharm中新建一个.py文件,使用QThread类创建线程,在重写的run()方法中以每隔1秒的频率叠加输出数字,并且在数字为10时,退出线程;最后添加主运行方法。
完整代码如下:

from PyQt5.QtCore import QThread    # 导入线程模块


class Thread(QThread):   # 创建线程类
    def __init__(self):
        super(Thread, self).__init__()

    def run(self):     # 重写run()方法
        num = 0      # 定义一个变量,用来叠加输出
        while True:    # 定义无限循环
            num = num + 1   # 变量叠加
            print(num)     # 输出变量
            Thread.sleep(1)    # 使线程休眠1秒
            if num == 10:     # 如果数字到10
                Thread.quit()    # 退出线程


if __name__ == "__main__":
    import sys     # 导入模块
    from PyQt5.QtWidgets import QApplication
    app = QApplication(sys.argv)    # 创建应用对象
    thread = Thread()      # 创建线程对象
    thread.start()     # 启动线程
    sys.exit(app.exec_())

      运行程序,在PyCharm的控制台中每隔1秒输出一个数字,数字为10时,退出程序。
在这里插入图片描述

2. 线程的生命周期

      任何事物都有始终,就像人的一生,就经历了少年、壮年、老年…,这就是一个人的生命周期。
在这里插入图片描述
      线程也有自己的生命周期,包含5种状态,分别是出生状态、就绪状态、运行状态、暂停状态(包括休眠、等待和阻塞等)、死亡状态。出生状态就是线程被创建时的状态;当线程对象调用start()方法后,线程处于就绪状态(又称为可执行状态);当线程得到系统资源后就进入运行状态
      一旦线程进入运行状态,它会在就绪和运行状态下转换,同时也可能进入暂停或死亡状态。当处于运行状态的线程调用sleep()、wait()或者发生阻塞时,会进入暂停状态;当在休眠结束或者发生阻塞解除时,线程会重新进入就绪状态;当线程的run()方法执行完毕,或者线程发生错误、异常时,线程就进入死亡状态
在这里插入图片描述

3. 线程的应用

      使用PyQt5中的QThread类模拟龟兔赛跑的故事。

示例:龟兔赛跑

      在Qt Designer设计器中创建一个窗口,在其中添加两个Label控件,分别用来标识兔子和乌龟的比赛记录;添加两个TextEdit控件,分别用来实时显示兔子和乌龟的比赛动态;添加一个PushButton控件,用来执行开始比赛操作。窗口设计完成后保存为.ui文件,并使用PyUIC工具将其转换为.py文件。
      在.py文件中,分别通过继承QThread类定义兔子线程类和乌龟线程类,这两个类的实现思路一致,主要通过自定义信号发射兔子和乌龟的比赛动态,区别是,兔子在90米处,会有“兔子在睡觉”的动态。然后在主窗口中创建定义的两个线程类对象,并使用start()方法启动。

from PyQt5 import QtCore, QtWidgets
from PyQt5.QtCore import *    # 导入线程相关模块


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        # MainWindow.resize(367, 267)
        MainWindow.resize(367, 367)
        MainWindow.setWindowTitle("龟兔赛跑")   # 设置窗口标题
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        # 创建兔子比赛标签
        self.label=QtWidgets.QLabel(self.centralwidget)
        self.label.setGeometry(QtCore.QRect(40, 10, 91, 21))
        self.label.setObjectName("label")
        self.label.setText("兔子的比赛记录")
        # 创建兔子的比赛记录
        self.textEdit = QtWidgets.QTextEdit(self.centralwidget)
        # self.textEdit.setGeometry(QtCore.QRect(10, 40, 161, 191))
        self.textEdit.setGeometry(QtCore.QRect(10, 40, 161, 291))
        self.textEdit.setObjectName("textEdit")
        # 创建乌龟比赛标签
        self.label_2 = QtWidgets.QLabel(self.centralwidget)
        self.label_2.setGeometry(QtCore.QRect(220, 10, 91, 21))
        self.label_2.setObjectName("label_2")
        self.label_2.setText("乌龟的比赛记录")
        # 显示乌龟的比赛记录
        self.textEdit_2 = QtWidgets.QTextEdit(self.centralwidget)
        # self.textEdit_2.setGeometry(QtCore.QRect(190, 40, 161, 191))
        self.textEdit_2.setGeometry(QtCore.QRect(190, 40, 161, 291))
        self.textEdit_2.setObjectName("textEdit_2")
        # 创建“开始比赛”按钮
        self.pushButton = QtWidgets.QPushButton(self.centralwidget)
        # self.pushButton.setGeometry(QtCore.QRect(140, 240, 75, 23))
        self.pushButton.setGeometry(QtCore.QRect(140, 340, 75, 23))
        self.pushButton.setObjectName("pushButton")
        self.pushButton.setText("开始比赛")
        MainWindow.setCentralWidget(self.centralwidget)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)
        self.r = Rabbit()        # 创建兔子线程对象
        self.r.sinOut.connect(self.rabbit)      # 将线程信号连接到槽函数
        self.t = Tortoise()  # 创建兔子线程对象
        self.t.sinOut.connect(self.tortoise)  # 将线程信号连接到槽函数
        self.pushButton.clicked.connect(self.start)     # 开始两个线程

    def start(self):
        self.r.start()    # 启动兔子线程
        self.t.start()     # 启动乌龟线程

    # 显示兔子的跑步距离
    def rabbit(self, str):
        self.textEdit.setPlainText(self.textEdit.toPlainText() + str)

    # 显示乌龟的跑步距离
    def tortoise(self, str):
        self.textEdit_2.setPlainText(self.textEdit_2.toPlainText() + str)


class Rabbit(QThread):     # 创建兔子线程类
    sinOut = pyqtSignal(str)     # 自定义信号,用来发射兔子比赛动态

    def __init__(self):
        super(Rabbit, self).__init__()

    # 重写run()方法
    def run(self):
        for i in range(1, 11):
            # 循环10次模拟赛跑的过程
            QThread.msleep(100)     # 线程休眠0.1秒,模拟兔子在跑步
            self.sinOut.emit("\n 兔子跑了" + str(i) + "0米")    # 显示兔子的跑步距离
            if i == 9:
                self.sinOut.emit("\n 兔子在睡觉")     # 当跑了90米时开始睡觉
                QThread.sleep(5)        # 休眠5秒
            if i == 10:
                self.sinOut.emit("\n 兔子到达终点")     # 显示兔子到达了终点


class Tortoise(QThread):       # 创建乌龟线程类
    sinOut = pyqtSignal(str)       # 自定义信号,用来发射乌龟比赛动态

    def __init__(self):
        super(Tortoise, self).__init__()

    # 重写run()方法
    def run(self):
        for i in range(1, 11):
            QThread.msleep(500)      # 线程休眠0.5秒。模拟乌龟在跑步
            self.sinOut.emit("\n 乌龟跑了" + str(i) + "0米")
            if i == 10:
                self.sinOut.emit("\n 乌龟到达终点")


if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)    # 创建窗体对象
    MainWindow = QtWidgets.QMainWindow()    # 创建PyQt5设计的窗体对象
    ui = Ui_MainWindow()    # 调用PyQt5窗体对象进行初始化设置
    ui.setupUi(MainWindow)
    MainWindow.show()      # 显示窗体
    sys.exit(app.exec_())    # 程序关闭时退出进程

      运行程序,单击“开始比赛”按钮,当兔子跑到90米处时,开始睡觉;乌龟跑至终点时,兔子醒了,随即跑至终点。
运行效果如下:
请添加图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

九、PyQt5多线程编程 的相关文章

  • cefsharp之devtools开发者工具API应用-Emulation

    这里仅介绍关键方法 xff0c 快捷键根据自己爱好定义 1 一键设置禁用 启用Javascript 核心代码 参数true禁用 xff0c false启用 xff0c sel webbrowser为你的浏览器 await sel webbr
  • CefSharp.WinForms-109.1.110升级

    包升级地址 NuGet Gallery CefSharp WinForms 109 1 110 NuGet Gallery CefSharp Common 109 1 110 NuGet Gallery cef redist x64 109
  • ESP32S2(12K)-DS18B20数码管显示温度

    一 物料清单 NODEMCU 32 S2 ESP32 12K 四段数码管 共阴 DS18B20 VCC DQ GND Arduino IDE 2 0 3 二 实现方法及效果图 2 1 引用库 include lt OneWire h gt
  • cefsharp-物联网浏览器-升级至110.0.250

    基于cefsharp二次开发的物联网浏览器升级最新版 110 0 250 暂不支持H264功能 一款属于自己的浏览器 支持H264的最新版本100 0 230 更多功能了解 关注我
  • 德赛西威NAV75*-SV731*导航升级(凯立德J30)实战

    一 前言 xff1a 升级导航德赛西威 xff08 2015年买的 xff09 地图几年没升级过了 xff08 之前自己折腾了一个 xff09 之前的启动是DSA2013 xff08 电子G已经无法升级数据文件了 xff0c 本次只升级地图
  • 网络串口调试助手(串口透传网络调试)

    一 前言 xff1a 1 本地串口调试同步到网络 xff08 可以远程实时查看发送 串口打印数据 xff09 2 远程直接发送串口调试指令透传到串口设备 3 通讯协议MQTT 43 WebSocket 串口 二 效果展示 网络客户端 xff
  • log4net(winform)使用方法

    前言 xff1a log4net是 Net下一个非常优秀的开源日志记录组件 log4net记录日志的功能非常强大 它可以将日志分不同的等级 xff0c 以不同的格式 xff0c 输出到不同的媒介 第一步 xff1a 引入组件 xff1a 引
  • cefsharp111.2.20(winform)版本体验

    第一步 xff1a 更新步骤 xff1a 先下载再本地更新会快一点 https globalcdn nuget org packages cefsharp winforms 111 2 20 nupkg https globalcdn nu
  • CefSharp.WinForms 112.2.70最新版体验

    一 准备 下载最新包及依赖包 对应 NET4 5 2 后续版本可能4 6 2 到packages中 本地升级更快 NuGet Gallery CefSharp WinForms 112 2 70 NuGet Gallery CefSharp
  • VUE3(.NET6)管理后台

    基于Admin NET框架 xff0c 预览下效果 内置功能 主控面板 xff1a 控制台页面 xff0c 可进行工作台 xff0c 分析页 xff0c 统计等功能的展示 用户管理 xff1a 对企业用户和系统管理员用户的维护 xff0c
  • 2011年养成的一个工作习惯

    作者 xff1a 朱金灿 来源 xff1a http blog csdn net clever101 有一句名言 xff0c 没有记录的公司 xff0c 迟早要垮掉的 xff0c 多么尖锐 个人也不是如此吗 xff1f 在下半年 xff0c
  • Cefsharp.WinForms-v112.3.0 带您最新版体验(小更新)

    一 准备 下载最新包及依赖包 对应 NET4 5 2 后续版本可能4 6 2 到packages中 本地升级更快 NuGet Gallery CefSharp WinForms 112 3 0 NuGet Gallery CefSharp
  • 无人机飞行控制算法、控制律设计软件与半物理仿真

    工业级多功能可编程飞行控制系统专业的图形化控制律设计软件灵活强大的工程应用开发平台DSP处理器及高精度传感器自定义高速遥测数据采集嵌入式半物理仿真系统丰富的用户设备接口适用于固定翼 旋翼机 特殊飞行器 车船艇 机器人 云台等 概 述 xff
  • 程序调试记录

    最近把师兄的程序在万兆网络上进行测试 xff0c 现在把调试中出现的问题进行记录 xff1a 1 xff09 其中一共是十六块板子 xff0c 板子的配置文件是sipixel xml xff0c 每块板子的配置信息里都有对应的IP xff0
  • 华清远见嵌入式学习day27——编译工具和环境搭建

    0 系统移植四天课程安排 1 编译工具 xff0c 环境搭建 2 bootloader 3 kernel 4 文件系统 1 嵌入式系统的应用领域 1 军事 2 医疗 3 移动设备 4 家电 5 工控 2 什么是嵌入式系统 一般的定义 xff
  • tf.Variable函数的用法

    tf Variable xff08 initializer xff0c name xff09 xff1a initializer是初始化参数 xff0c 可以有tf random normal xff0c tf constant xff0c
  • Docker入门操作+文件备份

    文件备份操作 bin sh it is a shell script which provides function of auto backup ecology logfiles regularly by 494389 date 61 9
  • Qnap Docker(Container Station)更改国内镜像源

    0x01qpkg环境 通常qnap市场中下载的qpkg应用 xff0c 其环境变量就在自己的包环境中 所以要修改系统中的配置 xff0c 通常需要修改qpkg应用中对应的配置 即 share CACHEDEV1 DATA qpkg xxx
  • docker源码编译(containerd+runc源码编译)

    目录 源码下载docker cli docker enginebugsgo get timeoutdebian超时git clone TLS containerd源码编译runc源码编译docker 43 containerd 43 run
  • ros中激光雷达的消息类型(sensor_msgs/LaserScan Message)说明

    最近在做一些视觉和激光数据融合的项目 xff0c 但是对激光数据的结构不是太了解 xff0c 因此查了很多相关的内容 xff0c 记录以下 下图是在http wiki ros org中截取的图片 xff1a Header 是一个结构体 xf

随机推荐