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类的实例对象。
5.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线程中进行控件的处理。