本文详细介绍了如何使用PyQt进行GUI桌面应用程序开发。我们逐步讲解了PyQt的基础概念,包括核心模块和主要类,并通过实际示例展示了基本控件的创建和使用、窗口布局管理、信号与槽机制、以及事件处理。此外,还探讨了自定义控件和多线程的高级主题。最终,通过实现一个简单的计算器项目,综合运用了所学知识,展示了PyQt强大的功能和灵活性。通过本教程,你应该掌握了使用PyQt创建和管理GUI应用的基本技巧。PyQt功能强大,继续深入学习和实践将帮助你更熟练地开发高效、美观的桌面应用程序。希望本文能为你的PyQt学习旅程提供坚实的基础,助你在GUI开发中游刃有余,创造出更多优秀的应用。Happy coding!
引言
如果你想使用Python快速构建图形用户界面(GUI)桌面应用程序,PyQt是一个强大且灵活的选择。PyQt是一组Python绑定,基于流行的Qt应用程序框架。无论是对GUI开发完全陌生,还是有一定编程基础的读者,本文都将带你一步一步地深入了解PyQt的核心内容,帮助你快速掌握使用PyQt进行桌面应用开发。
1. PyQt简介
什么是PyQt?
PyQt是由Riverbank Computing公司开发的一组Python插件,它将Qt应用程序框架的功能暴露给Python。Qt是一个跨平台的C++库,用于开发图形用户界面和多平台应用程序。
PyQt的优势
- 跨平台:支持Windows、macOS、Linux等多个操作系统。
- 强大的控件集:提供了丰富的GUI控件,满足常见和复杂的UI需求。
- 全面的文档:官网文档详尽,支持社区活跃。
- 高度可扩展:支持自定义控件与信号槽机制,功能强大。
2. 环境搭建
安装Python
确保你已安装Python 3.5或更高版本。可以从Python官方网站下载最新版本的Python。
安装PyQt
可以使用pip来安装PyQt5和相关工具包:
pip install PyQt5 PyQt5-tools
如果安装过程中有如下错误:
Using cached colorama-0.4.6-py2.py3-none-any.whl (25 kB)
Using cached importlib_metadata-6.7.0-py3-none-any.whl (22 kB)
Using cached qt5_applications-5.15.2.2.3-py3-none-win_amd64.whl (64.5 MB)
Using cached typing_extensions-4.7.1-py3-none-any.whl (33 kB)
Using cached zipp-3.15.0-py3-none-any.whl (6.8 kB)
Building wheels for collected packages: PyQt5-sip
Building wheel for PyQt5-sip (pyproject.toml): started
Building wheel for PyQt5-sip (pyproject.toml): finished with status 'error'
error: subprocess-exited-with-error
Building wheel for PyQt5-sip (pyproject.toml) did not run successfully.
exit code: 1
[5 lines of output]
running bdist_wheel
running build
running build_ext
building 'PyQt5.sip' extension
error: Microsoft Visual C++ 14.0 or greater is required. Get it with "Microsoft C++ Build Tools": https://visualstudio.microsoft.com/visual-cpp-build-tools/
[end of output]
note: This error originates from a subprocess, and is likely not a problem with pip.
ERROR: Failed building wheel for PyQt5-sip
Failed to build PyQt5-sip
ERROR: Could not build wheels for PyQt5-sip, which is required to install pyproject.toml-based projects
Windows平台安装过程中遇到这个错误,说明需要用到Microsoft C++ Build Tools
。大概步骤如下:
- 访问微软开发工具官方下载网站:visualstudio.microsoft.com/visual-cpp-…
- 点击下载生成工具按钮,下载安装包。安装包只是个壳,很快就可以下载完成。
- 运行安装包,如下图所示,点击
继续
按钮,进入安装界面之后,选择使用C++的桌面开发
,然后点击右下角的安装,直至安装完成即可。 - 重新执行pyqt安装命令,即可安装成功。
pip install PyQt5 PyQt5-tools
下载安装Microsoft C++ Build Tools
,需要很长的时间,另外需要下载大量的文件,耗时费力。如果你不想那么费事,还可以手动下载whl文件,手动安装,以跳过这个错误,方法如下:
- 下载whl文件
whl下载地址:pypi.tuna.tsinghua.edu.cn/simple/pyqt…。比如我是
Win10 64位(对应win_amd64)
系统,python版本为3.7.0(对应cp37)
,所以我选择了适配与我的系统和python版本的PyQt5_sip最新版本whl文件:PyQt5_sip-12.12.2-cp37-cp37m-win_amd64.whl。 - whl文件下载后使用
pip install PyQt5_sip-12.12.2-cp37-cp37m-win_amd64.whl
命令安装它。 - 最后,重新执行pyqt安装命令,如下图所示,即可安装成功。
pip install PyQt5 PyQt5-tools
3. PyQt基础概念
python基础教程
本文阅读需要一定的Python基础,可以参考其它博文进行学习。
Qt库的核心模块
- QtWidgets:包含了所有的重要GUI类。
- QtCore:包含了非GUI功能,如事件循环与信号槽机制。
- QtGui:包含了图形和字体相关的类。
PyQt中的主要类
- QApplication:管理GUI应用程序的控制流和主设置。
- QWidget:所有用户界面对象的基类。
- QMainWindow:创建主窗口。
- QPushButton:按钮控件。
- QLabel:标签控件。
- QLineEdit:单行文本输入框。
- QVBoxLayout/QHBoxLayout:垂直/水平布局管理器。
4. 创建第一个PyQt应用
让我们从一个简单的"Hello, World!"应用程序开始。
- 创建一个Python文件,例如
hello.py
。 - 编写以下代码:
import sys
from PyQt5.QtWidgets import QApplication, QLabel, QWidget
def main():
app = QApplication(sys.argv)
mainWindow = QWidget()
mainWindow.setWindowTitle('Hello, World!')
mainWindow.setGeometry(100, 100, 280, 80)
label = QLabel('<h1>Hello, World!</h1>', parent=mainWindow)
label.move(60, 15)
mainWindow.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
代码讲解:
QApplication
:创建应用对象。QWidget
:创建主窗口。QLabel
:在窗口上显示文本。setGeometry
:设置窗口的位置和大小。show
:展示窗口。sys.exit
:确保应用程序干净退出。
运行代码
在命令行或终端中运行:
python hello.py
你将看到一个标题为"Hello, World!"的窗口。
5. 窗口与控件
创建主窗口
使用QMainWindow
来创建主窗口,可以添加菜单栏、工具栏等。
- 创建一个Python文件,例如
main_window.py
。 - 编写以下代码:
import sys
from PyQt5.QtWidgets import QApplication, QMainWindow
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle('Main Window')
self.setGeometry(100, 100, 600, 400)
def main():
app = QApplication(sys.argv)
mainWindow = MainWindow()
mainWindow.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
运行代码
在命令行或终端中运行:
python main_window.py
你将看到一个标题为"Main Window"的窗口。
添加按钮
继续在main_window.py
文件中添加以下代码:
import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle('Main Window')
self.setGeometry(100, 100, 600, 400)
button = QPushButton('Click Me', self)
button.setGeometry(100, 100, 200, 50)
button.clicked.connect(self.on_button_click)
def on_button_click(self):
print('Button clicked!')
def main():
app = QApplication(sys.argv)
mainWindow = MainWindow()
mainWindow.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
运行代码
在命令行或终端中运行:
python main_window.py
你将看到一个带有按钮的窗口,点击按钮会在控制台输出"Button clicked!"。
更多控件
继续在main_window.py
文件中添加以下代码:
import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QLineEdit, QTextEdit, QCheckBox, QRadioButton, QComboBox, QPushButton
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle('Main Window')
self.setGeometry(100, 100, 600, 400)
lineEdit = QLineEdit(self)
lineEdit.setGeometry(100, 50, 200, 30)
textEdit = QTextEdit(self)
textEdit.setGeometry(100, 100, 200, 100)
checkBox = QCheckBox('Check me', self)
checkBox.setGeometry(100, 220, 100, 30)
radioButton = QRadioButton('Select me', self)
radioButton.setGeometry(100, 260, 100, 30)
comboBox = QComboBox(self)
comboBox.setGeometry(100, 300, 200, 30)
comboBox.addItems(['Option 1', 'Option 2', 'Option 3'])
def main():
app = QApplication(sys.argv)
mainWindow = MainWindow()
mainWindow.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
运行代码
在命令行或终端中运行:
python main_window.py
你将看到一个窗口,包含单行输入框、多行输入框、复选框、单选按钮和下拉列表框。
6. 布局管理
垂直布局
创建一个新的Python文件,例如vertical_layout.py
,并编写以下代码:
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QPushButton
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle('Vertical Layout')
self.setGeometry(100, 100, 400, 300)
layout = QVBoxLayout()
layout.addWidget(QPushButton('Button 1'))
layout.addWidget(QPushButton('Button 2'))
layout.addWidget(QPushButton('Button 3'))
self.setLayout(layout)
def main():
app = QApplication(sys.argv)
mainWindow = MainWindow()
mainWindow.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
运行代码
在命令行或终端中运行:
python vertical_layout.py
你将看到一个窗口,按钮垂直排列。
水平布局
创建一个新的Python文件,例如horizontal_layout.py
,并编写以下代码:
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QHBoxLayout, QPushButton
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle('Horizontal Layout')
self.setGeometry(100, 100, 400, 300)
layout = QHBoxLayout()
layout.addWidget(QPushButton('Button 1'))
layout.addWidget(QPushButton('Button 2'))
layout.addWidget(QPushButton('Button 3'))
self.setLayout(layout)
def main():
app = QApplication(sys.argv)
mainWindow = MainWindow()
mainWindow.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
运行代码
在命令行或终端中运行:
python horizontal_layout.py
你将看到一个窗口,按钮水平排列。
网格布局
创建一个新的Python文件,例如grid_layout.py
,并编写以下代码:
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QGridLayout, QPushButton
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle('Grid Layout')
self.setGeometry(100, 100, 400, 300)
layout = QGridLayout()
layout.addWidget(QPushButton('Button 1'), 0, 0)
layout.addWidget(QPushButton('Button 2'), 0, 1)
layout.addWidget(QPushButton('Button 3'), 1, 0)
layout.addWidget(QPushButton('Button 4'), 1, 1)
self.setLayout(layout)
def main():
app = QApplication(sys.argv)
mainWindow = MainWindow()
mainWindow.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
运行代码
在命令行或终端中运行:
python grid_layout.py
你将看到一个窗口,按钮网格排列。
7. 信号与槽机制
什么是信号与槽?
信号与槽机制是Qt的重要特性之一,用于对象间通信。信号是某个事件发生的反馈,槽是响应信号的函数。
使用信号与槽
创建一个新的Python文件,例如signals_slots.py
,并编写以下代码:
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle('Signals and Slots')
self.setGeometry(100, 100, 400, 300)
button = QPushButton('Click Me', self)
button.setGeometry(150, 130, 100, 50)
button.clicked.connect(self.on_button_click)
def on_button_click(self):
print('Button clicked!')
def main():
app = QApplication(sys.argv)
mainWindow = MainWindow()
mainWindow.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
运行代码
在命令行或终端中运行:
python signals_slots.py
你将看到一个带有按钮的窗口,点击按钮会在控制台输出"Button clicked!"。
自定义信号与槽
创建一个新的Python文件,例如custom_signals_slots.py
,并编写以下代码:
import sys
from PyQt5.QtCore import pyqtSignal, QObject
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton
class Communicate(QObject):
signal = pyqtSignal()
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle('Custom Signals and Slots')
self.setGeometry(100, 100, 400, 300)
self.comm = Communicate()
self.comm.signal.connect(self.on_custom_signal)
button = QPushButton('Emit Signal', self)
button.setGeometry(150, 130, 100, 50)
button.clicked.connect(self.comm.signal.emit)
def on_custom_signal(self):
print('Custom signal emitted!')
def main():
app = QApplication(sys.argv)
mainWindow = MainWindow()
mainWindow.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
运行代码
在命令行或终端中运行:
python custom_signals_slots.py
你将看到一个带有按钮的窗口,点击按钮会在控制台输出"Custom signal emitted!"。
8. 事件处理
事件与事件处理
事件处理机制用于响应用户的输入、窗口状态变化等事件。可以重载特定事件处理方法来处理事件。
常见事件处理方法
创建一个新的Python文件,例如event_handling.py
,并编写以下代码:
import sys
from PyQt5.QtWidgets import QApplication, QWidget
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle('Event Handling')
self.setGeometry(100, 100, 400, 300)
def mousePressEvent(self, event):
print('Mouse button pressed!')
def keyPressEvent(self, event):
print(f'Key pressed: {event.key()}')
def paintEvent(self, event):
print('Window painting!')
def main():
app = QApplication(sys.argv)
mainWindow = MainWindow()
mainWindow.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
运行代码
在命令行或终端中运行:
python event_handling.py
点击窗口或按下键盘按键,可以在控制台观察事件输出。
9. 高级主题:自定义控件与多线程
自定义控件
创建一个新的Python文件,例如custom_widget.py
,并编写以下代码:
import sys
from PyQt5.QtWidgets import QApplication, QLabel, QWidget
class CustomLabel(QLabel):
def __init__(self, text, parent=None):
super().__init__(text, parent)
def mousePressEvent(self, event):
self.setText('Label clicked!')
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle('Custom Widgets')
self.setGeometry(100, 100, 400, 300)
label = CustomLabel('Click me', self)
label.setGeometry(150, 130, 100, 50)
def main():
app = QApplication(sys.argv)
mainWindow = MainWindow()
mainWindow.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
运行代码
在命令行或终端中运行:
python custom_widget.py
点击Click me
标签,会看到标签文本变为Label clicked!
。
多线程
创建一个新的Python文件,例如multithreading.py
,并编写以下代码:
import sys
from PyQt5.QtCore import QThread, pyqtSignal
from PyQt5.QtWidgets import QApplication, QLabel, QWidget
class WorkerThread(QThread):
progress = pyqtSignal(int)
def run(self):
for i in range(100):
self.progress.emit(i+1)
self.sleep(1)
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle('Multithreading')
self.setGeometry(100, 100, 400, 300)
self.label = QLabel('0%', self)
self.label.setGeometry(150, 130, 100, 50)
self.worker = WorkerThread()
self.worker.progress.connect(self.update_label)
self.worker.start()
def update_label(self, value):
self.label.setText(f'{value}%')
def main():
app = QApplication(sys.argv)
mainWindow = MainWindow()
mainWindow.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
运行代码
在命令行或终端中运行:
python multithreading.py
你将看到进度逐渐更新的效果。
10. 项目实例:创建一个简单的计算器
项目背景
我们将通过一个简单的计算器项目来综合运用上述知识点。该计算器支持基本的加减乘除运算。
项目结构
- 主窗口
QMainWindow
- 数字按钮
QPushButton
- 运算符按钮
QPushButton
- 显示屏
QLineEdit
- 布局
QGridLayout
完整代码
创建一个Python文件,例如calculator.py
,并编写以下代码:
import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QPushButton, QLineEdit, QGridLayout
from PyQt5.QtCore import Qt
class Calculator(QMainWindow):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.setWindowTitle('Calculator')
self.setGeometry(100, 100, 400, 500)
widget = QWidget(self)
self.setCentralWidget(widget)
self.display = QLineEdit()
self.display.setAlignment(Qt.AlignRight)
self.display.setReadOnly(True)
grid = QGridLayout()
widget.setLayout(grid)
grid.addWidget(self.display, 0, 0, 1, 4)
names = ['Cls', 'Bck', '', 'Close',
'7', '8', '9', '/',
'4', '5', '6', '*',
'1', '2', '3', '-',
'0', '.', '=', '+']
positions = [(i, j) for i in range(1, 6) for j in range(4)]
for position, name in zip(positions, names):
if name == '':
continue
button = QPushButton(name)
grid.addWidget(button, *position)
button.clicked.connect(self.onButtonClick)
def onButtonClick(self):
sender = self.sender()
text = sender.text()
if text == 'Cls':
self.display.clear()
elif text == 'Bck':
self.display.setText(self.display.text()[:-1])
elif text == 'Close':
self.close()
elif text == '=':
try:
result = eval(self.display.text())
self.display.setText(str(result))
except Exception as e:
self.display.setText('Error')
else:
self.display.setText(self.display.text() + text)
def main():
app = QApplication(sys.argv)
calculator = Calculator()
calculator.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
代码讲解
- 界面布局:我们使用
QGridLayout
实现计算器布局。按钮按行和列添加进网格布局。 - 按钮功能:通过
QPushButton
创建按钮,并使用button.clicked.connect(self.onButtonClick)
将按钮点击信号连接到onButtonClick
槽函数。 - 槽函数处理:
onButtonClick
函数处理不同按钮点击事件,如清除、删除、计算等。
运行代码
在命令行或终端中运行以下命令:
python calculator.py
你将看到一个计算器界面,支持基本的加减乘除运算。
11. 总结
通过本教程,你已经了解了如何使用PyQt来创建和管理GUI桌面应用程序。我们从安装和环境配置开始,逐步讲解了PyQt的基础概念,展示了基本控件的使用、布局管理以及信号和槽机制。之后,我们探讨了事件处理、自定义控件和多线程等高级话题,最后以一个简单的计算器项目为实战例子,综合应用了这些知识。
PyQt功能强大且灵活,继续深入学习和实际项目应用将帮助你更加熟练地掌握它。希望本文能为你的PyQt学习之旅打下坚实的基础。祝你在GUI开发的道路上越走越远,创造出更多优秀的桌面应用程序!