1,简介
前面写过2篇Qt无边框窗口的文章:
文1 介绍主窗口、标题栏制作
文2 增加了右下角缩放窗口
但其实效果还不够理想
这次介绍一个终极版本,
4个边框4个角都可以随意缩放
而且封装成一个工具类,调用更简单,代码更简洁
源码源自博主“一去二三里”
原本链接:https://blog.csdn.net/liang19890820/article/details/50557240
但原博客只提供了代码,使用上的说明好像不太详尽,
我下载后对这个代码后花费了不少时间研究,
还修复了一处细节bug,目前已经应用到项目中运行了一段时间,没什么问题
现在共享给大家,制作一个演示demo,并介绍调用方法
省去再研究的时间
2,效果
3,工具类代码
无边框窗口工具类:
FramelessHelper.h:
#ifndef FRAMELESS_HELPER_H
#define FRAMELESS_HELPER_H
#include <QObject>
#include <QRect>
#include <QRubberBand>
#include <QMouseEvent>
#include <QHoverEvent>
class QWidget;
class FramelessHelperPrivate;
/*****
* CursorPosCalculator
* 计算鼠标是否位于左、上、右、下、左上角、左下角、右上角、右下角
*****/
class CursorPosCalculator
{
public:
explicit CursorPosCalculator();
void reset();
void recalculate(const QPoint &globalMousePos, const QRect &frameRect);
public:
bool m_bOnEdges : true;
bool m_bOnLeftEdge : true;
bool m_bOnRightEdge : true;
bool m_bOnTopEdge : true;
bool m_bOnBottomEdge : true;
bool m_bOnTopLeftEdge : true;
bool m_bOnBottomLeftEdge : true;
bool m_bOnTopRightEdge : true;
bool m_bOnBottomRightEdge : true;
static int m_nBorderWidth;
static int m_nTitleHeight;
};
/*****
* WidgetData
* 更新鼠标样式、移动窗体、缩放窗体
*****/
class WidgetData : public QObject
{
Q_OBJECT
public:
explicit WidgetData(FramelessHelperPrivate *d, QWidget *pTopLevelWidget, QObject *parent = 0);
~WidgetData();
QWidget* widget();
// 处理鼠标事件-划过、按下、释放、移动
void handleWidgetEvent(QEvent *event);
// 更新橡皮筋状态
void updateRubberBandStatus();
void setMax(bool bMax);
bool isMax();
private:
// 更新鼠标样式
void updateCursorShape(const QPoint &gMousePos);
// 重置窗体大小
void resizeWidget(const QPoint &gMousePos);
// 移动窗体
void moveWidget(const QPoint &gMousePos);
// 处理鼠标按下
void handleMousePressEvent(QMouseEvent *event);
// 处理鼠标释放
void handleMouseReleaseEvent(QMouseEvent *event);
// 处理鼠标移动
void handleMouseMoveEvent(QMouseEvent *event);
//处理鼠标双击
void handleMouseDblClickEvent(QMouseEvent *event);
// 处理鼠标离开
void handleLeaveEvent(QEvent *event);
// 处理鼠标进入
void handleHoverMoveEvent(QHoverEvent *event);
signals:
void sigMax(bool bMax);
private:
FramelessHelperPrivate *d;
QRubberBand *m_pRubberBand;
QWidget *m_pWidget;
QPoint m_ptDragPos;
CursorPosCalculator m_pressedMousePos;
CursorPosCalculator m_moveMousePos;
bool m_bLeftButtonPressed;
bool m_bCursorShapeChanged;
bool m_bLeftButtonTitlePressed;
bool m_bMax;
QRect m_rect;
Qt::WindowFlags m_windowFlags;
};
class FramelessHelper : public QObject
{
Q_OBJECT
public:
explicit FramelessHelper(QObject *parent = 0);
~FramelessHelper();
// 激活窗体
void activateOn(QWidget *topLevelWidget);
// 移除窗体
void removeFrom(QWidget *topLevelWidget);
// 设置窗体移动
void setWidgetMovable(bool movable);
// 设置窗体缩放
void setWidgetResizable(bool resizable);
// 设置橡皮筋移动
void setRubberBandOnMove(bool movable);
// 设置橡皮筋缩放
void setRubberBandOnResize(bool resizable);
// 设置边框的宽度
void setBorderWidth(uint width);
// 设置标题栏高度
void setTitleHeight(uint height);
bool widgetResizable();
bool widgetMovable();
bool rubberBandOnMove();
bool rubberBandOnResisze();
uint borderWidth();
uint titleHeight();
void setMax(QWidget *w,bool bMax);
bool isMax(QWidget *w);
protected:
// 事件过滤,进行移动、缩放等
virtual bool eventFilter(QObject *obj, QEvent *event);
signals:
void sigMax(bool bMax);
private:
FramelessHelperPrivate *d;
};
#endif //FRAMELESS_HELPER_H
FramelessHelper.cpp:
#include <QRect>
#include <QRubberBand>
#include <QMouseEvent>
#include <QHoverEvent>
#include <QApplication>
#include <QDesktopWidget>
#include "FramelessHelper.h"
/*****
* FramelessHelperPrivate
* 存储界面对应的数据集合,以及是否可移动、可缩放属性
*****/
class FramelessHelperPrivate
{
public:
QHash<QWidget*, WidgetData*> m_widgetDataHash;
bool m_bWidgetMovable : true;
bool m_bWidgetResizable : true;
bool m_bRubberBandOnResize : true;
bool m_bRubberBandOnMove : true;
};
int CursorPosCalculator::m_nBorderWidth = 5;
int CursorPosCalculator::m_nTitleHeight = 30;
/***** CursorPosCalculator *****/
CursorPosCalculator::CursorPosCalculator()
{
reset();
}
void CursorPosCalculator::reset()
{
m_bOnEdges = false;
m_bOnLeftEdge = false;
m_bOnRightEdge = false;
m_bOnTopEdge = false;
m_bOnBottomEdge = false;
m_bOnTopLeftEdge = false;
m_bOnBottomLeftEdge = false;
m_bOnTopRightEdge = false;
m_bOnBottomRightEdge = false;
}
void CursorPosCalculator::recalculate(const QPoint &gMousePos, const QRect &frameRect)
{
int globalMouseX = gMousePos.x();
int globalMouseY = gMousePos.y();
int frameX = frameRect.x();
int frameY = frameRect.y();
int frameWidth = frameRect.width();
int frameHeight = frameRect.height();
m_bOnLeftEdge = (globalMouseX >= frameX &&
globalMouseX <= frameX + m_nBorderWidth );
m_bOnRightEdge = (globalMouseX >= frameX + frameWidth - m_nBorderWidth &&
globalMouseX <= frameX + frameWidth);
m_bOnTopEdge = (globalMouseY >= frameY &&
globalMouseY <= frameY + m_nBorderWidth );
m_bOnBottomEdge = (globalMouseY >= frameY + frameHeight - m_nBorderWidth &&
globalMouseY <= frameY + frameHeight);
m_bOnTopLeftEdge = m_bOnTopEdge && m_bOnLeftEdge;
m_bOnBottomLeftEdge = m_bOnBottomEdge && m_bOnLeftEdge;
m_bOnTopRightEdge = m_bOnTopEdge && m_bOnRightEdge;
m_bOnBottomRightEdge = m_bOnBottomEdge && m_bOnRightEdge;
m_bOnEdges = m_bOnLeftEdge || m_bOnRightEdge || m_bOnTopEdge || m_bOnBottomEdge;
}
/***** WidgetData *****/
WidgetData::WidgetData(FramelessHelperPrivate *_d, QWidget *pTopLevelWidget, QObject *parent) : QObject(parent)
{
d = _d;
m_pWidget = pTopLevelWidget;
m_bLeftButtonPressed = false;
m_bCursorShapeChanged = false;
m_bLeftButtonTitlePressed = false;
m_bMax = false;
m_rect = m_pWidget->geometry();
m_pRubberBand = NULL;
m_windowFlags = m_pWidget->windowFlags();
m_pWidget->setMouseTracking(true);
m_pWidget->setAttribute(Qt::WA_Hover, true);
updateRubberBandStatus();
}
WidgetData::~WidgetData()
{
m_pWidget->setMouseTracking(false);
m_pWidget->setWindowFlags(m_windowFlags);
m_pWidget->setAttribute(Qt::WA_Hover, false);
delete m_pRubberBand;
m_pRubberBand = NULL;
}
QWidget* WidgetData::widget()
{
return m_pWidget;
}
void WidgetData::handleWidgetEvent(QEvent *event)
{
switch (event->type())
{
default:
break;
case QEvent::MouseButtonPress:
handleMousePressEvent(static_cast<QMouseEvent*>(event));
break;
case QEvent::MouseButtonRelease:
handleMouseReleaseEvent(static_cast<QMouseEvent*>(event));
break;
case QEvent::MouseMove:
handleMouseMoveEvent(static_cast<QMouseEvent*>(event));
break;
case QEvent::MouseButtonDblClick:
handleMouseDblClickEvent(static_cast<QMouseEvent*>(event));
break;
case QEvent::Leave:
handleLeaveEvent(static_cast<QMouseEvent*>(event));
break;
case QEvent::HoverMove:
handleHoverMoveEvent(static_cast<QHoverEvent*>(event));
break;
}
}
void WidgetData::updateRubberBandStatus()
{
if (d->m_bRubberBandOnMove || d->m_bRubberBandOnResize)
{
if (NULL == m_pRubberBand)
m_pRubberBand = new QRubberBand(QRubberBand::Rectangle);
}
else
{
delete m_pRubberBand;
m_pRubberBand = NULL;
}
}
void WidgetData::setMax(bool bMax)
{
if(m_bMax != bMax)
{
if(bMax)
{
m_rect = m_pWidget->geometry();
m_pWidget->setGeometry(QApplication::desktop()->availableGeometry());
}
else
{
if(m_rect.x() == 0 && m_rect.y() == 0)
{
QSize size = QApplication::desktop()->size();
int x = (size.width() - m_rect.width())/2;
int y = (size.height() - m_rect.height())/2;
m_rect.moveTo(x,y);
}
m_pWidget->setGeometry(m_rect);
}
m_bMax = bMax;
emit sigMax(m_bMax);
}
}
bool WidgetData::isMax()
{
return m_bMax;
}
void WidgetData::updateCursorShape(const QPoint &gMousePos)
{
if (m_pWidget->isFullScreen() || m_pWidget->isMaximized())
{
if (m_bCursorShapeChanged)
{
m_pWidget->unsetCursor();
}
return;
}
m_moveMousePos.recalculate(gMousePos, m_pWidget->frameGeometry());
if(m_moveMousePos.m_bOnTopLeftEdge || m_moveMousePos.m_bOnBottomRightEdge)
{
m_pWidget->setCursor( Qt::SizeFDiagCursor );
m_bCursorShapeChanged = true;
}
else if(m_moveMousePos.m_bOnTopRightEdge || m_moveMousePos.m_bOnBottomLeftEdge)
{
m_pWidget->setCursor( Qt::SizeBDiagCursor );
m_bCursorShapeChanged = true;
}
else if(m_moveMousePos.m_bOnLeftEdge || m_moveMousePos.m_bOnRightEdge)
{
m_pWidget->setCursor( Qt::SizeHorCursor );
m_bCursorShapeChanged = true;
}
else if(m_moveMousePos.m_bOnTopEdge || m_moveMousePos.m_bOnBottomEdge)
{
m_pWidget->setCursor( Qt::SizeVerCursor );
m_bCursorShapeChanged = true;
}
else
{
if (m_bCursorShapeChanged)
{
m_pWidget->unsetCursor();
m_bCursorShapeChanged = false;
}
}
}
void WidgetData::resizeWidget(const QPoint &gMousePos)
{
QRect origRect;
if (d->m_bRubberBandOnResize)
origRect = m_pRubberBand->frameGeometry();
else
origRect = m_pWidget->frameGeometry();
int left = origRect.left();
int top = origRect.top();
int right = origRect.right();
int bottom = origRect.bottom();
origRect.getCoords(&left, &top, &right, &bottom);
int minWidth = m_pWidget->minimumWidth();
int minHeight = m_pWidget->minimumHeight();
if (m_pressedMousePos.m_bOnTopLeftEdge)
{
left = gMousePos.x();
top = gMousePos.y();
}
else if (m_pressedMousePos.m_bOnBottomLeftEdge)
{
left = gMousePos.x();
bottom = gMousePos.y();
}
else if (m_pressedMousePos.m_bOnTopRightEdge)
{
right = gMousePos.x();
top = gMousePos.y();
}
else if (m_pressedMousePos.m_bOnBottomRightEdge)
{
right = gMousePos.x();
bottom = gMousePos.y();
}
else if (m_pressedMousePos.m_bOnLeftEdge)
{
left = gMousePos.x();
}
else if (m_pressedMousePos.m_bOnRightEdge)
{
right = gMousePos.x();
}
else if (m_pressedMousePos.m_bOnTopEdge)
{
top = gMousePos.y();
}
else if (m_pressedMousePos.m_bOnBottomEdge)
{
bottom = gMousePos.y();
}
QRect newRect(QPoint(left, top), QPoint(right, bottom));
if (newRect.isValid())
{
if (minWidth > newRect.width())
{
if (left != origRect.left())
newRect.setLeft(origRect.left());
else
newRect.setRight(origRect.right());
}
if (minHeight > newRect.height())
{
if (top != origRect.top())
newRect.setTop(origRect.top());
else
newRect.setBottom(origRect.bottom());
}
if (d->m_bRubberBandOnResize)
{
m_pRubberBand->setGeometry(newRect);
}
else
{
m_pWidget->setGeometry(newRect);
}
}
}
void WidgetData::moveWidget(const QPoint& gMousePos)
{
if (d->m_bRubberBandOnMove)
{
m_pRubberBand->move(gMousePos - m_ptDragPos);
}
else
{
m_pWidget->move(gMousePos - m_ptDragPos);
}
}
void WidgetData::handleMousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton)
{
m_bLeftButtonPressed = true;
m_bLeftButtonTitlePressed = event->pos().y() < m_moveMousePos.m_nTitleHeight;
QRect frameRect = m_pWidget->frameGeometry();
m_pressedMousePos.recalculate(event->globalPos(), frameRect);
m_ptDragPos = event->globalPos() - frameRect.topLeft();
if (m_pressedMousePos.m_bOnEdges)
{
if (d->m_bRubberBandOnResize)
{
m_pRubberBand->setGeometry(frameRect);
m_pRubberBand->show();
}
}
else if (d->m_bRubberBandOnMove)
{
m_pRubberBand->setGeometry(frameRect);
m_pRubberBand->show();
}
}
}
void WidgetData::handleMouseReleaseEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton)
{
m_bLeftButtonPressed = false;
m_bLeftButtonTitlePressed = false;
m_pressedMousePos.reset();
if (m_pRubberBand && m_pRubberBand->isVisible())
{
m_pRubberBand->hide();
m_pWidget->setGeometry(m_pRubberBand->geometry());
}
}
}
void WidgetData::handleMouseMoveEvent(QMouseEvent *event)
{
if(m_bMax)
{
return;
}
if (m_bLeftButtonPressed)
{
if (d->m_bWidgetResizable && m_pressedMousePos.m_bOnEdges)
{
resizeWidget(event->globalPos());
}
else if (d->m_bWidgetMovable && m_bLeftButtonTitlePressed)
{
moveWidget(event->globalPos());
}
}
else if (d->m_bWidgetResizable)
{
updateCursorShape(event->globalPos());
}
}
void WidgetData::handleMouseDblClickEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton)
{
if(event->pos().y() < m_moveMousePos.m_nTitleHeight)
{
setMax(!m_bMax);
}
}
}
void WidgetData::handleLeaveEvent(QEvent *event)
{
Q_UNUSED(event)
if (!m_bLeftButtonPressed)
{
m_pWidget->unsetCursor();
}
}
void WidgetData::handleHoverMoveEvent(QHoverEvent *event)
{
if(m_bMax)
{
return;
}
if (d->m_bWidgetResizable)
{
updateCursorShape(m_pWidget->mapToGlobal(event->pos()));
}
}
/*****FramelessHelper*****/
FramelessHelper::FramelessHelper(QObject *parent)
: QObject(parent),
d(new FramelessHelperPrivate())
{
d->m_bWidgetMovable = true;
d->m_bWidgetResizable = true;
d->m_bRubberBandOnResize = false;
d->m_bRubberBandOnMove = false;
}
FramelessHelper::~FramelessHelper()
{
QList<QWidget*> keys = d->m_widgetDataHash.keys();
int size = keys.size();
for (int i = 0; i < size; ++i)
{
delete d->m_widgetDataHash.take(keys[i]);
}
delete d;
}
bool FramelessHelper::eventFilter(QObject *obj, QEvent *event)
{
switch (event->type())
{
case QEvent::MouseMove:
case QEvent::HoverMove:
case QEvent::MouseButtonPress:
case QEvent::MouseButtonRelease:
case QEvent::MouseButtonDblClick:
case QEvent::Leave:
{
WidgetData *data = d->m_widgetDataHash.value(static_cast<QWidget*>(obj));
if (data)
{
data->handleWidgetEvent(event);
return true;
}
}
}
return QObject::eventFilter(obj, event);
}
void FramelessHelper::activateOn(QWidget *topLevelWidget)
{
if (!d->m_widgetDataHash.contains(topLevelWidget))
{
WidgetData *data = new WidgetData(d, topLevelWidget);
d->m_widgetDataHash.insert(topLevelWidget, data);
connect(data, SIGNAL(sigMax(bool)), this, SIGNAL(sigMax(bool)));
topLevelWidget->installEventFilter(this);
}
}
void FramelessHelper::removeFrom(QWidget *topLevelWidget)
{
WidgetData *data = d->m_widgetDataHash.take(topLevelWidget);
if (data)
{
topLevelWidget->removeEventFilter(this);
delete data;
}
}
void FramelessHelper::setRubberBandOnMove(bool movable)
{
d->m_bRubberBandOnMove = movable;
QList<WidgetData*> list = d->m_widgetDataHash.values();
foreach (WidgetData *data, list)
{
data->updateRubberBandStatus();
}
}
void FramelessHelper::setWidgetMovable(bool movable)
{
d->m_bWidgetMovable = movable;
}
void FramelessHelper::setWidgetResizable(bool resizable)
{
d->m_bWidgetResizable = resizable;
}
void FramelessHelper::setRubberBandOnResize(bool resizable)
{
d->m_bRubberBandOnResize = resizable;
QList<WidgetData*> list = d->m_widgetDataHash.values();
foreach (WidgetData *data, list)
{
data->updateRubberBandStatus();
}
}
void FramelessHelper::setBorderWidth(uint width)
{
if (width > 0)
{
CursorPosCalculator::m_nBorderWidth = width;
}
}
void FramelessHelper::setTitleHeight(uint height)
{
if (height > 0)
{
CursorPosCalculator::m_nTitleHeight = height;
}
}
bool FramelessHelper::widgetMovable()
{
return d->m_bWidgetMovable;
}
bool FramelessHelper::widgetResizable()
{
return d->m_bWidgetResizable;
}
bool FramelessHelper::rubberBandOnMove()
{
return d->m_bRubberBandOnMove;
}
bool FramelessHelper::rubberBandOnResisze()
{
return d->m_bRubberBandOnResize;
}
uint FramelessHelper::borderWidth()
{
return CursorPosCalculator::m_nBorderWidth;
}
uint FramelessHelper::titleHeight()
{
return CursorPosCalculator::m_nTitleHeight;
}
void FramelessHelper::setMax(QWidget *w,bool bMax)
{
WidgetData* data = d->m_widgetDataHash[w];
if(data)
{
data->setMax(bMax);
}
}
bool FramelessHelper::isMax(QWidget *w)
{
WidgetData* data = d->m_widgetDataHash[w];
if(data)
{
return data->isMax();
}
return false;
}
4,MainWindow
MainWindow.h:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include "FramelessHelper.h"
#include <QMainWindow>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
void on_btnMin_clicked();
void on_btnMax_clicked();
void on_btnExit_clicked();
void slotMax(bool bMax);
private:
Ui::MainWindow *ui;
FramelessHelper* mHelper; //无边框窗口工具类
};
#endif
MainWindow.cpp:
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
setWindowFlags(Qt::FramelessWindowHint);
setStyleSheet("QMainWindow{color:#E8E8E8;background:#43CD80;}\
QWidget#widgetTitle{background:#363636;}\
QLabel#labelTitle{color:white;padding:8px 0px 5px;}\
QPushButton#btnMin,QPushButton#btnMax,QPushButton#btnExit{\
border-radius:0px;\
color: #F0F0F0;\
background-color:rgba(0,0,0,0);\
border-style:none;}\
QPushButton#btnMin:hover,QPushButton#btnMax:hover{\
background-color: qlineargradient(spread:pad, x1:0, y1:1, x2:0, y2:0, stop:0 rgba(25, 134, 199, 0), stop:1 #5CACEE);}\
QPushButton#btnExit:hover{\
background-color: qlineargradient(spread:pad, x1:0, y1:1, x2:0, y2:0, stop:0 rgba(238, 0, 0, 128), stop:1 rgba(238, 44, 44, 255));}");
ui->btnMin->setIcon(QIcon(":/image/min.png"));
ui->btnMax->setIcon(QIcon(":/image/max1.png"));
ui->btnExit->setIcon(QIcon(":/image/exit.png"));
mHelper = new FramelessHelper(this);
mHelper->activateOn(this); //激活当前窗体
mHelper->setTitleHeight(30); //设置窗体的标题栏高度
mHelper->setWidgetMovable(true); //设置窗体可移动
mHelper->setWidgetResizable(true); //设置窗体可缩放
//mHelper->setRubberBandOnResize(true); //设置橡皮筋效果-可缩放
//mHelper->setRubberBandOnMove(true); //设置橡皮筋效果-可移动
//mHelper->setMax(this,true); //最大化
//绑定最大化/取消最大化信号,修改最大化图标
connect(mHelper, &FramelessHelper::sigMax, this, &MainWindow::slotMax);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_btnMin_clicked()
{
showMinimized();
}
void MainWindow::on_btnMax_clicked()
{
mHelper->setMax(this, !mHelper->isMax(this));
}
void MainWindow::on_btnExit_clicked()
{
// qApp->exit();
close();
}
void MainWindow::slotMax(bool bMax)
{
if (bMax)
{
ui->btnMax->setIcon(QIcon(":/image/max2.png"));
}
else
{
ui->btnMax->setIcon(QIcon(":/image/max1.png"));
}
}
5,调用方法
核心调用方法是在MainWindow构造函数中:
setTitleHeight 传入的高度要和你的界面设计的标题栏高度一致,这个高度以内的区域在拖动时就可以拖动整个窗口
绑定sigMax信号是为了在最大化/取消最大化时替换掉对应的图标
mHelper = new FramelessHelper(this);
mHelper->activateOn(this); //激活当前窗体
mHelper->setTitleHeight(30); //设置窗体的标题栏高度
mHelper->setWidgetMovable(true); //设置窗体可移动
mHelper->setWidgetResizable(true); //设置窗体可缩放
//mHelper->setRubberBandOnResize(true); //设置橡皮筋效果-可缩放
//mHelper->setRubberBandOnMove(true); //设置橡皮筋效果-可移动
//mHelper->setMax(this,true); //最大化
//绑定最大化/取消最大化信号,修改最大化图标
connect(mHelper, &FramelessHelper::sigMax, this, &MainWindow::slotMax);
6,演示Demo源码下载
直接把上面的工具类代码 FramelessHelper.h,FramelessHelper.cpp 复制出来,就可以在你工程里使用了。
Qt无边框窗口3 终极版,自由缩放(各个角+边框):
链接:https://pan.baidu.com/s/1yJL0p4iEBwmtFph1GNCkGQ
提取码:lgn8
专栏全套源码可在学习群免费下载!
群号码:1149411109
群名称:Qt实战派学习群