最近项目需要实现windows下橡皮筋的效果,所以对此做了一些了解,特此记录。

首先windows系统是支持橡皮筋效果的,需要使用win32方 法:SystemParametersInfo(SPI_SETDRAGFULLWINDOWS, showFullWindow, NULL, 0);showFullWindow是一个变量,如果需要windows默认支持橡皮筋则需要传递参数false,否则传递参数true,如果使用 windows默认的橡皮筋缩放,效果如图1所示,会产生一个矩形框,不管是窗口移动还是放大缩小,都会对该矩形框作用,然后当鼠标弹起时,真实窗口才会 移动或者放大缩小。如果不使用橡皮筋拖拽的方式,那么窗口就是实时的拖拽。

图1 windows橡皮筋

在使用Qt窗口时,如果需要支持windows系统这种方式的拖拽,不能够使用setGeometry该函数来移动或者放大缩小窗口,而需要 重写QWidget::nativeEvent这个方法,该方法是在消息进入qt事件循环之前调用的,也就是说该方法会在mouseEvent等方法之前 调用,nativeEvent方法的实现请看另一篇文章qt 拖拽 修改大小,不过在我使用的过程中,使用了HTCAPTION这个属性后,原始窗口的双击放大事件被屏蔽掉了,到现在原因未搞清。在qt 拖拽 修改大小这篇文字中提到的bug,我用迂回的方式解决了,那就是使用Qt::WindowSystemMenuHint属性,但是窗口的放大和缩小使用另一种方式解决。

下面就是我使用代理的方式来支持窗口拖拽,由于该代理代码行数过多,我只写下重点的部分,该代理代码我也是从别人那儿拷贝的,后来根据我自己的理解和项目需求添加了一些东西。

