Qt------自定义标题栏(最小化、最大化、关闭、边框拉伸、拖动)

这篇博客介绍了如何在Qt中创建一个自定义标题栏,实现无边框窗口并支持窗口移动和大小缩放。通过重载`mousePressEvent`处理拖动,利用`nativeEvent`处理边框拉伸,并通过设置布局来确保标题栏与边框的交互。代码示例展示了如何在Windows系统中实现这一功能,包括双击标题栏进行最大化/还原,以及处理不同分辨率下的坐标转换。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

转载:Qt之界面(自定义标题栏、无边框、可移动、缩放)_我不是萧海哇~~~~的博客-优快云博客

参考了两篇文章处理,还有半屏没处理,就是左右边框的时候半屏。

重点:

1.拖动需要重载标题栏的 mousePressEvent方法。

2.边框拉伸的是采用的window的消息通信nativeEvent。需要注意的是这里面windows鼠标点获取的点和qt中获取的鼠标点位置不同,这里涉及到分辨下等问题,需要做转换处理,上面的文章没处理。

3.还有设置了自定义标题栏,需要对边框四周留缝隙,特别是顶部位置,就是在设置布局的时候,要给顶部留空隙,这时候边框拖动才能捕捉到。

效果:

 

代码:

widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QMouseEvent>
#include <QLabel>
#include <QPushButton>
#include <QPoint>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();
    bool nativeEvent(const QByteArray &eventType, void *message, qintptr *result);
private:
    Ui::Widget *ui;

    int m_nBorderWidth; //m_nBorder表示鼠标位于边框缩放范围的宽度
};


class TitleBar : public QWidget
{
    Q_OBJECT
public:
    explicit TitleBar(QWidget *parent = 0);

protected:

    // 双击标题栏进行界面的最大化/还原
    virtual void mouseDoubleClickEvent(QMouseEvent *event);

protected:
    virtual void mousePressEvent(QMouseEvent *event);

signals:

public slots:
    //the slots function of min\max\close bt
    void onChangeClicked();

private:
    QLabel *m_luLabel;           //left-upper label
    QLabel *m_midLabel;          //mid title label
    QPushButton *m_minBt;        //min bt
    QPushButton *m_maxBt;        //max bt
    QPushButton *m_closeBt;      //close bt

};

#endif // WIDGET_H

widget.cpp

#include "widget.h"
#include "ui_widget.h"
#include "titlebar.h"
#include <QVBoxLayout>
#include <QPalette>
#include <QWindow>
//调用WIN API需要用到的头文件与库 [实现缩放]
#ifdef Q_OS_WIN
#include <qt_windows.h>
#include <Windowsx.h>
#endif

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{

    ui->setupUi(this);
    setMouseTracking(true);
    //Qt::FramelessWindowHint设置窗口标志为无边框,而Qt::WindowStaysOnTopHint使窗口位于所有界面之上
    this->setWindowFlags(Qt::FramelessWindowHint);

    //定义自定义标题栏对象
    TitleBar *pTitleBar = new TitleBar();
    pTitleBar->setParent(this);
    installEventFilter(pTitleBar);

    //使用调色板设置窗口的背景色
    QPalette pal(palette());
    pal.setColor(QPalette::Window, QColor(0, 255, 255));
    setAutoFillBackground(true);
    setPalette(pal);

    //窗口布局中加入标题栏
    QVBoxLayout *pLayout = new QVBoxLayout();
    pLayout->addWidget(pTitleBar);
    pLayout->addStretch();
    pLayout->setSpacing(0);
    //自定义标题的时候需要留一些边框空隙给拉伸的时候使用
    pLayout->setContentsMargins(5, 5, 5, 5);
    setLayout(pLayout);

    //m_nBorder表示鼠标位于边框缩放范围的宽度,可以设置为5
    m_nBorderWidth=5;
}

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

//nativeEvent主要用于进程间通信-消息传递,使用这种方式后来实现窗体的缩放 [加上了这函数,窗口也能移动了]
bool Widget::nativeEvent(const QByteArray &eventType, void *message, qintptr *result)
{
    Q_UNUSED(eventType)

    MSG *param = static_cast<MSG *>(message);

    switch (param->message)
    {
    case WM_NCHITTEST:
    {
        //由于windows的分辨率不一样,需要我们做转化
        int xPt = GET_X_LPARAM(param->lParam);
        int yPt = GET_Y_LPARAM(param->lParam);
        QPoint gpos(xPt,yPt);
        QWindow * handle = this->window()->windowHandle();
        if(handle && handle->screen())
        {
            QScreen * screen = handle->screen();
            QPoint offset = screen->geometry().topLeft();
            gpos = (gpos - offset) / screen->devicePixelRatio() + offset;
        }

        int nX = gpos.x() - this->geometry().x();
        int nY = gpos.y()- this->geometry().y();
        //如果是子控件
        if (childAt(nX, nY) != nullptr)
            return QWidget::nativeEvent(eventType, message, result);

        *result = HTCAPTION;//在标题栏  2
        int width=this->width();
        int height=this->height();

        // 鼠标区域位于窗体边框,进行缩放
        if ((nX > 0) && (nX < m_nBorderWidth))
            *result = HTLEFT;//在窗口的左边框  10

        if ((nX > (width - m_nBorderWidth)) && (nX < width))
            *result = HTRIGHT;//在窗口的右边框  11

        if ((nY > 0) && (nY < m_nBorderWidth))
            *result = HTTOP;//在窗口框的上面的水平线上  12

        if ((nY > (height - m_nBorderWidth)) && (nY < height))
            *result = HTBOTTOM;//窗口框的下面的水平线上  15

        if ((nX > 0) && (nX < m_nBorderWidth) && (nY > 0)
            && (nY < m_nBorderWidth))
            *result = HTTOPLEFT;//在窗口框的左上角  13

        if ((nX > (width - m_nBorderWidth)) && (nX < width)
            && (nY > 0) && (nY < m_nBorderWidth))
            *result = HTTOPRIGHT;//在窗口框的右上角 14

        if ((nX > 0) && (nX < m_nBorderWidth)
            && (nY > (height - m_nBorderWidth)) && (nY < height))
            *result = HTBOTTOMLEFT;//在窗口框的左下角 16

        if ((nX > (width - m_nBorderWidth)) && (nX < width)
            && (nY > (height - m_nBorderWidth)) && (nY < height))
            *result = HTBOTTOMRIGHT;//在窗口框的右下角 17

        return true;
    }
    }
    return QWidget::nativeEvent(eventType, message, result);
}

