只需要十分钟 ,快速体验一把 PyQT6 好不好用

3,901 阅读7分钟

一. 前言

一直在为 Python 寻求一个桌面端的框架 ,对于 PyQT5 之前也有一定的研究 ,但是不太符合期望。

最近发现 PyQT6 已经发布很长一段时间了 ,但是国内文档偏少, 所以决定自己体验一下效果。

本文目的 :

  • 记录 Python PyQT6 的快速上手流程 ,以及使用方式

二. PyQT6 使用案例

2.1 基础入门

入门代码是从 ChatGPT 上面抄的案例 ,我们很简单就能实现一个窗口功能 :

  • S1 : 安装 PyQT6 的依赖 -- pip install PyQt6
  • S2 : 代码中引入 PyQT6 的相关对象依赖
  • S3 : 创建一个 QApplication 对象 ,用于承载整个应用
  • S4 : 丰富布局和功能
import sys
from PyQt6.QtWidgets import QApplication, QWidget, QPushButton, QVBoxLayout, QMessageBox

class MyApp(QWidget):
    def __init__(self):
        super().__init__()

        # 设置窗口标题
        self.setWindowTitle('PyQt6 Demo')

        # 创建一个按钮
        self.button = QPushButton('点击一下', self)
        self.button.clicked.connect(self.show_message)

        # 设置布局
        layout = QVBoxLayout()
        layout.addWidget(self.button)
        self.setLayout(layout)

    def show_message(self):
        # 显示消息框
        QMessageBox.information(self, 'Message', 'Hello, PyQt6!')

if __name__ == '__main__':
    app = QApplication(sys.argv)

    # 创建 MyApp 实例
    my_app = MyApp()
    my_app.resize(300, 200)  # 设置窗口大小
    my_app.show()  # 显示窗口

    sys.exit(app.exec())

image.png

2.2 稍微复杂一下

入门很简单, 但是生产应用上不会有这么简单的功能 ,在这篇里面会简单演示以下功能 :

  • 更复杂的页面体系 ,包括 Tab 页 ,子项 ,控制台等各种功能组件
  • 更好看的界面样式 : 整体的风格,流程的切换 ,布局等
  • 应用发布 : 打包成可执行的应用进行发布

2.3 功能点一 : 页面体系

PyQT 中有着丰富的组件 ,可以帮助我们实现各种功能

组件描述
QApplication应用程序的入口点,处理事件循环和初始化。
QWidget所有用户界面对象的基类,是空白的 GUI 容器。
QMainWindow一个主窗口控件,支持菜单栏、工具栏、状态栏等标准窗口功能。
QDialog对话框窗口,用于短期任务或用户输入。
QPushButton按钮控件,用户可以点击它来触发动作。
QLabel文本或图像标签,用于显示只读信息。
QLineEdit单行文本输入控件,用户可以输入和编辑文本。
QTextEdit多行文本编辑器,支持富文本格式。
QComboBox下拉列表控件,允许用户从多个选项中选择。
QCheckBox复选框控件,允许用户选择或取消选择一个选项。
QRadioButton单选按钮控件,用户可以从一组互斥的选项中选择一个。
QListView列表视图控件,用于显示和操作项目列表。
QTreeView树形视图控件,用于显示和操作层次结构数据。
QTableView表格视图控件,用于显示二维数据。
QVBoxLayout垂直布局管理器,用于垂直排列控件。
QHBoxLayout水平布局管理器,用于水平排列控件。
QGridLayout网格布局管理器,用于创建行和列的控件布局。
QFormLayout表单布局管理器,用于以标签-控件对的形式排列控件。
QMessageBox消息框控件,用于显示信息、警告或错误对话框。
QFileDialog文件对话框,用于打开、保存文件或选择目录。
QColorDialog颜色对话框,用于选择颜色。
QFontDialog字体对话框,用于选择字体。
QProgressBar进度条控件,用于显示任务的进度。
QSlider滑动条控件,用于选择范围内的数值。
QSpinBox旋转框控件,用于选择数值(带步进按钮)。
QTimer定时器对象,用于触发定时事件。
QCanvas绘图区域,用于自定义图形的绘制。
QMenuBar菜单栏控件,用于创建和管理应用程序菜单。
QStatusBar状态栏控件,用于显示应用程序的状态信息。

