Qt实战 无边框窗口的实现

本次分享,是基于Qt实现了无边框的窗口,并支持拖动缩放、最大化、最小化、关闭、双击全屏等。

实现无边框其实很简单,一行代码搞定。

setWindowFlag(Qt::FramelessWindowHint);

但是,隐藏了窗口的默认边框,标题栏没了,窗口无法进行拖动缩放了,最小化最大化关闭按钮也没有了,因此都需要自己实现。

为了实现无边框窗口到窗口背景的清晰过度,还需要给边框加border线或过度阴影。(本文不提供过度阴影,感兴趣的可以自己去研究,Qt有相关的类可实现)

如果将这个无边框的窗口应用到所有窗口,即如何将无边框窗口作为一个容器使用,也是本文需要解决的问题

首先给大家展示一下效果图。

Qt实战 无边框窗口的实现

无边框窗口类实现

SuperWindow.ui

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>SuperWindow</class>
 <widget class="QWidget" name="SuperWindow">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>800</width>
    <height>600</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>SuperWindow</string>
  </property>
  <property name="styleSheet">
   <string notr="true">QWidget#titleBar{
	background-color: rgb(150, 150, 150);
}

QPushButton[type=&quot;btn_win_ctrl&quot;]
{
	max-width: 20px;
	max-height: 20px;

	border:  1px solid rgba(100, 100, 100, 50);
}
QPushButton[type=&quot;btn_win_ctrl&quot;]:hover
{
	border:  1px solid blue;
}
QPushButton[type=&quot;btn_win_ctrl&quot;]:pressed
{
	border:  2px solid blue;
}
QPushButton[type=&quot;btn_win_ctrl&quot;]#btnMinimize
{
	background: center url(:/icons/minium.png) no-repeat;
}
QPushButton[type=&quot;btn_win_ctrl&quot;]#btnClose
{
	background: center url(:/icons/close.png) no-repeat;
}


QLabel#lbWinIcon
{
	max-width: 20px;
	max-height: 20px;
	min-width: 20px;
	min-height: 20px;
}</string>
  </property>
  <layout class="QVBoxLayout" name="verticalLayout">
   <property name="spacing">
    <number>2</number>
   </property>
   <property name="leftMargin">
    <number>3</number>
   </property>
   <property name="topMargin">
    <number>3</number>
   </property>
   <property name="rightMargin">
    <number>3</number>
   </property>
   <property name="bottomMargin">
    <number>3</number>
   </property>
   <item>
    <widget class="QWidget" name="titleBar" native="true">
     <property name="maximumSize">
      <size>
       <width>16777215</width>
       <height>30</height>
      </size>
     </property>
     <layout class="QHBoxLayout" name="horizontalLayout">
      <property name="spacing">
       <number>2</number>
      </property>
      <property name="leftMargin">
       <number>2</number>
      </property>
      <property name="topMargin">
       <number>2</number>
      </property>
      <property name="rightMargin">
       <number>2</number>
      </property>
      <property name="bottomMargin">
       <number>2</number>
      </property>
      <item>
       <widget class="QLabel" name="lbWinIcon">
        <property name="text">
         <string/>
        </property>
       </widget>
      </item>
      <item>
       <widget class="QLabel" name="lbTitle">
        <property name="text">
         <string>标题</string>
        </property>
       </widget>
      </item>
      <item>
       <spacer name="horizontalSpacer">
        <property name="orientation">
         <enum>Qt::Horizontal</enum>
        </property>
        <property name="sizeHint" stdset="0">
         <size>
          <width>40</width>
          <height>20</height>
         </size>
        </property>
       </spacer>
      </item>
      <item>
       <widget class="QPushButton" name="btnMinimize">
        <property name="text">
         <string/>
        </property>
        <property name="type" stdset="0">
         <string>btn_win_ctrl</string>
        </property>
       </widget>
      </item>
      <item>
       <widget class="QPushButton" name="btnMaxmize">
        <property name="text">
         <string/>
        </property>
        <property name="type" stdset="0">
         <string>btn_win_ctrl</string>
        </property>
        <property name="bMax" stdset="0">
         <bool>false</bool>
        </property>
       </widget>
      </item>
      <item>
       <widget class="QPushButton" name="btnClose">
        <property name="text">
         <string/>
        </property>
        <property name="type" stdset="0">
         <string>btn_win_ctrl</string>
        </property>
       </widget>
      </item>
     </layout>
    </widget>
   </item>
   <item>
    <widget class="QWidget" name="wgtContent" native="true">
     <layout class="QGridLayout" name="gridLayout_2">
      <property name="leftMargin">
       <number>0</number>
      </property>
      <property name="topMargin">
       <number>0</number>
      </property>
      <property name="rightMargin">
       <number>0</number>
      </property>
      <property name="bottomMargin">
       <number>0</number>
      </property>
      <item row="0" column="0">
       <layout class="QGridLayout" name="gridContent"/>
      </item>
     </layout>
    </widget>
   </item>
  </layout>
 </widget>
 <resources/>
 <connections/>
</ui>

SuperWindow.h

#pragma once

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