TitleBar::TitleBar(QWidget *parent)
    : QWidget{parent}
{
    m_luLabel    = new QLabel;
    m_midLabel   = new QLabel;
    m_minBt  = new QPushButton;
    m_maxBt  = new QPushButton;
    m_closeBt     = new QPushButton;

    //set size
    m_luLabel->setFixedSize(20, 20);
    m_luLabel->setScaledContents(true);

    //save window default layout
    m_midLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
    m_midLabel->setFixedHeight(32);
    m_minBt->setFixedSize(20, 20);
    m_maxBt->setFixedSize(20, 20);
    m_closeBt->setFixedSize(20, 20);


    //detele button frame
    m_minBt->setFlat(true);
    m_maxBt->setFlat(true);
    m_closeBt->setFlat(true);

    //set icon":/new/prefix1/rc/minimize.png"
    m_minBt->setIcon(QIcon(":/new/prefix1/rc/minimize.png"));
    m_minBt->setIconSize(m_minBt->size());
    m_maxBt->setIcon(QIcon(":/new/prefix1/rc/maximize.png"));
    m_maxBt->setIconSize(m_maxBt->size());
    m_closeBt->setIcon(QIcon(":/new/prefix1/rc/close-small.png"));
    m_closeBt->setIconSize(m_closeBt->size());

    QPixmap icon(":/new/prefix1/rc/avatar.png");
    m_luLabel->setPixmap(icon);

    //set name
    m_midLabel->setObjectName("titleLabel");
    m_minBt->setObjectName("minimizeButton");
    m_maxBt->setObjectName("maximizeButton");
    m_closeBt->setObjectName("closeButton");

    //set label text
    m_midLabel->setText("这里是项目名称字段");

    //prompt name when Mouse over the button
    m_minBt->setToolTip("最小化");
    m_maxBt->setToolTip("放大/缩小");
    m_closeBt->setToolTip("关闭");



    QHBoxLayout *pLayout = new QHBoxLayout(this);
    pLayout->addWidget(m_luLabel);

    pLayout->addWidget(m_midLabel,0,Qt::AlignCenter);
    pLayout->addWidget(m_minBt);
    pLayout->addWidget(m_maxBt);
    pLayout->addWidget(m_closeBt);

    pLayout->setSpacing(10);//set distance between controls
    pLayout->setContentsMargins(0, 0, 0, 0);//set Surrounding margin

    this->setLayout(pLayout);

    connect(m_minBt,SIGNAL(clicked()),this,SLOT(onChangeClicked()));
    connect(m_maxBt,SIGNAL(clicked()),this,SLOT(onChangeClicked()));
    connect(m_closeBt,SIGNAL(clicked()),this,SLOT(onChangeClicked()));
}

void TitleBar::onChangeClicked()
{
    //if called by slot,return pointer,else nullptr
    QPushButton *pBtn = qobject_cast<QPushButton *>(sender());

    QWidget *pWindow = this->window();
    if (!pBtn||!pWindow->isWindow())
    {
        return;
    }
    if (pBtn == m_minBt)
    {
        pWindow->showMinimized();//min window。
    }
    else if (pBtn == m_maxBt)
    {
        pWindow->isMaximized() ? pWindow->showNormal() : pWindow->showMaximized();//scaled window
    }
    else if (pBtn == m_closeBt)
    {
        pWindow->close();//close window
    }
}

void TitleBar::mouseDoubleClickEvent(QMouseEvent *event)
{
    Q_UNUSED(event);

    emit m_maxBt->clicked();
}

void TitleBar::mousePressEvent(QMouseEvent *event)
{
    if (ReleaseCapture())
    {
        QWidget *pWindow = this->window();
        //if mouse press is top window
        if (pWindow->isTopLevel())
        {
            SendMessage(HWND(pWindow->winId()), WM_SYSCOMMAND, SC_MOVE + HTCAPTION, 0);
        }
    }
    event->ignore();
}

main.cpp

#include "widget.h"

#include <QApplication>

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

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值