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()。app

舉個例子:秒錶功能(每隔一秒刷新界面,直到按下中止按鈕)ide

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

相關文章
相關標籤/搜索