代理头文件

 #ifndef NC_FRAMELESS_HELPER_H

 #define NC_FRAMELESS_HELPER_H

 #include

 #include

 #include

 #include "commonControls/include/commoncontrols_global.h"

 class WidgetResizeHandlerImpl;

 class CRubberBand : public QRubberBand

 {

 public:

     CRubberBand(QRubberBand::Shape s, QWidget * parent = nullptr);

     ~CRubberBand();

 protected:

     virtual void paintEvent(QPaintEvent *) Q_DECL_OVERRIDE;

     virtual void resizeEvent(QResizeEvent *) Q_DECL_OVERRIDE;

     void changeEvent(QEvent *) Q_DECL_OVERRIDE;

     void showEvent(QShowEvent *) Q_DECL_OVERRIDE;

     void moveEvent(QMoveEvent *) Q_DECL_OVERRIDE;

 private:

 };

 //鼠标状态,可以获取鼠标当前和目标窗口的关系

 class CursorPosCalculator

 {

 public:

     CursorPosCalculator(){ reset(); }

     void reset();

     void recalculate(const QPoint& globalMousePos, const QRect& frameRect);

 public:

     bool onEdges;

     bool onLeftEdge;

     bool onRightEdge;

     bool onTopEdge;

     bool onBottomEdge;

     bool onTopLeftEdge;

     bool onBottomLeftEdge;

     bool onTopRightEdge;

     bool onBottomRightEdge;

     static int mBorderWidth;

 };

 //真正的处理操作类

 class WidgetData

 {

 public:

     WidgetData(WidgetResizeHandlerImpl * d, QWidget* topLevelWidget);

     ~WidgetData();

     QWidget * widget();

     void handleWidgetEvent(QEvent * event);//处理指定窗口事件入口函数

     void updateRubberBandStatus();

 private:

     void updateCursorShape(const QPoint& globalMousePos);

     void resizeWidget(const QPoint& globalMousePos);

     void moveWidget(const QPoint& globalMousePos);

     void handleMousePressEvent(QMouseEvent* event);

     void handleMouseReleaseEvent(QMouseEvent* event);

     void handleMouseMoveEvent(QMouseEvent* event);

     void handleLeaveEvent(QEvent* event);

     void handleHoverMoveEvent(QHoverEvent* event);

 private:

     bool mLeftButtonPressed = false;

     bool mCursorShapeChanged = false;

     Qt::WindowFlags mWindowFlags;

     QPoint mDragPos;//拖拽位置起点

     QWidget * mWidget = nullptr;//被代理的窗口指针

     CRubberBand * mRubberBand = nullptr;//橡胶类,支持橡胶操作

     CursorPosCalculator mPressedMousePos;//鼠标按下时光标信息

     CursorPosCalculator mMoveMousePos;//鼠标移动时光标信息

     WidgetResizeHandlerImpl * d_ptr;

 };

 ///说明:当QWidget设置了Qt::FramelessWindowHint属性时,可以借助该类完成:拖拽+窗口大小更改

 class COMMONCONTROLS_EXPORT WidgetResizeHandler : public QObject

 {

 public:

     explicit WidgetResizeHandler(QObject* parent = );

     ~WidgetResizeHandler();

 public:

     void activateOn(QWidget * topLevelWidget);//添加topLevelWidget事件代理

     void removeFrom(QWidget * topLevelWidget);//移除topLevelWidget事件代理

     Qt::CursorShape CursorShape(QWidget * widget);

     //窗口移动 default:true

     void setWidgetMovable(bool movable);

     bool isWidgetMovable();

     //大小可变 default:true

     void setWidgetResizable(bool resizable);

     bool isWidgetResizable();

     // 橡胶式窗口移动 default:false

     void useRubberBandOnMove(bool use);

     bool isUsingRubberBandOnMove();

     //橡胶式修改大小 default:false

     void useRubberBandOnResize(bool use);

     bool isUsingRubberBandOnResisze();

     void setBorderWidth(int newBorderWidth);

     int borderWidth();

     //局部可移动

     void useLocalMoveabled(bool use);

     void addLocalWidget(QWidget *);

 protected:

     virtual bool eventFilter(QObject * obj, QEvent * event) Q_DECL_OVERRIDE;//?????????????????????????????????????

 private:

     WidgetResizeHandlerImpl * d_ptr;

 };

 #endif // NC_FRAMELESS_HELPER_H

上边头文件中都有基本的注释,我就不过多解释了,下边我主要说下原理

在需要代理的类中声明WidgetResizeHandler对象,然后使用activateOn方法把需要代理的窗口添加到代理,注意被代 理的窗口需要含有Qt::Window属性明也就是需要时顶层窗口,如果对于一个复杂的窗口进行代理时,可能会出现一些意向不到的问题,比如:1、 QLabel接受富文本时,代理拿不到鼠标弹起事件,QToolButton对象不放到布局时,代理也拿不到鼠标弹起事件,这会导致代理不能正常使用,因 此我对该代理进行了修改,添加了useLocalMoveabled接口,允许代理只对局部窗口进行移动,这样是解决了我前边提到的两个问题。如果仔细看 应该也能看到我的代理也是使用setGeometry方法来拖拽窗口的,那么和我之前谈论的橡皮筋方式就有出入了,这个时候重点才来了,哈哈哈,继续往下 看,头文件中有个类CRubberBand,他是继承自QRubberBand,该类就模拟了一个橡皮筋的过程,只是qt提供的类接口有限,有一些写过很 难达到,比如说我要实现一些复杂的代理界面,那么我们就只能自己绘制了,我通过重新实现paintEvent函数,对该类画了一个灰色的矩形框,代码如 下:

QPainter p(this);

p.setPen(QPen(QColor(102, 102, 102), 4));

QRect rect = this->rect().adjusted(2, 2, -3, -3);

p.drawRect(rect);

如果照着我我上边所说的流程走,就会发现除了一个矩形框之外还会有一个背景色填充,这个时候就奇怪了,我们paintEvent并没有画背景 啊,呵呵呵,只需要在构造函数里加上这句话即可setAttribute(Qt::WA_NoSystemBackground),效果如图2所示。

