QT 实现自定义标题栏带阴影的窗口

实现效果:

工程目录:

具体实现:CFrameLessWidgetBase主要实现窗口鼠标拖拽和缩放,CTitleBar主要实现标题栏,

代码位置:https://download.csdn.net/download/wanglei_11/87426331

CFrameLessWidgetBase.h

#ifndef CFRAMELESSWIDGETBASE_H
#define CFRAMELESSWIDGETBASE_H


#include <QWidget>

class CFrameLessWidgetBase : public QWidget
{
public:
    CFrameLessWidgetBase(QWidget* parent = nullptr);
    ~CFrameLessWidgetBase();

protected:
    bool nativeEvent(const QByteArray& eventType, void* message, long* result) override;

private:
    int mouse_margin = 5;
};
#endif

CFrameLessWidgetBase.cpp

#include "CFrameLessWidgetBase.h"

#ifdef Q_OS_WIN
#include <qt_windows.h>
#include <Windows.h>
#include <windowsx.h>
#endif

#pragma comment(lib, "user32.lib")
#pragma comment(lib,"dwmapi.lib")

CFrameLessWidgetBase::CFrameLessWidgetBase(QWidget* parent)
{
    setWindowFlags(Qt::FramelessWindowHint | Qt::WindowMinMaxButtonsHint);
    setAttribute(Qt::WA_Hover);
}

CFrameLessWidgetBase::~CFrameLessWidgetBase()
{
}

