Qt、C++自定义按钮、组件、事件编程开发练习,万字实战解析!!

发布于:2025-06-22 ⋅ 阅读:(20) ⋅ 点赞:(0)

x项目地址:https://gitee.com/fan-wenshan/qt_learn_button-andevent_zhengzhuo

项目界面截图:

### 项目介绍:comstomSingal (Qt应用程序) 项目基本信息
- 项目类型 :Qt Widgets应用程序
- 开发环境 :Qt 5.12.12 + MinGW编译器(支持32/64位构建)
- 项目路径 : e:\QT\comstomSingal
- 构建配置 :支持Debug、Release和Profile三种构建模式 项目结构与核心文件
```

文件层级构成:
comstomSingal/
├── comstomSingal.pro        # 项目配置文件
├── main.cpp                 # 程序入口
├── widget.h/.cpp            # 主窗口类
├── widget.ui                # 主窗口UI设计
├── mybutton.h/.cpp          # 自定义按钮组件
├── res.qrc                  # 资源文件配置
└── icon/                    # 图标资源文件夹
``` 核心功能模块 1. 主窗口功能(Widget类)
- 事件处理 :实现了鼠标进入/离开事件、滚轮事件、窗口关闭事件和窗口大小改变事件的重写
  ```
  void Widget::wheelEvent(QWheelEvent 
  *event)
  {
      qDebug() << event->angleDelta();
  }
  ```
- 文件操作 :提供文件读写功能,支持文本文件的读取和写入,使用QFile和QTextStream
- 文件对话框 :集成QFileDialog实现文件选择、保存功能
- UI交互 :包含按钮、下拉框等控件,如 on_btnRead_clicked 、 on_btnWrite_clicked 等槽函数 2. 自定义按钮组件(MyButton类)
- 继承自QWidget,重写了鼠标事件和绘制事件
- 支持自定义图标显示(使用QPixmap)
- 实现了自定义点击信号(clicked()) 3. 资源管理
- 通过res.qrc管理图标资源
- icon文件夹包含多种状态的图标文件(如c1.png、o1.png等) 技术特点
- 使用Qt信号槽机制实现UI交互
- 采用MVC设计模式分离界面与业务逻辑
- 支持中文编码(文件操作中显式设置UTF-8编码)
- 模块化设计,自定义组件可复用 构建与运行
- 默认构建目录: E:\QT\build-comstomSingal-Desktop_Qt_5_12_12_MinGW_64_bit-Debug
- 支持32位和64位两种架构构建
- 可通过Qt Creator直接编译运行

源代码展示:

comstomSingal.pro :

QT       += core gui

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

CONFIG += c++11

# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0

SOURCES += \
    main.cpp \
    mybutton.cpp \
    widget.cpp

HEADERS += \
    mybutton.h \
    widget.h

FORMS += \
    widget.ui

# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target

RESOURCES += \
    res.qrc

你提供的这段代码是一个 Qt 项目的 .pro 文件(项目配置文件),它告诉 qmake(Qt 的构建工具)如何编译和链接你的项目。下面我将为你逐行详细解释这段 .pro 文件的每一部分,并说明其作用、用法以及背后的技术原理。


📁 一、基本结构与语法简介

.pro 文件是 Qt 使用的项目描述文件,由 qmake 解析并生成对应的 Makefile,用于控制项目的编译过程。

常见关键字:

关键字 含义
QT 需要使用的 Qt 模块
CONFIG 编译配置选项
SOURCES C++ 源文件列表
HEADERS 头文件列表
FORMS .ui 文件(Qt Designer 设计的界面文件)
RESOURCES 资源文件(.qrc)
TARGET 最终生成的可执行文件名(默认为项目文件名)
INSTALLS 安装规则(如部署路径)

🧾 二、逐行解析 .pro 文件内容

QT       += core gui
  • 添加 Qt 的核心模块:
    • core:提供基础类(如 QString、QList、QMap 等);
    • gui:提供图形渲染、窗口系统集成等功能;
  • 这两个模块是 GUI 应用程序的基础。
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
  • 条件语句:如果 Qt 版本大于 4(即 Qt5 或 Qt6),则添加 widgets 模块;
  • widgets 是 Qt5 引入的模块,包含传统的 QWidget 控件库(如 QPushButton、QLabel 等);
  • 如果使用的是 Qt4,则不启用该模块(因为 Qt4 的控件在 QtGui 中);
CONFIG += c++11
  • 启用 C++11 标准支持;
  • 这样就可以在项目中使用 Lambda 表达式、auto、nullptr 等现代 C++ 特性;
  • 如果你使用的是较新的 Qt(如 Qt5.14+ 或 Qt6),可以写成 c++17 或 c++20
# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0
  • 注释掉的宏定义,用于禁用 Qt 中被弃用的 API;
  • 如果取消注释,使用这些旧 API 的代码将无法通过编译;
  • 0x060000 表示 Qt 6.0.0 及以前版本中废弃的 API;
  • 这个设置有助于保持代码现代化、避免依赖即将移除的功能;
SOURCES += \
    main.cpp \
    mybutton.cpp \
    widget.cpp
  • 列出所有 C++ 源文件;
  • \ 是换行符,表示继续下一行;
  • main.cpp:程序入口;
  • mybutton.cpp 和 widget.cpp:自定义控件和主窗口实现;
HEADERS += \
    mybutton.h \
    widget.h
  • 列出所有头文件;
  • mybutton.hMyButton 类声明;
  • widget.h:主窗口 Widget 类声明;
FORMS += \
    widget.ui
  • 使用了 Qt Designer 设计的界面文件;
  • widget.ui 是一个 XML 文件,描述了界面布局和控件;
  • Qt 会自动将其转换为 ui_widget.h 并生成 Ui::Widget 类;
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
  • 部署规则(安装路径):
    • 在 QNX 系统上,把可执行文件安装到 /tmp/你的程序名/bin
    • 在其他 Unix 系统(非 Android)上,安装到 /opt/你的程序名/bin
    • !isEmpty(...):如果设置了路径,则加入 INSTALLS
    • INSTALLS 是 qmake 的安装目标变量,表示哪些文件需要被复制到指定目录;
RESOURCES += \
    res.qrc
  • 添加资源文件 res.qrc
  • .qrc 是 Qt 的资源文件格式,允许将图片、图标等资源嵌入到可执行文件中;
  • 可以通过 :/ 路径访问资源(如 QPixmap(":/icon.png"));

🧩 三、总结 .pro 文件的作用

这个 .pro 文件主要完成了以下任务:

功能 描述
指定 Qt 模块 包括 core、gui 和根据版本选择是否使用 widgets
设置 C++ 标准 支持 C++11
禁用弃用 API(可选) 提高代码质量
添加源码和头文件 构建时包含所有必要的代码
加载 UI 文件 支持 Qt Designer 界面设计
配置部署路径 控制不同平台下的安装位置
加载资源文件 支持内嵌资源(如图标、图片)

✅ 四、推荐优化建议

1. 更清晰地组织代码

你可以将多个文件分组,提高可读性:

SOURCES += main.cpp \
        mybutton.cpp \
        widget.cpp

HEADERS += mybutton.h \
        widget.h