图2 定制橡皮筋

下边我添加一些代理部分代码

1、CRubberBand构造函数

 CRubberBand::CRubberBand(QRubberBand::Shape s, QWidget * parent) :QRubberBand(QRubberBand::Rectangle, parent)

 {

     setAttribute(Qt::WA_TranslucentBackground);

 #ifndef Q_DEAD_CODE_FROM_QT4_WIN

     setAttribute(Qt::WA_NoSystemBackground);

 #endif //Q_DEAD_CODE_FROM_QT4_WIN

     setWindowFlags(windowFlags() | Qt::FramelessWindowHint);

 }

2、使用activateOn后,窗口存储并验证

 WidgetData::WidgetData(WidgetResizeHandlerImpl * d, QWidget * topLevelWidget)

 {

     this->d_ptr = d;

     mWidget = topLevelWidget;

     mWidget->setMouseTracking(true);

     mWindowFlags = mWidget->windowFlags();

     //mWindowFlags |= Qt::CustomizeWindowHint | Qt::FramelessWindowHint;

     mWindowFlags |= Qt::FramelessWindowHint;

     mWidget->setWindowFlags(mWindowFlags);

     //mWidget->setWindowFlags( Qt::Popup | Qt::CustomizeWindowHint|Qt::FramelessWindowHint );

     //Bug fix, mouse move events does not propagate from child widgets.

     //so need the hover events.

     mWidget->setAttribute(Qt::WA_Hover);

     updateRubberBandStatus();

     bool visible = mWidget->isVisible();//防止非widget被代理

     mWidget->setVisible(visible);

 }

3、当被代理的窗口有鼠标事件时,先有代理处理

 void WidgetData::handleWidgetEvent(QEvent * event)

 {

     switch (event->type())

     {

     case QEvent::MouseButtonPress:

         handleMousePressEvent(static_cast(event));

         break;

     case QEvent::MouseButtonRelease:

         handleMouseReleaseEvent(static_cast(event));

         break;

     case QEvent::MouseMove:

         handleMouseMoveEvent(static_cast(event));

         break;

     case QEvent::Leave:

         handleLeaveEvent(event);

         break;

     case QEvent::HoverMove:

         handleHoverMoveEvent(static_cast(event));

         break;

     }

 }

4、下边5个函数分别是上边的具体实现

 void WidgetData::handleMousePressEvent(QMouseEvent * event)

 {

     if (event->button() == Qt::LeftButton)

     {

         mLeftButtonPressed = true;

         QRect frameRect = mWidget->frameGeometry();

         mPressedMousePos.recalculate(event->globalPos(), frameRect);

         mDragPos = event->globalPos() - frameRect.topLeft();

         if (mPressedMousePos.onEdges)

         {

             if (d_ptr->mUseRubberBandOnResize)

             {

                 mRubberBand->setGeometry(frameRect);

                 //mRubberBand->show();

             }

         }

         else if (d_ptr->mUseRubberBandOnMove)

         {

             mRubberBand->setGeometry(frameRect);

             //mRubberBand->show();

         }

         if (d_ptr->mLocalOnMove)//启用局部拖拽功能后   需要处理不在指定范围内的拖拽,并过滤掉

         {

             bool canMove = false;

             for (int i = ; i < d_ptr->mLocalWidget.size(); ++i)

             {

                 if (d_ptr->mLocalWidget[i]->rect().contains(d_ptr->mLocalWidget[i]->mapFromGlobal(event->globalPos())))

                 {

                     canMove = true;

                     break;

                 }

             }

             if (canMove == false && mPressedMousePos.onEdges == false)

             {

                 mLeftButtonPressed = false;

             }

         }

     }

 }

 void WidgetData::handleMouseReleaseEvent(QMouseEvent* event)

 {

     if (event->button() == Qt::LeftButton)

     {

         d_ptr->mCanMoveFlag = false;

         mLeftButtonPressed = false;

         mPressedMousePos.reset();

         if (mRubberBand && mRubberBand->isVisible())

         {

             mRubberBand->hide();

             mWidget->setGeometry(mRubberBand->geometry());

         }

     }

 }

 void WidgetData::handleMouseMoveEvent(QMouseEvent* event)

 {

     if (mLeftButtonPressed)

     {

         if (d_ptr->mWidgetResizable && mPressedMousePos.onEdges)

         {

             resizeWidget(event->globalPos());

         }

         else if (d_ptr->mWidgetMovable)

         {

             moveWidget(event->globalPos());

         }

     }

     else if (d_ptr->mWidgetResizable)

     {

         updateCursorShape(event->globalPos());

     }

 }

 void WidgetData::handleLeaveEvent(QEvent*)

 {

     if (!mLeftButtonPressed)

     {

         mWidget->unsetCursor();

     }

 }

 void WidgetData::handleHoverMoveEvent(QHoverEvent* event)

 {

     if (mLeftButtonPressed)

     {

         return;

     }

     if (d_ptr->mWidgetResizable)

     {

         updateCursorShape(mWidget->mapToGlobal(event->pos()));

     }

 }

