永成的學習部落格

這裡會記錄永成學習的一部分

0%

QT5 Python的GUI介面 使用紀錄

安裝引導

  • 建議使用conda安裝,conda安裝有中文介面
    QT5的應用程式路徑:anaconda3\envs\yolov5-py3.9\Library\bin\designer.exe

    1
    conda install -c anaconda pyqt

    介面畫面:

  • 使用pip安裝:

    1
    pip install PyQt5

學前參考

這一篇文章教學整理的不錯,重點都有記錄在裡面,但是也有些重點沒有記載,需要自己在摸索挖掘唷!


本篇我會記載著,我個人常用的QT5代碼,並且會持續更新。

⬇⬇⬇文章開始⬇⬇⬇

開始介紹

我開始接觸 QT5 的原因是因為我正在自學 Python 並為一家公司設計一個缺陷視覺識別系統。為了讓這家公司方便操作 GUI 界面,我開始學習如何使用 QT5 來設計 Python 程序的 GUI 界面。


轉換 .ui 檔案為 .py 程式碼檔案

回到輸入指令的畫面,輸入下列指令,就能將 .ui 檔案轉換為 .py 的程式碼檔案。

1
pyuic5 -o qt_test.py -x qt_test.ui

QtCore.pyqtSignal 信號

由於QT5都是「無窮迴圈」運作的原因,若直接將QT5的元件指令,放到QThread多執行緒裡執行,會導致Python程序運作卡住,並停止運作。所以我們要先學會利用信號來執行你要執行的元件指令,這個信號有點像是按鈕開關,但是它也可以傳int,數字透過判斷式,選擇執行的代碼。

QtCore.pyqtSignal 僅支援 class 寫法。
在 PyQt5 裡的元件,都是透過信號的傳遞進行溝通和互動,雖然大部分的元件都有 connect 接收訊息的機制,但也可以使用 QtCore.pyqtSignal 的方式自訂信號進行傳遞。

class的寫法代碼演示(點擊展開)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import QObject, pyqtSignal # 定義信號的模組

class Ui_MainWindow(QObject):
# ----------------------------------------------------
# 新增代碼
label_show_ON = pyqtSignal(int) # 宣布定義信號名稱
def label_show(self,show):
if show == 1:
self.label.setText("A")
if show == 2:
self.label.setText("B")

def A(self):
self.label_show_ON.emit(1)

def B(self):
self.label_show_ON.emit(2)
# ----------------------------------------------------

def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(221, 100)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.pushButtonA = QtWidgets.QPushButton(self.centralwidget)
self.pushButtonA.setGeometry(QtCore.QRect(20, 20, 75, 23))
self.pushButtonA.setObjectName("pushButtonA")
self.pushButtonB = QtWidgets.QPushButton(self.centralwidget)
self.pushButtonB.setGeometry(QtCore.QRect(130, 20, 75, 23))
self.pushButtonB.setObjectName("pushButtonB")
self.label = QtWidgets.QLabel(self.centralwidget)
self.label.setGeometry(QtCore.QRect(20, 50, 181, 21))
self.label.setAlignment(QtCore.Qt.AlignCenter)
self.label.setObjectName("label")
MainWindow.setCentralWidget(self.centralwidget)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)

self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)

def retranslateUi(self, MainWindow):
translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(translate("MainWindow", "TEST"))
self.pushButtonA.setText(translate("MainWindow", "Button A"))
self.pushButtonB.setText(translate("MainWindow", "Button B"))
self.label.setText(translate("MainWindow", "TextLabel"))
# ----------------------------------------------------
# 新增代碼
self.label_show_ON.connect(self.label_show)
self.pushButtonA.clicked.connect(self.A)
self.pushButtonB.clicked.connect(self.B)
# ----------------------------------------------------


if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())

QThread 多執行緒

使用 PyQt5 設計介面時,視窗主程式的本質是放在一個「無窮迴圈」裡執行,如果需要加入多個迴圈且不影響主視窗 ( 如果單純放入迴圈,會在所有迴圈結束後才啟動視窗 ),就需要使用 QThread 機制,讓多個執行緒同時執行,這裡會介紹 QThread 的使用方式,還會額外介紹搭配 Python threading 標準函式庫的作法。