@ QtWidgets — PyQt Documentation v6.7.1 (riverbankcomputing.com)

更复杂的功能可以参考官方文档 ,这里就不细说了 👉👉👉

image.png

2.4 功能点二 : 更复杂的界面样式

  • 方式一 : 直接通过代码实现界面的编排
  • 方式二 : 通过 QT Design 帮助我们实现界面的编排

毕竟还是新手 ,方式二对我而言上手过于困难 ,功能不复杂,我这里直接用代码画界面。

image.png

2.5 功能点三 : 部署执行文件

  • S1 : 安装依赖 - pip install pyinstaller
  • S2 : 执行打包命令 - pyinstaller --onefile --windowed --name MyApp main.py
  • S3 : 修改后执行 - pyinstaller MyApp.spec
# -*- mode: python ; coding: utf-8 -*-
a = Analysis( ['main.py','data_menu.py','layout_sidebar.py','layout_table.py','data_table.py','data_menu.py'],
    pathex=['D:\\code\python\\PythonDemoGit\\pythonDemoGit\\gui_pyqt6'],
    binaries=[],
    datas=[('styles.qss','.')],
    hiddenimports=[],
    hookspath=[],
    hooksconfig={},
    runtime_hooks=[],
    excludes=[],
    noarchive=False,
    optimize=0,
)

// .... 略
  • 暂时还不知道有没有不改 spec 文件 , 一次性打包成功的方式
  • datas 注意格式 ,否则也会打包失败
  • 最后文件如下图所示 ,运行即可

image.png

三. 实现一个简单的爬虫客户端

下面开始实现一个简单的爬虫客户端 ,作用就是从掘金抓取文章标题和链接,并且保存在本地 :

3.1 数据爬取

  • S1 : 安装依赖 - pip install requests beautifulsoup4
  • S2 : 先抓取专栏列表 ,再抓起专栏下的文章列表

专栏列表抓取

import requests
from bs4 import BeautifulSoup


def search_list(url):
    # 发送 HTTP 请求获取页面内容
    response = requests.get(url)

    # 检查请求是否成功
    if response.status_code != 200:
        print(f"调用接口异常:")
        return []

    # 解析页面内容
    soup = BeautifulSoup(response.text, 'html.parser')

    # 假设详情项在 <a> 标签内,并且包含标题和链接
    details = []

    # 通过 CSS 选择器抓取数据
    for a_tag in soup.select('.column-link'):

        # 获取 title
        title = "默认名称"
        for element in a_tag:
            for title_item in element.select(".title"):
                title = title_item.get_text().strip()
        # 获取地址
        link = a_tag['href']

        # 如果链接是相对路径,转换为绝对路径
        if not link.startswith('http'):
            link = requests.compat.urljoin(url, link)

        details.append({'title': title, 'link': link})
    return details

文章列表抓起

import requests
from bs4 import BeautifulSoup


def generate_data(url):
    # 发送 HTTP 请求获取页面内容
    response = requests.get(url)

    # 检查请求是否成功
    if response.status_code != 200:
        print(f"调用接口异常:")
        return []

    # 解析页面内容
    soup = BeautifulSoup(response.text, 'html.parser')

    # 假设详情项在 <a> 标签内,并且包含标题和链接
    details = []

    # 通过 CSS 选择器抓取数据
    for content_list in soup.select('.content-main'):
        # 获取 title
        title = "默认名称"
        link = ""
        for title_item in content_list.select(".title"):
            title = title_item.get_text().strip()
            link = title_item['href']
        if not link.startswith('http'):
            link = requests.compat.urljoin(url, link)
        details.append({'title': title, 'link': link})
    return details


3.2 关键操作节点

包括3个节点 :

左边的侧边栏

from PyQt6.QtWidgets import QWidget, QVBoxLayout, QPushButton
from data_menu import search_list
from PyQt6.QtCore import pyqtSignal

