Qt 无边框窗口、阴影、圆角 解决方案 QWidget 完全自定义边框样式

最终效果

白色背景

红色阴影

灰色标题栏

全透明

接口与功能预览

// 设置标题栏背景色
    void setTitleBarColor(const QColor &color);
    // 设置标题文字颜色
    void setTitleTextColor(const QColor &color);
    // 设置标题文字字体
    void setTitleTextFont(const QFont &font);
    // 设置标题
    void setTitleText(const QString &text);
    // 设置标题栏图标
    void setTitleIcon(const QString &path);
    // 设置标题栏图标
    void setTitleIcon(const QPixmap &icon);

    // 设置窗口颜色
    void setBackgroundColor(const QColor &color);

    // 设置边框圆角半径
    void setRadius(const uint &r);
    // 设置边框阴影颜色
    void setShadowColor(const QColor &color);
    // 设置边框阴影范围
    void setBlurRadius(const uint &r);

    // 设置隐藏标题栏最小化按扭
    void setHiddenMin(const bool &is);
    // 设置隐藏标题栏最大化按扭
    void setHiddenMax(const bool &is);
    // 设置隐藏标题栏
    void setHiddenTitleBar(const bool &is);
    // 设置标题栏高度
    void setTitleBarHeight(const uint &h);
    // 设置标题栏最小化图标
    void setMinIcon(const QIcon &icon);
    // 设置标题栏最大化图标
    void setMaxIcon(const QIcon &icon);
    // 设置标题栏关闭图标
    void setCloseIcon(const QIcon &icon);
    // 设置标题栏最小化按钮鼠标悬浮时背景色
    void setHoverColorMin(const QColor &color);
    // 设置标题栏最大化按钮鼠标悬浮时背景色
    void setHoverColorMax(const QColor &color);
    // 设置标题栏关闭按钮鼠标悬浮时背景色
    void setHoverColorClose(const QColor &color);
    // 设置标题栏最小化按钮鼠标按下时背景色
    void setPressedColorMin(const QColor &color);
    // 设置标题栏最大化按钮鼠标按下时背景色
    void setPressedColorMax(const QColor &color);
    // 设置标题栏关闭按钮鼠标按下时背景色
    void setPressedColorClose(const QColor &color);

    // 向标题后面添加控件
    void addWidgetToTitleBar(QWidget *w);
    // 向标题后面添加控件
    void addLayoutToTitleBar(QLayout *layout);

下载地址

github:https://github.com/yibobunengyuntian/FrameWgt

百度网盘:链接:https://pan.baidu.com/s/1dw4DUNJCfD9Q6iHje-mLQA?pwd=1008 提取码:1008

前话

  qt窗口的默认边框样式都是根据操作系统固定的,在写项目的时候,总会自己隐藏边框然后重新实现关闭、最小化等相关功能; 在网上找一些无边框窗口的解决方案,大都不怎么如意,且不太适合复用,自由度低;所以 工作之余花了一些时间自己实现了一个高复用且自由度高的方案;当然,此方案也并不完美。

        完全自定义解决方案; QWidget 自定义边框、标题栏样式;自定义最小化、最大化、关闭图标及样式;可移动、拉伸、缩放;自定义窗口阴影、圆角且不影响窗口最大化。

项目分析

        阴影的实现开始用的QGraphicsDropShadowEffect方案, 结果发现这个方案在某些情况下cpu占用较大, 性能下降严重(特别表现在程序使用QGraphics的时候);所以采取了重写paintEvent自己绘制阴影效果。

使用方式

pro文件引入

include($$PWD/FrameWgt/FrameWgt.pri)
#include "framewgt.h" // 头文件引入

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QWidget *w = new QWidget; // 假设此窗口为你的窗口

    int r = 10; // 圆角半径
    // 你的窗口需要有背景色和你需要的圆角,否则会背景透明
    w->setStyleSheet(QString("QWidget{background-color: white;border-bottom-left-radius: %1px;border-bottom-right-radius: %1px;}").arg(r));
    FrameWgt *pFrameWgt = new FrameWgt(w); // 将你的窗口加入边框
    pFrameWgt->setTitleIcon("D:/test/Ui_test/res/widget.png"); //设置标题栏图标
    pFrameWgt->setTitleText("无边框窗口"); // 设置标题文字
    pFrameWgt->setShadowColor(QColor(255, 0, 0, 100)); // 设置阴影颜色
    pFrameWgt->setBlurRadius(10); // 设置阴影范围
    pFrameWgt->setRadius(r); // 设置边框圆角
    pFrameWgt->setBackgroundColor(Qt::white); // 设置背景色
    // QPushButton *btn = new QPushButton("自定义按钮");
    // pFrameWgt->addWidgetToTitleBar(btn);

    //其他设置...

    pFrameWgt->show();
    return a.exec();
}

主要代码      

framewgt.h

此文件就是所提供的全部接口

#ifndef FRAMEWGT_H
#define FRAMEWGT_H

