QT 无边框窗口的实现窗口移动和拉伸功能

1,352 阅读4分钟

QT 无边框窗口的实现窗口移动和拉伸功能

先上效果图

demo.gif

废话不说,直接上代码

自定义的标题栏 TitleBar.h

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

class TitleBar : public QWidget

{
    Q_OBJECT

public:
    // 构造函数
    explicit TitleBar(QWidget *parent = nullptr);
    // 设置title的颜色
    void setBackgroundColor(const QString &color);

private:
    // 水平布局对象
    QHBoxLayout* layout;
    // 按钮
    QPushButton* optionBtn;

    QPushButton* mininzeBtn;

    QPushButton* closeBtn;

    QString bgColor;

protected:

    // 重写绘图事件
    void paintEvent(QPaintEvent *) override;

signals:
    // 按钮自定义信号
    void MininzeBtnClicked();

    void CloseBtnClicked();

};

#endif // TITLE_H

TitleBar.cpp 实现自定标题栏相关功能


#include "titlebar.h"

#include <QPainter>

#include <QDebug>

TitleBar::TitleBar(QWidget *parent) : QWidget(parent)

{

    // 开启鼠标跟踪 自定义控件

    setMouseTracking(true);

    // 初始化布局

    layout = new QHBoxLayout(this);

    optionBtn = new QPushButton;

    mininzeBtn = new QPushButton;

    closeBtn = new QPushButton;

    bgColor ="#1EA19D";

    optionBtn->setObjectName("OptionButton");

    optionBtn->setFocusPolicy(Qt::NoFocus);

    optionBtn->setFixedSize(27,23);

    optionBtn->setCursor(Qt::PointingHandCursor);

    mininzeBtn->setObjectName("MinimizeButton");

    mininzeBtn->setFocusPolicy(Qt::NoFocus);

    mininzeBtn->setFixedSize(27,23);

    mininzeBtn->setCursor(Qt::PointingHandCursor);

    closeBtn->setObjectName("CloseButton");

    closeBtn->setFocusPolicy(Qt::NoFocus);

    closeBtn->setFixedSize(27,23);

    closeBtn->setCursor(Qt::PointingHandCursor);

    layout->addStretch();

    layout->addWidget(optionBtn);

    layout->addWidget(mininzeBtn);

    layout->addWidget(closeBtn);

    layout->addSpacing(5);

    layout->setSpacing(0);

    layout->setContentsMargins(0, 0, 0, 0);

    setFixedHeight(36);

    // 信号连接 信号

    connect(mininzeBtn, &QPushButton::clicked, this, &TitleBar::MininzeBtnClicked);

    connect(closeBtn, &QPushButton::clicked, this, &TitleBar::CloseBtnClicked);

}

void TitleBar::setBackgroundColor(const QString &color)

{

    bgColor = color;

    // 调用系统重绘,调用paintEnvent 方法

    update();

}


// 重绘的事件定义

void TitleBar::paintEvent(QPaintEvent *e)

{
    // 防止编译警告未使用变量
    Q_UNUSED(e);

    qDebug() << "调用重绘接口";

    QPainter painter(this);

    painter.setPen(Qt::NoPen);

    painter.setBrush(QColor(bgColor));

    // 重绘窗体

    painter.drawRect(rect());

}


重点来了,因为移除了外部窗体导致无法移动窗体和拉伸窗体,现在需要重写三个鼠标事件:

  1. mousePressEvent(QMouseEvent*) 鼠标按压事件,
  2. mouseReleaseEvent(QMouseEvent*) 鼠标释放事件
  3. mouseMoveEvent(QMouseEvent*) 鼠标移动事件

来自定义实现 相关的功能.

主窗口逻辑 MainWindow.h

demo2.gif


#ifndef MAINWINDOW_H

#define MAINWINDOW_H

#include <QMainWindow>

#include "titlebar.h"

#include <QMouseEvent>

namespace  {
    // 重点来讲下这个定义,好多教程没有做说明
    // 这块定义的四个边的边距大小都是5 ,这个可以自定义设置大小
    // 参考上面图片
    const int kMouseRegionLeft = 5;

