简述

通过上一节内容,我们实现了窗体的缩放,功能很不错,但是很遗憾-不支持跨平台!如果对于多平台来说,这是一个硬伤,所以,我们急需要一个能够支持跨平台的实现方案。

在网上看到过很多不同的实现方式,多多少少会存在一些问题-要么融合度太高、要么不能很好地进行移动、缩放。基于前人的分享与总结,最后,我花了很长时间来完善。独乐乐不如众乐乐,既然纯开源-那就全部分享出来。

效果

窗体缩放

实现

frameless_helper.h

#ifndef FRAMELESS_HELPER_H
#define FRAMELESS_HELPER_H #include <QObject> class QWidget;
class FramelessHelperPrivate; 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(); protected:
// 事件过滤,进行移动、缩放等
virtual bool eventFilter(QObject *obj, QEvent *event); private:
FramelessHelperPrivate *d;
}; #endif //FRAMELESS_HELPER_H

frameless_helper.cpp

FramelessHelperPrivate类

/*****
* 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;
};

CursorPosCalculator类

/*****
* 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;
};
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
* 更新鼠标样式、移动窗体、缩放窗体
*****/
class WidgetData
{
public:
explicit WidgetData(FramelessHelperPrivate *d, QWidget *pTopLevelWidget);
~WidgetData();
QWidget* widget();
// 处理鼠标事件-划过、厉害、按下、释放、移动
void handleWidgetEvent(QEvent *event);
// 更新橡皮筋状态
void updateRubberBandStatus(); 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 handleLeaveEvent(QEvent *event);
// 处理鼠标进入
void handleHoverMoveEvent(QHoverEvent *event); 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;
Qt::WindowFlags m_windowFlags;
};
/***** WidgetData *****/
WidgetData::WidgetData(FramelessHelperPrivate *_d, QWidget *pTopLevelWidget)
{
d = _d;
m_pWidget = pTopLevelWidget;
m_bLeftButtonPressed = false;
m_bCursorShapeChanged = false;
m_bLeftButtonTitlePressed = false;
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::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::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_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::handleLeaveEvent(QEvent *event)
{
Q_UNUSED(event)
if (!m_bLeftButtonPressed)
{
m_pWidget->unsetCursor();
}
} void WidgetData::handleHoverMoveEvent(QHoverEvent *event)
{
if (d->m_bWidgetResizable)
{
updateCursorShape(m_pWidget->mapToGlobal(event->pos()));
}
}

FramelessHelper类

#include <QRect>
#include <QRubberBand>
#include <QMouseEvent>
#include <QHoverEvent>
#include <QApplication>
#include "frameless_helper.h" class WidgetData; /*****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::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); 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;
}

接口说明

  • FramelessHelperPrivate

    存储界面对应的数据集合,以及是否可移动、可缩放属性

  • CursorPosCalculator

    计算鼠标是否位于左、上、右、下、左上角、左下角、右上角、右下角

  • WidgetData

    更新鼠标样式、移动窗体、缩放窗体

  • FramelessHelper

    激活窗体、移除窗体、设置窗体移动、窗体缩放、橡皮筋移动、橡皮筋缩放、边框的宽度、标题栏高度等

代码很多,我就不详细解答了,里面主要的接口我都添加了注释。其它接口的命名也是比较规范的-见名知义。

使用方式

这里的this指的是要处理的窗体。

FramelessHelper *pHelper = new FramelessHelper(this);
pHelper->activateOn(this); //激活当前窗体
pHelper->setTitleHeight(m_pTitleBar->height()); //设置窗体的标题栏高度
pHelper->setWidgetMovable(true); //设置窗体可移动
pHelper->setWidgetResizable(true); //设置窗体可缩放
pHelper->setRubberBandOnMove(true); //设置橡皮筋效果-可移动
pHelper->setRubberBandOnResize(true); //设置橡皮筋效果-可缩放

平台支持

因为使用的是纯Qt实现,所以支持跨平台!Win7、Win10、Redhat7.0已测试通过,其它平台尚未测试,有需要的童鞋可自行实验。


原文作者:一去丶二三里
作者博客:去作者博客空间

