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

    xiaoxiao2021-03-25  31

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

    简述

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

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

    简述效果窗体缩放 实现接口说明使用方式平台支持

    效果

    窗体缩放

    实现

    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 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748

    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; }; 12345678910111213 12345678910111213

    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; }; 12345678910111213141516171819202122232425 12345678910111213141516171819202122232425 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; } 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253

    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; }; 123456789101112131415161718192021222324252627282930313233343536373839404142434445 123456789101112131415161718192021222324252627282930313233343536373839404142434445 /***** 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())); } } 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288

    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; } 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149

    接口说明

    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); //设置橡皮筋效果-可缩放 1234567 1234567

    平台支持

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

    博乐点评 一共有 2位博乐进行推荐

    丁国华  2016-03-19 14:29:22  楼主写的写得非常好,尤其是代码非常整齐规范,注释也写的棒棒哒,值得推荐! foruok  2016-01-25 22:43:18  看图片效果不错啊,代码也比较清晰,赞一个

    转载请注明原文地址: https://ju.6miu.com/read-200026.html

    最新回复(0)