信號是用於界面自動變化的一個工具,原理是信號綁定了一個函數,當信號被觸發時函數即被調用網絡
舉個例子多線程
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()
假設咱們的主界面有一個用於顯示時間的 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()
上述代碼增長了一個WorkerThread類。WorkerThread繼承自QThread類,重寫了其run()函數。能夠認爲,run()函數就是新的線程須要執行的代碼。在這裏就是要執行這個循環,而後發出計算完成的信號。而在按鈕點擊的槽函數中,使用work()中的workThread.start()函數啓動一個線程(注意,這裏不是run()函數)。再次運行程序,你會發現如今界面已經不會被阻塞了。佈局