小项目实验:创建一个自定义的标题栏,使用QT

1.概要

需求来源

有时候,自定义的标题栏,在一定的尺寸上,显的太小,在工控车间,且是触摸屏的情况下,系统默认的那个关闭按钮往往不容点中,所以想自定义一个。

基本功能

1.最大化,最小化,还原

2.拖拽整个窗体

2.代码

2.1 代码

#include <QApplication>
#include <QWidget>
#include <QPushButton>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QMouseEvent>
#include <QStyle>

class CustomTitleBar : public QWidget {
public:
    explicit CustomTitleBar(QWidget* parent = nullptr) : QWidget(parent) {
        // 创建控制按钮
        minimizeBtn = new QPushButton("-", this);
        maximizeBtn = new QPushButton("□", this);
        closeBtn = new QPushButton("×", this);

        // 设置按钮样式(放大按钮)
        setButtonStyle(minimizeBtn);
        setButtonStyle(maximizeBtn);
        setButtonStyle(closeBtn);

        // 布局结构
        QHBoxLayout* layout = new QHBoxLayout(this);
        layout->addWidget(new QWidget, 1);  // 左侧占位
        layout->addWidget(minimizeBtn);
        layout->addWidget(maximizeBtn);
        layout->addWidget(closeBtn);
        layout->setContentsMargins(8, 0, 8, 0);
        layout->setSpacing(4);
        layout->setAlignment(Qt::AlignVCenter);

        // 固定标题栏高度
        setFixedHeight(30);

        // 设置标题栏背景色(新增功能)
        setStyleSheet("background-color: #F0F0F0;");  // 灰白色背景

        // 连接按钮信号
        connect(minimizeBtn, &QPushButton::clicked, parent, &QWidget::showMinimized);
        connect(closeBtn, &QPushButton::clicked, parent, &QWidget::close);
        connect(maximizeBtn, &QPushButton::clicked, [this](){
            if (parentWidget()->isMaximized()) {
                parentWidget()->showNormal();
                maximizeBtn->setText("□");
            } else {
                parentWidget()->showMaximized();
                maximizeBtn->setText("❐");
            }
        });
    }

private:
    void setButtonStyle(QPushButton* btn) {
        btn->setFixedSize(28, 28);
        btn->setStyleSheet(
            "QPushButton {"
            "    background: #DDDDDD;"  // 按钮背景色
            "    color: #444;"
            "    border: 1px solid #CCCCCC;"
            "    border-radius: 4px;"
            "}"
            "QPushButton:hover {"
            "    background: #EEEEEE;"
            "}"
            "QPushButton:pressed {"
            "    background: #CCCCCC;"
            "}"
            );
    }

    QPushButton* minimizeBtn;
    QPushButton* maximizeBtn;
    QPushButton* closeBtn;
};

class FramelessWindow : public QWidget {
public:
    FramelessWindow() {
        // 隐藏默认标题栏
        setWindowFlags(Qt::FramelessWindowHint | Qt::Window);
        setAttribute(Qt::WA_TranslucentBackground);

        // 创建布局
        QVBoxLayout* layout = new QVBoxLayout(this);
        layout->setContentsMargins(0, 0, 0, 0);
        layout->setSpacing(0);

        // 添加自定义标题栏
        titleBar = new CustomTitleBar(this);
        layout->addWidget(titleBar);

        // 添加内容区域
        content = new QWidget(this);
        content->setStyleSheet("background: white;");
        layout->addWidget(content);

        // 窗口移动处理
        setMouseTracking(true);
        titleBar->installEventFilter(this);
    }

protected:
    bool eventFilter(QObject* obj, QEvent* event) override {
        if (obj == titleBar && event->type() == QEvent::MouseButtonPress) {
            QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
            if (mouseEvent->button() == Qt::LeftButton) {
                dragPosition = mouseEvent->globalPosition().toPoint() - frameGeometry().topLeft();
                event->accept();
            }
        }
        return QWidget::eventFilter(obj, event);
    }

    void mouseMoveEvent(QMouseEvent* event) override {
        if (event->buttons() & Qt::LeftButton) {
            move(event->globalPosition().toPoint() - dragPosition);
            event->accept();
        }
    }

private:
    CustomTitleBar* titleBar;
    QWidget* content;
    QPoint dragPosition;
};

// 使用示例
int main(int argc, char *argv[]) {
    QApplication a(argc, argv);

    FramelessWindow window;
    window.resize(800, 600);
    window.show();

    return a.exec();
}

2.2 代码分析

2.1.1 处理标题栏的拖拽事件 

2.2.2 最大化/恢复按钮分析 

 