5、更新鼠标状态

 void WidgetData::updateCursorShape(const QPoint & globalMousePos)

 {

     if (mWidget->isFullScreen() || mWidget->isMaximized())

     {

         if (mCursorShapeChanged)

         {

             mWidget->unsetCursor();

         }

         return;

     }

     mMoveMousePos.recalculate(globalMousePos, mWidget->frameGeometry());

     if (mMoveMousePos.onTopLeftEdge || mMoveMousePos.onBottomRightEdge)

     {

         mWidget->setCursor(Qt::SizeFDiagCursor);

         mCursorShapeChanged = true;

     }

     else if (mMoveMousePos.onTopRightEdge || mMoveMousePos.onBottomLeftEdge)

     {

         mWidget->setCursor(Qt::SizeBDiagCursor);

         mCursorShapeChanged = true;

     }

     else if (mMoveMousePos.onLeftEdge || mMoveMousePos.onRightEdge)

     {

         mWidget->setCursor(Qt::SizeHorCursor);

         mCursorShapeChanged = true;

     }

     else if (mMoveMousePos.onTopEdge || mMoveMousePos.onBottomEdge)

     {

         mWidget->setCursor(Qt::SizeVerCursor);

         mCursorShapeChanged = true;

     }

     else

     {

         if (mCursorShapeChanged)//修改鼠标状态

         {

             mWidget->unsetCursor();

             mCursorShapeChanged = false;

         }

     }

 }