FORMS += widget.ui
RESOURCES += res.qrc

2. 明确指定目标名称(可选)

如果你希望生成的可执行文件不是默认的 .pro 文件名,可以加:

TARGET = MyNotebookApp

3. 启用调试信息(开发阶段建议)

CONFIG += debug

或者更完整的调试配置:

CONFIG(debug, release) {
    message("Debug build")
} else {
    message("Release build")
}

📘 五、延伸知识:qmake 与 CMake 的区别

对比项 qmake CMake
使用方式 .pro 文件 CMakeLists.txt
Qt 亲和度 高(Qt 自带) 中(跨平台)
配置复杂度 简单直观 更强大灵活
推荐用途 小型 Qt 项目 大型跨平台项目

🧠 六、常见问题解答(FAQ)

❓ 我修改了 .pro 文件后,为什么没生效?

  • 清理项目并重新运行 qmake:
    • 在 Qt Creator 中点击 Build → Run qmake;
    • 或者删除 build 目录后重新构建;

❓ 如何添加图标资源?

  • 把图标文件(如 icon.ico)放到资源文件 res.qrc 中;
  • 在 .pro 中已经添加了 RESOURCES += res.qrc
  • 然后可以用 setWindowIcon(QIcon(":/icon.ico")); 设置窗口图标;

如果你还有关于 .pro 文件的更多疑问,或者想了解如何迁移到 CMake、如何打包发布应用等高级话题,也欢迎继续提问 😊

mybutton.h:

#ifndef MYBUTTON_H
#define MYBUTTON_H

#include <QWidget>

class MyButton : public QWidget
{
    Q_OBJECT
private:
    QPixmap pic;
public:
    explicit MyButton(QWidget *parent = nullptr);

protected:
    void mousePressEvent(QMouseEvent *event) override;
    void leaveEvent(QEvent *event) override;
    void enterEvent(QEvent *event) override;
    void paintEvent(QPaintEvent *event) override;

signals:
    void clicked();
};

#endif // MYBUTTON_H

你提供的这段代码是一个 自定义按钮类的头文件 mybutton.h,它继承自 Qt 的 QWidget 类,并实现了基本的按钮功能,包括响应鼠标点击、进入/离开事件和绘制图像。下面是对这段代码的逐行详细解释 + 技术解析,帮助你理解它的结构和用途。


📌 一、头文件保护宏(防止重复包含)

#ifndef MYBUTTON_H
#define MYBUTTON_H
  • 这是经典的 头文件保护宏(include guard)
  • 防止在多个地方重复包含该头文件导致编译错误;
  • 如果没有定义 MYBUTTON_H,则定义并继续编译;
  • 结束时用:
#endif // MYBUTTON_H

📦 二、包含必要的头文件

#include <QWidget>
  • 包含 Qt 提供的基础控件类 QWidget
  • 因为 MyButton 继承自 QWidget,所以必须包含这个头文件;

🔧 三、类定义:MyButton

class MyButton : public QWidget
{
    Q_OBJECT
  • 定义一个类 MyButton,继承自 QWidget
  • Q_OBJECT 是 Qt 的宏,用于启用:
    • 信号与槽机制(signals/slots);
    • 元对象系统(Meta Object System)
    • 所有使用了信号/槽或需要运行时信息的类都必须加上这个宏;

✅ 成员变量

private:
    QPixmap pic;
  • QPixmap 是 Qt 中用来处理图像的对象;
  • pic 用于存储按钮的图片资源;
  • 可以用于绘制按钮背景、图标等;

🎯 构造函数

public:
    explicit MyButton(QWidget *parent = nullptr);
  • 构造函数;
  • explicit 表示不能隐式转换构造;
  • parent 参数是 Qt 中父子对象管理机制的一部分;
  • 默认值为 nullptr,表示可以作为顶层窗口;

🖱️ 重写事件处理函数

protected:
    void mousePressEvent(QMouseEvent *event) override;
  • 重写鼠标按下事件;
  • 当用户点击按钮时触发;
  • 你可以在这里发射 clicked() 信号;
    void leaveEvent(QEvent *event) override;
  • 鼠标离开控件区域时触发;
  • 可用于恢复按钮样式或取消高亮效果;
    void enterEvent(QEvent *event) override;
  • 鼠标进入控件区域时触发;
  • 可用于实现按钮的悬停效果;
    void paintEvent(QPaintEvent *event) override;
  • 绘制事件;
  • 用于在界面上绘制按钮的外观(如显示图片、边框等);
  • 必须实现这个函数才能看到按钮内容;

🔔 信号

signals:
    void clicked();
  • 自定义信号 clicked()
  • 当用户点击按钮时,可以通过 emit clicked(); 发射这个信号;
  • 允许外部连接到其他槽函数,实现按钮点击响应逻辑;

🧩 四、技术总结

技术点 描述
Q_OBJECT 宏 启用信号/槽机制,支持 Qt 元对象系统
QPixmap 图像容器,用于绘制按钮图形
事件重写 如 mousePressEventpaintEvent 等,实现交互和界面绘制
自定义信号 clicked() 用于通知外界按钮被点击
面向对象设计 继承 QWidget 实现自定义控件
Qt 事件模型 通过重写事件函数响应用户操作

✅ 示例补充说明(配合 .cpp 文件)

以下是可能的 .cpp 文件中实现的内容概览:

构造函数设置图片(假设从资源加载)

MyButton::MyButton(QWidget *parent) : QWidget(parent), pic(":/images/button.png")
{
    setFixedSize(pic.size());  // 设置按钮大小为图片大小
}

鼠标按下事件(发射点击信号)

void MyButton::mousePressEvent(QMouseEvent *event)
{
    if (event->button() == Qt::LeftButton) {
        emit clicked();  // 发射信号
    }
    QWidget::mousePressEvent(event);  // 调用父类实现
}

绘图事件(绘制图片)

void MyButton::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);
    painter.drawPixmap(rect(), pic);  // 将图片绘制到整个按钮区域
}

💡 应用场景举例

这种自定义按钮常用于:

  • 实现带有图标或特殊样式的按钮;
  • 游戏中的 UI 控件(如开始游戏、暂停等);
  • 替代默认按钮样式,实现更丰富的视觉效果;
  • 学习 Qt 自定义控件开发的入门案例;

✅ 推荐改进方向

  1. 添加多种状态图片
    • 比如普通状态、悬停状态、按下状态分别使用不同图片;
  2. 支持缩放和样式定制
    • 使用 QStyleOption 或 QIcon::Mode 改变按钮外观;
  3. 添加动画效果
    • 在 enterEvent 和 leaveEvent 中添加渐变或放大效果;
  4. 封装为可复用组件
    • 添加属性(如 texticon)并通过 .ui 设计器使用;

如果你希望我帮你写出完整的 .cpp 文件,或者教你如何在 Qt Designer 中使用这个按钮,请继续提问 😊

widget.cpp:

#ifndef WIDGET_H
#define WIDGET_H

#include<iostream>
#include <QWidget>
#include<QDebug>
#include <QResizeEvent>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