    const int kMouseRegionTop = 5;

    const int kMouseRegionRight = 5;

    const int kMouseRegionBottom = 5;

}

// 鼠标的 活动范围的 枚举
enum MousePosition

{
    // 这个是上面图片划分的区域 1,1 区 就用 11 代表 , 1,2 就用12 代表 以此类推
   kMousePositionLeftTop = 11,

   kMousePositionTop = 12,

   kMousePositionRightTop = 13,

   kMousePositionLeft = 21,

   kMousePositionMid = 22,

   kMousePositionRight = 23,

   kMousePositionLeftBottom = 31,

   kMousePositionBottom = 32,

   kMousePositionRightBottom = 33,

};

class MainWindow : public QMainWindow

{

    Q_OBJECT

public:

    MainWindow(QWidget *parent = nullptr);

    ~MainWindow();

protected:

    void mousePressEvent(QMouseEvent *);

    void mouseReleaseEvent(QMouseEvent *);

    void mouseMoveEvent(QMouseEvent *);

private:

    //根据鼠标的设置鼠标样式,用于拉伸

    void SetMouseCursor(int x, int y);

    //判断鼠标的区域,用于拉伸

    int GetMouseRegion(int x, int y);

private:

    bool isPress;

    QPoint windowsLastPs;

    QPoint mousePs;

    TitleBar* titleBar;

    int mouse_press_region = kMousePositionMid;

};

#endif // MAINWINDOW_H

MainWindow.cpp 主窗口的实现


#include "mainwindow.h"

MainWindow::MainWindow(QWidget *parent)

    : QMainWindow(parent)

{

    // 初始化 标题栏

    titleBar = new TitleBar();

    isPress = false;

    setMouseTracking(true);

    //setAttribute(Qt::WA_Hover);

    // frameless

    setWindowFlag(Qt::FramelessWindowHint);

    //设置标题栏

    setMenuWidget(titleBar);

    // 重置窗口大小

    this->resize(800, 600);

    // 设置槽函数

    connect(titleBar, &TitleBar::MininzeBtnClicked, this, &MainWindow::showMinimized);

    connect(titleBar, &TitleBar::CloseBtnClicked, this, &MainWindow::close);

}

MainWindow::~MainWindow()

{

}

// 定义重写 鼠标事件

void MainWindow::mousePressEvent(QMouseEvent * ev)

{

    if(ev->buttons() == Qt::LeftButton){

        // 如果是鼠标左键
        // 获取当前窗口位置,以窗口左上角
        windowsLastPs = pos();
        // 获取鼠标在屏幕的位置  就是全局的坐标 以屏幕左上角为坐标系
        mousePs = ev->globalPos();

        isPress = true;
        // 获取鼠标在那个区域
        mouse_press_region = GetMouseRegion(ev->pos().x(), ev->pos().y());

    }

}

void MainWindow::mouseReleaseEvent(QMouseEvent *ev)

{

    if(ev->button() == Qt::LeftButton){

        isPress = false;

    }

}

void MainWindow::mouseMoveEvent(QMouseEvent *ev)

{

    // 设置鼠标的形状

    SetMouseCursor(ev->pos().x(), ev->pos().y());
    
    // 计算的鼠标移动偏移量, 就是鼠标全局坐标 - 减去点击时鼠标坐标
    QPoint point_offset = ev->globalPos() - mousePs;

    if((ev->buttons() == Qt::LeftButton) && isPress)

    {

        if(mouse_press_region == kMousePositionMid)

        {
            // 如果鼠标是在窗口的中间位置,就是移动窗口
            move(windowsLastPs+ point_offset);

        }else {

            // 其他部分 是拉伸窗口

            // 获取客户区

            QRect rect = geometry();

            switch (mouse_press_region)

            {

            // 左上角

            case kMousePositionLeftTop:

                rect.setTopLeft(rect.topLeft() + point_offset);

                                break;

            case kMousePositionTop:

                rect.setTop(rect.top() + point_offset.y());

                break;

            case kMousePositionRightTop:

                rect.setTopRight(rect.topRight() + point_offset);

                break;

            case kMousePositionRight:

                rect.setRight(rect.right() + point_offset.x());

                break;

            case kMousePositionRightBottom:

                rect.setBottomRight(rect.bottomRight() + point_offset);

                break;

            case kMousePositionBottom:

                rect.setBottom(rect.bottom() + point_offset.y());

                break;

            case kMousePositionLeftBottom:

                rect.setBottomLeft(rect.bottomLeft() + point_offset);

                break;

            case kMousePositionLeft:

                rect.setLeft(rect.left() + point_offset.x());

                break;

            default:

                break;

            }

            setGeometry(rect);

            mousePs = ev->globalPos();

            }

        }

    }