【Qt】Qt之自定义界面(窗体缩放-跨平台终极版)【转】的更多相关文章

  1. Qt之自定义界面(窗体缩放-跨平台终极版)

    简述 通过上一节内容,我们实现了窗体的缩放,功能很不错,但是很遗憾-不支持跨平台!如果对于多平台来说,这是一个硬伤,所以,我们急需要一个能够支持跨平台的实现方案. 在网上看到过很多不同的实现方式,多多 ...

  2. Qt之自定义界面(窗体缩放)

    简述 通过前两节内容,我们实现了自定义窗体的移动,以及自定义标题栏-用来显示窗体的图标.标题,以及控制窗体最小化.最大化.关闭. 在这之后,我们还缺少窗体的缩放-当鼠标移动到窗体的边框-左.上.右.下 ...

  3. 【Qt】Qt之自定义界面(窗体缩放)【转】

    简述 通过前两节内容,我们实现了自定义窗体的移动,以及自定义标题栏-用来显示窗体的图标.标题,以及控制窗体最小化.最大化.关闭. 在这之后,我们还缺少窗体的缩放-当鼠标移动到窗体的边框-左.上.右.下 ...

  4. pyqt5对用qt designer设计的窗体实现弹出子窗口的示例

    pyqt5对用qt designer设计的窗体实现弹出子窗口的示例 脚本专栏 python 1. 用qt designer编写主窗体,窗体类型是MainWindow,空白窗口上一个按钮.并转换成mai ...

  5. QT QT creator QTsdk的区别

    Qt是一个跨平台的C++图形用户界面应用程序框架.它提供给应用程序开发者建立艺术级的图形用户界面所需的所用功能.Qt是完全面向对象的,很容易扩展,并且允许真正地组件编程. QT Creator 跨平台 ...

  6. QT,QT SDK, QT Creator 区别

    Qt是一个跨平台的C++图形用户界面应用程序框架.(不仅仅是C++,还包括QML,Qquick,html5)它提供给应用程序开发者建立艺术级的图形用户界面所需的所用功能.Qt是完全面向对象的,很容易扩 ...

  7. Qt... configure: error: Qt (>= Qt 2.2.2) (headers…

    转载:http://blog.chinaunix.net/uid-23733724-id-290980.html     昨天开始在自己的fedora12下装qt~ 但是按照教程在/opt/Embed ...

  8. Qt, QT/E, Qtopia 的区别

    转自Qt, QT/E, Qtopia 的区别 Qt泛指Qt的所有桌面版本,比如Qt/X11,Qt Windows,Qt Mac等.由于Qt最早是在Linux中随着KDE流行开来的,因此通常很多人说的Q ...

  9. C#winform 窗体缩放自适应

    1.  首先在窗体上放上一个Panel容器,并将容器的Dock属性设为Fill,即所有的控件都放在了这个容器里. using System; using System.Collections.Gene ...

随机推荐

  1. iOS H5容器的一些探究(二):iOS 下的黑魔法 NSURLProtocol

    来源:景铭巴巴 链接:http://www.jianshu.com/p/03ddcfe5ebd7 iOS H5 容器的一些探究(一):UIWebView 和 WKWebView 的比较和选择 一.前言 ...

  2. JavaScript工厂模式代码

    function createPerson(name,age,job){ var o=new Object(); o.name=name; o.age=age; o.job=job; o.sayNam ...

  3. iOS 中的加密方式

    iOS 中的加密方式 1 加密方式主要有: Base64,MD5,RSA,DES,AES,钥匙串存储,Cookie 2 各加密方式的比较 2.1 Base64 2.1.1 基本原理:采用64个基本的 ...

  4. vb6如何将MSHFlexGrid控件中的内容导出为Excel

    首先的是是准备工作 需要引用 Microsoft Excel 11.0 Object Library 需要添加控件Mircrosoft Hierarchical FlexGrid Control 6. ...

  5. POJ 1679 The Unique MST (最小生成树)

    The Unique MST Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 22668   Accepted: 8038 D ...

  6. MongoDB - The mongo Shell, Data Types in the mongo Shell

    MongoDB BSON provides support for additional data types than JSON. Drivers provide native support fo ...

  7. 可以伸缩的查询面板 (searchBar)

    最近有这样的需求,一个页面查询条件特别多,一次全部展示出来的话就占用大量的空间,所以分成了两类,简单搜索和高级搜索,当点击高级搜索的时候就会全部显示. 这样就存在一个问题,页面(navTab,dial ...

  8. Jquery实现循环删除Reaper某一行

    一.实现的效果图:(点击删除图标,juery实现删除整行) 二.MVC开发模式 SQLServer层 #region 删除 /// <summary> /// 根据自动编号删除快递线路信息 ...

  9. asp双表查询

    sql="select sum(***) as ** from ** as a,** as b where a.id=b.id" Rs(A.id) Rs(B.id) [提示.双表中 ...

  10. 简单的表单验证插件(Jquery)

    在做web开发的时候经常遇到表单验证问题,表单验证一般有客户端验证和服务器端验证,这个验证插件仅仅能满足我的项目中的基本需求的. Validate_Tools.js function Validate ...