QT_BEGIN_NAMESPACE
namespace Ui { class SuperWindow; }
QT_END_NAMESPACE

class SuperWindow : public QWidget
{
    Q_OBJECT

public:
    SuperWindow(QWidget *parent = nullptr);
    ~SuperWindow();
    template<typename T>
    T *CentralWidget()
    {
        if (!m_centralWgt) return nullptr;
        return qobject_cast<T*>(m_centralWgt);
    }

    void SetTitle(QString title);

    template<typename T>
    static SuperWindow* Create(QWidget* parent = nullptr)
    {
        SuperWindow* fw = new SuperWindow(parent);
        T* centralWgt = new T(fw);
        fw->SetCentralWidget(centralWgt);
        return fw;
    }

protected:
    void mouseDoubleClickEvent(QMouseEvent* event);
    void mousePressEvent(QMouseEvent* event);
    void mouseMoveEvent(QMouseEvent* event);
    void mouseReleaseEvent(QMouseEvent* event);
    void paintEvent(QPaintEvent* e);
    void leaveEvent(QEvent* e);

private:
    void SetCentralWidget(QWidget *widget);

    //计算九宫格行
    int row(QPointF pos);
    //计算九宫格列
    int col(QPointF pos);
    //点击区域 相对于九宫格
    int moveArea(QPointF pos);
    void setMouseStyle(int moveArea);

private slots:
    void OnMaxmized();

private:
    Ui::SuperWindow *ui;
    QWidget* m_centralWgt{ nullptr };

    bool m_bPressed{ false };
    bool m_bResizing{false};
    int m_flag;
    QPoint curPos;
    int m_nBorder{ 3 };
};

SuperWindow.cpp

#include "SuperWindow.h"

#include <QMouseEvent>
#include <QPainter>

SuperWindow::SuperWindow(QWidget* parent)
    : QWidget(parent)
    , ui(new Ui::SuperWindow)
{
    ui->setupUi(this);
    setWindowFlag(Qt::FramelessWindowHint);
    setMouseTracking(true);

    ui->btnMaxmize->setIcon(QIcon(":/icons/maxsize.png"));


    for (auto obj : children()) {
        auto wgt = qobject_cast<QWidget*>(obj);
        if (wgt) wgt->setMouseTracking(true);
    }

    connect(ui->btnMinimize, &QPushButton::clicked, this, &SuperWindow::showMinimized);
    connect(ui->btnMaxmize, &QPushButton::clicked, this, &SuperWindow::OnMaxmized);
    connect(ui->btnClose, &QPushButton::clicked, this, &SuperWindow::close);
}

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

void SuperWindow::SetTitle(QString title)
{
    ui->lbTitle->setText(title);
    setWindowTitle(title);
}

void SuperWindow::SetCentralWidget(QWidget *widget)
{
    if (m_centralWgt) return;
    m_centralWgt = widget;
    ui->gridContent->addWidget(widget);
}

void SuperWindow::mouseDoubleClickEvent(QMouseEvent* event)
{
    if (event->button() == Qt::LeftButton)
    {
        if (ui->titleBar->geometry().contains(event->pos()))
        {
            OnMaxmized();
        }
    }
}

void SuperWindow::mousePressEvent(QMouseEvent* event)
{
    if (event->button() == Qt::LeftButton)
    {
        if (ui->titleBar->geometry().contains(event->pos()) &&
            this->cursor() == Qt::ArrowCursor)
        {
            m_bPressed = true;
            curPos = event->globalPosition().toPoint();
        }
        else if (m_flag != 22)
        {
            m_bResizing = true;
            curPos = event->globalPosition().toPoint();
        }
    }
}

void SuperWindow::mouseMoveEvent(QMouseEvent* event)
{
    if (m_bPressed) //如果鼠标左键按下
    {
        QPoint tmpPos = event->globalPosition().toPoint();
        QPoint delta = tmpPos - curPos;
        move(pos() + delta);
        curPos = tmpPos;
    }
    else
    {
        if (!m_bResizing)
        {
            m_flag = moveArea(event->pos());
            setMouseStyle(m_flag);
            return;
        }

        qDebug() << "resizing " << event->pos();

        QPoint tmpPos = event->globalPosition().toPoint();
        QPoint delta = tmpPos - curPos;
        curPos = tmpPos;

        //记录窗体当前位置
        QRect rect = geometry();
        //鼠标左键处于拖拽拉伸区域
        //m_flag为鼠标点击左键时  鼠标样式状态
        switch (m_flag)
        {
        case 11: rect.setTopLeft(rect.topLeft() + delta); break; //左上角
        case 13: rect.setTopRight(rect.topRight() + delta); break; //右上角
        case 31: rect.setBottomLeft(rect.bottomLeft() + delta);  break;  //左下角
        case 33: rect.setBottomRight(rect.bottomRight() + delta); break;  //右下角
        case 12: rect.setTop(rect.top() + delta.y()); break; //上
        case 21: rect.setLeft(rect.left() + delta.x()); break; //左
        case 23: rect.setRight(rect.right() + delta.x()); break; //右
        case 32: rect.setBottom(rect.bottom() + delta.y()); break; //下        
        default: break;
        }
        this->setGeometry(rect);
    }
}