6、修改窗口大小和移动位置

 void WidgetData::resizeWidget(const QPoint& globalMousePos)

 {

     QRect origRect;

     if (d_ptr->mUseRubberBandOnResize)

     {

         origRect = mRubberBand->frameGeometry();

     }

     else

     {

         origRect = mWidget->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 = mWidget->minimumWidth();

     int minHeight = mWidget->minimumHeight();

     if (mPressedMousePos.onTopLeftEdge)

     {

         left = globalMousePos.x();

         top = globalMousePos.y();

     }

     else if (mPressedMousePos.onBottomLeftEdge)

     {

         left = globalMousePos.x();

         bottom = globalMousePos.y();

     }

     else if (mPressedMousePos.onTopRightEdge)

     {

         right = globalMousePos.x();

         top = globalMousePos.y();

     }

     else if (mPressedMousePos.onBottomRightEdge)

     {

         right = globalMousePos.x();

         bottom = globalMousePos.y();

     }

     else if (mPressedMousePos.onLeftEdge)

     {

         left = globalMousePos.x();

         int max_width = mWidget->maximumWidth();

         if (right - left > max_width)

         {

             return;

         }

     }

     else if (mPressedMousePos.onRightEdge)

     {

         right = globalMousePos.x();

     }

     else if (mPressedMousePos.onTopEdge)

     {

         top = globalMousePos.y();

     }

     else if (mPressedMousePos.onBottomEdge)

     {

         bottom = globalMousePos.y();

     }

     QRect newRect(QPoint(left, top), QPoint(right, bottom));

     if (newRect.isValid())

     {

         if (minWidth > newRect.width())

         {

             //determine what has caused the width change.

             if (left != origRect.left())

                 newRect.setLeft(origRect.left());

             else

                 newRect.setRight(origRect.right());

         }

         if (minHeight > newRect.height())

         {

             //determine what has caused the height change.

             if (top != origRect.top())

                 newRect.setTop(origRect.top());

             else

                 newRect.setBottom(origRect.bottom());

         }

         if (d_ptr->mUseRubberBandOnResize)

         {

             if (mRubberBand->isVisible() == false)

             {

                 mRubberBand->show();

             }

             mRubberBand->setGeometry(newRect);

         }

         else

         {

             //mWidget->setGeometry(newRect);

             mWidget->move(newRect.topLeft());

             mWidget->resize(newRect.size());

         }

     }

     else

     {

         //qDebug() << "Calculated Rect is not valid" << newRect;

     }

 }

 void WidgetData::moveWidget(const QPoint & globalMousePos)

 {

     bool canMove = false;

     if (d_ptr->mLocalOnMove == true && d_ptr->mCanMoveFlag != true)

     {

         for (int i = ; i < d_ptr->mLocalWidget.size(); ++i)

         {

             if (d_ptr->mLocalWidget[i]->rect().contains(d_ptr->mLocalWidget[i]->mapFromGlobal(globalMousePos)))

             {

                 canMove = true;

                 d_ptr->mCanMoveFlag = true;

                 break;

             }

         }

     }

     else

     {

         canMove = true;

     }

     if (canMove)

     {

         if (d_ptr->mUseRubberBandOnMove)

         {

             if (mRubberBand->isVisible() == false)

             {

                 mRubberBand->show();

             }

             mRubberBand->move(globalMousePos - mDragPos);

         }

         else

         {

             mWidget->move(globalMousePos - mDragPos);

         }

     }

 }

如果您觉得文章不错,不妨给个打赏,写作不易,感谢各位的支持。您的支持是我最大的动力,谢谢!!! 

 

很重要--转载声明

  1. 本站文章无特别说明,皆为原创,版权所有,转载时请用链接的方式,给出原文出处。同时写上原作者:朝十晚八 or Twowords
  2. 如要转载,请原文转载,如在转载时修改本文,请事先告知,谢绝在转载时通过修改本文达到有利于转载者的目的。

qt 拖拽 修改大小(二)的更多相关文章

  1. qt 拖拽 修改大小

    写次篇文章之前,qt窗口的放大缩小和拖拽我都是通过setGeometry方法实现的,但是作为windows程序,windows支持橡 皮筋式(拖拽时有一个虚框)拖拽和拉伸.通过setGeometry方 ...

  2. qt 拖拽 修改大小(使用了nativeEvent和winEvent)

    http://www.cnblogs.com/swarmbees/p/5621543.html http://blog.sina.com.cn/s/blog_9e59cf590102w3r6.html

  3. Qt::QWidget 无默认标题栏边框的拖拽修改大小方式

    开发环境:win10+vs2015+qt5.9.1 背景:开发过程中,一般很少会使用系统提供的标题栏和边框:往往都是自定义一个自己设计的方案.这时候在QWidget中需要加上flag:Qt::Fram ...

  4. Qt拖拽界面 (*.ui) 缩放问题及解决办法

    问题 使用Qt Designer 设计的界面,在缩放的时候不能随着主窗口一起缩放. 解决办法 之前遇到这个问题的时候,都是直接重写resizeEvent接口来实现的,在自动生成的Ui_Widget或U ...

  5. canvas 图片拖拽旋转之二——canvas状态保存(save和restore)

    引言 在上一篇日志“canvas 图片拖拽旋转之一”中,对坐标转换有了比较深入的了解,但是仅仅利用坐标转换实现的拖拽旋转,会改变canvas坐标系的状态,从而影响画布上其他元素的绘制.因此,这个时候需 ...

  6. 解决Delphi图形化界面的TEdit、TLable等组件手动拖拽固定大小,但是编译之后显示有差别的情况

    经常遇到这样的情况,在我们使用Delphi的可视化工具进行UI设计的时候,我们拖拽TEdit或者Label组件,并且在可视化界面上设置它们的长.宽 但是当我们编译和运行程序的时候,却发现真正显示出来的 ...

  7. Qt拖拽界面 (*.ui) 缩放问题及解决办法(在最顶层放一个Layout)

    问题 使用Qt Designer 设计的界面,在缩放的时候不能随着主窗口一起缩放. 解决办法 之前遇到这个问题的时候,都是直接重写resizeEvent接口来实现的,在自动生成的Ui_Widget或U ...

  8. JavaScript小实例:拖拽应用(二)

    经常在网站别人的网站的注册页中看到一个拖拽验证的效果,就是它的验证码刚开始不出来,而是有一个拖拽的条,你必须将这个拖拽条拖到底,验证码才出来,说了感觉跟没说一样,你还是不理解,好吧,我给个图你看看: ...

  9. react之每日一更(实现canvas拖拽,增、删、改拖拽模块大小功能)

    效果图: import React, { Component } from 'react'; import scaleImage from './images/scale.png'; import c ...

随机推荐

  1. Unity Shader Learning

    Toon 表面没有均匀的阴影. 为了达到这个效果,我们需要一个斜坡图. 其目的是将朗伯光强度NdotL重新映射到另一个值. 使用没有渐变的渐变映射,我们可以强制照明逐步渲染.下图显示了如何使用斜坡图来 ...

  2. SpringCloud Gateway 测试问题解决

    本文针对于测试环境SpringCloud Gateway问题解决. 1.背景介绍 本文遇到的问题都是在测试环境真正遇到的问题,不一定试用于所有人,仅做一次记录,便于遇到同样问题的干掉这些问题. 使用版 ...

  3. 2018-2019-2 网络对抗技术 20162329 Exp2 后门原理与实践

    目录 1.实践基础 1.1.什么是后门 1.2.基础问题 2.实践内容 2.1.使用netcat获取主机操作Shell,cron启动 2.2.使用socat获取主机操作Shell, 任务计划启动 2. ...

  4. 使用curl上传图片的方法

    关键:当参数名为"@绝对路径",这时 CURL 會幫你做 multipart/form-data 編碼 实现方法: $params = array( 'file' => '@ ...

  5. json格式的数据及遍历:

    代码: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8 ...

  6. ES6学习

    一.ES6的特点 1.let(变量),const(常量) 2.在ES6中不能重复定义 3.块级作用域 普通作用域 if(true){ var test =1; } console.log(test); ...

  7. CSS _text-align:justify;实现两端对齐

    参考:https://segmentfault.com/q/1010000007136263 法一:text-align-last:justify: html <div> <p cl ...

  8. LeetCode 81 Search in Rotated Sorted Array II [binary search] <c++>

    LeetCode 81 Search in Rotated Sorted Array II [binary search] <c++> 给出排序好的一维有重复元素的数组,随机取一个位置断开 ...

  9. cnn神经网络入门

    1.神经网络最基本的原理也是函数拟合,所以lose function就显得非常重要了,我们训练的目的之一就是减小损失函数,常用的损失函数参考:https://www.cnblogs.com/hypnu ...

  10. CSS背景图片

    1.背景图片插入 代码格式:background-image:url(): 括号内填写图片路径 2.背景图片设置大小 代码格式:background-size:宽.高 3.背景图片设置不平铺 代码格式 ...