#include <QWidget>
#include <QGridLayout>
#include <QApplication>
#include <QPainter>
#include <QPainterPath>

#include "titlebar.h"

class FrameWgt : public QWidget
{
    Q_OBJECT
public:
    explicit FrameWgt(QWidget *centerWidget);
    ~FrameWgt();

    // 设置标题栏背景色
    void setTitleBarColor(const QColor &color);
    // 设置标题文字颜色
    void setTitleTextColor(const QColor &color);
    // 设置标题文字字体
    void setTitleTextFont(const QFont &font);
    // 设置标题
    void setTitleText(const QString &text);
    // 设置标题栏图标
    void setTitleIcon(const QString &path);
    // 设置标题栏图标
    void setTitleIcon(const QPixmap &icon);

    // 设置窗口颜色
    void setBackgroundColor(const QColor &color);

    // 设置边框圆角半径
    void setRadius(const uint &r);
    // 设置边框阴影颜色
    void setShadowColor(const QColor &color);
    // 设置边框阴影范围
    void setBlurRadius(const uint &r);

    // 设置隐藏标题栏最小化按扭
    void setHiddenMin(const bool &is);
    // 设置隐藏标题栏最大化按扭
    void setHiddenMax(const bool &is);
    // 设置隐藏标题栏
    void setHiddenTitleBar(const bool &is);
    // 设置标题栏高度
    void setTitleBarHeight(const uint &h);
    // 设置标题栏最小化图标
    void setMinIcon(const QIcon &icon);
    // 设置标题栏最大化图标
    void setMaxIcon(const QIcon &icon);
    // 设置标题栏关闭图标
    void setCloseIcon(const QIcon &icon);
    // 设置标题栏最小化按钮鼠标悬浮时背景色
    void setHoverColorMin(const QColor &color);
    // 设置标题栏最大化按钮鼠标悬浮时背景色
    void setHoverColorMax(const QColor &color);
    // 设置标题栏关闭按钮鼠标悬浮时背景色
    void setHoverColorClose(const QColor &color);
    // 设置标题栏最小化按钮鼠标按下时背景色
    void setPressedColorMin(const QColor &color);
    // 设置标题栏最大化按钮鼠标按下时背景色
    void setPressedColorMax(const QColor &color);
    // 设置标题栏关闭按钮鼠标按下时背景色
    void setPressedColorClose(const QColor &color);

    // 向标题后面添加控件
    void addWidgetToTitleBar(QWidget *w);
    // 向标题后面添加控件
    void addLayoutToTitleBar(QLayout *layout);

protected:
    enum OpFlag
    {
        NONE = -1,
        Top,
        Bottom,
        Left,
        Right,
        TL,
        TR,
        BL,
        BR
    };
    void initialize();
    void calculateOpflag(QPoint pos);
    void updateRadius(const uint &r);

    void mousePressEvent(QMouseEvent *event) override;
    void mouseMoveEvent(QMouseEvent *event) override;
    void mouseReleaseEvent(QMouseEvent *event) override;

    void paintEvent(QPaintEvent *event) override;
    void changeEvent(QEvent *event) override;

private:
    QWidget *m_pCenter_widget = nullptr; // 用户所提供的中间窗口
    QWidget *m_pBorder = nullptr; // 模拟边框的窗口
    TitleBar *m_pTitleBar = nullptr; // 标题栏
    QGridLayout *m_pGridLayout = nullptr;

    int m_opFlag = OpFlag::NONE; // 窗口移动、拉伸控制
    bool m_isOp = false; // 是否能移动、拉伸
    QPointF m_lastPos; // 上一次的鼠标位置
    QColor m_shadowColor = QColor(91, 91, 91, 90); // 边框阴影颜色
    int m_blurRadius = 10; // 边框阴影范围
    int m_radius = 10; // 边框圆角半径
    QColor m_bgColor = QColor(0, 0, 0, 0);
};

#endif // FRAMEWGT_H

framewgt.cpp

#include "framewgt.h"

FrameWgt::FrameWgt(QWidget *centerWidget)
{
    m_pCenter_widget = centerWidget;
    initialize();
}

FrameWgt::~FrameWgt()
{

}

void FrameWgt::setTitleBarColor(const QColor &color)
{
    m_pTitleBar->setBackgroundColor(color);
}

void FrameWgt::setTitleTextColor(const QColor &color)
{
    m_pTitleBar->setTextColor(color);
}

void FrameWgt::setTitleText(const QString &text)
{
    m_pTitleBar->setTitleText(text);
}

void FrameWgt::setTitleIcon(const QString &path)
{
    m_pTitleBar->setTitleIcon(path);
}

void FrameWgt::setTitleIcon(const QPixmap &icon)
{
    m_pTitleBar->setTitleIcon(icon);
}

void FrameWgt::setBackgroundColor(const QColor &color)
{
    m_bgColor = color;
    if(this->isMaximized() || this->isFullScreen())
    {
        updateRadius(0);
    }
    else
    {
        updateRadius(m_radius);
    }
}