//signals:
//        void mysignal();
//        void mysignalparams(int value);
//private slots:
//        void myslot();
//        void myslotparams(int value);
public:
    void enterEvent(QEvent *event)override;
    void leaveEvent(QEvent *event)override;
    void wheelEvent(QWheelEvent *event)override;
    void closeEvent(QCloseEvent *event)override;
    void resizeEvent(QResizeEvent *event)override;

private slots:
    void on_btnRead_clicked();

    void on_btnWrite_clicked();

    void on_btnstreamRead_clicked();

    void on_btnstreamWrite_clicked();

    void on_btnQFileDialog_clicked();

    void on_btnSe_clicked();

    void on_btnSave_clicked();

    void oncurrentIndexChanged(int index);
private:
    Ui::Widget *ui;
};
#endif // WIDGET_H

这段代码是 Qt C++ 项目中主窗口类 Widget 的头文件 widget.h,它定义了一个继承自 QWidget 的类,并包含了各种 事件处理函数、按钮点击槽函数、信号连接声明 等。下面是对这段代码的逐行详细解释 + 技术解析,帮助你全面理解它的功能和背后使用的技术。


📌 一、头文件保护宏(防止重复包含)

#ifndef WIDGET_H
#define WIDGET_H
  • 这是经典的 头文件保护宏(include guard)
  • 防止在多个地方重复包含该头文件导致编译错误;
  • 结束时用:
#endif // WIDGET_H

📦 二、引入必要的头文件

#include <iostream>
#include <QWidget>
#include <QDebug>
#include <QResizeEvent>
头文件 作用
<iostream> 提供标准输入输出流(如 std::cout),用于调试或日志输出
<QWidget> Qt 基础控件类,所有可视化控件都继承自它
<QDebug> Qt 提供的调试输出工具,替代 std::cout 更方便
<QResizeEvent> 用于响应窗口大小变化事件

🔧 三、命名空间与 UI 类前置声明

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
  • 这是 Qt 自动生成的代码结构;
  • Ui::Widget 是由 .ui 文件通过 uic 工具生成的界面类;
  • 这里只是提前声明这个类,避免在头文件中直接包含 ui_widget.h

🧱 四、类定义:Widget

class Widget : public QWidget
{
    Q_OBJECT
  • 定义一个类 Widget,继承自 QWidget
  • Q_OBJECT 是 Qt 的宏,用于启用:
    • 信号与槽机制(signals/slots);
    • 元对象系统(Meta Object System)
    • 所有使用了信号/槽或需要运行时信息的类都必须加上这个宏;

🎯 五、构造函数与析构函数

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();
  • 构造函数:创建窗口对象;
  • 析构函数:释放资源;
  • parent 参数用于 Qt 的父子对象管理机制;

🖱️ 六、重写的事件处理函数(override)

public:
    void enterEvent(QEvent *event) override;
  • 当鼠标进入控件区域时触发;
  • 可用于实现悬停效果;
    void leaveEvent(QEvent *event) override;
  • 当鼠标离开控件区域时触发;
  • 可用于取消高亮或动画效果;
    void wheelEvent(QWheelEvent *event) override;
  • 鼠标滚轮事件;
  • 可用于缩放、滚动等操作;
    void closeEvent(QCloseEvent *event) override;
  • 窗口关闭前触发;
  • 可用于弹出确认对话框或保存未保存的内容;
    void resizeEvent(QResizeEvent *event) override;
  • 窗口大小改变时触发;
  • 可用于重新布局或更新控件尺寸;

🔔 七、信号与槽函数(被注释掉)

//signals:
//    void mysignal();
//    void mysignalparams(int value);
//private slots:
//    void myslot();
//    void myslotparams(int value);
  • 这些是你预留的自定义信号和槽函数;
  • 被注释掉了,可能是为了示例或后续扩展;
  • 使用方式:
    • 在适当的地方调用 emit mysignal();
    • 槽函数可以绑定到其他组件或逻辑中;

💡 八、按钮点击事件的槽函数(重要!)

这些函数是在 .ui 中设计的按钮点击事件对应的槽函数:

private slots:
    void on_btnRead_clicked();
    void on_btnWrite_clicked();
    void on_btnstreamRead_clicked();
    void on_btnstreamWrite_clicked();
    void on_btnQFileDialog_clicked();
    void on_btnSe_clicked();
    void on_btnSave_clicked();
函数名 描述
on_btnRead_clicked() “读取”按钮点击事件
on_btnWrite_clicked() “写入”按钮点击事件
on_btnstreamRead_clicked() 流式读取按钮(如 QTextStream)
on_btnstreamWrite_clicked() 流式写入按钮
on_btnQFileDialog_clicked() 弹出文件选择对话框
on_btnSe_clicked() 可能是“搜索”或“设置”按钮
on_btnSave_clicked() 保存按钮(可能保存文本内容)

这些函数名符合 Qt 的自动连接规则:on_<控件名>_<信号名>()
Qt 会自动将这些函数连接到对应按钮的 clicked() 信号上。


🔄 九、下拉框选项变化的槽函数

    void oncurrentIndexChanged(int index);
  • 当 QComboBox 下拉框的当前索引发生变化时触发;
  • 通常用于根据用户选择切换编码格式、语言、主题等;

🧩 十、私有成员变量

private:
    Ui::Widget *ui;
  • ui 是指向由 .ui 文件生成的界面对象的指针;
  • 通过它访问界面上的所有控件(如按钮、标签、文本框等);

🧰 十一、完整技术总结

技术点 描述
Q_OBJECT 宏 启用信号/槽机制,支持 Qt 元对象系统
自定义事件处理 如 enterEventresizeEvent 等,用于响应用户交互
槽函数自动绑定 使用 on_<控件名>_<事件名>() 格式自动连接按钮事件
UI 类封装 使用 Ui::Widget 实现界面与逻辑分离
事件模型 Qt 的事件驱动机制,响应窗口生命周期和用户操作
资源管理 通过父对象机制自动管理内存,避免内存泄漏
可扩展性 预留了自定义信号/槽接口,便于后期扩展

✅ 示例补充说明(配合 .cpp 文件)

示例:on_btnRead_clicked() 函数(读取文件内容)

void Widget::on_btnRead_clicked() {
    QFile file("example.txt");
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
        qDebug() << "无法打开文件";
        return;
    }

    QTextStream in(&file);
    QString content = in.readAll();
    ui->textEdit->setPlainText(content);

    file.close();
}

示例:oncurrentIndexChanged(int) 函数(切换编码)

void Widget::oncurrentIndexChanged(int index) {
    QString encoding = ui->comboBox->itemText(index);
    qDebug() << "当前编码:" << encoding;
    // 可以在这里设置 QTextStream 的编码格式
}

💡 应用场景举例

这类 Widget 主窗口常用于:

  • 文本编辑器(记事本、日志查看器等);
  • 文件读写工具;
  • 编码转换工具;
  • 教学项目中的 GUI 编程练习;
  • 自定义控件开发的基础框架;

✅ 推荐改进方向

  1. 添加撤销/重做功能
    • 使用 QUndoStack 实现;
  2. 支持拖拽文件打开
    • 重写 dragEnterEvent 和 dropEvent
  3. 美化界面
    • 使用样式表(QSS)或 Qt Style Sheets;
  4. 国际化支持
    • 使用 tr() 包裹所有界面字符串;
  5. 多文档支持
    • 使用 QMdiArea 实现类似 Word 的多文档界面;

