使用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,欢迎使用并提出改进意见。