void FrameWgt::setRadius(const uint &r)
{
    m_radius = r;
    updateRadius(m_radius);
}

void FrameWgt::setShadowColor(const QColor &color)
{
    m_shadowColor = color;
    update();
}

void FrameWgt::setBlurRadius(const uint &r)
{
    m_blurRadius = r;
    if(this->isMaximized() || this->isFullScreen())
    {
        m_pGridLayout->setContentsMargins(0, 0, 0, 0);
        return;
    }
    else
    {
        // 给出阴影绘制区域
        m_pGridLayout->setContentsMargins(m_blurRadius, m_blurRadius, m_blurRadius, m_blurRadius);
    }
}

void FrameWgt::setHiddenMin(const bool &is)
{
    m_pTitleBar->setHiddenMin(is);
}

void FrameWgt::setHiddenMax(const bool &is)
{
    m_pTitleBar->setHiddenMax(is);
}

void FrameWgt::setHiddenTitleBar(const bool &is)
{
    m_pTitleBar->setHidden(is);
    updateRadius(m_radius);
}

void FrameWgt::setTitleBarHeight(const uint &h)
{
    m_pTitleBar->setHeight(h);
}

void FrameWgt::setMinIcon(const QIcon &icon)
{
    m_pTitleBar->setMinIcon(icon);
}

void FrameWgt::setMaxIcon(const QIcon &icon)
{
    m_pTitleBar->setMaxIcon(icon);
}

void FrameWgt::setCloseIcon(const QIcon &icon)
{
    m_pTitleBar->setCloseIcon(icon);
}

void FrameWgt::setTitleTextFont(const QFont &font)
{
    m_pTitleBar->setTitleTextFont(font);
}

void FrameWgt::addWidgetToTitleBar(QWidget *w)
{
    m_pTitleBar->addWidget(w);
}

void FrameWgt::addLayoutToTitleBar(QLayout *layout)
{
    m_pTitleBar->addLayout(layout);
}

void FrameWgt::initialize()
{
    this->setWindowFlags(Qt::FramelessWindowHint | windowFlags()); // 隐藏默认边框
    this->setAttribute(Qt::WA_TranslucentBackground, true); // 设置透明背景
    setMouseTracking(true); // 启用鼠标追踪
    this->resize(400, 300);
    m_pBorder = new QWidget;
    m_pBorder->setCursor(Qt::ArrowCursor);
    m_pTitleBar = new TitleBar(m_pBorder);
    QVBoxLayout *pCenterLayout = new QVBoxLayout;
    pCenterLayout->setContentsMargins(0, 0,0 ,0);
    pCenterLayout->setSpacing(0);
    pCenterLayout->setAlignment(Qt::AlignTop);
    pCenterLayout->addWidget(m_pTitleBar);

    if(m_pCenter_widget){
        this->setTitleText(m_pCenter_widget->windowTitle());
        this->setTitleIcon(m_pCenter_widget->windowIcon().pixmap(64, 64));
        pCenterLayout->addWidget(m_pCenter_widget);
        pCenterLayout->setStretch(1, 1);
        m_pBorder->setCursor(m_pCenter_widget->cursor());
    }
    m_pBorder->setLayout(pCenterLayout);
    m_pGridLayout = new QGridLayout;
    m_pGridLayout->setContentsMargins(m_blurRadius, m_blurRadius, m_blurRadius, m_blurRadius); // 给边框阴影预留位置
    m_pGridLayout->addWidget(m_pBorder);
    this->setLayout(m_pGridLayout);

    setTitleBarColor(Qt::white);
    setRadius(m_radius);

    connect(m_pTitleBar, SIGNAL(closed()), this, SLOT(close()));
}

void FrameWgt::calculateOpflag(QPoint pos)
{
    QCursor cursor = Qt::ArrowCursor;
    m_opFlag = NONE;
    if(pos.x() < m_blurRadius)
    {
        if(pos.y() < m_blurRadius + m_radius)
        {
            m_opFlag = OpFlag::TL;
            cursor = Qt::SizeFDiagCursor;
        }
        else if(pos.y() > this->height() - m_blurRadius - m_radius)
        {
            m_opFlag = OpFlag::BL;
            cursor = Qt::SizeBDiagCursor;
        }
        else
        {
            m_opFlag = OpFlag::Left;
            cursor = Qt::SizeHorCursor;
        }
    }
    else if(pos.x() > this->width() - m_blurRadius)
    {
        if(pos.y() < m_blurRadius + m_radius)
        {
            m_opFlag = OpFlag::TR;
            cursor = Qt::SizeBDiagCursor;
        }
        else if(pos.y() > this->height() - m_blurRadius - m_radius)
        {
            m_opFlag = OpFlag::BR;
            cursor = Qt::SizeFDiagCursor;
        }
        else
        {
            m_opFlag = OpFlag::Right;
            cursor = Qt::SizeHorCursor;
        }
    }
    else
    {
        if(pos.y() < m_blurRadius)
        {
            m_opFlag = OpFlag::Top;
            cursor = Qt::SizeVerCursor;
        }
        else if(pos.y() > this->height() - m_blurRadius)
        {
            m_opFlag = OpFlag::Bottom;
            cursor = Qt::SizeVerCursor;
        }
        else
        {
            m_opFlag = OpFlag::NONE;
            cursor = Qt::ArrowCursor;
        }
    }
    this->setCursor(cursor);
}

void FrameWgt::updateRadius(const uint &r)
{
    m_pBorder->setObjectName("Border");
    QString cr = QString::number(m_bgColor.red());   // 获取红色分量
    QString cg = QString::number(m_bgColor.green()); // 获取绿色分量
    QString cb = QString::number(m_bgColor.blue());  // 获取蓝色分量
    QString ca = QString::number(m_bgColor.alpha()); // 获取透明度分量
    m_pBorder->setStyleSheet(QString("QWidget#Border{border-radius: %1px;background-color: rgba(%2, %3, %4, %5);}").arg(QString::number(r), cr, cg, cb, ca));

    m_pTitleBar->setRadius(r);
    update();
}

void FrameWgt::mousePressEvent(QMouseEvent *event)
{
    QPoint pos = event->pos();
    if(m_pBorder->geometry().contains(pos))
    {
        event->ignore();
        return;
    }
    if(m_opFlag != -1)
    {
        m_isOp = true;
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
        m_lastPos = event->globalPos();
#else
        m_lastPos = event->globalPosition();
#endif
    }
}

void FrameWgt::mouseMoveEvent(QMouseEvent *event)
{
    QPoint pos = event->pos();
    if(!m_isOp)
    {
        calculateOpflag(pos);
    }

    //获取鼠标拖动的位置,
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
    QPointF currPos = event->globalPos();
#else
    QPointF currPos = event->globalPosition();
#endif
    // 计算鼠标按下并拖动的的位移
    QPointF ptemp = currPos - m_lastPos ;
    //保存当前鼠标拖动的位置,用于下一次计算拖动位移
    m_lastPos = currPos;
    event->ignore();
    if(m_isOp)
    {
        if(this->isMaximized()) return;
        int x = 0,y = 0,w = 0,h = 0;
        // 根据拖动的那一条边框,确定拉伸还是缩小窗口。
        switch (m_opFlag) {
        // 左边框被拉伸
        case Left:
            x = this->pos().x() + ptemp.x();
            y = this->pos().y();
            w = this->size().width() - ptemp.x();
            h = this->size().height();
            if(w < this->minimumWidth())
            {
                m_lastPos.setX(this->pos().x());
            }
            break;
            // 右边框被拉伸
        case Right:
            x = this->pos().x();
            y = this->pos().y();
            w = this->size().width() + ptemp.x();
            h = this->size().height();
            if(w < this->minimumWidth())
            {
                m_lastPos.setX(this->pos().x() + this->width());
            }
            break;
            // 上边框被拉伸
        case Top:
            x = this->pos().x();
            y = this->pos().y() + ptemp.y();
            w = this->size().width() ;
            h = this->size().height() - ptemp.y();
            if(h < this->minimumHeight())
            {
                m_lastPos.setY(this->pos().y());
            }
            break;
            // 下边框被拉伸
        case Bottom:
            x = this->pos().x();
            y = this->pos().y();
            w = this->size().width() ;
            h = this->size().height() + ptemp.y();
            if(h < this->minimumHeight())
            {
                m_lastPos.setY(this->pos().y() + this->height());
            }
            break;
            //右上角被拉伸
        case TR:
            x = this->pos().x();
            y = this->pos().y() + ptemp.y();
            w = this->size().width() + ptemp.x() ;
            h = this->size().height() - ptemp.y();
            if(w < this->minimumWidth())
            {
                m_lastPos.setX(this->pos().x() + this->width());
            }
            if(h < this->minimumHeight())
            {
                m_lastPos.setY(this->pos().y());
            }
            break;
            //左上角被拉伸
        case TL:
            x = this->pos().x() + ptemp.x();
            y = this->pos().y() + ptemp.y();
            w = this->size().width() - ptemp.x() ;
            h = this->size().height() - ptemp.y();
            if(w < this->minimumWidth())
            {
                m_lastPos.setX(this->pos().x());
            }
            if(h < this->minimumHeight())
            {
                m_lastPos.setY(this->pos().y());
            }
            break;
            // 右下角被拉伸
        case BR:
            x = this->pos().x();
            y = this->pos().y();
            w = this->size().width() + ptemp.x() ;
            h = this->size().height() + ptemp.y();
            if(w < this->minimumWidth())
            {
                m_lastPos.setX(this->pos().x() + this->width());
            }
            if(h < this->minimumHeight())
            {
                m_lastPos.setY(this->pos().y() + this->height());
            }
            break;
            // 左下角被拉伸
        case BL:
            x = this->pos().x() + ptemp.x();
            y = this->pos().y();
            w = this->size().width() - ptemp.x() ;
            h = this->size().height() + ptemp.y();
            if(w < this->minimumWidth())
            {
                m_lastPos.setX(this->pos().x());
            }
            if(h < this->minimumHeight())
            {
                m_lastPos.setY(this->pos().y() + this->height());
            }
            break;
        default:
            return;
        }
        // 改变窗口的大小和位置。
        if(w < this->minimumWidth())
        {
            x = this->pos().x();
            w = this->size().width();
        }
        if(h < this->minimumHeight())
        {
            y = this->pos().y();
            h = this->size().height();
        }
        this->setGeometry(x,y,w,h);
    }
}

void FrameWgt::mouseReleaseEvent(QMouseEvent *event)
{
    Q_UNUSED(event)
    m_isOp = false;
}

void FrameWgt::paintEvent(QPaintEvent *event)
{
    if(this->isMaximized() || this->isFullScreen())
    {
        m_pGridLayout->setContentsMargins(0, 0, 0, 0);
        return;
    }
    else
    {
        // 给出阴影绘制区域
        m_pGridLayout->setContentsMargins(m_blurRadius, m_blurRadius, m_blurRadius, m_blurRadius);
    }
    int radius = m_blurRadius + m_radius;
    int width = this->width();
    int height = this->height();
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing, true); //抗锯齿
    painter.setPen(Qt::NoPen);

    QPainterPath rectPath;
    rectPath.addRect(this->rect());
    QRect borderRect(m_blurRadius, m_blurRadius, m_pBorder->width(), m_pBorder->height());
    // 创建圆角矩形的路径
    QPainterPath clipPath;
    clipPath.addRoundedRect(borderRect, m_radius, m_radius);
    // 限制绘制范围
    painter.setClipPath(rectPath - clipPath);

    //线性渐变
    QLinearGradient linearGradient;
    linearGradient.setColorAt(0, m_shadowColor);
    linearGradient.setColorAt(m_radius*1.0/radius, m_shadowColor);
    QColor color = m_shadowColor;
    color.setAlpha(0);
    linearGradient.setColorAt(1, color);

    //圆形渐变
    QRadialGradient radialGradient;
    radialGradient.setColorAt(0, m_shadowColor);
    radialGradient.setColorAt(m_radius*1.0/radius, m_shadowColor);
    radialGradient.setColorAt(1, color);

    //左上角
    radialGradient.setCenter(radius, radius); //中心点
    radialGradient.setRadius(radius); //半径
    radialGradient.setFocalPoint(radius, radius); //焦点
    painter.setBrush(radialGradient);
    QRectF rectf(0, 0, radius*2, radius*2);
    QPainterPath path;
    path.moveTo(radius, radius);//移动圆心
    path.arcTo(rectf, 90, 90);
    painter.drawPath(path);   //画路径(扇形)

    //左边
    linearGradient.setStart(radius, height/2);
    linearGradient.setFinalStop(0, height/2);
    painter.setBrush(linearGradient);
    path.clear();
    path.addRect(0, radius, radius, height - radius*2);
    painter.drawPath(path);

    //左下角
    radialGradient.setCenter(radius, height - radius); //中心点
    radialGradient.setRadius(radius); //半径
    radialGradient.setFocalPoint(radius, height - radius); //焦点
    painter.setBrush(radialGradient);
    path.clear();
    path.moveTo(radius, height - radius);//移动圆心
    rectf.setRect(0, height - radius*2, radius*2, radius*2);
    path.arcTo(rectf, 180, 90);
    painter.drawPath(path);   //画路径(扇形)

    //下边
    linearGradient.setStart(width/2, height - radius);
    linearGradient.setFinalStop(width/2, height);
    painter.setBrush(linearGradient);
    path.clear();
    path.addRect(radius, height - radius, width - radius*2, radius);
    painter.drawPath(path);

    //右下角
    radialGradient.setCenter(width - radius, height - radius); //中心点
    radialGradient.setRadius(radius); //半径
    radialGradient.setFocalPoint(width - radius, height - radius); //焦点
    painter.setBrush(radialGradient);
    path.clear();
    path.moveTo(width - radius, height - radius);//移动圆心
    rectf.setRect(width - radius*2, height - radius*2, radius*2, radius*2);
    path.arcTo(rectf, 270, 90);
    painter.drawPath(path);   //画路径(扇形)

    //右边
    linearGradient.setStart(width - radius, height/2);
    linearGradient.setFinalStop(width, height/2);
    painter.setBrush(linearGradient);
    path.clear();
    path.addRect(width - radius, radius, radius, height - radius*2);
    painter.drawPath(path);

    //右上角
    radialGradient.setCenter(width - radius, radius); //中心点
    radialGradient.setRadius(radius); //半径
    radialGradient.setFocalPoint(width - radius, radius); //焦点
    painter.setBrush(radialGradient);
    path.clear();
    path.moveTo(width - radius, radius);//移动圆心
    rectf.setRect(width - radius*2, 0, radius*2, radius*2);
    path.arcTo(rectf, 0, 90);
    painter.drawPath(path);   //画路径(扇形)

    //上边
    linearGradient.setStart(height/2, radius);
    linearGradient.setFinalStop(height/2, 0);
    painter.setBrush(linearGradient);
    path.clear();
    path.addRect(radius, 0, width - radius*2, radius);
    painter.drawPath(path);
}