bool CFrameLessWidgetBase::nativeEvent(const QByteArray& eventType, void* message, long* result)
{
    MSG* param = static_cast<MSG*>(message);

    switch (param->message)
    {
    case WM_NCHITTEST:
    {
        int nX = GET_X_LPARAM(param->lParam) - this->geometry().x();
        int nY = GET_Y_LPARAM(param->lParam) - this->geometry().y();

        // 如果鼠标位于内部子控件上,则不进行处理
        if (nX > mouse_margin && nX < width() - mouse_margin &&
            nY > mouse_margin && nY < this->height() - mouse_margin)
        {
            if (childAt(nX, nY) != nullptr)
                return QWidget::nativeEvent(eventType, message, result);
        }

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

        if ((nX > this->width() - mouse_margin) && (nX < this->width()))
            *result = HTRIGHT;

        if ((nY > 0) && (nY < mouse_margin))
            *result = HTTOP;

        if ((nY > this->height() - mouse_margin) && (nY < this->height()))
            *result = HTBOTTOM;

        if ((nX > 0) && (nX < mouse_margin) && (nY > 0)
            && (nY < mouse_margin))
            *result = HTTOPLEFT;

        if ((nX > this->width() - mouse_margin) && (nX < this->width())
            && (nY > 0) && (nY < mouse_margin))
            *result = HTTOPRIGHT;

        if ((nX > 0) && (nX < mouse_margin)
            && (nY > this->height() - mouse_margin) && (nY < this->height()))
            *result = HTBOTTOMLEFT;

        if ((nX > this->width() - mouse_margin) && (nX < this->width())
            && (nY > this->height() - mouse_margin) && (nY < this->height()))
            *result = HTBOTTOMRIGHT;

        return true;
    }
    }

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

CTitleBar.h

#ifndef CTITLEBAR_H
#define CTITLEBAR_H

#include <QWidget>
#include "ui_CTitleBar.h"

class CTitleBar : public QWidget
{
    Q_OBJECT

public:
    CTitleBar(QWidget *parent = Q_NULLPTR);
    ~CTitleBar();

protected:
    void paintEvent(QPaintEvent* event) override;
    void mousePressEvent(QMouseEvent* event) override;
    void mouseDoubleClickEvent(QMouseEvent* event) override;

signals:
    void sig_close();
    void sig_max(bool isMax);

private slots:
    void on_Clicked();

private:
    Ui::CTitleBar ui;
};

#endif // CTITLEBAR_H

CTitleBar.cpp

#include "CTitleBar.h"
#include <QMouseEvent>
#include <QStyleOption>
#include <QPushButton>
#include <QPainter>

#include <qt_windows.h>
#pragma comment(lib, "user32.lib")

CTitleBar::CTitleBar(QWidget *parent)
    : QWidget(parent)
{
    ui.setupUi(this);

    //禁止父窗口影响子窗口样式
    setAttribute(Qt::WA_StyledBackground);

    //左上,右上圆角10;左下,右下圆角0
    this->setStyleSheet("QWidget{background-color:rgb(54,54,54); \
        border-top-left-radius:10px; \
        border-top-right-radius:10px; \
        border-bottom-left-radius:0px; \
        border-bottom-right-radius:0px;}");

    ui.label_title->setText(u8"我是标题");
    ui.label_title->setStyleSheet("QLabel{font-family: Microsoft YaHei; \
        font-size:18px; \
        color:#BDC8E2;background-color:rgb(54,54,54);}");

    ui.btnSet->setFixedSize(32, 32);
    ui.btnSet->setText("");
    ui.btnSet->setFlat(true);
    ui.btnSet->setStyleSheet("QPushButton{background-image:url(:/titlebar/resource/titlebar/set.svg);border:none}" \
        "QPushButton:hover{" \
        "background-color:rgb(99, 99, 99);" \
        "background-image:url(:/titlebar/resource/titlebar/set_hover.svg);border:none;}");

    ui.btnMin->setFixedSize(32, 32);
    ui.btnMin->setText("");
    ui.btnMin->setFlat(true);
    ui.btnMin->setStyleSheet("QPushButton{background-image:url(:/titlebar/resource/titlebar/min.svg);border:none}" \
        "QPushButton:hover{" \
        "background-color:rgb(99, 99, 99);" \
        "background-image:url(:/titlebar/resource/titlebar/min_hover.svg);border:none;}");

    ui.btnMax->setFixedSize(35, 35);
    ui.btnMax->setFlat(true);
    ui.btnMax->setText("");
    ui.btnMax->setStyleSheet("QPushButton{background-image:url(:/titlebar/resource/titlebar/normal.svg);border:none; \
        background-position:center; \
        background-repeat:no-repeat;}  \
        QPushButton:hover{ \
        background-color:rgb(99, 99, 99); \
        background-image:url(:/titlebar/resource/titlebar/normal_hover.svg);border:none;}");

    ui.btnClose->setFixedSize(32, 32);
    ui.btnClose->setFlat(true);
    ui.btnClose->setText("");
    ui.btnClose->setStyleSheet("QPushButton{background-image:url(:/titlebar/resource/titlebar/close.svg);border:none}" \
        "QPushButton:hover{" \
        "background-color:rgb(99, 99, 99);" \
        "background-image:url(:/titlebar/resource/titlebar/close_hover.svg);border:none;}");

    connect(ui.btnMin, SIGNAL(clicked(bool)), this, SLOT(on_Clicked()));
    connect(ui.btnMax, SIGNAL(clicked(bool)), this, SLOT(on_Clicked()));
    connect(ui.btnClose, SIGNAL(clicked(bool)), this, SLOT(on_Clicked()));
}

CTitleBar::~CTitleBar()
{
}

void CTitleBar::paintEvent(QPaintEvent* event)
{
    //决定样式表是否起作用
    /*QStyleOption opt;
    opt.init(this);
    QPainter p(this);
    style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
    QWidget::paintEvent(event);*/
}

void CTitleBar::on_Clicked()
{
    QPushButton* pButton = qobject_cast<QPushButton*>(sender());

    QWidget* pWindow = this->window();

    if (pWindow->isTopLevel())
    {
        if (pButton == ui.btnMin)
        {
            pWindow->showMinimized();
        }
        else if (pButton == ui.btnMax)
        {
            if (pWindow->isMaximized())
            {
                pWindow->showNormal();
                ui.btnMax->setStyleSheet("QPushButton{background-image:url(:/titlebar/resource/titlebar/normal.svg);border:none;  \
                    background-position:center; \
                    background-repeat:no-repeat;}  \
                    QPushButton:hover{ \
                    background-color:rgb(99, 99, 99); \
                    background-image:url(:/titlebar/resource/titlebar/normal_hover.svg);border:none;}");

                emit sig_max(false);
            }
            else
            {
                pWindow->showMaximized();
                ui.btnMax->setStyleSheet("QPushButton{background-image:url(:/titlebar/resource/titlebar/max.svg);border:none;  \
                    background-position:center; \
                    background-repeat:no-repeat;}  \
                    QPushButton:hover{ \
                    background-color:rgb(99, 99, 99);  \
                    background-image:url(:/titlebar/resource/titlebar/max_hover.svg);border:none;}");

                emit sig_max(true);
            }
        }
        else if (pButton == ui.btnClose)
        {
            emit sig_close();
        }
    }
}

void CTitleBar::mousePressEvent(QMouseEvent* event)
{
    if (ReleaseCapture())
    {
        QWidget* pWindow = this->window();
        if (pWindow->isTopLevel())
        {
            SendMessage(HWND(pWindow->winId()), WM_SYSCOMMAND, SC_MOVE + HTCAPTION, 0);
        }
    }

    event->ignore();
}

void CTitleBar::mouseDoubleClickEvent(QMouseEvent* event)
{
    emit ui.btnMax->clicked();
}

CTopWidget.h

#ifndef CTOPWIDGET_H
#define CTOPWIDGET_H

#include <QWidget>

class CTopWidget : public QWidget
{
    Q_OBJECT

public:
    CTopWidget(QWidget* p = nullptr);
    ~CTopWidget();

signals:
    void sig_close();
    void sig_max(bool isMax);
};
#endif

CTopWidget.cpp

#include "CTopWidget.h"
#include "CTitleBar.h"
#include <QVBoxLayout>

CTopWidget::CTopWidget(QWidget* p)
    :QWidget(p)
{
    setAttribute(Qt::WA_StyledBackground);
    this->setStyleSheet("QWidget{background-color:rgb(255, 254, 253);border-radius:10px;}");
    /*this->setStyleSheet("QWidget{background-color:rgb(255, 254, 253); \
        border-bottom-left-radius:15px; \
        border-bottom-right-radius:15px;}");*/

    QVBoxLayout* pVLay = new QVBoxLayout(this);
    CTitleBar* pTitle = new CTitleBar(this);
    QWidget* pWidget = new QWidget(this);

    //需要指定pWidget最小尺寸,或最大尺寸
    pWidget->setMinimumSize(1200, 800);

    pVLay->addWidget(pTitle);
    pVLay->addWidget(pWidget);
    pVLay->setContentsMargins(0, 0, 0, 0);
    setLayout(pVLay);

    connect(pTitle, &CTitleBar::sig_close, this, &CTopWidget::sig_close);
    connect(pTitle, &CTitleBar::sig_max, this, &CTopWidget::sig_max);
}

CTopWidget::~CTopWidget()
{
}
MainWidget.h
#ifndef MAINWIDGET_H
#define MAINWIDGET_H

#include <QtWidgets/QWidget>
#include "CFrameLessWidgetBase.h"
#include "CTopWidget.h"
#include <QVBoxLayout>
#include <QGraphicsDropShadowEffect>

//派生于CFrameLessWidgetBase
class MainWidget : public CFrameLessWidgetBase
{
    Q_OBJECT

public:
    MainWidget(QWidget *parent = Q_NULLPTR);

private slots:
    void onClose();
    void onDoMax(bool isMax);

private:
    QVBoxLayout* m_pMainVLay = nullptr;
    CTopWidget* m_pTopWidget = nullptr;
    QGraphicsDropShadowEffect* m_pShadow = nullptr;
};
#endif

MainWidget.cpp

#include "MainWidget.h"


MainWidget::MainWidget(QWidget *parent)
    : CFrameLessWidgetBase(parent)
{
    //设置窗体透明
    this->setAttribute(Qt::WA_TranslucentBackground, true);

    m_pMainVLay = new QVBoxLayout(this);
    m_pTopWidget = new CTopWidget(this);
    m_pMainVLay->addWidget(m_pTopWidget);

    int shadow_width = 30;
    m_pMainVLay->setContentsMargins(shadow_width, shadow_width, shadow_width, shadow_width);
    setLayout(m_pMainVLay);

    //给顶层widget设置背景颜色,不然看不见,因为底层widget已经透明了
    m_pTopWidget->setStyleSheet("background-color:rgb(255, 254, 253)");
    m_pShadow = new QGraphicsDropShadowEffect(this);

    //设置阴影距离
    m_pShadow->setOffset(0, 0);

    //设置阴影颜色  686868
    m_pShadow->setColor(QColor("#0000FF"));

    //设置阴影区域
    m_pShadow->setBlurRadius(shadow_width - 5);

    //给顶层QWidget设置阴影
    m_pTopWidget->setGraphicsEffect(m_pShadow);

    connect(m_pTopWidget, &CTopWidget::sig_close, this, &MainWidget::onClose);
    connect(m_pTopWidget, &CTopWidget::sig_max, this, &MainWidget::onDoMax);
}

void MainWidget::onClose()
{
    close();
}

void MainWidget::onDoMax(bool isMax)
{
    int shadow_width = 25;
    if (isMax)
    {
        shadow_width = 0;
    }
    else
    {

    }

    m_pMainVLay->setContentsMargins(shadow_width, shadow_width, shadow_width, shadow_width);

    //设置阴影区域
    m_pShadow->setBlurRadius(shadow_width);

    //给顶层QWidget设置阴影
    m_pTopWidget->setGraphicsEffect(m_pShadow);
}
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
在Linux下,可以通过自定义QWidget来实现自定义标题栏。具体实现步骤如下: 1. 创建一个继承自QWidget的类,用于实现自定义标题栏。 ```cpp class MyTitleBar : public QWidget { Q_OBJECT public: explicit MyTitleBar(QWidget *parent = nullptr); protected: void mousePressEvent(QMouseEvent *event) override; void mouseMoveEvent(QMouseEvent *event) override; private: QPoint m_lastPos; }; ``` 2. 在构造函数中设置标题栏的大小、背景色和布局。 ```cpp MyTitleBar::MyTitleBar(QWidget *parent) : QWidget(parent) { setFixedHeight(30); setStyleSheet("background-color: #333333"); QHBoxLayout *layout = new QHBoxLayout(); layout->setContentsMargins(0, 0, 0, 0); layout->setSpacing(0); setLayout(layout); } ``` 3. 重写mousePressEvent和mouseMoveEvent函数,实现标题栏的拖动。 ```cpp void MyTitleBar::mousePressEvent(QMouseEvent *event) { if (event->button() == Qt::LeftButton) { m_lastPos = event->globalPos() - this->parentWidget()->geometry().topLeft(); } } void MyTitleBar::mouseMoveEvent(QMouseEvent *event) { if (event->buttons() & Qt::LeftButton) { QPoint pos = event->globalPos() - m_lastPos; this->parentWidget()->move(pos); } } ``` 4. 在主窗口中添加自定义标题栏,并将系统标题栏隐藏。 ```cpp MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { setWindowTitle("Custom Title Bar"); setWindowFlags(Qt::FramelessWindowHint | windowFlags()); MyTitleBar *titleBar = new MyTitleBar(this); setMenuWidget(titleBar); // 添加其他控件及布局 // ... } ``` 5. 编译运行程序,即可看到自定义标题栏
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

水火汪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值