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()函數)。再次運行程序,你會發現現在界面已經不會被阻塞了。

 

 

 

      

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM