在现代化的软件设计中,个性化和美观是应用程序界面不可或缺的元素。而传统的Qt窗口往往风格单一,标题栏固定死板,这严重限制了界面的灵活性和视觉体验。有没有一种方法,可以摆脱这些束缚,为我们的Qt应用注入无限活力?今天,我们就来探索自定义Qt标题栏的奥秘,打造属于你自己的曲线玲珑、别具一格的窗口王冠!



一、Qt自定义标题栏的优势

相比于使用Qt默认的标题栏,自定义标题栏有以下显著优势:

  1. 无限个性化:可自由设计标题栏的外观、布局、控件等,实现与众不同的风格。
  2. 跨平台统一:在不同操作系统下,标题栏界面保持一致,提升应用的整体性。
  3. 功能扩展性强:可方便地添加自定义按钮、搜索框等新功能,提高用户体验。

二、标题栏组件拆解

要实现自定义标题栏,我们需要自行构建其各个组成部分,包括:

  • 标题文字:用于显示应用程序名称或当前窗口标题
  • 窗口控制按钮:最小化、最大化/恢复、关闭按钮
  • 拖动区域:用于实现整个窗口的拖动
  • 其他功能按钮:比如菜单按钮、搜索框等

下面就让我们一一部署这些组件吧。



三、标题栏组件实现

1、标题栏基本界面搭建

我们首先创建一个无边框的Qt窗口,在其上绘制标题栏:

QWidget *window = new QWidget;
window->setWindowFlags(Qt::FramelessWindowHint); //设置无边框



然后通过样式表设置标题栏的基本外观:

#titlebar {
    background-color: #323232;
}

#titlebar QLabel {
    color: white; 
    font-weight: bold;
}



在代码中,我们使用QPushButton、QLabel等Qt控件来创建标题文字、最小化/最大化/关闭按钮:

// 标题文字
auto titleLabel = new QLabel(this);
titleLabel->setText("My App");

// 最小化按钮
auto minimizeBtn = new QPushButton(this);
minimizeBtn->setIcon(style->standardIcon(QStyle::SP_TitleBarMinButton));
connect(minimizeBtn, &QPushButton::clicked, this, &CustomTitleBar::showMinimized);

// 其他按钮...



2、实现拖动功能

我们需要在标题栏捕获鼠标的按下、移动、释放事件,根据鼠标的位移来移动整个窗口。

// 在mousePressEvent中记录初始位置
m_startPos = event->globalPos();

// 在mouseMoveEvent中根据位移调整窗口位置 
move(geometry().topLeft() + event->globalPos() - m_startPos);

// 在mouseReleaseEvent中重置状态



3、实现双击最大化/恢复

通过重写mouseDoubleClickEvent,当双击标题栏时,我们切换窗口的最大化/恢复状态:

void CustomTitleBar::mouseDoubleClickEvent(QMouseEvent *event)
{
    if(window()->isMaximized())
        window()->showNormal();
    else
        window()->showMaximized();
}



四、实战案例

这里给出自定义标题栏的完整源码:

titlebar.h

#ifndef TITLEBAR_H
#define TITLEBAR_H

#include <QWidget>
#include <QHBoxLayout>
#include <QLabel>
#include <QPushButton>

class TitleBar : public QWidget
{
    Q_OBJECT

public:
    explicit TitleBar(QWidget *parent = nullptr);

    void setWindowTitle(const QString &title);
    void setWindowIcon(const QIcon &icon);

protected:
    void mousePressEvent(QMouseEvent *event) override;
    void mouseMoveEvent(QMouseEvent *event) override;
    void mouseReleaseEvent(QMouseEvent *event) override;
    void mouseDoubleClickEvent(QMouseEvent *event) override;

private slots:
    void onButtonClicked();

private:
    QLabel *m_titleLabel;
    QPushButton *m_minimizeButton;
    QPushButton *m_maximizeButton;
    QPushButton *m_closeButton;

    QPoint m_startPos;
    bool m_isMousePressed;
};

#endif // TITLEBAR_H

titlebar.cpp:

#include "titlebar.h"

#include <QHBoxLayout>
#include <QMouseEvent>
#include <QApplication>
#include <QStyle>

