QMovie 动画播放

QMovie 是 Qt 中用于播放动画(主要是 GIF 格式)的类。它本身不是一个控件,不能直接显示在界面上,通常需要借助 QLabel(或 QGraphicsView)来展示播放效果。 它的核心作用是:逐帧读取动图文件,按时间间隔把每一帧渲染出来,从而实现动画播放。 1. 效果演示 通过 QLabel 显示一个加载中的 GIF 动画,并提供「开始 / 暂停 / 停止」三个按钮,演示 QMovie 的常用接口。 2. 属性和方法 QMovie 的工作流程大致是:加载文件 → 启动播放 → 播放结束。常用的属性和方法如下。 2.1 构造与加载 // 通过文件名构造 QMovie(const QString &fileName, const QByteArray &format = QByteArray(), QObject *parent = nullptr); // 通过 QIODevice 构造(可用于资源文件、网络数据等) QMovie(QIODevice *device, const QByteArray &format = QByteArray(), QObject *parent = nullptr); // 设置 / 获取动画的文件名 void setFileName(const QString &fileName); QString fileName() const; // 判断动画是否有效(文件是否能被正确解析) bool isValid() const; 2.2 播放控制 // 开始播放 void start(); // 暂停 / 继续(QMovie 没有 pause()/resume(),只有这个开关式接口,且它是一个槽) void setPaused(bool paused); // true 为暂停,false 为继续 // 没有 paused()/isPaused() 这样的查询方法,是否暂停用 state() 判断 MovieState state() const; // NotRunning / Paused / Running // 停止播放,并回到第一帧 void stop(); // 缓存模式 CacheMode cacheMode() const; void setCacheMode(CacheMode mode); QMovie 提供的播放控制比较简单,注意 stop() 与 setPaused(true) 的区别: ...

2026 年 10 月 13 日

QSlider滑动条

本文搬运转载自 明王讲QT - QSlider,仅供学习记录使用。版权归原作者所有。 QSlider 滑动条控件 QSlider是滑动条控件 它允许用户通过拖动滑块来选择一个范围内的值。通常用于调整音量、亮度、进度等需要连续数值输入的场景 1. 效果演示三个滑动条,分别用来调节RGB三个颜色的值,并显示到左侧各自的文本框中 并且将RGB这三个颜色的组合,来设置上面的文本框的背景颜色 2. 属性和方法QSlider继承自QAbstractSlider,它的绝大多数属性都是从QAbstractSlider继承而来的。 QSlider有很多属性,完整的可查看帮助文档。这里仅列出常用的属性和方法 2.1 值滑动条和值相关的属性包括:当前值、最大值、最小值 // 获取和设置当前值 int value() const; void setValue(int); // 获取和设置最大值 int maximum() const; void setMaximum(int); // 获取和设置最小值 int minimum() const; void setMinimum(int); // 一次设置最大值和最小值 void setRange(int min, int max) 2.2 方向Qt中滑动条有水平滑动条和垂直滑动条之分,通过修改orientation属性来设置 // 获取和设置滑动条的方向 Qt::Orientation orientation() const; void setOrientation(Qt::Orientation); 2.3 步长步长是指滑动条一次增加或减少的值。这里又包括两个步长: singleStep:是指按键盘的左右箭头(←/→)时的步长 pageStep:是指点击滑块两侧时的步长 // 获取和设置singleStep int singleStep() const; void setSingleStep(int); // 获取和设置pageStep int pageStep() const; void setPageStep(int); 2.4 刻度线```cpp // 获取和设置刻度线间隔 int tickInterval() const void setTickInterval(int ti) // 获取和设置刻度线的位置 QSlider::TickPosition tickPosition() const void setTickPosition(QSlider::TickPosition position) ...

2026 年 10 月 13 日

Qt Model/View 架构详解 — Item Model 体系