如果你希望我帮你写出完整的 .cpp 文件,或者教你如何在 Qt Designer 中设计这个界面,请继续提问 😊

main.cpp:

#include "widget.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();
    return a.exec();
}

这段代码是 Qt C++ 项目中的 程序入口文件 main.cpp,它的作用是启动整个应用程序并显示主窗口。虽然看起来很简短,但它是整个 GUI 应用的“心脏”。下面我将为你逐行详细解释这段代码,并说明其背后的技术原理。


📌 一、包含头文件

#include "widget.h"
  • 包含自定义的主窗口类 Widget 的声明;
  • 这个类继承自 QWidget,是你应用程序的核心界面类;
  • 它定义了按钮、事件处理、布局等内容;
#include <QApplication>
  • 包含 Qt 提供的 QApplication 类;
  • 是所有基于 Qt 的 GUI 应用程序的必需类;
  • 负责管理应用程序的生命周期、事件循环、界面初始化等;

🧱 二、程序入口函数:main()

int main(int argc, char *argv[])
  • 标准的 C/C++ 程序入口函数;
  • argc 和 argv 是命令行参数:
    • argc 表示参数数量;
    • argv 是一个数组,保存每个参数字符串;
  • 在 Qt 中,这些参数会被传给 QApplication 来处理(如设置样式、语言等);

✅ 创建 QApplication 对象

    QApplication a(argc, argv);
  • 创建一个 QApplication 实例 a
  • 每个 Qt GUI 应用必须有且只有一个 QApplication 对象;
  • 它会完成以下工作:
    • 初始化 Qt 内部系统(图形驱动、输入设备等);
    • 处理命令行参数;
    • 启动主事件循环(event loop);
    • 管理资源释放和退出逻辑;

⚠️ 注意:如果你开发的是控制台程序或非 GUI 程序,请使用 QCoreApplication


✅ 创建并显示主窗口对象

    Widget w;
    w.show();
  • 创建一个 Widget 对象 w,这是你自定义的主窗口;
  • w.show() 显示这个窗口;
  • show() 并不会立即绘制窗口,它只是将窗口标记为“需要显示”,真正的绘制由 Qt 的事件系统完成;

✅ 启动主事件循环

    return a.exec();
  • 调用 QApplication::exec() 启动 Qt 的主事件循环;
  • 这是一个无限循环,直到用户关闭最后一个窗口或调用 exit() 才会退出;
  • 在事件循环中,Qt 会不断监听并分发各种事件:
    • 鼠标点击、键盘输入;
    • 定时器事件;
    • 窗口重绘请求;
    • 自定义信号触发;
  • 返回值通常用于操作系统判断程序是否正常退出;

🧩 三、技术总结

技术点 描述
QApplication Qt GUI 应用的核心类,管理应用程序生命周期
主事件循环(exec() Qt 的核心机制之一,负责响应用户交互和系统事件
main() 函数 程序入口,标准 C++ 结构
命令行参数处理 支持通过命令行传递参数给 Qt 应用
窗口创建与显示 创建主窗口并调用 show() 显示出来

💡 四、常见问题解答(FAQ)

❓ 为什么不能在 main() 中直接写业务逻辑?

  • main() 函数只负责启动应用;
  • 真正的界面交互、事件处理都在 QApplication::exec() 启动的事件循环中进行;
  • 所以你应该把逻辑写在 Widget 或其他类中,而不是 main()

❓ 如果我想让窗口居中显示怎么办?

可以在 Widget 构造函数中添加:

#include <QDesktopWidget>

// ...

QRect screen = QApplication::desktop()->screenGeometry();
move((screen.width() - width()) / 2, (screen.height() - height()) / 2);

或者使用更现代的方式(Qt5.10+):

setWindowFlag(Qt::MSWindowsCenterCursor); // Windows 下有效

✅ 五、完整流程图解(文字版)

main()
│
├── 创建 QApplication 对象 a
│
├── 创建主窗口 Widget 对象 w
│
├── 调用 w.show() 显示窗口
│
└── 调用 a.exec() 启动事件循环
     │
     ├── 监听用户操作(鼠标、键盘)
     ├── 触发槽函数(按钮点击等)
     ├── 刷新界面(paintEvent)
     └── 等待退出条件(如关闭窗口)

📘 六、延伸知识:Qt 程序结构概览

一个完整的 Qt 项目通常包括:

文件名 作用
main.cpp 程序入口,创建 QApplication 和主窗口
widget.h/cpp 主窗口类定义和实现
widget.ui 使用 Qt Designer 设计的界面文件
.pro 文件 qmake 项目配置文件
mybutton.h/cpp 自定义控件类(可选)
res.qrc 资源文件(图标、图片等)

✅ 示例扩展建议

1. 添加启动画面(QSplashScreen)

#include <QSplashScreen>

QSplashScreen splash(QPixmap(":/images/splash.png"));
splash.show();

QTimer::singleShot(2000, &splash, &QSplashScreen::close);

2. 设置应用程序图标

a.setWindowIcon(QIcon(":/images/app_icon.ico"));

3. 添加国际化支持

QTranslator translator;
translator.load(":/translations/zh_CN.qm");
a.installTranslator(&translator);

如果你希望我帮你:

  • 写出完整的 widget.cpp 文件;
  • 教你如何设计 .ui 文件;
  • 或者打包发布你的 Qt 应用;

欢迎继续提问 😊

mybutton.cpp:

#include "mybutton.h"

#include <QPainter>

MyButton::MyButton(QWidget *parent) : QWidget(parent)
{
    pic.load(":/icon/o1.png");
    setFixedSize(pic.size());
    update();
}

void MyButton::mousePressEvent(QMouseEvent *event)
{
    pic.load(":/icon/o3.png");
    update();
    emit clicked();
}

void MyButton::leaveEvent(QEvent *event)
{
    pic.load(":/icon/o1.png");
    update();
//    emit clicked();
}

void MyButton::enterEvent(QEvent *event)
{
    pic.load(":/icon/o2.png");
    update();
//    emit clicked();
}

void MyButton::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);
    painter.drawPixmap(rect(), pic);
}

你提供的这段代码是 MyButton 类的实现文件 mybutton.cpp,它是一个自定义按钮控件,继承自 QWidget。这个按钮通过加载不同的图片来实现 普通状态、悬停状态和按下状态 的视觉效果,并支持点击信号(clicked())。

下面我将为你逐行详细解析这段代码,并讲解它的设计思想、使用的技术以及可以优化的方向。


🧱 一、头文件引入


cpp

深色版本

#include "mybutton.h"
  • 引入类定义头文件;
  • 包含了 MyButton 类的声明;

cpp

深色版本

#include <QPainter>
  • 引入 Qt 的绘图类 QPainter
  • 用于在 paintEvent() 中绘制图像;

🎯 二、构造函数:初始化按钮外观


cpp

深色版本

MyButton::MyButton(QWidget *parent) : QWidget(parent)
{
    pic.load(":/icon/o1.png");
    setFixedSize(pic.size());
    update();
}