TitleBar::TitleBar(QWidget *parent)
    : QWidget(parent)
    , m_isMousePressed(false)
{
    // 标题文字
    m_titleLabel = new QLabel(this);
    m_titleLabel->setStyleSheet("color: white; font-weight: bold;");

    // 最小化按钮
    m_minimizeButton = new QPushButton(this);
    m_minimizeButton->setIcon(style()->standardIcon(QStyle::SP_TitleBarMinButton));
    connect(m_minimizeButton, &QPushButton::clicked, this, &TitleBar::onButtonClicked);

    // 最大化按钮
    m_maximizeButton = new QPushButton(this);
    m_maximizeButton->setIcon(style()->standardIcon(QStyle::SP_TitleBarMaxButton));
    connect(m_maximizeButton, &QPushButton::clicked, this, &TitleBar::onButtonClicked);

    // 关闭按钮
    m_closeButton = new QPushButton(this);
    m_closeButton->setIcon(style()->standardIcon(QStyle::SP_TitleBarCloseButton));
    connect(m_closeButton, &QPushButton::clicked, this, &TitleBar::onButtonClicked);

    // 布局
    QHBoxLayout *layout = new QHBoxLayout(this);
    layout->addWidget(m_titleLabel);
    layout->addWidget(new QWidget(), 1); // 用于占据中间空间
    layout->addWidget(m_minimizeButton);
    layout->addWidget(m_maximizeButton);
    layout->addWidget(m_closeButton);
    layout->setContentsMargins(5, 5, 5, 5);

    // 设置标题栏样式
    setStyleSheet("background-color: #323232;");
}

void TitleBar::setWindowTitle(const QString &title)
{
    m_titleLabel->setText(title);
}

void TitleBar::setWindowIcon(const QIcon &icon)
{
    m_titleLabel->setPixmap(icon.pixmap(16, 16));
}

void TitleBar::mousePressEvent(QMouseEvent *event)
{
    if (event->button() == Qt::LeftButton) {
        m_isMousePressed = true;
        m_startPos = event->globalPos();
    }
}

void TitleBar::mouseMoveEvent(QMouseEvent *event)
{
    if (m_isMousePressed) {
        QPoint delta = event->globalPos() - m_startPos;
        window()->move(window()->pos() + delta);
    }
}

void TitleBar::mouseReleaseEvent(QMouseEvent *event)
{
    if (event->button() == Qt::LeftButton) {
        m_isMousePressed = false;
    }
}

void TitleBar::mouseDoubleClickEvent(QMouseEvent *event)
{
    Q_UNUSED(event)
    if (window()->isMaximized()) {
        window()->showNormal();
    } else {
        window()->showMaximized();
    }
}

void TitleBar::onButtonClicked()
{
    QPushButton *button = qobject_cast<QPushButton *>(sender());
    QWidget *window = this->window();

    if (button == m_minimizeButton) {
        window->showMinimized();
    } else if (button == m_maximizeButton) {
        if (window->isMaximized()) {
            window->showNormal();
        } else {
            window->showMaximized();
        }
    } else if (button == m_closeButton) {
        window->close();
    }
}



在主窗口中使用自定义标题栏:

#include "titlebar.h"
#include <QVBoxLayout>

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    QWidget window;
    window.setWindowFlags(Qt::FramelessWindowHint); // 设置无边框

    TitleBar *titleBar = new TitleBar(&window);
    titleBar->setWindowTitle("My Application");
    titleBar->setWindowIcon(QIcon(":/icons/app.ico"));

    QWidget *centralWidget = new QWidget;
    // 添加你的主界面组件...

    QVBoxLayout *layout = new QVBoxLayout(&window);
    layout->addWidget(titleBar);
    layout->addWidget(centralWidget);

    window.show();
    return app.exec();
}

以上代码实现了一个基本的自定义标题栏,包括标题文字、最小化/最大化/关闭按钮、窗口拖动等功能。您可以根据需求对代码进行修改和扩展,比如添加更多自定义按钮、集成QSS样式等。



结语

通过本文的实战演练,我们成功实现了一个基本的自定义标题栏。当然,这只是一个起点,你可以根据需求进一步扩展和优化,比如添加透明度调节、自定义按钮等。