• PyQt5信号、定时器及多线程


    信号

      信号是用于界面自动变化的一个工具,原理是信号绑定了一个函数,当信号被触发时函数即被调用

    举个例子

    from PyQt5 import QtWidgets,QtCore
    from untitled import Ui_Form
    import  time
      
    class MyWindow(QtWidgets.QWidget,Ui_Form):
        _signal=QtCore.pyqtSignal(str) #定义信号,定义参数为str类型
        def __init__(self):  
            super(MyWindow,self).__init__()
            self.setupUi(self)
            self.myButton.clicked.connect(self.myPrint)# 按下按钮执行myPrint
            self._signal.connect(self.mySignal) #将信号连接到函数mySignal
     
        def myPrint(self):
            self.tb.setText("")
            self.tb.append("正在打印,请稍候")
            self._signal.emit("打印结束了吗")# 信号被触发
        def mySignal(self,string):
            print(string)
            self.tb.append("打印结束")
     
    if __name__=="__main__":  
        # 以下代码作用为展现ui界面
        import sys  
      
        app=QtWidgets.QApplication(sys.argv)  
        myshow=MyWindow()
        myshow.show()  
        sys.exit(app.exec_())  

    定时器

      定时器的作用是让某个函数定时的启动,原理是创建一个QTimer对象,将其timeout信号连接到相应的槽(绑定函数名),并调用start(),定时器会以恒定的间隔发出timeout信号,直到调用stop()。

    举个例子:秒表功能(每隔一秒刷新界面,直到按下停止按钮)

    from PyQt5.QtWidgets import *
    from PyQt5.QtCore import *
    import sys
    from datetime import datetime
     
    class WinTimer(QWidget):
        def __init__(self,parent=None):
            super(WinTimer,self).__init__(parent)
     
            ###界面显示
            self.label_start=QLabel("开始时间:")
            self.label_curr=QLabel("当前时间:")
            self.label_total=QLabel("时间总计:")
            self.startBtn=QPushButton("开始")
            self.endBtn=QPushButton("停止")
            self.endBtn.setEnabled(False)
     
            ##时间变量
            self.start_time=QDateTime.currentDateTime()
            self.stop_time = QDateTime.currentDateTime()
     
            ###定时器
            self.timer=QTimer()
            self.timer.timeout.connect(self.currTime)
     
            layout=QGridLayout()
            layout.addWidget(self.label_start,0,0,1,2)
            layout.addWidget(self.label_curr, 1,0,1,2)
            layout.addWidget(self.label_total, 2,0,1,2)
            layout.addWidget(self.startBtn, 3, 0)
            layout.addWidget(self.endBtn, 3, 1)
            self.setLayout(layout)
     
            self.startBtn.clicked.connect(self.startTimer)
            self.endBtn.clicked.connect(self.endTimer)
     
            self.setWindowTitle("QTimer")
            self.resize(250,100)
     
        def currTime(self):
            self.stop_time=QDateTime.currentDateTime()
            str_time = self.stop_time.toString("yyyy-MM-dd hh:mm:ss dddd")
            self.label_curr.setText("当前时间:"+str_time)
            
            str_start = self.start_time.toString("yyyy-MM-dd hh:mm:ss")
            str_curr = self.stop_time.toString("yyyy-MM-dd hh:mm:ss")
            startTime = datetime.strptime(str_start, "%Y-%m-%d %H:%M:%S")
            endTime = datetime.strptime(str_curr, "%Y-%m-%d %H:%M:%S")
            seconds = (endTime - startTime).seconds
            self.label_total.setText("时间总计:" + str(seconds)+"s")
     
        def startTimer(self):
            self.start_time = QDateTime.currentDateTime()
            str_time = self.start_time.toString("yyyy-MM-dd hh:mm:ss dddd")
            self.label_start.setText("开始时间:" + str_time)
            self.timer.start(1000)
            self.startBtn.setEnabled(False)
            self.endBtn.setEnabled(True)
     
        def endTimer(self):
            self.timer.stop()
            self.startBtn.setEnabled(True)
            self.endBtn.setEnabled(False)
     
    if __name__=="__main__":
        app=QApplication(sys.argv)
        form=WinTimer()
        form.show()
    View Code

    多线程

      假设我们的主界面有一个用于显示时间的 LCD 数字面板和一个用于启动任务的按钮。程序的目的是用户点击按钮,开始一个非常耗时的运算(程序中我们以一个 2000000000 次的循环来替代这个非常耗时的工作,在真实的程序中,这可能是一个网络访问,可能是需要复制一个很大的文件或者其它任务),同时 LCD 开始显示逝去的毫秒数。毫秒数通过一个计时器QTimer进行更新。计算完成后,计时器停止。这是一个很简单的应用,也看不出有任何问题。但是当我们开始运行程序时,问题就来了:点击按钮之后,程序界面直接停止响应,直到循环结束才开始重新更新,于是计时器使用显示0。

      这是因为 Qt 中所有界面都是在 UI 线程中(也被称为主线程,就是执行了QApplication::exec()的线程),在这个线程中执行耗时的操作(比如那个循环),就会阻塞 UI 线程,从而让界面停止响应。界面停止响应,用户体验自然不好,不过更严重的是,有些窗口管理程序会检测到你的程序已经失去响应,可能会建议用户强制停止程序,这样一来程序可能就此终止,任务再也无法完成。所以,为了避免这一问题,我们要使用 QThread 开启一个新的线程:

    # coding=utf-8 
    __author__ = 'a359680405' 
    from PyQt5.QtCore import *
    from PyQt5.QtGui import *
    from PyQt5.QtWidgets import * 
    global sec 
    sec=0
    class WorkThread(QThread): 
      trigger = pyqtSignal() 
      def __int__(self): 
        super(WorkThread,self).__init__() 
      
      def run(self): 
        for i in range(203300030): 
          pass
        self.trigger.emit()     #循环完毕后发出信号 
      
    def countTime(): 
      global sec 
      sec+=1
      lcdNumber.display(sec)     #LED显示数字+1 
      
    def work(): 
      timer.start(1000)        #计时器每秒计数 
      workThread.start()       #计时开始 
      workThread.trigger.connect(timeStop)  #当获得循环完毕的信号时,停止计数 
      
    def timeStop(): 
      timer.stop() 
      print("运行结束用时",lcdNumber.value()) 
      global sec 
      sec=0 
    app=QApplication([]) 
    top=QWidget() 
    layout=QVBoxLayout(top)       #垂直布局类QVBoxLayout; 
    lcdNumber=QLCDNumber()       #加个显示屏 
    layout.addWidget(lcdNumber) 
    button=QPushButton("测试") 
    layout.addWidget(button)  
    timer=QTimer() 
    workThread=WorkThread() 
    button.clicked.connect(work) 
    timer.timeout.connect(countTime)   #每次计时结束,触发setTime  
    top.show() 
    app.exec()
    View Code

      上述代码增加了一个WorkerThread类。WorkerThread继承自QThread类,重写了其run()函数。可以认为,run()函数就是新的线程需要执行的代码。在这里就是要执行这个循环,然后发出计算完成的信号。而在按钮点击的槽函数中,使用work()中的workThread.start()函数启动一个线程(注意,这里不是run()函数)。再次运行程序,你会发现现在界面已经不会被阻塞了。

          

  • 相关阅读:
    C#位操作符
    NGEN 本机映像生成器 【转载】
    Azure Services Platform
    补补算术基础:编程中的进制问题
    泛型约束
    去除Live Messenger 中的广告
    对代码性能进行调试和量测
    几个常用的文档转换工具(Office System)
    LINQ to DataSet
    使用 Entity Framework 實現彈性的資料模型 【转载】
  • 原文地址:https://www.cnblogs.com/zhuminghui/p/9507982.html
Copyright © 2020-2023  润新知