✅ 解析:

  • pic.load(":/icon/o1.png")
    • 从资源文件中加载默认状态的图片;
    • :/icon/o1.png 是 Qt 资源系统的路径;
  • setFixedSize(pic.size())
    • 设置按钮为固定大小,等于图片的尺寸;
    • 防止用户调整窗口时按钮变形;
  • update()
    • 请求重绘界面,触发 paintEvent()
    • 保证构造完成后立即显示初始图片;

🖱️ 三、鼠标事件处理

1. mousePressEvent(QMouseEvent *event) —— 点击事件


cpp

深色版本

void MyButton::mousePressEvent(QMouseEvent *event)
{
    pic.load(":/icon/o3.png");
    update();
    emit clicked();
}
  • 当用户按下鼠标左键时触发;
  • 加载“按下状态”的图片;
  • 调用 update() 刷新界面;
  • 发射 clicked() 信号,通知外部响应按钮点击;

✅ 这个信号可以通过 connect(...) 绑定到其他槽函数,例如执行某个操作或跳转页面。


2. leaveEvent(QEvent *event) —— 鼠标离开事件


cpp

深色版本

void MyButton::leaveEvent(QEvent *event)
{
    pic.load(":/icon/o1.png");
    update();
//    emit clicked();
}
  • 鼠标移出按钮区域时触发;
  • 恢复“默认状态”图片;
  • 注释掉的 emit clicked(); 是无效逻辑,应删除;

3. enterEvent(QEvent *event) —— 鼠标进入事件


cpp

深色版本

void MyButton::enterEvent(QEvent *event)
{
    pic.load(":/icon/o2.png");
    update();
//    emit clicked();
}
  • 鼠标进入按钮区域时触发;
  • 加载“悬停状态”的图片;
  • 同样注释掉的 emit clicked(); 应该删除;

🎨 四、绘制事件:paintEvent()


cpp

深色版本

void MyButton::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);
    painter.drawPixmap(rect(), pic);
}
  • 所有可视化控件都必须实现这个函数;
  • 使用 QPainter 对象在界面上绘制内容;
  • drawPixmap(rect(), pic)
    • 将图片绘制在整个按钮区域内;
    • rect() 表示当前控件的矩形区域;

🧩 五、技术总结

技术点 描述
自定义控件 继承 QWidget 实现一个新控件
图像绘制 使用 QPainter 和 QPixmap 实现按钮样式
事件响应 重写 mousePressEvententerEventleaveEvent 等响应用户交互
信号机制 使用 emit clicked() 提供按钮点击通知功能
资源系统 使用 qrc 文件管理图标资源,路径格式为 :/

💡 六、应用举例

你可以这样使用这个按钮:

1. 在 .ui 文件中手动添加一个 MyButton 控件(需注册为提升控件)

或者:

2. 在代码中创建并连接信号:


cpp

深色版本

MyButton *btn = new MyButton(this);
connect(btn, &MyButton::clicked, [=](){
    qDebug() << "按钮被点击了!";
});

✅ 七、建议改进方向

1. 增加多种状态图片的支持(更优雅)

可以提前将所有状态图片保存在成员变量中,避免频繁加载:

private:
    QPixmap normalPic, hoverPic, pressedPic;

然后在构造函数中加载:

normalPic.load(":/icon/o1.png");
hoverPic.load(":/icon/o2.png");
pressedPic.load(":/icon/o3.png");

切换时只需赋值即可:

pic = pressedPic;
update();

2. 支持缩放和拉伸(适应不同分辨率)

可以使用 QPixmap::scaled() 方法缩放图片:

painter.drawPixmap(rect(), pic.scaled(size(), Qt::KeepAspectRatio, Qt::SmoothTransformation));

3. 添加动画效果(如淡入淡出)

可以结合 QPropertyAnimation 实现按钮颜色渐变或大小变化的动画效果。


4. 支持文字 + 图标混合显示

可以在按钮上叠加文字说明:

painter.drawText(rect(), Qt::AlignCenter, "确定");

5. 支持设置图片路径(可配置化)

提供接口让用户动态设置按钮图片:

void MyButton::setNormalImage(const QString &path);
void MyButton::setHoverImage(const QString &path);
void MyButton::setPressedImage(const QString &path);

✅ 八、完整结构图解(文字版)

MyButton (继承 QWidget)
│
├── 构造函数
│   └── 加载默认图片、设置大小、刷新界面
│
├── mousePressEvent()
│   └── 加载按下图片、更新界面、发射 clicked()
│
├── enterEvent()
│   └── 加载悬停图片、更新界面
│
├── leaveEvent()
│   └── 加载默认图片、更新界面
│
└── paintEvent()
    └── 使用 QPainter 绘制当前图片

📘 九、延伸知识:Qt 自定义控件开发流程

  1. 设计需求
    • 明确控件的功能、外观、交互方式;
  2. 选择基类
    • QWidget(最灵活)、QPushButton(已有功能丰富)等;
  3. 实现核心逻辑
    • 事件处理、绘制、状态管理;
  4. 封装成独立组件
    • 可以打包为 .dll 或 .so 插件;
  5. 集成进 Qt Designer
    • 通过插件机制让 .ui 编辑器识别你的控件;

如果你希望我帮你:

  • 完善这个按钮类(支持多状态、动画、文字);
  • 教你如何将其添加到 Qt Designer 中;
  • 或者教你如何制作资源文件 .qrc; 欢迎继续提问 😊
#include "widget.h"
#include "ui_widget.h"

#include <QCloseEvent>
#include <QFile>
#include <QFileDialog>
#include <QMessageBox>
#include <QWheelEvent>
#include "mybutton.h"

Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{
    ui->setupUi(this);
    connect(ui->comboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(oncurrentIndexChanged(int)));
    //    connect(this, SIGNAL(mysignal()), this, SLOT(myslot()));
    //    connect(this, SIGNAL(mysignalparams(int)), this, SLOT(myslotparams(int)));
    //    emit mysignal();
    //    emit mysignalparams(100);
    connect(ui->mybtn, &MyButton::clicked, [=](){
        qDebug() << "myButton is clicked";
    });
}

Widget::~Widget()
{
    delete ui;
}

void Widget::enterEvent(QEvent *event)
{
    qDebug() << "mouse enter";
}

void Widget::leaveEvent(QEvent *event)
{
    qDebug() << "mouse leave";
}

void Widget::wheelEvent(QWheelEvent *event)
{

    qDebug() << event->angleDelta();
}

void Widget::closeEvent(QCloseEvent *event)
{
    int ret = QMessageBox::warning(this, tr("My Application"),
                                   tr("关闭窗口\n"
                                      "你想关闭窗口吗?"),
                                   QMessageBox::Ok | QMessageBox::No
                                   );

    switch (ret) {
    case QMessageBox::Ok :
         event->accept();
        break;
    case QMessageBox::No :
         event->ignore();
        break;
    }
}

void Widget::resizeEvent(QResizeEvent *event)
{
    qDebug() << "oldSize:" << event->oldSize()
             << "newSize:" << event->size();
}

//void Widget::myslot()
//{
//    std::cout << "myslot" << std::endl;
//}

//void Widget::myslotparams(int value)
//{
//    qDebug() << "myslotparams";
//    qDebug() << value;
//}


