使用PyQt5开发应用程序总结
PyQt5 使用笔记
PyQt5 是一个用于创建图形用户界面(GUI)的 Python 框架,基于 Qt 库开发而来。它提供了丰富的工具和组件,使开发者能够轻松地创建各种强大的桌面应用程序。本文将介绍 PyQt5 的基本用法,并提供一些示例代码帮助你入门。
安装 PyQt5
首先,需要安装 PyQt5 模块。你可以使用 pip 命令来安装:
1 | pip install PyQt5 |
创建一个基本的 PyQt5 窗口
在 PyQt5 中,你可以通过两种方法来创建窗口:
面向对象编程: 这种方法涉及创建一个继承自特定窗口类的新类,并在新类中重写需要的方法来配置界面和处理事件。这种方法更加面向对象,可以更好地组织和管理代码。
直接编写代码: 这种方法涉及直接编写代码来创建窗口和组件,然后配置属性和信号槽等。这种方法更加直接,适用于一些简单的界面或快速原型开发。
下面分别展示了这两种方法的示例:
面向对象编程
1 | import sys |
直接编写代码
1 | import sys |
无论你选择哪种方法,都可以根据项目需求来灵活调整和扩展代码。如果界面较为复杂或需要更好的代码组织,建议使用面向对象编程。如果界面简单且直接,可以选择直接编写代码。
以下是一个使用面向对象编程简单的示例代码,展示了如何创建一个基本的 PyQt5 窗口:
1 | import sys |
在这个示例中,我们首先导入了必要的模块,然后创建了一个继承自 QMainWindow
的自定义窗口类 MyWindow
。在 __init__
构造函数中,我们设置了窗口的标题和初始大小。最后,我们创建了一个应用对象并显示窗口。
常用的 PyQt5 组件
当使用 PyQt5 创建图形用户界面时,会涉及多种常用的组件,每个组件都有其特定的属性和用法。以下是一些常用组件的用法:
QLabel(标签)
标签用于显示文本或图像,可以用来展示信息、标题、说明等。常用属性和方法包括:
setText(text)
:设置标签的文本内容。text()
:获取标签的文本内容。setPixmap(pixmap)
:设置标签显示的图像。setAlignment(alignment)
:设置文本对齐方式。setFont(font)
:设置字体。
1 | from PyQt5.QtWidgets import QLabel |
QLineEdit(单行文本输入框)
单行文本输入框用于接收用户输入的文本,例如用户名、密码等。常用属性和方法包括:
setText(text)
:设置文本框的初始文本。text()
:获取用户输入的文本内容。setPlaceholderText(text)
:设置提示文本。
1 | from PyQt5.QtWidgets import QLineEdit |
QTextEdit(多行文本输入框)
多行文本输入框用于接收多行文本输入,支持富文本格式。常用属性和方法包括:
setText(text)
:设置文本框的初始文本。toPlainText()
:获取用户输入的纯文本内容。insertHtml(html)
:插入富文本内容。
1 | from PyQt5.QtWidgets import QTextEdit |
QComboBox(下拉框)
下拉框提供了一组选项供用户选择。常用属性和方法包括:
addItem(item)
:添加选项。addItems(items)
:批量添加选项。currentIndex()
:获取当前选中的选项索引。currentText()
:获取当前选中的选项文本。activated.connect(slot)
:连接选项激活的信号。
1 | from PyQt5.QtWidgets import QComboBox |
QPushButton(按钮)
按钮用于触发特定操作或事件。常用属性和方法包括:
setText(text)
:设置按钮显示的文本。clicked.connect(slot)
:连接按钮点击事件的信号。
1 | from PyQt5.QtWidgets import QPushButton |
QCheckBox(复选框)
复选框用于表示一个二选一的选项。常用属性和方法包括:
isChecked()
:检查复选框是否被选中。text()
:获取复选框的文本内容。toggled.connect(slot)
:连接复选框状态变化的信号。
1 | from PyQt5.QtWidgets import QCheckBox |
QRadioButton(单选按钮)
单选按钮用于从多个选项中选择一个。常用属性和方法包括:
isChecked()
:检查单选按钮是否被选中。text()
:获取单选按钮的文本内容。toggled.connect(slot)
:连接单选按钮状态变化的信号。
1 | from PyQt5.QtWidgets import QRadioButton |
QSlider(滑块)
滑块用于选择一个范围内的值。常用属性和方法包括:
setRange(minimum, maximum)
:设置滑块的范围。setValue(value)
:设置滑块的当前值。value()
:获取滑块的当前值。sliderMoved.connect(slot)
:连接滑块移动事件的信号。
1 | from PyQt5.QtWidgets import QSlider |
QProgressBar(进度条)
进度条用于显示任务的进度。常用属性和方法包括:
setRange(minimum, maximum)
:设置进度条的范围。setValue(value)
:设置进度条的当前值。value()
:获取进度条的当前值。
1 | from PyQt5.QtWidgets import QProgressBar |
QSpinBox(数值输入框)
数值输入框用于输入整数值。常用属性和方法包括:
setRange(minimum, maximum)
:设置数值输入框的范围。setValue(value)
:设置数值输入框的当前值。value()
:获取数值输入框的当前值。
1 | from PyQt5.QtWidgets import QSpinBox |
QDateTimeEdit(日期时间输入框)
日期时间输入框用于输入日期和时间。常用属性和方法包括:
setDateTime(datetime)
:设置日期时间输入框的日期时间。dateTime()
:获取日期时间输入框的日期时间。
1 | from PyQt5.QtWidgets import QDateTimeEdit |
QFileDialog(文件对话框)
文件对话框用于选择文件或目录。常用方法包括:
getOpenFileName()
:打开文件选择对话框并返回选择的文件路径。getSaveFileName()
:打开文件保存对话框并返回选择的文件路径。getExistingDirectory()
:打开目录选择对话框并返回选择的目录路径。
1 | from PyQt5.QtWidgets import QFileDialog |
QMessageBox(消息框)
消息框用于显示提示、警告或错误信息。常用方法包括:
information(parent, title, text)
:显示信息提示框。warning(parent, title, text)
:显示警告提示框。critical(parent, title, text)
:显示错误提示框。question(parent, title, text)
:显示询问提示框。
1 | from PyQt5.QtWidgets import QMessageBox |
布局管理
在 PyQt5 中,布局管理用于自动排列和定位组件,以便适应不同窗口大小。以下是一些常用的布局类型和使用示例:
QGridLayout(网格布局)
网格布局将组件按照行和列的方式排列。常用方法包括:
addWidget(widget, row, column, rowSpan, columnSpan)
:将组件添加到指定行列位置,可跨行列。
1 | from PyQt5.QtWidgets import QGridLayout |
QVBoxLayout(垂直布局)
垂直布局将组件按垂直方向排列。常用方法包括:
addWidget(widget)
:将组件按顺序添加到布局。
1 | from PyQt5.QtWidgets import QVBoxLayout |
QHBoxLayout(水平布局)
水平布局将组件按水平方向排列。常用方法包括:
addWidget(widget)
:将组件按顺序添加到布局。
1 | from PyQt5.QtWidgets import QHBoxLayout |
这些是一些常用的 PyQt5 组件和布局,通过合理地使用它们,你可以创建出丰富多彩的图形用户界面。根据项目的需求,你可以灵活地选择合适的组件和布局方式。
布局管理使得窗口中的组件自动适应并排列,无需手动调整位置和大小。
多线程与线程间通信
创建线程
在 PyQt5 中,可以使用 QThread
类来创建线程。为了创建一个自定义线程,需要继承 QThread
并重写其 run
方法,将耗时操作放在 run
方法中执行。
1 | from PyQt5.QtCore import QThread |
在线程间传递信号
在多线程应用中,线程之间的通信是常见的需求。PyQt5 提供了信号与槽机制来实现线程间的通信。可以通过自定义信号,在一个线程中发射信号,然后在另一个线程中连接该信号到槽函数来接收信号。
1 | from PyQt5.QtCore import QThread, pyqtSignal |
主线程接收信号
主线程可以连接自定义信号的槽函数,以接收在子线程中发射的信号。
1 | class MainWindow(QMainWindow): |
安全退出子线程
为了确保线程的安全退出,可以在窗口关闭事件中停止子线程并等待其完成。
1 | class MainWindow(QMainWindow): |
进一步解释:
self.my_signal.emit(result)
:这行代码在子线程中发射了一个自定义信号my_signal
,并传递了参数result
。这个信号可以携带任意数量和类型的参数,这里我们传递了一个字符串result
。self.thread.my_signal.connect(self.update_label)
:这行代码在主线程中连接了子线程发射的信号my_signal
到主线程的槽函数update_label
。这样一旦子线程发射了信号,主线程就会调用update_label
方法来处理这个信号。def update_label(self, result):
:这是主线程中的槽函数。当子线程发射信号时,主线程会调用这个函数,并将子线程传递的参数result
作为参数传递给这个函数。因此,result
确实代表了子线程传递的result
。关于
def update_label(self, result):
中的参数名
参数名只是一个标识符,它并不影响信号的传递和槽函数的调用。例如,你可以这样修改函数定义:
1
2def update_label(self, data):
# 使用 data 参数进行处理然后在连接信号时,也需要相应地修改:
1
self.thread.my_signal.connect(self.update_label)
只要信号和槽函数的参数类型匹配,无论参数名是什么,信号传递的参数都能够被成功传递给槽函数进行处理。
关于传递参数的类型
在 PyQt5 中,你可以使用自定义信号来传递多种类型的参数。除了 str
类型,还可以传递以下常用的参数类型:
int
:整数类型。float
:浮点数类型。bool
:布尔类型。list
或tuple
:列表或元组类型,可以传递多个参数。object
:Python 对象,可以传递任意类型的参数。
需要注意的是,信号和槽函数的参数类型必须匹配,否则会引发错误。当然,你也可以使用 pyqtSignal(object)
来传递任意类型的参数,但在槽函数内部需要根据参数类型进行适当的处理。
以下是一个示例,展示了如何使用不同类型的参数传递自定义信号:
1 | from PyQt5.QtCore import pyqtSignal, QObject |
在上述示例中,我们定义了一个 MyObject
类,它包含了不同类型的自定义信号。然后,我们通过连接这些信号到同一个槽函数 my_slot
来展示如何传递不同类型的参数。在槽函数内部,我们可以根据参数的类型来进行相应的处理。
多线程进阶
当涉及多线程编程和线程间通信时,以下是一些重要的概念和技术
互斥锁和信号量:
互斥锁用于保护共享资源,以确保在任何时候只有一个线程可以访问资源。信号量用于限制同时访问资源的线程数量。
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
27from PyQt5.QtCore import QMutex, QSemaphore, QThread
class SharedResource:
def __init__(self):
self.mutex = QMutex() # 创建互斥锁
self.semaphore = QSemaphore(3) # 创建信号量,允许3个线程同时访问
def access_resource(self):
self.semaphore.acquire() # 获取信号量
self.mutex.lock() # 上锁
# 访问和操作共享资源
self.mutex.unlock() # 解锁
self.semaphore.release() # 释放信号量
class WorkerThread(QThread):
def __init__(self, resource):
super().__init__()
self.resource = resource
def run(self):
self.resource.access_resource()
resource = SharedResource()
threads = [WorkerThread(resource) for _ in range(5)]
for thread in threads:
thread.start()线程池:
线程池可以有效地管理和调度多个线程执行任务,避免频繁地创建和销毁线程。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15from PyQt5.QtCore import QThreadPool, QRunnable, Qt
class Task(QRunnable):
def __init__(self, task_id):
super().__init__()
self.task_id = task_id
def run(self):
print(f"Task {self.task_id} is running in thread {int(QThread.currentThreadId())}")
pool = QThreadPool.globalInstance()
for i in range(5):
task = Task(i)
pool.start(task)定时器和延迟:
使用定时器可以在一段时间后触发任务,避免阻塞线程。
1
2
3
4
5
6
7
8
9
10
11
12
13from PyQt5.QtCore import QTimer, pyqtSlot
class TimerExample:
def __init__(self):
self.timer = QTimer()
self.timer.timeout.connect(self.on_timer_timeout)
self.timer.start(1000) # 每秒触发一次
def on_timer_timeout(self):
print("Timer triggered")
example = TimerExample()线程间通信的其他方式:
除了信号和槽函数,还可以使用队列来在线程之间传递数据。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23import queue
from PyQt5.QtCore import QThread, pyqtSlot
class QueueExample(QThread):
def __init__(self):
super().__init__()
self.message_queue = queue.Queue()
def run(self):
while True:
message = self.message_queue.get()
if message == "exit":
break
print(f"Received message: {message}")
def send_message(self, message):
self.message_queue.put(message)
example = QueueExample()
example.start()
example.send_message("Hello")
example.send_message("World")
example.send_message("exit")
总结
本文介绍了如何使用 PyQt5 创建常见的 GUI 组件,包括标签、按钮、文本框、下拉框、复选框和绘图区域,以及如何使用布局管理来排列这些组件。
应用
应用以上方法,笔者试着写了一个简易串口调试助手 MA-SerialDebugger,欢迎使用并提出改进意见。