3.运行结果

4.关联链接

5.关联知识

1.标题栏布局解析

// 布局结构
        QHBoxLayout* layout = new QHBoxLayout(this);
        layout->addWidget(new QWidget, 1);  // 左侧占位
        layout->addWidget(minimizeBtn);
        layout->addWidget(maximizeBtn);
        layout->addWidget(closeBtn);
        layout->setContentsMargins(8, 0, 8, 0);
        layout->setSpacing(4);
        layout->setAlignment(Qt::AlignVCenter);

这段代码使用Qt框架创建了一个水平布局(QHBoxLayout),主要用于实现窗口标题栏的按钮布局(最小化、最大化/还原、关闭按钮)。以下是逐层解析:

  1. 布局容器创建
QHBoxLayout* layout = new QHBoxLayout(this);
  • 创建水平布局并绑定到当前窗口(this),使布局成为窗口的主布局
  1. 左侧弹性占位
layout->addWidget(new QWidget, 1); // 左侧占位
  • 添加一个空白QWidget作为占位符
  • 拉伸因子设为1,使其占据所有剩余空间
  • 效果:将后续按钮推到布局最右侧
  1. 功能按钮排列

layout->addWidget(minimizeBtn);
layout->addWidget(maximizeBtn);
layout->addWidget(closeBtn);
  • 依次添加三个标准窗口按钮
  • 默认按添加顺序从左到右排列
  • 实际显示效果为:[占位空间] [最小化] [最大化] [关闭]
  1. 视觉参数设置

layout->setContentsMargins(8, 0, 8, 0); // 左右8px边距,上下无边距
layout->setSpacing(4); // 按钮间距4px
layout->setAlignment(Qt::AlignVCenter); // 垂直居中对齐
  1. 布局特性总结
  • 典型应用场景:窗口标题栏右侧按钮组
  • 布局效果:
    
    
    [弹性空间] [最小化] [4px] [最大化] [4px] [关闭]
  • 按钮始终垂直居中
  • 按钮组与窗口边缘保持8px间距
  • 按钮间距均匀且紧凑
  1. 扩展建议
  • 可为按钮设置固定大小(setFixedSize())保持一致性
  • 添加鼠标悬停效果提升交互体验
  • 最大化按钮可切换为还原按钮(通过setCheckable(true)实现状态切换)

这种布局模式广泛用于跨平台桌面应用开发,能自动适配不同分辨率和DPI设置,是现代GUI界面设计的标准实践。

2.eventFilter 

eventFilter 是 Qt 框架中用于实现事件过滤的核心机制,允许你在不修改目标对象源码的情况下,拦截并处理其事件。以下是关键点解析和典型使用场景:


核心原理

  1. 事件传播路径
    • 事件首先发送到过滤器 → 过滤器决定是否处理/拦截 → 未处理则继续传递给目标对象
    • 通过返回 true 阻止事件进一步传递,返回 false 允许事件继续传播
  2. 实现步骤

    // 1. 子类化目标对象或创建过滤器对象
    class MyFilter : public QObject {
    protected:
        bool eventFilter(QObject *obj, QEvent *event) override {
            if (event->type() == QEvent::MouseButtonPress) {
                qDebug() << "Mouse pressed on" << obj;
                return true; // 拦截事件,目标对象不再接收
            }
            return QObject::eventFilter(obj, event); // 默认处理
        }
    };
    
    // 2. 安装过滤器到目标对象
    QWidget *targetWidget = new QWidget;
    MyFilter *filter = new MyFilter(targetWidget);
    targetWidget->installEventFilter(filter);

代码分析 


典型应用场景

  1. 全局快捷键监听

    bool eventFilter(QObject *obj, QEvent *event) {
        if (event->type() == QEvent::KeyPress) {
            QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
            if (keyEvent->key() == Qt::Key_F5 && keyEvent->modifiers() == Qt::NoModifier) {
                qDebug() << "F5 pressed!";
                return true; // 阻止默认刷新行为
            }
        }
        return false;
    }
  2. 监控输入框回车事件

    if (obj == lineEdit && event->type() == QEvent::KeyPress) {
        QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
        if (keyEvent->key() == Qt::Key_Return) {
            qDebug() << "Enter pressed in lineEdit";
            return true; // 阻止默认换行行为
        }
    }
  3. 禁用控件交互

    if (obj == disabledButton) {
        if (event->type() == QEvent::MouseButtonPress || 
            event->type() == QEvent::HoverEnter) {
            return true; // 完全屏蔽鼠标事件
        }
    }