一、架构总览 Qt 的 Model/View 架构是对经典 MVC 模式的实现与改造: 层级 职责 对应类 Model(数据源) 存储数据、提供数据访问接口 QAbstractItemModel 系列 View(视图) 显示数据、处理用户交互 QTableView, QTreeView, QListView Delegate(委托) 自定义渲染和编辑 QStyledItemDelegate 核心思想:数据(Model)与展示(View)完全解耦,同一个 Model 可以同时被多个 View 显示。 二、类继承体系 QObject └── QAbstractItemModel ← 所有 Item Model 的根基类 ├── QAbstractListModel ← 简单一维列表的抽象基类 │ └── QStringListModel ├── QAbstractTableModel ← 二维表格的抽象基类(自己写表模型时继承它) ├── QStandardItemModel ← 开箱即用的通用模型(树/表/列表通吃) └── SQL 模型是三级单链,不是从 QAbstractTableModel 派生: QAbstractItemModel → QSqlQueryModel → QSqlTableModel → QSqlRelationalTableModel 其他直接从 QAbstractItemModel 派生的专用模型: QFileSystemModel — 文件系统树 三、核心索引系统:QModelIndex Model/View 的通信枢纽是 QModelIndex(模型索引)。 ...

2026 年 10 月 13 日

Qt 架构阅读方法:从构造函数入手