void FrameWgt::changeEvent(QEvent *event)
{
    if (event->type() == QEvent::WindowStateChange) {
        QWindowStateChangeEvent *stateChangeEvent = static_cast<QWindowStateChangeEvent *>(event);
        Qt::WindowStates oldState = stateChangeEvent->oldState();
        Qt::WindowStates newState = windowState();
        if (newState == Qt::WindowMaximized || newState == Qt::WindowFullScreen) {
            // 窗口被最大化
            updateRadius(0);
        } else if (newState == Qt::WindowMinimized) {
            // 窗口被最小化
            updateRadius(m_radius);
        } else if(oldState == Qt::WindowMaximized || oldState == Qt::WindowMinimized || oldState == Qt::WindowFullScreen)
        {
            // 窗口被还原
            updateRadius(m_radius);
        }
    }
    QWidget::changeEvent(event);
}

titlebar.h

此为标题栏(看上面那个文件的接口注释就行)

#ifndef TITLEBAR_H
#define TITLEBAR_H

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

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

    void setTitleText(const QString &text);
    void setTitleIcon(const QString &path);
    void setTitleIcon(const QPixmap &icon);
    void setBackgroundColor(const QColor &color);
    void setTextColor(const QColor &color);
    void setHeight(const uint &h);
    void setMinIcon(const QIcon &icon);
    void setMaxIcon(const QIcon &icon);
    void setCloseIcon(const QIcon &icon);
    void setTitleTextFont(const QFont &font);
    void setHiddenMin(const bool &is);
    void setHiddenMax(const bool &is);
    void addWidget(QWidget *w);
    void addLayout(QLayout *layout);
    void setHoverColorMin(const QColor &color);
    void setHoverColorMax(const QColor &color);
    void setHoverColorClose(const QColor &color);
    void setPressedColorMin(const QColor &color);
    void setPressedColorMax(const QColor &color);
    void setPressedColorClose(const QColor &color);

    void showFull(const bool &isFull);
    void setMoveEnable(const bool &moveEnable);
    void setRadius(const uint &radius);
public slots:
    void onMax();
    void onMin();
signals:
    void closed();
    void movePos(QPointF movePoint);
    void maxChange(bool is);
protected:
    void initialize();
    void updateStyle();

    void moveTopParent(QWidget *pWgt, QPoint movePoint);

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

private:
    QHBoxLayout *m_pMainLayout = nullptr;
    QHBoxLayout *m_pCustomLayout = nullptr;
    QPushButton *m_pBtnClose = nullptr;
    QPushButton *m_pBtnMax = nullptr;
    QPushButton *m_pBtnMin = nullptr;
    QLabel *m_pLabIcon = nullptr;
    QLabel *m_pLabTitle = nullptr;

    bool m_isPressed = false;
    bool m_moveEnable = true;
    QPointF m_alof_win_mousePos = QPoint(0,0);
    uint m_height = 30;
    QColor m_bgColor = Qt::white;
    QColor m_textColor = Qt::black;
    uint m_radius = 10;
    QColor m_minHoverColor = QColor(240, 240, 240);
    QColor m_maxHoverColor = QColor(240, 240, 240);
    QColor m_closeHoverColor = QColor(255, 123, 123);
    QColor m_minPressedColor = QColor(220, 220, 220);
    QColor m_maxPressedColor = QColor(220, 220, 220);
    QColor m_closePressedColor = QColor(255, 103, 103);
};

#endif // TITLEBAR_H

titlebar.cpp

#include "titlebar.h"

TitleBar::TitleBar(QWidget *parent) : QWidget(parent)
{
    initialize();
}

TitleBar::~TitleBar()
{

}

void TitleBar::setTitleText(const QString &text)
{
    m_pLabTitle->setText(text);
    this->window()->setWindowTitle(text);
}

void TitleBar::setTitleIcon(const QString &path)
{
    QPixmap pix(path);
    this->setTitleIcon(pix);
}

void TitleBar::setTitleIcon(const QPixmap &icon)
{
    if(icon.isNull()){
        return;
    }
    m_pLabIcon->setHidden(false);
    QPixmap pix = icon.scaledToHeight(m_height - 10, Qt::SmoothTransformation);
    m_pLabIcon->setPixmap(pix);
    this->window()->setWindowIcon(pix);
}

void TitleBar::setBackgroundColor(const QColor &color)
{
    m_bgColor = color;
    updateStyle();
}

