不可思议的简单!一分钟学会用Python和PyQt秒变GUI开发大神!

1,050 阅读12分钟

本文详细介绍了如何使用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。大概步骤如下:

  1. 访问微软开发工具官方下载网站:visualstudio.microsoft.com/visual-cpp-… 在这里插入图片描述
  2. 点击下载生成工具按钮,下载安装包。安装包只是个壳,很快就可以下载完成。
  3. 运行安装包,如下图所示,点击继续按钮,进入安装界面之后,选择使用C++的桌面开发,然后点击右下角的安装,直至安装完成即可。 在这里插入图片描述 在这里插入图片描述 在这里插入图片描述
  4. 重新执行pyqt安装命令,即可安装成功。
pip install PyQt5 PyQt5-tools

下载安装Microsoft C++ Build Tools,需要很长的时间,另外需要下载大量的文件,耗时费力。如果你不想那么费事,还可以手动下载whl文件,手动安装,以跳过这个错误,方法如下:

  1. 下载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
  2. whl文件下载后使用pip install PyQt5_sip-12.12.2-cp37-cp37m-win_amd64.whl命令安装它。 在这里插入图片描述
  3. 最后,重新执行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!"应用程序开始。

  1. 创建一个Python文件,例如hello.py
  2. 编写以下代码:
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来创建主窗口,可以添加菜单栏、工具栏等。

  1. 创建一个Python文件,例如main_window.py
  2. 编写以下代码:
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开发的道路上越走越远,创造出更多优秀的桌面应用程序!