一般的寫法(點擊展開)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
from PyQt5 import QtWidgets
from PyQt5.QtCore import QThread
import sys, time

app = QtWidgets.QApplication(sys.argv)

Form = QtWidgets.QWidget()
Form.setWindowTitle('oxxo.studio')
Form.resize(300, 200)

label_a = QtWidgets.QLabel(Form)
label_a.setGeometry(10, 10, 100, 30)

label_b = QtWidgets.QLabel(Form)
label_b.setGeometry(10, 50, 100, 30)

def a():
for i in range(0,5):
label_a.setText(str(i))
print('A:',i)
time.sleep(0.5)

def b():
for i in range(0,50,10):
label_b.setText(str(i))
print('B:',i)
time.sleep(0.5)

thread_a = QThread() # 建立 Thread()
thread_a.run = a # 設定該執行緒執行 a()
thread_a.start() # 啟動執行緒

thread_b = QThread() # 建立 Thread()
thread_b.run = b # 設定該執行緒執行 b()
thread_b.start() # 啟動執行緒

Form.show()
sys.exit(app.exec_())
class的寫法(點擊展開)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import QThread
import time

class Ui_MainWindow(object):
def a(self):
for i in range(0,5):
self.label_1.setText(str(i))
print('A:',i)
time.sleep(1)

def b(self):
for i in range(0,50,10):
self.label_2.setText(str(i))
print('B:',i)
time.sleep(1)

def run(self):
self.thread_a.start() # 執行多執行緒 thread_a
self.thread_b.start() # 執行多執行緒 thread_b

def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(221, 100)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.pushButton = QtWidgets.QPushButton(self.centralwidget)
self.pushButton.setGeometry(QtCore.QRect(20, 20, 75, 23))
self.pushButton.setObjectName("pushButton")
self.label_1 = QtWidgets.QLabel(self.centralwidget)
self.label_1.setGeometry(QtCore.QRect(20, 50, 71, 21))
self.label_1.setAlignment(QtCore.Qt.AlignCenter)
self.label_1.setObjectName("label_1")
self.label_2 = QtWidgets.QLabel(self.centralwidget)
self.label_2.setGeometry(QtCore.QRect(130, 50, 71, 21))
self.label_2.setAlignment(QtCore.Qt.AlignCenter)
self.label_2.setObjectName("label_2")
MainWindow.setCentralWidget(self.centralwidget)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)

def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "TEST"))
self.pushButton.setText(_translate("MainWindow", "Button A"))
self.thread_a = QThread() # 建立 Thread()
self.thread_a.run = self.a # 設定該執行緒執行 a()
self.thread_b = QThread() # 建立 Thread()
self.thread_b.run = self.b # 設定該執行緒執行 b()
self.pushButton.clicked.connect(self.run)

if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())

QThread 常用方法

方法 參數 說明
start() 啟動執行緒
wait() 等待該執行緒結束
sleep() Sec 等待該執行緒幾秒

保存關閉QT介面的當前值

目的是關閉QT時保存上次的執行結果,並且再下一次開啟程式時,可以繼續引用上次執行的結果值。

1
2
3
4
5
6
7
def setupUi(self, WindowForms):
# 設定QT的QSettings
self.settings = QSettings('MyCompany', 'yolov5-AINTEC')
self.settings_save = QThread()

self.settings_save.run = self.settings_save_data # 選擇執行的副程式
self.settings_save.start() # 開始背景執行

保存變數值到索引

1
2
3
4
5
6
7
def settings_save_data(self):
try:
while True:
self.settings.setValue('Setting_bool1', self.SettingOther_radioButton_1.isChecked())
# self.settings.setValue("保存值的索引名稱", "欲保存的變數")
except:
pass # 避免突然關閉QT介面時,產生的錯誤訊息

調用保存索引的變數值

1
2
3
a = self.settings.value('CCDtest1', defaultValue=0, type=int) # 引用int範例
b = self.settings.value('Setting_bool1', defaultValue=False, type=bool) # 引用bool範例
c = self.settings.value('threshold_mode_1', defaultValue='無') # 引用string範例