// 获取鼠标当前在那个区域(区域上图分9个区域)
int MainWindow::GetMouseRegion(int x, int y)

{

    int region_x = 0, region_y = 0;
    
    // 鼠标的X坐标小于 边界5 说明他在最上层区域 第一区域
    if(x < kMouseRegionLeft)

    {

        region_x = 1;

    }else if (x >(this->width()/*窗体宽度*/ - kMouseRegionRight/*边界宽度5*/)) {
        // 如果鼠标X坐标 大于 最右侧的边界 说明他在第三区域
        region_x = 3;

    }else {

        region_x = 2;

    }

    if( y< kMouseRegionTop)

    {
        // 同理 鼠标Y坐标 小于上层边界5  说明鼠标在第一区域
        region_y = 1;

    }else if (y > (this->height() -kMouseRegionBottom)) {
        // 鼠标Y坐标的 大于 最下面的坐标,鼠标就在 第三区
        region_y = 3;

    }else {

        region_y = 2;

    }
    // 最后计算 表示区域的 数值 (x=1, y=1) 计算 = 1*10+1 =11 
    // x=2,y=3 = 3*10+2 = 32 在图的 3,2 区域
    return region_y* 10 + region_x;

}

// 设置鼠标形状
void MainWindow::SetMouseCursor(int x, int y)

{

    // 鼠标形状对象

    Qt::CursorShape cursor;

    int region = GetMouseRegion(x, y);

    switch (region)

    {

        case kMousePositionLeftTop:

        case kMousePositionRightBottom:

             cursor =  Qt::SizeFDiagCursor; break;

        case kMousePositionRightTop:

        case kMousePositionLeftBottom:

             cursor =  Qt::SizeBDiagCursor; break;

        case kMousePositionLeft:

        case kMousePositionRight:

            cursor = Qt::SizeHorCursor; break;

        case kMousePositionTop:

        case kMousePositionBottom:

            cursor = Qt::SizeVerCursor; break;

        case kMousePositionMid:

            cursor = Qt::ArrowCursor; break;

        default:

            break;

    }

    setCursor(cursor);

}

读取QSS的工具类 FileUtils.h


#ifndef FILEUTILS_H

#define FILEUTILS_H

#include <QObject>

namespace Utils{

    QString loadQssFile(const QString& path);

}

#endif // FILEUTILS_H

FileUtils.cpp


#include "fileutils.h"

#include <QFile>

QString Utils::loadQssFile(const QString& path)

{

    QFile file(path);

    QString qss;

    file.open(QIODevice::ReadOnly);

    qss = file.readAll();

    file.close();

    return qss;

}

主函数 main.cpp


#include "mainwindow.h"

#include <QApplication>

#include <QDesktopWidget>

#include "fileutils.h"

int main(int argc, char *argv[])

{
    QApplication a(argc, argv);

    MainWindow w;

    // 窗口居中显示

    w.move((QApplication::desktop()->width() - w.width()) / 2,(QApplication::desktop()->height() - w.height()) / 2);

    w.show();

    // 设置样式

    qApp->setStyleSheet(Utils::loadQssFile(":/qss/light.qss"));

    return a.exec();

}

使用的资源 图片,qss的文件 需要加载Resource资源

QQ截图20220918132306.png

资源下载

Qss 链接:pan.baidu.com/s/1V9pZN_iT… 提取码:whjj

图片 链接:pan.baidu.com/s/1oyMWPKSa… 提取码:8rkn

结束