注意事项

  1. 性能优化:避免在过滤器中执行耗时操作,否则可能阻塞事件循环
  2. 对象生命周期:确保过滤器对象存活时间不短于目标对象
  3. 事件类型判断:优先使用 event->type() 而非直接类型转换,提高可读性
  4. 组合使用:可同时安装多个过滤器,按安装顺序依次处理

调试技巧

  • 使用 qDebug() << event->type() 打印所有事件类型
  • 通过 obj->metaObject()->className() 查看事件来源对象类型
  • 结合 Qt Creator 的 Event Debugger 工具可视化事件流

事件过滤器是 Qt 中实现非侵入式事件处理的利器,特别适合需要动态监控/修改第三方控件行为的场景。合理使用可显著提升代码的模块化和可维护性。

3.installEventFilter 

installEventFilter 是 Qt 中用于注册事件过滤器的核心方法,允许你为任意 QObject 派生类对象安装自定义事件处理器。以下是关键使用指南和注意事项:


基本用法

// 1. 创建过滤器对象(需继承自 QObject)
class MyEventFilter : public QObject {
protected:
    bool eventFilter(QObject *watched, QEvent *event) override {
        if (event->type() == QEvent::MouseMove) {
            qDebug() << "Mouse moved over" << watched;
            return true; // 拦截事件,阻止目标对象处理
        }
        return QObject::eventFilter(watched, event); // 默认处理
    }
};

// 2. 安装过滤器到目标对象
QWidget *targetWidget = new QWidget;
MyEventFilter *filter = new MyEventFilter(targetWidget); // 通常设置父对象管理内存
targetWidget->installEventFilter(filter);

关键特性

  1. 事件传递链
    • 事件首先发送到过滤器 → 过滤器返回 true 表示已处理,事件停止传播
    • 返回 false 则事件继续传递给目标对象的 event() 方法
  2. 多过滤器支持
    • 可为同一对象安装多个过滤器,按安装顺序依次处理
    • 最后一个安装的过滤器最先接收事件
  3. 对象生命周期
    • 过滤器对象必须比目标对象存活时间长
    • 推荐将目标对象设为过滤器的父对象,自动管理内存:
      MyEventFilter *filter = new MyEventFilter(targetWidget); // targetWidget 销毁时自动删除 filter

典型应用场景

  1. 全局输入监控

    // 监控所有 QLineEdit 的回车事件
    class LineEditFilter : public QObject {
    protected:
        bool eventFilter(QObject *obj, QEvent *event) override {
            if (event->type() == QEvent::KeyPress) {
                QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
                if (keyEvent->key() == Qt::Key_Return && qobject_cast<QLineEdit*>(obj)) {
                    qDebug() << "Enter pressed in line edit";
                    return true; // 阻止默认提交行为
                }
            }
            return false;
        }
    };
  2. 禁用控件交互

    // 完全屏蔽按钮的鼠标和键盘事件
    class DisablerFilter : public QObject {
    protected:
        bool eventFilter(QObject *obj, QEvent *event) override {
            if (obj == targetButton && (event->type() == QEvent::MouseButtonPress ||
                                       event->type() == QEvent::KeyPress)) {
                return true; // 阻止所有交互
            }
            return false;
        }
    };

注意事项

  1. 性能影响
    • 避免在过滤器中执行耗时操作(如网络请求),否则会阻塞事件循环
    • 使用 QCoreApplication::processEvents() 谨慎处理需要异步操作的情况
  2. 调试技巧
    • 打印事件类型和来源对象:

      qDebug() << "Event type:" << event->type() 
      << "from" << obj->metaObject()->className();
    • 使用 Qt Creator 的 Event Debugger 工具可视化事件流
  3. 卸载过滤器
    • 手动移除过滤器防止内存泄漏:
      targetWidget->removeEventFilter(filter);
    • 或通过父对象机制自动管理(推荐)

高级用法

  1. 动态过滤规则

    class DynamicFilter : public QObject {
        bool m_blockMouse = false;
    public:
        void setBlockMouse(bool block) { m_blockMouse = block; }
    
    protected:
        bool eventFilter(QObject *obj, QEvent *event) override {
            if (m_blockMouse && event->type() == QEvent::MouseMove) {
                return true;
            }
            return false;
        }
    };
  2. 跨线程过滤(需谨慎):

    • 确保过滤器对象和目标对象在同一个线程
    • 或使用信号槽进行线程间通信

通过合理使用 installEventFilter,可以实现非侵入式的事件监控和修改,特别适合需要动态调整控件行为或实现全局交互逻辑的场景。正确管理对象生命周期是避免崩溃的关键。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值