QT 无边框窗口的实现窗口移动和拉伸功能
先上效果图
废话不说,直接上代码
自定义的标题栏 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());
}
重点来了,因为移除了外部窗体导致无法移动窗体和拉伸窗体,现在需要重写三个鼠标事件:
- mousePressEvent(QMouseEvent*) 鼠标按压事件,
- mouseReleaseEvent(QMouseEvent*) 鼠标释放事件
- mouseMoveEvent(QMouseEvent*) 鼠标移动事件
来自定义实现 相关的功能.
主窗口逻辑 MainWindow.h
#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资源
资源下载
Qss 链接:pan.baidu.com/s/1V9pZN_iT… 提取码:whjj
图片 链接:pan.baidu.com/s/1oyMWPKSa… 提取码:8rkn