1.窗口類型
QMainWindow:可以包含菜單欄、工具欄、標題欄、狀態欄,是最常見的窗口形式
QWidgets:不確定窗口的用途,使用QWidgets
QDialog:對話窗口的基類,用於執行短期任務,沒有菜單欄、工具欄、狀態欄
2.創建窗體應用程序
import sys from PyQt5.QtWidgets import QMainWindow, QApplication class MainWin(QMainWindow): def __init__(self): super(MainWin, self).__init__() self.setWindowTitle("抓取工具") self.resize(400, 300) self.status = self.statusBar() self.status.showMessage('抓取工具') if __name__ == "__main__": app = QApplication(sys.argv) mainWin = MainWin() mainWin.show() sys.exit(app.exec_())
從Main中可知,創建窗體程序,先要創建QApplication對象即應用對象,然后創建窗口對象(繼承自三種窗口類型),然后調用窗口的顯示函數show(),注意:沒有show函數,運行時指揮創建相關進程,即用用程序,窗口不顯示,唯有調用show函數,窗口才會顯示。
3.在窗體創建控件對象
按鈕、輸入框、標簽等控件基本上都在QtWidgets類中,添加控件代碼示例
class MainWin(QMainWindow): path = './app_icon.cio' def __init__(self): super(MainWin, self).__init__() self.init_ui() # 界面初始化 def init_ui(self): # 創建控件對象 self.edit_name = QtWidgets.QLineEdit() self.btn = QtWidgets.QPushButton('退出') # 創建布局 layout = QHBoxLayout() # 往布局中添加控件 layout.addWidget(self.btn) layout.addWidget(self.edit_name) self.btn.clicked.conect(self.btn_click) # 把布局放到窗口上 mainFrame = QtWidgets.QWidget() mainFrame.setLayout(layout) # 居中充滿屏幕 self.setCentralWidget(mainFrame) # 設置圖標 self.setWindowIcon(QIcon(self.path)) self.setWindowTitle("窗體程序") self.resize(400, 300) self.status = self.statusBar() self.status.showMessage('狀態欄')
4.使用QtDesigner進行界面設計
1.配置PyCharm相關開發環境,下載安裝相關包,pyqt5以及pyqt5-tools。然后在Pycharm中ExtenalTools中添加
QtDesigner路徑:$Python安裝路徑$\Lib\site-packages\pyqt5_tools\Qt\bin\designer.exe,工作空間:$ProjectFileDir$
PyUIC路徑:Python.exe路徑,工作空間:$FileDir$,參數變量:-m PyQt5.uic.pyuic $FileName$ -o $FileNameWithoutExtension$.py
二者使用時請在Tools -》 ExtenalTools下選擇
2.打開QtDesigner設計UI文件,選中UI文件使用PyUIC將其轉為Pyton文件。
3.使用UI文件
UI類
from PyQt5 import QtCore, QtGui, QtWidgets # UI執行窗口界面 class Ui_ExecWindow(object): def setupUi(self, Form): Form.setObjectName("Form") Form.resize(400, 300) self.process_display = QtWidgets.QTextBrowser(Form) self.process_display.setGeometry(QtCore.QRect(50, 50, 300, 200)) self.process_display.setObjectName("process_display") self.retranslateUi(Form) QtCore.QMetaObject.connectSlotsByName(Form) def retranslateUi(self, Form): _translate = QtCore.QCoreApplication.translate Form.setWindowTitle(_translate("Form", "Form"))
窗口類
class ExecWindow(QWidget): def __init__(self): QWidget.__init__(self) self.ui = Ui_ExecWindow() self.ui.setupUi(self)
建議二者分開,添加槽和信號的綁定可以在窗口類的初始化函數中書寫。例如:
class ConfWindow1(QDialog): def __init__(self, task_type): QDialog.__init__(self) self.ui = Ui_ConfWindow1() self.ui.setupUi(self) self.new_actions = list() self.task_type = task_type # 任務類型 # 信號與槽綁定 self.ui.rbtn_b.toggled.connect(self.input_type) self.ui.btn_save.clicked.connect(self.save_configuration) self.ui.btn_save_single.clicked.connect(self.save_single_action)
ui是UI類的實例對象。
4.重寫事件
以關閉窗口事件為例:
def closeEvent(self, event): reply = QMessageBox.question(self, '確認', '確認退出嗎', QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if reply == QMessageBox.Yes: event.accept() else: event.ignore()
當窗口關閉時會觸發該事件,可以用其他事件綁定窗口對象的close方法進行觸發,或其他關閉事件觸發
5.界面跳轉
事件綁定對應的方法(信號和槽),如單擊事件綁定下面函數:(注意如果要傳參時請在請在函數(槽)前使用lambda:標記,否則會出錯)
請使用self標記窗口類對象為成員變量,否則直接調用show函數會發生閃退現象。
# 信號與槽綁定
self.ui.btn_conf1.clicked.connect(lambda: self.configure_win_open(1))
self.ui.btn_conf2.clicked.connect(lambda: self.configure_win_open(2))
# 打開對應的配置界面 def configure_win_open(self, win_id): if win_id == 1: self.conf_win = ConfWindow1(self.task_type) self.conf_win.show() elif win_id == 2: self.conf_win = ConfWindow2(self.task_type) self.conf_win.show() else: print("沒有該窗口")
6.PyQt中多線程處理,解決界面假死問題
當界面要執行一個耗時間較長的任務時,使用單線程會出現界面假死現象。使用多線程可以解決,模板如下
線程子類
# 多線程,執行任務 from PyQt5 import QtCore from PyQt5.QtCore import QThread from selenium import webdriver from application_task.Task import Task from customize_exception.CustomizeException import NoSuchSelectorError from utils import Util class Task_Excutor(QThread): _signal = QtCore.pyqtSignal(str) # 自定義信號 def __init__(self, task_type): super().__init__() self.task_type = task_type def __del__(self): self.wait() def run(self): driver = webdriver.Chrome() actions = [] task_conf = {} # 出現異常傳遞信號,因為當前線程無法使用GUI try: actions = Util.json_2_element_actions( "./configuration/" + self.task_type + "動作配置.json") task_conf = Util.load_conf("./configuration/" + self.task_type + "雜項配置.json") except FileNotFoundError as e: self._signal.emit(str("e")) except NoSuchSelectorError as e1: self._signal.emit(str("e1")) except Exception as e2: self._signal.emit(str("e2")) else: try: task = Task(driver, actions, task_conf) task.original_task() except Exception as e3: self._signal.emit(str("e3")) else: # 成功信號 self._signal.emit(str("succeed"))
界面調用類中定義的方法
# 任務開始 def start_task(self): self.thread = Task_Excutor(self.ui.task_selector.currentText()) self.thread._signal.connect(self.call_back) self.thread.start() # 信號回調處理 def call_back(self, msg): if msg == "e": QMessageBox(QMessageBox.NoIcon, '錯誤', '指定路徑下配置文件不存在').exec() elif msg == "e1": QMessageBox(QMessageBox.NoIcon, '錯誤', '配置文件出錯,沒有指定的選擇器').exec() elif msg == "e2": QMessageBox(QMessageBox.NoIcon, '錯誤', '未知錯誤,執行過程中出錯').exec() elif msg == "e3": QMessageBox(QMessageBox.NoIcon, '錯誤', '未知錯誤,初始化動作或配置失敗').exec() elif msg == 'succeed': QMessageBox(QMessageBox.NoIcon, '提示', '任務執行成功').exec() else: QMessageBox(QMessageBox.NoIcon, '提示', '未知線程回調消息').exec()
創建一個線程的子類,重寫run方法。並定義信號,調用該線程的界面類中添加開始線程的方法start_task(),並與觸發線程開始的事件綁定。start_task()方法中為自定義信號綁定方法call_back,用於處理創建的線程的執行任務過程中信號傳遞處理。
注意:由於pyqt中,非GUI線程是無法調用控件的,如在執行任務中實例化QMessageBox會出現錯誤導致程序異常退出。所以通過回調函數觸發指定的信號並傳遞到GUI線程中進行控件的處理。