void SuperWindow::mouseReleaseEvent(QMouseEvent* event)
{
    Q_UNUSED(event);
    if (event->button() == Qt::LeftButton)
    {
        if (m_bPressed) m_bPressed = false;
       
        if (m_bResizing)
        {
            m_bResizing = false;
            m_flag = 22;
            update();
        }
    }
}

void SuperWindow::paintEvent(QPaintEvent* e)
{
    QPainter painter(this);
    {
        painter.save();
        QRect rect(m_nBorder/2, m_nBorder/2, width()-m_nBorder/2, height()-m_nBorder/2);

        QPen pen;
        pen.setColor(Qt::lightGray);
        pen.setWidth(m_nBorder);
        painter.setPen(pen);

        painter.drawRect(rect);
        painter.restore();
    }

    if (m_flag == 22) return;
    else
    {
        QBrush brush;
        brush.setColor(QColor(73, 95, 22, 150));//设置颜色
        brush.setStyle(Qt::SolidPattern);
        painter.setBrush(brush);
        painter.setPen(Qt::NoPen);

        switch (m_flag)
        {
        case 12:
        {
            QRect rect(0, 0, width(), m_nBorder);
            painter.drawRect(rect);
            break;
        }
        case 21:
        {
            QRect rect(0, 0, m_nBorder, height());
            painter.drawRect(rect);
            break;
        }
        case 23:
        {
            QRect rect(width()-m_nBorder, 0, m_nBorder, height());
            painter.drawRect(rect);
            break;
        }
        case 32:
        {
            QRect rect(0, height()-m_nBorder, width(), m_nBorder);
            painter.drawRect(rect);
            break;
        }
        case 11:
        {
            QRect rect(0, 0, width(), m_nBorder);
            painter.drawRect(rect);
            rect = QRect(0, 0, m_nBorder, height());
            painter.drawRect(rect);
            break;
        }
        case 13:
        {
            QRect rect(0, 0, width(), m_nBorder);
            painter.drawRect(rect);
            rect = QRect(width()-m_nBorder, 0, m_nBorder, height());
            painter.drawRect(rect);
            break;
        }
        case 31:
        {
            QRect rect(0, 0, m_nBorder, height());
            painter.drawRect(rect);
            rect = QRect(0, height()-m_nBorder, width(), m_nBorder);
            painter.drawRect(rect);
            break;
        }
        case 33:
        {
            QRect rect(0, height()-m_nBorder, width(), m_nBorder);
            painter.drawRect(rect);
            rect = QRect(width()-m_nBorder, 0, m_nBorder, height());
            painter.drawRect(rect);
            break;
        }
        default:
            break;
        }
    }
}

void SuperWindow::leaveEvent(QEvent *e)
{
    Q_UNUSED(e);
    m_flag = 22;
}

int SuperWindow::row(QPointF pos)
{
    if (pos.y() < m_nBorder)
        return 10;
    else if (pos.y() > height() - m_nBorder)
        return 30;
    else
        return 20;
}

int SuperWindow::col(QPointF pos)
{
    if (pos.x() < m_nBorder)
        return 1;
    else if (pos.x() > width() - m_nBorder)
        return 3;
    else
        return 2;
}

int SuperWindow::moveArea(QPointF pos)
{
    return row(pos) + col(pos);
}

void SuperWindow::setMouseStyle(int moveArea)
{
    switch (moveArea)
    {
    case 11: setCursor(Qt::SizeFDiagCursor); break;
    case 12: setCursor(Qt::SizeVerCursor); break;
    case 13: setCursor(Qt::SizeBDiagCursor); break;
    case 21: setCursor(Qt::SizeHorCursor); break;
    case 22:
    {
        if (!m_bPressed && !m_bResizing)
            setCursor(Qt::ArrowCursor);
        break;
    }
    case 23: setCursor(Qt::SizeHorCursor); break;
    case 31: setCursor(Qt::SizeBDiagCursor); break;
    case 32: setCursor(Qt::SizeVerCursor); break;
    case 33: setCursor(Qt::SizeFDiagCursor); break;
    default: setCursor(Qt::ArrowCursor); break;
    }
}

void SuperWindow::OnMaxmized()
{
    if (this->isMaximized())
    {
        this->showNormal();
        ui->btnMaxmize->setIcon(QIcon(":/icons/maxsize.png"));
    }
    else
    {
        this->showMaximized();
        ui->btnMaxmize->setIcon(QIcon(":/icons/normal.png"));
    }
}

main.cpp

#include "MainWin.h"
#include "SuperWindow.h"

#include <QApplication>

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

    SuperWindow* win = SuperWindow::Create<MainWin>(nullptr);
    win->show();

    MainWin* mw = win->CentralWidget<MainWin>();
    if (!mw) qDebug() << "not nullptr";
    else mw->setVisible(false);

    return a.exec();
}

对于MainWin的实现,这里就不在放代码了,只要是集成QWidget的窗口即可。

如果我的分享,帮助了您,不要忘记点赞、评论和收藏。

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值