void TitleBar::setTextColor(const QColor &color)
{
    m_textColor = color;
    updateStyle();
}

void TitleBar::setHeight(const uint &h)
{
    m_height = h;
    updateStyle();
}

void TitleBar::setMinIcon(const QIcon &icon)
{
    m_pBtnMin->setIcon(icon);
}

void TitleBar::setMaxIcon(const QIcon &icon)
{
    m_pBtnMax->setIcon(icon);
}

void TitleBar::setCloseIcon(const QIcon &icon)
{
    m_pBtnClose->setIcon(icon);
}

void TitleBar::setTitleTextFont(const QFont &font)
{
    m_pLabTitle->setFont(font);
}

void TitleBar::setHiddenMin(const bool &is)
{
    m_pBtnMin->setHidden(is);
}

void TitleBar::setHiddenMax(const bool &is)
{
    m_pBtnMax->setHidden(is);
}

void TitleBar::addWidget(QWidget *w)
{
    m_pCustomLayout->addWidget(w);
}

void TitleBar::addLayout(QLayout *layout)
{
    m_pCustomLayout->addLayout(layout);
}

void TitleBar::setHoverColorMin(const QColor &color)
{
    m_minHoverColor = color;
    updateStyle();
}

void TitleBar::setHoverColorMax(const QColor &color)
{
    m_maxHoverColor = color;
    updateStyle();
}

void TitleBar::setHoverColorClose(const QColor &color)
{
    m_closeHoverColor = color;
    updateStyle();
}

void TitleBar::setPressedColorMin(const QColor &color)
{
    m_minPressedColor = color;
    updateStyle();
}

void TitleBar::setPressedColorMax(const QColor &color)
{
    m_maxPressedColor = color;
    updateStyle();
}

void TitleBar::setPressedColorClose(const QColor &color)
{
    m_closePressedColor = color;
    updateStyle();
}

void TitleBar::showFull(const bool &isFull)
{
    QWidget *p = window();
    if(isFull){
        p->showFullScreen();
    }else{
        p->showMaximized();
    }
}

void TitleBar::setMoveEnable(const bool &moveEnable)
{
    m_moveEnable = moveEnable;
}

void TitleBar::setRadius(const uint &radius)
{
    m_radius = radius;
    updateStyle();
}

void TitleBar::onMax()
{
    QWidget *p = this->window();
    if(p->isMaximized()){
        p->showNormal();
        emit maxChange(false);
    }else{
        p->showMaximized();
        emit maxChange(true);
    }
}

void TitleBar::onMin()
{
    this->window()->showMinimized();
}

void TitleBar::initialize()
{
    this->setFocusPolicy(Qt::NoFocus);
    this->setFixedHeight(m_height);

    m_pBtnClose = new QPushButton(this);
    m_pBtnMax = new QPushButton(this);
    m_pBtnMin = new QPushButton(this);
    m_pLabIcon = new QLabel(this);
    m_pLabTitle = new QLabel(this);

    m_pBtnClose->setFixedSize(m_height * 2, m_height);
    m_pBtnMin->setFixedSize(m_height * 2, m_height);
    m_pBtnMax->setFixedSize(m_height * 2, m_height);

    m_pLabTitle->setAlignment(Qt::AlignVCenter | Qt::AlignHCenter);
    m_pLabTitle->setContentsMargins(5, 0, 0, 0);
    m_pLabIcon->setScaledContents(true);
    m_pLabIcon->setHidden(true);
    m_pLabIcon->setFixedHeight(m_height - 10);
    m_pBtnClose->setIcon(QIcon(":/res/close.png"));
    m_pBtnMin->setIcon(QIcon(":/res/min.png"));
    m_pBtnMax->setIcon(QIcon(":/res/max.png"));

    this->setAttribute(Qt::WA_StyledBackground);

    m_pMainLayout = new QHBoxLayout;
    m_pCustomLayout = new QHBoxLayout;
    m_pMainLayout->addWidget(m_pLabIcon);
    m_pMainLayout->addWidget(m_pLabTitle);
    m_pMainLayout->addLayout(m_pCustomLayout);
    m_pMainLayout->addStretch();
    m_pMainLayout->addWidget(m_pBtnMin);
    m_pMainLayout->addWidget(m_pBtnMax);
    m_pMainLayout->addWidget(m_pBtnClose);
    m_pMainLayout->setContentsMargins(10, 0, 0, 0);
    m_pMainLayout->setSpacing(2);
    m_pCustomLayout->setContentsMargins(0, 0, 0, 0);
    m_pCustomLayout->setSpacing(0);

    this->setLayout(m_pMainLayout);

    m_pBtnClose->setFocusPolicy(Qt::NoFocus);
    m_pBtnMin->setFocusPolicy(Qt::NoFocus);
    m_pBtnMax->setFocusPolicy(Qt::NoFocus);
    connect(m_pBtnMin,SIGNAL(clicked(bool)),this,SLOT(onMin()));
    connect(m_pBtnMax,SIGNAL(clicked(bool)),this,SLOT(onMax()));
    connect(m_pBtnClose,SIGNAL(clicked(bool)),this,SIGNAL(closed()));
}