看 Qt 桌面程序,不要从头文件按顺序"从小往大"看——Qt 没有顺序这一说,要从主页面构造函数去总结架构。 一、核心观点 Qt 程序(尤其带界面的桌面应用)里,构造函数是"所有功能的展示": 成员初始化 资源/数据加载函数 功能函数(布局、控件创建、业务初始化) 信号与槽连接(最重要) 想看懂某一个页面的功能大局,步骤是: 看构造函数 → 看构造函数里的注释 → 找被调用的实现方法 → 看"哪些被使用了、有什么用" → 再去对应头文件/源文件看定义 → 看懂 而不是:打开一个头文件,从头到尾按声明顺序啃。 二、为什么 Qt 不能"从头文件按顺序看" 误区 原因 按头文件声明顺序读 头文件只是声明集合,不含调用关系;顺序 ≠ 执行顺序 从最小的类看起 小类之间没有依赖线索,看完全部也不懂"为什么存在" 先读业务函数 不知道它被谁调用、在什么时机触发(信号槽是异步/解耦的) 想找"程序入口→顺序执行" Qt 是事件驱动的,没有线性执行流 Qt 程序的"执行顺序"不是代码顺序,而是: main() 里创建主窗口 → 执行构造函数 构造函数一次性搭好:界面、数据、连接 之后程序进入事件循环,靠信号槽响应——调用关系藏在构造函数里 所以构造函数 = 架构入口,是唯一能"一览全局"的地方。 三、构造函数里到底有什么 以常见主窗口为例: MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { // 1. 窗口属性初始化 setWindowTitle("示例程序"); resize(800, 600); // 2. 加载函数:资源、配置、数据 loadConfig(); // 读配置文件 initDatabase(); // 连接数据库 setupToolbar(); // 加载图标/工具栏 // 3. 功能函数:创建控件与布局 createCentralWidget(); createStatusBar(); createMenu(); // 4. ⭐ 信号与槽连接(核心:把"谁"和"谁"绑起来) connect(ui->startBtn, &QPushButton::clicked, this, &MainWindow::onStart); connect(this, &MainWindow::dataReady, ui->table, &TableWidget::refresh); connect(timer, &QTimer::timeout, this, &MainWindow::updateStatus); } 构造函数 = 组装说明书: ...

2026 年 10 月 13 日

Qt 网络编程 01:TCP 客户端与服务端(第一天)

适用版本:Qt 6.9 / MinGW / C++17 更新日期:2026-08-19 源码位置:<你的工程目录>\net(TcpSocket / Tcpserver / TCP_Client / TCP_Server 四个项目) 目录 一、今天做了什么 二、项目清单与角色分工 三、准备工作:.pro 加 network 模块 四、控制台版:客户端 TcpSocket 五、控制台版:服务器 Tcpserver(多客户端广播) 六、GUI 版:TCP_Client 七、GUI 版:TCP_Server 八、核心知识点总结 九、今天踩的坑 / 值得注意的点 十、进阶写法:多客户端封装 ClientConnection 模式 十一、明天可以做什么 一、今天做了什么 用 Qt 实现了 TCP 客户端 / 服务器 双向通信,共 4 个项目: 项目 类型 角色 特点 TcpSocket 控制台(QCoreApplication) 客户端 连接即自动发一条 “Hello Server!” Tcpserver 控制台(QCoreApplication) 服务器 多客户端 + 广播 TCP_Client GUI(QWidget) 客户端 界面输入 IP/端口,收发消息 TCP_Server GUI(QWidget) 服务器 界面选择端口,收发消息 我已有 Qt 基础,但网络编程是零基础。今天的关键收获是建立了"客户端/服务器"模型和 Qt 异步收发的心智模型。 ...

2026 年 10 月 13 日

Qt 网络编程学习路线(网络编程零基础视角)

适用人群:已掌握 Qt 基础(信号槽、事件循环、QWidget),网络编程零基础 适用版本:Qt 6.x(Qt 5 大部分通用) 更新日期:2026-08-19 目录 1. 学习定位与目标 2. 网络编程零基础必须先懂的 6 个概念 3. 阶段一:TCP 编程入门(第 1~2 周) 4. 阶段二:TCP 深入与实战化(第 3~4 周) 5. 阶段三:UDP 编程(第 5 周) 6. 阶段四:HTTP 与高层网络(第 6~7 周) 7. 阶段五:进阶与安全(第 8 周起) 8. 阶段六:实战项目 9. 推荐资源 10. 总结 1. 学习定位与目标 核心要点:你的 Qt 基础(信号槽、事件循环、对象树)是最大的优势——Qt 把网络编程封装成了"发信号 + 收信号",你要补的不是 C++ 网络 API,而是网络协议的基本概念。 1.1 为什么用 Qt 学网络编程 优势 说明 异步事件驱动 收发数据都靠信号槽触发,不用自己写多线程阻塞 IO 跨平台 Windows / Linux / Android 同一套代码 高层封装 QTcpSocket / QUdpSocket / QNetworkAccessManager 把 socket API 包装成 Qt 风格 与 GUI 天然结合 收发数据直接刷新界面,客户端/服务器程序最容易做出可视化效果 1.2 和 C++ 原生 socket 的对比 原生 C++(Winsock / POSIX) Qt 封装 socket() / bind() / listen() / accept() QTcpServer::listen() / nextPendingConnection() connect() / send() / recv() QTcpSocket::connectToHost() / write() / readAll() 阻塞线程等待,需多线程处理 信号槽异步通知,事件循环驱动 手动管理缓冲区、错误码 readyRead 信号 + readAll(),errorString() 结论:先学概念,再学 Qt API。概念(TCP、端口、粘包)是通用的,Qt 只是更友好的实现工具。 ...

2026 年 10 月 13 日

Qt5 信号槽重载与 explicit 关键字详解

适用版本:Qt 5.x / C++14+ 更新日期:2026-07-29 目录 一、信号与槽函数重载(Overload) 场景假设 方案一:QOverload / qOverload(最推荐) 方案二:static_cast 显式转型 方案三:Lambda 表达式(最灵活) 信号和槽同时重载的混搭处理 总结建议 二、explicit 关键字详解 1. 不加 explicit(允许隐式转换) 2. 加上 explicit(禁止隐式转换) 3. Qt 中的经典实战场景 总结 一、信号与槽函数重载(Overload) 在 Qt5 中,信号和槽函数发生重载是非常常见的场景,也是最容易引起编译错误或运行时行为不符合预期的地方。 由于 Qt5 使用 &ClassName::functionName 的新式语法,当遇到重载时,编译器无法自动推断你要使用哪一个具体的函数指针,因此必须进行强制类型转换或使用 Lambda 表达式来消除歧义。 场景假设 假设有一个类,里面定义了两个重载的普通成员函数(在 Qt5 中都可以当槽): class Receiver : public QObject { Q_OBJECT public: // 槽函数 1:无参 void handleUpdate() { qDebug() << "无参槽被触发"; } // 槽函数 2:带一个 int 参数 void handleUpdate(int value) { qDebug() << "带参槽被触发,值为" << value; } }; 现在有一个信号想要连接到这个槽,有以下几种方案: ...

2026 年 10 月 13 日

QTabWidget 与 QStackedWidget 的区别及常用方式

一、概述 QTabWidget 和 QStackedWidget 都是 Qt 中用于管理多页面切换的控件,但它们的设计定位和使用场景有显著区别。 特性 QTabWidget QStackedWidget 内置导航 自带标签栏(Tab Bar) 无内置导航 页面切换方式 点击标签切换 需通过代码 setCurrentIndex() 切换 独立性 独立控件,开箱即用 需配合其他控件(如 QListWidget)使用 灵活性 固定标签栏样式 可自定义任意切换方式 适用场景 设置对话框、分类浏览 向导流程、无标签界面 二、QTabWidget — 自带标签栏的多页容器 2.1 特点 顶部(或底部/左侧/右侧)自带 QTabBar 每个 Tab 对应一个页面(QWidget 子类) 点击 Tab 自动切换页面,无需额外代码 2.2 常用方式 // 创建 TabWidget QTabWidget *tabWidget = new QTabWidget(this); // 创建页面 QWidget *page1 = new QWidget(); QWidget *page2 = new QWidget(); // 添加页面(传入 widget 和标题) tabWidget->addTab(page1, "基本设置"); tabWidget->addTab(page2, "高级设置"); // 设置当前页 tabWidget->setCurrentIndex(0); // 常用设置 tabWidget->setTabPosition(QTabWidget::North); // 标签位置 tabWidget->setTabShape(QTabWidget::Rounded); // 标签形状 tabWidget->setMovable(true); // 信号与槽:标签切换 connect(tabWidget, &QTabWidget::currentChanged, [](int index) { qDebug() << "切换到第" << index << "页"; }); 三、QStackedWidget — 无内置导航的页面堆栈 3.1 特点 不提供任何导航控件,只负责管理页面 一次只显示一个页面(当前页) 必须通过代码或其他控件控制切换 3.2 常用方式 // 创建 StackedWidget QStackedWidget *stackedWidget = new QStackedWidget(this); // 创建页面 QWidget *page1 = new QWidget(); QWidget *page2 = new QWidget(); QWidget *page3 = new QWidget(); // 添加页面(返回 index) int idx1 = stackedWidget->addWidget(page1); int idx2 = stackedWidget->addWidget(page2); int idx3 = stackedWidget->addWidget(page3); // 切换页面 stackedWidget->setCurrentIndex(1); // 按索引切换 stackedWidget->setCurrentWidget(page3); // 按指针切换 // 获取当前页面 int currentIdx = stackedWidget->currentIndex(); 3.3 配合 QListWidget 实现导航 // 左侧列表 + 右侧堆栈的经典布局 QListWidget *list = new QListWidget(); list->addItem("基本设置"); list->addItem("高级设置"); list->addItem("关于"); QStackedWidget *stack = new QStackedWidget(); stack->addWidget(new BasicSettingsPage()); stack->addWidget(new AdvancedSettingsPage()); stack->addWidget(new AboutPage()); // 连接信号 connect(list, &QListWidget::currentRowChanged, stack, &QStackedWidget::setCurrentIndex); 3.4 配合 QPushButton 实现向导流程 // 上一步 / 下一步 向导 connect(btnNext, &QPushButton::clicked, [=]() { int next = stack->currentIndex() + 1; if (next < stack->count()) { stack->setCurrentIndex(next); } }); connect(btnPrev, &QPushButton::clicked, [=]() { int prev = stack->currentIndex() - 1; if (prev >= 0) { stack->setCurrentIndex(prev); } }); 四、核心区别总结 对比维度 QTabWidget QStackedWidget 内置 Tab 栏 有 无 实现代码量 少(开箱即用) 多(需自行实现导航) 切换方式 点击 Tab setCurrentIndex() / setCurrentWidget() 自定义导航 受限(可隐藏 Tab 栏) 完全灵活 典型场景 偏好设置、分类浏览 安装向导、多步表单 灵活性 低(样式固定) 高(完全可控) 如何选择? 需要标准标签切换 → QTabWidget(简单快速) 需要自定义切换方式 → QStackedWidget(如列表导航、按钮翻页) 需要向导/步骤流程 → QStackedWidget(搭配按钮更自然) 既有标签又想自定义 → 用 QTabWidget 隐藏 TabBar + 代码控制切换 五、完整示例:左侧列表 + 右侧堆栈 #include <QApplication> #include <QHBoxLayout> #include <QListWidget> #include <QStackedWidget> #include <QLabel> int main(int argc, char *argv[]) { QApplication app(argc, argv); QWidget window; QHBoxLayout *layout = new QHBoxLayout(&window); // 左侧导航 QListWidget *list = new QListWidget(); list->addItem("页面一"); list->addItem("页面二"); list->addItem("页面三"); list->setFixedWidth(120); // 右侧页面 QStackedWidget *stack = new QStackedWidget(); stack->addWidget(new QLabel("这是页面一的内容")); stack->addWidget(new QLabel("这是页面二的内容")); stack->addWidget(new QLabel("这是页面三的内容")); layout->addWidget(list); layout->addWidget(stack, 1); connect(list, &QListWidget::currentRowChanged, stack, &QStackedWidget::setCurrentIndex); window.resize(600, 400); window.show(); return app.exec(); }

2026 年 10 月 13 日

QWidget 深入详解

目录 1. 什么是 QWidget 2. QWidget 的继承体系 3. 创建与基本使用 4. 窗口属性 5. 几何布局与坐标系统 6. 事件系统 7. 绘制系统 8. 样式表 (QSS) 9. 布局管理 10. 子控件与层级管理 11. 自定义控件实战 12. 性能优化 13. 常见陷阱与最佳实践 14. 总结 1. 什么是 QWidget QWidget 是 Qt 框架中所有用户界面对象的基类。它既是"控件"(widget),也是"窗口"(window)。在 Qt 的世界里,几乎所有可见元素都是 QWidget——按钮、标签、输入框、对话框,乃至整个应用程序的主窗口。 核心要点:QWidget 是一个"空白的矩形区域",它知道如何绘制自己、如何处理鼠标键盘事件、如何管理子控件。你可以在它之上构建任意复杂的 UI。 核心职责 职责 说明 窗口管理 最小化、最大化、关闭、标题栏 绘制 通过 paintEvent 自绘外观 事件处理 鼠标、键盘、触控、拖放等 子控件管理 添加、删除、查找子控件 布局管理 与 QLayout 协作自动排列子控件 样式设置 通过 QSS 或 QPalette 定制外观 2. QWidget 的继承体系 QObject └── QPaintDevice └── QWidget ← 本文主角 ├── QFrame │ ├── QLabel │ ├── QAbstractScrollArea │ └── ... ├── QAbstractButton │ ├── QPushButton │ ├── QCheckBox │ ├── QRadioButton │ └── ... ├── QComboBox ├── QLineEdit ├── QSlider ├── QSpinBox ├── QDialog ├── QMainWindow └── ... QObject:提供了信号槽机制、对象树管理、事件过滤器等基础设施。 QPaintDevice:声明了"可以被绘制"的能力,提供了绘制设备的抽象接口。 QWidget:将两者结合,成为一个可交互、可绘制、可包含子对象的矩形区域。 3. 创建与基本使用 3.1 最简单的 QWidget #include <QApplication> #include <QWidget> int main(int argc, char *argv[]) { QApplication app(argc, argv); QWidget w; w.setWindowTitle("Hello QWidget"); w.resize(400, 300); w.show(); return app.exec(); } 3.2 创建自定义 QWidget 子类 // MyWidget.h #pragma once #include <QWidget> #include <QPainter> class MyWidget : public QWidget { Q_OBJECT // 启用信号槽、元对象特性 public: explicit MyWidget(QWidget *parent = nullptr); protected: void paintEvent(QPaintEvent *event) override; void mousePressEvent(QMouseEvent *event) override; }; // MyWidget.cpp #include "MyWidget.h" #include <QPainter> MyWidget::MyWidget(QWidget *parent) : QWidget(parent) { setMinimumSize(200, 150); } void MyWidget::paintEvent(QPaintEvent *) { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); painter.setBrush(QColor(100, 150, 255)); painter.drawRoundedRect(rect().adjusted(5, 5, -5, -5), 10, 10); } void MyWidget::mousePressEvent(QMouseEvent *event) { // 点击时改变颜色 Q_UNUSED(event); update(); } 3.3 对象树与父子关系 QWidget 的构造函数接受一个 parent 参数: ...

2026 年 10 月 13 日