class Sidebar(QWidget):

    # 定义一个信号,将生成的数据传递出去
    data_generated = pyqtSignal(str)

    def __init__(self,  parent=None):
        super().__init__(parent)
        detail_list = search_list(
            "https://juejin.cn/user/3790771822007822/columns")

        sidebar_layout = QVBoxLayout()
        self.setLayout(sidebar_layout)

        for detail in detail_list:
            button = QPushButton(detail['title'])
            button.setProperty("url", detail['link'])
            # 为 button 绑定事件
            button.clicked.connect(self.on_button_clicked)
            sidebar_layout.addWidget(button)
         
        sidebar_layout.addStretch() 

    def on_button_clicked(self):
        button = self.sender()  # 获取发送信号的按钮
        url = button.property("url")
        self.data_generated.emit(url)

右边的文章列表

from PyQt6.QtWidgets import QWidget, QVBoxLayout, QTableWidget, QTableWidgetItem
from data_table import generate_data


class TableWithPagination(QWidget):
    def __init__(self, data, rows_per_page=10, parent=None):
        super().__init__(parent)

        self.table = QTableWidget()
        self.table.setRowCount(rows_per_page)
        self.table.setColumnCount(3)
        self.table.setHorizontalHeaderLabels(["文章名称", "文章路径"])

        self.current_page = 0
        self.rows_per_page = rows_per_page
        self.total_data = data

        # 右侧布局
        right_layout = QVBoxLayout()
        right_layout.addWidget(self.table)
        self.setLayout(right_layout)

    def update_table(self, url):
        print("拿到url地址:", url)
        self.table.clearContents()
        data_list = generate_data(url)
        for row, data_detail in enumerate(data_list):
            print("查询到对象", row, data_detail)
            self.table.setItem(row, 0, QTableWidgetItem(data_detail['title']))
            self.table.setItem(row, 1, QTableWidgetItem(data_detail['link']))

以及最核心的入口函数

import sys
import os
from PyQt6.QtWidgets import QApplication, QMainWindow, QWidget, QHBoxLayout
from layout_sidebar import Sidebar
from layout_table import TableWithPagination

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()

        self.setWindowTitle("PyQt Modular Example")
        self.resize(800, 600)

        # 右侧表单和分页控制
        data = []
        self.table_info = TableWithPagination(data)

        # 左侧侧边栏
        self.sidebar = Sidebar()

        # 主布局
        main_layout = QHBoxLayout()
        main_layout.addWidget(self.sidebar)
        main_layout.addWidget(self.table_info)

        # 设置结构
        container = QWidget()
        container.setLayout(main_layout)
        self.setCentralWidget(container)

        # 连接信号与槽函数
        self.sidebar.data_generated.connect(self.table_info.update_table)

        # 加载样式表
        stylesheet_path = os.path.join(os.path.dirname(__file__), 'styles.qss')
        self.load_stylesheet(stylesheet_path)

    def load_stylesheet(self, filename):
        """加载并应用样式表"""
        with open(filename, "r", encoding="utf-8") as file:
            self.setStyleSheet(file.read())


if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec())


3.3 基础效果展示

image.png

  • 这个插件仅仅进行了简单的优化 ,总的来说可视化效果还不错
  • 卡顿较为明显 ,主要是在互联网调用延迟上面
  • 初始化布局未自适应 ,体验感一般 ,需要花时间优化样式

针对这些问题 ,我们下一篇着重优化一下展示的效果

3.4 源码已上传 gitee

gitee.com/antblack/Py…

  • 后续的 Python 代码都会陆续提交上去,欢迎 star❤️❤️

image.png

总结

文章内容不复杂, 只是把 PyQT6 学习过程中的核心关键点和内容梳理了出来 ,便于后续业务中开箱即用。

关于这个系列后续还会有计划 ,但是作为子分支 ,更新时间不定 ,欢迎关注。

总结一下 :

  • 入门难度 : 上手不难 , Python 本地人半天足够 ,外地人1-2天。复杂场景要花一些时间。
  • 用法 : 和 Java 或者其他后端语言写前端界面的方式类似, 样式和前端的 CSS 类似
  • 场景 : 能覆盖绝大多数场景

最后的最后 ❤️❤️❤️👇👇👇