void Widget::on_btnRead_clicked()
{
    //    QFile file("E:/QT2/test.txt");
    QFile file;
    file.setFileName("E:/QT/test.txt");
    if(!file.open(QIODevice::ReadOnly | QIODevice::Text)){
        qDebug() << "file open error";
    }
    int size = file.size();
    char* context = new char(size);
    char* con = (char *)malloc(sizeof(char)*size);
    //    char context[100] = {"\0"};
    if(file.read(context, 100) == -1) return;
    qDebug() << context;
    file.close();
}


void Widget::on_btnWrite_clicked()
{
    QFile file("E:/QT/test2.txt");
    file.open(QIODevice::WriteOnly | QIODevice::Text);
    file.write("Program 45-QFile001 write something to This File 我是测试");
}


void Widget::on_btnstreamRead_clicked()
{
    QFile file;
    file.setFileName("E:/QT/test.txt");
    if(!file.open(QIODevice::ReadOnly | QIODevice::Text)){
        qDebug() << "file open error";
    }

    QTextStream in(&file);
    in.setCodec("UTF-8");
    //QString context = in.read(file.size());
    while(!in.atEnd()){
        QString context = in.readLine();
        qDebug() << context;
        qDebug() << "---------";
    }

    file.close();
}


void Widget::on_btnstreamWrite_clicked()
{
    QFile file;
    file.setFileName("E:/QT/test3.txt");
    if(!file.open(QIODevice::WriteOnly | QIODevice::Text)){
        qDebug() << "file open error";
    }
    QTextStream out(&file);
    out.setCodec("UTF-8");
    out << "I write stream char to file ";
    file.close();
}


void Widget::on_btnQFileDialog_clicked()
{
    QString fileName = QFileDialog::getOpenFileName(this, tr("Open Image"), "E:/QT/",
                                                    tr(" Images (*.png *.xpm *.jpg);;Text files (*.txt);;XML files (*.xml)"));
    qDebug() << fileName;

    QFile file;
    file.setFileName(fileName);
    if(!file.open(QIODevice::ReadOnly | QIODevice::Text)){
        qDebug() << "file open error";
    }

    QTextStream in(&file);
    in.setCodec("UTF-8");
    //QString context = in.read(file.size());
    while(!in.atEnd()){
        QString context = in.readLine();
        qDebug() << context;
        qDebug() << "---------";
    }

    file.close();
}


void Widget::on_btnSe_clicked()
{
    //    int arr[120];
    //    for (int i : arr) {

    //    }

    QFileDialog qFileDialog;
    qFileDialog.setFileMode(QFileDialog::ExistingFiles);
    qFileDialog.setNameFilter("*.txt");
    qFileDialog.exec();
    QStringList qstrings = qFileDialog.selectedFiles();
    for(QString str : qstrings){
        qDebug() << str;
    }
}


void Widget::on_btnSave_clicked()
{
    QString fileName = QFileDialog::getSaveFileName(this, tr("Save File"),
                                                    "E:/QT/untitled.txt",
                                                    tr("Text (*.txt *.doc)"));
    qDebug() << fileName;
    QFile file;
    file.setFileName(fileName);
    if(!file.open(QIODevice::WriteOnly | QIODevice::Text)){
        qDebug() << "file open error";
    }
    QTextStream out(&file);
    out.setCodec("UTF-8");
    out << "Qdialog Write Data to the Txt File";
    file.close();
}

void Widget::oncurrentIndexChanged(int index)
{
    qDebug() << index;
    qDebug() << ui->comboBox->currentText();
}

你提供的这段 widget.cpp 文件是一个 Qt 主窗口类 Widget 的实现文件,它完整地实现了:

  • 按钮点击事件处理
  • 文件读写操作(QFile)
  • 文本流读写(QTextStream)
  • 对话框使用(QFileDialog、QMessageBox)
  • 自定义事件响应(enter/leave/wheel/resize/close 事件)
  • 信号与槽连接机制

🧱 一、头文件引入

#include "widget.h"
#include "ui_widget.h"
  • 引入主窗口类和 UI 自动生成的界面类;
  • ui_widget.h 是由 .ui 文件通过 uic 工具生成的;
#include <QCloseEvent>
#include <QFile>
#include <QFileDialog>
#include <QMessageBox>
#include <QWheelEvent>
#include "mybutton.h"
  • 包含了必要的 Qt 类用于:
    • 窗口关闭事件处理;
    • 文件读写;
    • 对话框;
    • 自定义按钮类;