void TitleBar::updateStyle()
{
    QString styleMin = "QPushButton{border: 0px;}QPushButton:hover{background-color: hoverColor;border-radius: 0px;}QPushButton:pressed{background-color: pressedColor;border-radius: 0px;}";
    QString styleMax = "QPushButton{border: 0px;}QPushButton:hover{background-color: hoverColor;border-radius: 0px;}QPushButton:pressed{background-color: pressedColor;border-radius: 0px;}";
    QString styleClose = "QPushButton{border: 0px;}QPushButton:hover{background-color: hoverColor;borderRadius}QPushButton:pressed{background-color: pressedColor;borderRadius}";

    this->setObjectName("titleBar");
    QString cr = QString::number(m_bgColor.red());   // 获取红色分量
    QString cg = QString::number(m_bgColor.green()); // 获取绿色分量
    QString cb = QString::number(m_bgColor.blue());  // 获取蓝色分量
    QString ca = QString::number(m_bgColor.alpha()); // 获取透明度分量
    this->setStyleSheet(QString("QWidget#titleBar{border-top-left-radius: %1px;"
                                "border-top-right-radius: %1px;"
                                "border-bottom-left-radius: 0px;"
                                "border-bottom-right-radius: 0px;"
                                "background-color: rgba(%2, %3, %4, %5);}").arg(QString::number(m_radius), cr, cg, cb, ca));
    m_pLabTitle->setStyleSheet(QString("color: %1;").arg(m_textColor.name()));

    QString borderRadius = QString("border-top-left-radius: 0px;border-top-right-radius: %1px;").arg(QString::number(m_radius));
    m_pBtnClose->setStyleSheet(styleClose.replace("hoverColor", m_closeHoverColor.name()).replace("pressedColor", m_closePressedColor.name()).replace("borderRadius", borderRadius));
    m_pBtnMax->setStyleSheet(styleMax.replace("hoverColor", m_maxHoverColor.name()).replace("pressedColor", m_maxPressedColor.name()));
    m_pBtnMin->setStyleSheet(styleMin.replace("hoverColor", m_minHoverColor.name()).replace("pressedColor", m_minPressedColor.name()));
    m_pLabIcon->setStyleSheet(QString("border-top-left-radius: %1px;").arg(QString::number(m_radius)));
    m_pLabIcon->setAttribute(Qt::WA_TranslucentBackground, true);
    m_pLabTitle->setAttribute(Qt::WA_TranslucentBackground, true);
    this->setFixedHeight(m_height);
    m_pBtnClose->setFixedSize(m_height * 2, m_height);
    m_pBtnMin->setFixedSize(m_height * 2, m_height);
    m_pBtnMax->setFixedSize(m_height * 2, m_height);
    m_pLabIcon->setFixedHeight(m_height - 10);
}

void TitleBar::moveTopParent(QWidget *pWgt,QPoint movePoint)
{
    if(nullptr == pWgt){
        return;
    }

    if(pWgt->parentWidget()){
        moveTopParent(pWgt->parentWidget(),movePoint);
        return;
    }

    QPoint widgetPos = pWgt->pos();
    pWgt->move(widgetPos.x() + movePoint.x(), widgetPos.y() + movePoint.y());
}

void TitleBar::mousePressEvent(QMouseEvent *event)
{
    m_isPressed = true;
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
    m_alof_win_mousePos = event->globalPos();
#else
    m_alof_win_mousePos = event->globalPosition();
#endif
    QWidget::mousePressEvent(event);
}

void TitleBar::mouseMoveEvent(QMouseEvent *event)
{
    if(!m_moveEnable){
        return;
    }
    if (m_isPressed && !this->window()->isMaximized() && !this->window()->isFullScreen())
    {
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
        QPointF movePoint = event->globalPos() - m_alof_win_mousePos;
        m_alof_win_mousePos = event->globalPos();
#else
        QPointF movePoint = event->globalPosition() - m_alof_win_mousePos;
        m_alof_win_mousePos = event->globalPosition();
#endif
        this->window()->move(this->window()->pos() + movePoint.toPoint());
        emit movePos(movePoint);
    }
    QWidget::mouseMoveEvent(event);
}

void TitleBar::mouseReleaseEvent(QMouseEvent *event)
{
    m_isPressed = false;
    QWidget::mouseReleaseEvent(event);
}

void TitleBar::mouseDoubleClickEvent(QMouseEvent *event)
{
    if(!m_pBtnMax->isVisible() || event->button() != Qt::MouseButton::LeftButton){
        return;
    }
    onMax();
    QWidget::mouseDoubleClickEvent(event);
}

结语

        代码中已经敲了大致注释, 欢迎提问和反馈。

        国际站点:https://github.com/yibobunengyuntian

        QQ:2947467985

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值