🎯 二、构造函数:初始化 UI 和信号连接

Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{
    ui->setupUi(this);
  • 调用 setupUi() 初始化界面布局;
  • 创建并绑定所有在 .ui 中设计的控件;
    connect(ui->comboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(oncurrentIndexChanged(int)));
  • 连接下拉框的索引变化信号到对应的槽函数;
  • 使用的是传统的 SIGNAL/SLOT 宏方式;
    connect(ui->mybtn, &MyButton::clicked, [=](){
        qDebug() << "myButton is clicked";
    });
  • 使用现代 Lambda 表达式连接 MyButton 的点击信号;
  • 演示了如何对接自定义控件的信号;

✅ 推荐:对于 Qt5+,建议统一使用现代 Lambda 方式,更安全、可读性更好。


🖱️ 三、重写的事件处理函数

1. 鼠标进入/离开事件

void Widget::enterEvent(QEvent *event)
{
    qDebug() << "mouse enter";
}

void Widget::leaveEvent(QEvent *event)
{
    qDebug() << "mouse leave";
}
  • 用于调试或触发悬停效果;
  • 可以配合样式表实现动态 UI 效果;

2. 鼠标滚轮事件

void Widget::wheelEvent(QWheelEvent *event)
{
    qDebug() << event->angleDelta();
}
  • 获取滚轮滚动方向和幅度;
  • 可用于缩放、切换页面等;

3. 窗口关闭事件

void Widget::closeEvent(QCloseEvent *event)
{
    int ret = QMessageBox::warning(this, tr("My Application"),
                                   tr("关闭窗口\n""你想关闭窗口吗?"),
                                   QMessageBox::Ok | QMessageBox::No);

    switch (ret) {
    case QMessageBox::Ok : event->accept(); break;
    case QMessageBox::No : event->ignore(); break;
    }
}
  • 用户点击关闭时弹出确认对话框;
  • 根据用户选择决定是否允许关闭;

4. 窗口大小改变事件

void Widget::resizeEvent(QResizeEvent *event)
{
    qDebug() << "oldSize:" << event->oldSize()
             << "newSize:" << event->size();
}
  • 打印窗口大小变化前后尺寸;
  • 可用于动态调整布局或刷新内容;

💾 四、文件操作相关函数

1. on_btnRead_clicked() —— 原始方式读取文件

void Widget::on_btnRead_clicked()
{
    QFile file("E:/QT/test.txt");
    if(!file.open(QIODevice::ReadOnly | QIODevice::Text)){
        qDebug() << "file open error";
    }

    char* context = new char(100);
    file.read(context, 100);
    qDebug() << context;

    file.close();
}
  • 使用原始 C 风格读取文件;
  • 存在潜在问题:
    • 分配内存不正确(应为 new char[100]);
    • 没有检查实际读取长度;
    • 不推荐这种方式,建议改用 QTextStream

2. on_btnWrite_clicked() —— 写入文件

void Widget::on_btnWrite_clicked()
{
    QFile file("E:/QT/test2.txt");
    file.open(QIODevice::WriteOnly | QIODevice::Text);
    file.write("Program 45-QFile001 write something to This File 我是测试");
}
  • 直接写入字符串到文件;
  • 简单实用;
  • 注意编码问题(中文可能乱码);

3. on_btnstreamRead_clicked() —— 使用 QTextStream 读取

void Widget::on_btnstreamRead_clicked()
{
    QFile file("E:/QT/test.txt");
    file.open(QIODevice::ReadOnly | QIODevice::Text);
    QTextStream in(&file);
    in.setCodec("UTF-8");

    while (!in.atEnd()) {
        QString context = in.readLine();
        qDebug() << context;
    }

    file.close();
}
  • 推荐使用方式;
  • 支持 Unicode 编码;
  • 更适合处理文本内容;

4. on_btnstreamWrite_clicked() —— 使用 QTextStream 写入

void Widget::on_btnstreamWrite_clicked()
{
    QFile file("E:/QT/test3.txt");
    file.open(QIODevice::WriteOnly | QIODevice::Text);
    QTextStream out(&file);
    out.setCodec("UTF-8");
    out << "I write stream char to file ";
    file.close();
}
  • 同样推荐使用;
  • 支持中文、换行符等;

5. on_btnQFileDialog_clicked() —— 打开文件并读取内容

QString fileName = QFileDialog::getOpenFileName(...);
QFile file(fileName);
...
  • 弹出文件选择对话框;
  • 读取选中文件内容;
  • 显示在控制台;

6. on_btnSe_clicked() —— 多文件选择

QFileDialog qFileDialog;
qFileDialog.setFileMode(QFileDialog::ExistingFiles);
qFileDialog.setNameFilter("*.txt");
qFileDialog.exec();

QStringList qstrings = qFileDialog.selectedFiles();
for(QString str : qstrings){
    qDebug() << str;
}
  • 设置为多文件选择模式;
  • 过滤 .txt 文件;
  • 获取所有选中的文件路径;

7. on_btnSave_clicked() —— 保存文件

QString fileName = QFileDialog::getSaveFileName(...);
QFile file(fileName);
file.open(QIODevice::WriteOnly | QIODevice::Text);
QTextStream out(&file);
out << "Qdialog Write Data to the Txt File";
file.close();
  • 弹出“另存为”对话框;
  • 将内容写入指定文件;

🔔 五、下拉框变化事件

void Widget::oncurrentIndexChanged(int index)
{
    qDebug() << index;
    qDebug() << ui->comboBox->currentText();
}
  • 当下拉框选项变化时触发;
  • 输出当前索引和显示文本;
  • 可用于切换编码、语言、主题等;

✅ 六、代码优化建议

1. 内存管理问题(在 on_btnRead_clicked() 中)

char* context = new char(100); // ❌ 错误分配方式

应改为:

char* context = new char[100]; // ✅ 正确分配数组

但更推荐使用 QByteArrayQTextStream

QByteArray data = file.readAll();
qDebug() << data;

2. 使用智能指针避免内存泄漏(C++11+)

std::unique_ptr<char[]> context(new char[100]);

3. 使用现代 Lambda 连接方式(统一风格)

connect(ui->comboBox, &QComboBox::currentIndexChanged, this, &Widget::oncurrentIndexChanged);

4. 使用 tr() 包裹所有界面对话内容(支持国际化)

QMessageBox::warning(this, tr("My Application"), tr("确定要关闭窗口吗?"));

📘 七、项目结构总结

功能模块 描述
界面交互 QPushButton、QComboBox、QTextEdit 等控件
事件系统 enter/leave/wheel/resize/close 事件
文件操作 QFile + QTextStream 实现读写功能
对话框 QFileDialog、QMessageBox
自定义控件 MyButton,支持状态切换和点击信号
信号与槽 使用传统和现代两种方式连接

🧩 八、技术栈一览

技术 用途
Qt Core 基础类(QString、QList 等)
Qt Gui 图形绘制、窗口系统集成
Qt Widgets 提供传统控件(QPushButton、QComboBox)
Qt Network(未使用) 网络通信
Qt Quick(未使用) QML 开发框架
Qt Designer 可视化界面编辑工具

✅ 九、完整流程图解(文字版)

Widget 构造函数
│
├── setupUi()
├── connect comboBox -> oncurrentIndexChanged
├── connect mybtn -> lambda clicked
│
└── 运行后响应各种事件
     │
     ├── 鼠标进入/离开 → 控制台输出
     ├── 滚轮 → 输出角度
     ├── 关闭 → 弹窗确认
     ├── 调整大小 → 输出尺寸
     ├── 按钮点击 → 触发对应函数
     │   └── 读取/写入文件、打开对话框等
     └── 下拉框变化 → 输出当前项

📚 十、延伸学习建议

如果你希望继续深入 Qt 开发,可以尝试以下方向:

方向 推荐内容
网络编程 QTcpSocket、QUdpSocket、QNetworkAccessManager
数据库 QSqlDatabase、SQLite、MySQL 驱动
多线程 QThread、QtConcurrent、QRunnable
动画 QPropertyAnimation、QGraphicsView
自定义控件 继承 QWidget/QPushButton,重写 paintEvent
国际化 使用 tr() 和 .ts 文件生成多语言版本
发布打包 使用 windeployqt 或 macdeployqt 打包程序
QML 学习 Qt Quick,开发现代 UI

如果你希望我帮你:

  • 完善 MyButton 类,增加动画或图标+文字混合;
  • 教你如何将这个项目打包发布成独立可执行程序;
  • 或者教你如何迁移到 CMake + Qt Quick(QML); 欢迎继续提问 😊

widget.ui:

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>Widget</class>
 <widget class="QWidget" name="Widget">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>971</width>
    <height>683</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>Widget</string>
  </property>
  <widget class="QPushButton" name="btnRead">
   <property name="geometry">
    <rect>
     <x>70</x>
     <y>90</y>
     <width>101</width>
     <height>51</height>
    </rect>
   </property>
   <property name="text">
    <string>读取文件</string>
   </property>
  </widget>
  <widget class="QPushButton" name="btnWrite">
   <property name="geometry">
    <rect>
     <x>190</x>
     <y>90</y>
     <width>101</width>
     <height>51</height>
    </rect>
   </property>
   <property name="text">
    <string>写入文件</string>
   </property>
  </widget>
  <widget class="QPushButton" name="btnstreamRead">
   <property name="geometry">
    <rect>
     <x>310</x>
     <y>90</y>
     <width>101</width>
     <height>51</height>
    </rect>
   </property>
   <property name="text">
    <string>Stream读取</string>
   </property>
  </widget>
  <widget class="QPushButton" name="btnstreamWrite">
   <property name="geometry">
    <rect>
     <x>430</x>
     <y>90</y>
     <width>101</width>
     <height>51</height>
    </rect>
   </property>
   <property name="text">
    <string>Stream写入</string>
   </property>
  </widget>
  <widget class="QPushButton" name="btnQFileDialog">
   <property name="geometry">
    <rect>
     <x>70</x>
     <y>180</y>
     <width>101</width>
     <height>51</height>
    </rect>
   </property>
   <property name="text">
    <string>打开文件选择框</string>
   </property>
  </widget>
  <widget class="QPushButton" name="btnSe">
   <property name="geometry">
    <rect>
     <x>190</x>
     <y>180</y>
     <width>101</width>
     <height>51</height>
    </rect>
   </property>
   <property name="text">
    <string>分步骤打开</string>
   </property>
  </widget>
  <widget class="QPushButton" name="btnSave">
   <property name="geometry">
    <rect>
     <x>310</x>
     <y>180</y>
     <width>101</width>
     <height>51</height>
    </rect>
   </property>
   <property name="text">
    <string>保存文件</string>
   </property>
  </widget>
  <widget class="QComboBox" name="comboBox">
   <property name="geometry">
    <rect>
     <x>430</x>
     <y>180</y>
     <width>101</width>
     <height>51</height>
    </rect>
   </property>
   <item>
    <property name="text">
     <string>GBK</string>
    </property>
   </item>
   <item>
    <property name="text">
     <string>GB2312</string>
    </property>
   </item>
   <item>
    <property name="text">
     <string>UTF-8</string>
    </property>
   </item>
   <item>
    <property name="text">
     <string>ANSI</string>
    </property>
   </item>
   <item>
    <property name="text">
     <string>UTF-16</string>
    </property>
   </item>
  </widget>
  <widget class="MyButton" name="mybtn" native="true">
   <property name="geometry">
    <rect>
     <x>110</x>
     <y>330</y>
     <width>120</width>
     <height>80</height>
    </rect>
   </property>
  </widget>
 </widget>
 <customwidgets>
  <customwidget>
   <class>MyButton</class>
   <extends>QWidget</extends>
   <header location="global">mybutton.h</header>
   <container>1</container>
  </customwidget>
 </customwidgets>
 <resources/>
 <connections/>
</ui>

你提供的这段 XML 是 Qt Designer 生成的 .ui 文件,用于描述主窗口类 Widget 的界面布局和控件结构。它定义了一个功能完整的文件操作与自定义控件演示界面。


📄 一、整体结构概览

<ui version="4.0">
    <class>Widget</class>
    <widget class="QWidget" name="Widget">
        ...
    </widget>
    <customwidgets>...</customwidgets>
    <resources/>
    <connections/>
</ui>
  • <class>Widget</class>:表示这个 .ui 文件对应的是 Widget 类;
  • 根 <widget> 标签:代表整个主窗口,继承自 QWidget
  • <customwidgets>:注册了自定义控件 MyButton
  • <resources/>:资源文件(图标、图片等)引用;
  • <connections/>:信号与槽连接关系(这里为空,说明是在代码中动态连接);

🧱 二、界面元素详解

✅ 主要控件一览:

控件名 类型 功能
btnRead QPushButton 原始方式读取文件
btnWrite QPushButton 写入文件
btnstreamRead QPushButton 使用 QTextStream 读取文本
btnstreamWrite QPushButton 使用 QTextStream 写入文本
btnQFileDialog QPushButton 打开文件选择对话框
btnSe QPushButton 多选文件对话框
btnSave QPushButton 保存文件对话框
comboBox QComboBox 编码格式下拉框(GBK/UTF-8 等)
mybtn MyButton 自定义按钮控件

🎨 三、控件布局分析

所有控件都使用绝对坐标定位(通过 <geometry> 标签),这是 Qt Designer 默认的方式,适用于静态布局。

示例:按钮 btnRead

<widget class="QPushButton" name="btnRead">
    <property name="geometry">
        <rect>
            <x>70</x>
            <y>90</y>
            <width>101</width>
            <height>51</height>
        </rect>
    </property>
    <property name="text">
        <string>读取文件</string>
    </property>
</widget>
  • 位置:左上角 (70, 90)
  • 大小:宽 101px,高 51px
  • 显示文字读取文件

🧩 四、自定义控件注册

<customwidgets>
  <customwidget>
   <class>MyButton</class>
   <extends>QWidget</extends>
   <header location="global">mybutton.h</header>
   <container>1</container>
  </customwidget>
</customwidgets>
  • <class>MyButton</class>:控件名称;
  • <extends>QWidget</extends>:继承自 QWidget
  • <header>:头文件路径;
  • <container>1</container>:表示是否可以在设计器中作为容器使用(通常为 0);

⚠️ 注意:如果你在 .ui 中使用了自定义控件,必须确保:

  1. mybutton.h 存在且正确;
  2. 在项目中注册该控件(或通过插件方式添加到 Qt Designer);
  3. 否则打开 .ui 文件会报错;

📌 五、编码风格建议

虽然 .ui 文件由 Qt Designer 自动生成,但你可以手动编辑以实现更灵活的控制。以下是一些推荐实践:

1. 使用相对布局(Layout)代替绝对定位

目前是固定坐标,不适应窗口缩放。可以改为:

QHBoxLayout *layout = new QHBoxLayout;
layout->addWidget(ui->btnRead);
layout->addWidget(ui->btnWrite);
...
setLayout(layout);

或直接在 .ui 中拖动添加 Layout 容器。


2. 设置对象名时统一命名规范

比如 btn_ 开头表示按钮,cmb_ 表示下拉框:

  • btnRead → btn_read
  • comboBox → cmb_encoding

这样更容易维护和查找。


3. 支持样式表(QSS)

你可以为按钮设置样式:

<property name="styleSheet">
    <string notr="true">background-color: #FFA500; color: white;</string>
</property>

或者在代码中设置:

ui->btnRead->setStyleSheet("background-color: #FFA500; color: white;");

🧪 六、运行效果预览(文字版)

+---------------------------------------------------+
| Widget (标题)                                     |
+---------------------------------------------------+
|                                                   |
| [读取文件] [写入文件] [Stream读取] [Stream写入]     |
|                                                   |
| [打开文件选择框] [分步骤打开] [保存文件] [下拉框]  |
|                                                   |
|                +------------------+               |
|                | 选项:GBK         |               |
|                |       UTF-8       |               |
|                |       ANSI        |               |
|                +------------------+               |
|                                                   |
|          +----------------------+                 |
|          |                      |                 |
|          |      自定义按钮      |                 |
|          |                      |                 |
|          +----------------------+                 |
|                                                   |
+---------------------------------------------------+

📦 七、完整项目结构建议

文件 作用
main.cpp 程序入口
widget.h/cpp 主窗口逻辑
ui_widget.h 由 .ui 自动生成
mybutton.h/cpp 自定义按钮类
*.qrc 资源文件(图标、图片)
Widget.ui 可视化界面设计文件

📘 八、延伸学习建议

如果你希望我帮你:

  • 把 .ui 文件转换为纯代码实现;
  • 教你如何将 MyButton 注册为 Qt Designer 插件;
  • 或者教你如何用 QML 替代当前界面; 欢迎继续提问 😊

如需我为你生成 .pro 文件、资源文件 .qrc 或帮助打包发布程序,也请告诉我!


网站公告

今日签到

点亮在社区的每一天
去签到