博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
qt 拖拽 修改大小(二)
阅读量:6500 次
发布时间:2019-06-24

本文共 16461 字,大约阅读时间需要 54 分钟。

最近项目需要实现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方法的实现请看另一篇文章,不过在我使用的过程中,使用了HTCAPTION这个属性后,原始窗口的双击放大事件被屏蔽掉了,到现在原因未搞清。在qt 拖拽 修改大小这篇文字中提到的bug,我用迂回的方式解决了,那就是使用Qt::WindowSystemMenuHint属性,但是窗口的放大和缩小使用另一种方式解决。

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

代理头文件

1 #ifndef NC_FRAMELESS_HELPER_H  2   3 #define NC_FRAMELESS_HELPER_H  4   5 #include  6   7 #include  8   9 #include 10  11 #include "commonControls/include/commoncontrols_global.h" 12  13 class WidgetResizeHandlerImpl; 14  15 class CRubberBand : public QRubberBand 16  17 { 18  19 public: 20  21     CRubberBand(QRubberBand::Shape s, QWidget * parent = nullptr); 22  23     ~CRubberBand(); 24  25 protected: 26  27     virtual void paintEvent(QPaintEvent *) Q_DECL_OVERRIDE; 28  29     virtual void resizeEvent(QResizeEvent *) Q_DECL_OVERRIDE; 30  31     void changeEvent(QEvent *) Q_DECL_OVERRIDE; 32  33     void showEvent(QShowEvent *) Q_DECL_OVERRIDE; 34  35     void moveEvent(QMoveEvent *) Q_DECL_OVERRIDE; 36  37 private: 38  39 }; 40  41 //鼠标状态,可以获取鼠标当前和目标窗口的关系 42  43 class CursorPosCalculator 44  45 { 46  47 public: 48  49     CursorPosCalculator(){ reset(); } 50  51     void reset(); 52  53     void recalculate(const QPoint& globalMousePos, const QRect& frameRect); 54  55 public: 56  57     bool onEdges; 58  59     bool onLeftEdge; 60  61     bool onRightEdge; 62  63     bool onTopEdge; 64  65     bool onBottomEdge; 66  67     bool onTopLeftEdge; 68  69     bool onBottomLeftEdge; 70  71     bool onTopRightEdge; 72  73     bool onBottomRightEdge; 74  75     static int mBorderWidth; 76  77 }; 78  79 //真正的处理操作类 80  81 class WidgetData 82  83 { 84  85 public: 86  87     WidgetData(WidgetResizeHandlerImpl * d, QWidget* topLevelWidget); 88  89     ~WidgetData(); 90  91     QWidget * widget(); 92  93     void handleWidgetEvent(QEvent * event);//处理指定窗口事件入口函数 94  95     void updateRubberBandStatus(); 96  97 private: 98  99     void updateCursorShape(const QPoint& globalMousePos);100 101     void resizeWidget(const QPoint& globalMousePos);102 103     void moveWidget(const QPoint& globalMousePos);104 105     void handleMousePressEvent(QMouseEvent* event);106 107     void handleMouseReleaseEvent(QMouseEvent* event);108 109     void handleMouseMoveEvent(QMouseEvent* event);110 111     void handleLeaveEvent(QEvent* event);112 113     void handleHoverMoveEvent(QHoverEvent* event);114 115 private:116 117     bool mLeftButtonPressed = false;118 119     bool mCursorShapeChanged = false;120 121     Qt::WindowFlags mWindowFlags;122 123     QPoint mDragPos;//拖拽位置起点124 125     QWidget * mWidget = nullptr;//被代理的窗口指针126 127     CRubberBand * mRubberBand = nullptr;//橡胶类,支持橡胶操作128 129     CursorPosCalculator mPressedMousePos;//鼠标按下时光标信息130 131     CursorPosCalculator mMoveMousePos;//鼠标移动时光标信息132 133     WidgetResizeHandlerImpl * d_ptr;134 135 };136 137 ///说明:当QWidget设置了Qt::FramelessWindowHint属性时,可以借助该类完成:拖拽+窗口大小更改138 139 class COMMONCONTROLS_EXPORT WidgetResizeHandler : public QObject140 141 {142 143 public:144 145     explicit WidgetResizeHandler(QObject* parent = 0);146 147     ~WidgetResizeHandler();148 149 public:150 151     void activateOn(QWidget * topLevelWidget);//添加topLevelWidget事件代理152 153     void removeFrom(QWidget * topLevelWidget);//移除topLevelWidget事件代理154 155     Qt::CursorShape CursorShape(QWidget * widget);156 157     //窗口移动 default:true158 159     void setWidgetMovable(bool movable);160 161     bool isWidgetMovable();162 163     //大小可变 default:true164 165     void setWidgetResizable(bool resizable);166 167     bool isWidgetResizable();168 169     // 橡胶式窗口移动 default:false170 171     void useRubberBandOnMove(bool use);172 173     bool isUsingRubberBandOnMove();174 175     //橡胶式修改大小 default:false176 177     void useRubberBandOnResize(bool use);178 179     bool isUsingRubberBandOnResisze();180 181     void setBorderWidth(int newBorderWidth);182 183     int borderWidth();184 185     //局部可移动186 187     void useLocalMoveabled(bool use);188 189     void addLocalWidget(QWidget *);190 191 protected:192 193     virtual bool eventFilter(QObject * obj, QEvent * event) Q_DECL_OVERRIDE;//?????????????????????????????????????194 195 private:196 197     WidgetResizeHandlerImpl * d_ptr;198 199 };200 201 #endif // NC_FRAMELESS_HELPER_H
View Code
上边头文件中都有基本的注释,我就不过多解释了,下边我主要说下原理

在需要代理的类中声明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构造函数

1 CRubberBand::CRubberBand(QRubberBand::Shape s, QWidget * parent) :QRubberBand(QRubberBand::Rectangle, parent) 2  3 { 4  5     setAttribute(Qt::WA_TranslucentBackground); 6  7 #ifndef Q_DEAD_CODE_FROM_QT4_WIN 8  9     setAttribute(Qt::WA_NoSystemBackground);10 11 #endif //Q_DEAD_CODE_FROM_QT4_WIN12 13     setWindowFlags(windowFlags() | Qt::FramelessWindowHint);14 15 }
View Code
2、使用activateOn后,窗口存储并验证
1 WidgetData::WidgetData(WidgetResizeHandlerImpl * d, QWidget * topLevelWidget) 2  3 { 4  5     this->d_ptr = d; 6  7     mWidget = topLevelWidget; 8  9     mWidget->setMouseTracking(true);10 11     mWindowFlags = mWidget->windowFlags();12 13     //mWindowFlags |= Qt::CustomizeWindowHint | Qt::FramelessWindowHint;14 15     mWindowFlags |= Qt::FramelessWindowHint;16 17     mWidget->setWindowFlags(mWindowFlags);18 19     //mWidget->setWindowFlags( Qt::Popup | Qt::CustomizeWindowHint|Qt::FramelessWindowHint );20 21     //Bug fix, mouse move events does not propagate from child widgets.22 23     //so need the hover events.24 25     mWidget->setAttribute(Qt::WA_Hover);26 27     updateRubberBandStatus();28 29     bool visible = mWidget->isVisible();//防止非widget被代理30 31     mWidget->setVisible(visible);32 33 }
View Code
3、当被代理的窗口有鼠标事件时,先有代理处理
1 void WidgetData::handleWidgetEvent(QEvent * event) 2  3 { 4  5     switch (event->type()) 6  7     { 8  9     case QEvent::MouseButtonPress:10 11         handleMousePressEvent(static_cast(event));12 13         break;14 15     case QEvent::MouseButtonRelease:16 17         handleMouseReleaseEvent(static_cast(event));18 19         break;20 21     case QEvent::MouseMove:22 23         handleMouseMoveEvent(static_cast(event));24 25         break;26 27     case QEvent::Leave:28 29         handleLeaveEvent(event);30 31         break;32 33     case QEvent::HoverMove:34 35         handleHoverMoveEvent(static_cast(event));36 37         break;38 39     }40 41 }
View Code
4、下边5个函数分别是上边的具体实现
1 void WidgetData::handleMousePressEvent(QMouseEvent * event)  2   3 {  4   5     if (event->button() == Qt::LeftButton)  6   7     {  8   9         mLeftButtonPressed = true; 10  11         QRect frameRect = mWidget->frameGeometry(); 12  13         mPressedMousePos.recalculate(event->globalPos(), frameRect); 14  15         mDragPos = event->globalPos() - frameRect.topLeft(); 16  17         if (mPressedMousePos.onEdges) 18  19         { 20  21             if (d_ptr->mUseRubberBandOnResize) 22  23             { 24  25                 mRubberBand->setGeometry(frameRect); 26  27                 //mRubberBand->show(); 28  29             } 30  31         } 32  33         else if (d_ptr->mUseRubberBandOnMove) 34  35         { 36  37             mRubberBand->setGeometry(frameRect); 38  39             //mRubberBand->show(); 40  41         } 42  43         if (d_ptr->mLocalOnMove)//启用局部拖拽功能后   需要处理不在指定范围内的拖拽,并过滤掉 44  45         { 46  47             bool canMove = false; 48  49             for (int i = 0; i < d_ptr->mLocalWidget.size(); ++i) 50  51             { 52  53                 if (d_ptr->mLocalWidget[i]->rect().contains(d_ptr->mLocalWidget[i]->mapFromGlobal(event->globalPos()))) 54  55                 { 56  57                     canMove = true; 58  59                     break; 60  61                 } 62  63             } 64  65             if (canMove == false && mPressedMousePos.onEdges == false) 66  67             { 68  69                 mLeftButtonPressed = false; 70  71             } 72  73         } 74  75     } 76  77 } 78  79 void WidgetData::handleMouseReleaseEvent(QMouseEvent* event) 80  81 { 82  83     if (event->button() == Qt::LeftButton) 84  85     { 86  87         d_ptr->mCanMoveFlag = false; 88  89         mLeftButtonPressed = false; 90  91         mPressedMousePos.reset(); 92  93         if (mRubberBand && mRubberBand->isVisible()) 94  95         { 96  97             mRubberBand->hide(); 98  99             mWidget->setGeometry(mRubberBand->geometry());100 101         }102 103     }104 105 }106 107 void WidgetData::handleMouseMoveEvent(QMouseEvent* event)108 109 {110 111     if (mLeftButtonPressed)112 113     {114 115         if (d_ptr->mWidgetResizable && mPressedMousePos.onEdges)116 117         {118 119             resizeWidget(event->globalPos());120 121         }122 123         else if (d_ptr->mWidgetMovable)124 125         {126 127             moveWidget(event->globalPos());128 129         }130 131     }132 133     else if (d_ptr->mWidgetResizable)134 135     {136 137         updateCursorShape(event->globalPos());138 139     }140 141 }142 143 void WidgetData::handleLeaveEvent(QEvent*)144 145 {146 147     if (!mLeftButtonPressed)148 149     {150 151         mWidget->unsetCursor();152 153     }154 155 }156 157 void WidgetData::handleHoverMoveEvent(QHoverEvent* event)158 159 {160 161     if (mLeftButtonPressed)162 163     {164 165         return;166 167     }168 169     if (d_ptr->mWidgetResizable)170 171     {172 173         updateCursorShape(mWidget->mapToGlobal(event->pos()));174 175     }176 177 }
View Code

5、更新鼠标状态

1 void WidgetData::updateCursorShape(const QPoint & globalMousePos) 2  3 { 4  5     if (mWidget->isFullScreen() || mWidget->isMaximized()) 6  7     { 8  9         if (mCursorShapeChanged)10 11         {12 13             mWidget->unsetCursor();14 15         }16 17         return;18 19     }20 21     mMoveMousePos.recalculate(globalMousePos, mWidget->frameGeometry());22 23     if (mMoveMousePos.onTopLeftEdge || mMoveMousePos.onBottomRightEdge)24 25     {26 27         mWidget->setCursor(Qt::SizeFDiagCursor);28 29         mCursorShapeChanged = true;30 31     }32 33     else if (mMoveMousePos.onTopRightEdge || mMoveMousePos.onBottomLeftEdge)34 35     {36 37         mWidget->setCursor(Qt::SizeBDiagCursor);38 39         mCursorShapeChanged = true;40 41     }42 43     else if (mMoveMousePos.onLeftEdge || mMoveMousePos.onRightEdge)44 45     {46 47         mWidget->setCursor(Qt::SizeHorCursor);48 49         mCursorShapeChanged = true;50 51     }52 53     else if (mMoveMousePos.onTopEdge || mMoveMousePos.onBottomEdge)54 55     {56 57         mWidget->setCursor(Qt::SizeVerCursor);58 59         mCursorShapeChanged = true;60 61     }62 63     else64 65     {66 67         if (mCursorShapeChanged)//修改鼠标状态68 69         {70 71             mWidget->unsetCursor();72 73             mCursorShapeChanged = false;74 75         }76 77     }78 79 }
View Code
6、修改窗口大小和移动位置
1 void WidgetData::resizeWidget(const QPoint& globalMousePos)  2   3 {  4   5     QRect origRect;  6   7     if (d_ptr->mUseRubberBandOnResize)  8   9     { 10  11         origRect = mRubberBand->frameGeometry(); 12  13     } 14  15     else 16  17     { 18  19         origRect = mWidget->frameGeometry(); 20  21     } 22  23     int left = origRect.left(); 24  25     int top = origRect.top(); 26  27     int right = origRect.right(); 28  29     int bottom = origRect.bottom(); 30  31     origRect.getCoords(&left, &top, &right, &bottom); 32  33     int minWidth = mWidget->minimumWidth(); 34  35     int minHeight = mWidget->minimumHeight(); 36  37     if (mPressedMousePos.onTopLeftEdge) 38  39     { 40  41         left = globalMousePos.x(); 42  43         top = globalMousePos.y(); 44  45     } 46  47     else if (mPressedMousePos.onBottomLeftEdge) 48  49     { 50  51         left = globalMousePos.x(); 52  53         bottom = globalMousePos.y(); 54  55     } 56  57     else if (mPressedMousePos.onTopRightEdge) 58  59     { 60  61         right = globalMousePos.x(); 62  63         top = globalMousePos.y(); 64  65     } 66  67     else if (mPressedMousePos.onBottomRightEdge) 68  69     { 70  71         right = globalMousePos.x(); 72  73         bottom = globalMousePos.y(); 74  75     } 76  77     else if (mPressedMousePos.onLeftEdge) 78  79     { 80  81         left = globalMousePos.x(); 82  83         int max_width = mWidget->maximumWidth(); 84  85         if (right - left > max_width) 86  87         { 88  89             return; 90  91         } 92  93     } 94  95     else if (mPressedMousePos.onRightEdge) 96  97     { 98  99         right = globalMousePos.x();100 101     }102 103     else if (mPressedMousePos.onTopEdge)104 105     {106 107         top = globalMousePos.y();108 109     }110 111     else if (mPressedMousePos.onBottomEdge)112 113     {114 115         bottom = globalMousePos.y();116 117     }118 119     QRect newRect(QPoint(left, top), QPoint(right, bottom));120 121     if (newRect.isValid())122 123     {124 125         if (minWidth > newRect.width())126 127         {128 129             //determine what has caused the width change.130 131             if (left != origRect.left())132 133                 newRect.setLeft(origRect.left());134 135             else136 137                 newRect.setRight(origRect.right());138 139         }140 141         if (minHeight > newRect.height())142 143         {144 145             //determine what has caused the height change.146 147             if (top != origRect.top())148 149                 newRect.setTop(origRect.top());150 151             else152 153                 newRect.setBottom(origRect.bottom());154 155         }156 157         if (d_ptr->mUseRubberBandOnResize)158 159         {160 161             if (mRubberBand->isVisible() == false)162 163             {164 165                 mRubberBand->show();166 167             }168 169             mRubberBand->setGeometry(newRect);170 171         }172 173         else174 175         {176 177             //mWidget->setGeometry(newRect);178 179             mWidget->move(newRect.topLeft());180 181             mWidget->resize(newRect.size());182 183         }184 185     }186 187     else188 189     {190 191         //qDebug() << "Calculated Rect is not valid" << newRect;192 193     }194 195 }196 197 void WidgetData::moveWidget(const QPoint & globalMousePos)198 199 {200 201     bool canMove = false;202 203     if (d_ptr->mLocalOnMove == true && d_ptr->mCanMoveFlag != true)204 205     {206 207         for (int i = 0; i < d_ptr->mLocalWidget.size(); ++i)208 209         {210 211             if (d_ptr->mLocalWidget[i]->rect().contains(d_ptr->mLocalWidget[i]->mapFromGlobal(globalMousePos)))212 213             {214 215                 canMove = true;216 217                 d_ptr->mCanMoveFlag = true;218 219                 break;220 221             }222 223         }224 225     }226 227     else228 229     {230 231         canMove = true;232 233     }234 235     if (canMove)236 237     {238 239         if (d_ptr->mUseRubberBandOnMove)240 241         {242 243             if (mRubberBand->isVisible() == false)244 245             {246 247                 mRubberBand->show();248 249             }250 251             mRubberBand->move(globalMousePos - mDragPos);252 253         }254 255         else256 257         {258 259             mWidget->move(globalMousePos - mDragPos);260 261         }262 263     }264 265 }
View Code
 

转载地址:http://xrvyo.baihongyu.com/

你可能感兴趣的文章
第一次操刀数据库分表的教训与经验
查看>>
录音声音小
查看>>
Ubuntu 12.04 安装 Chrome浏览器
查看>>
java 反射
查看>>
ORACLE物化视图(物理视图)
查看>>
android 读取json数据(遍历JSONObject和JSONArray)(转)
查看>>
UIScrollView中的手势
查看>>
递归和迭代的差别
查看>>
基于jquery的可拖动div
查看>>
可以简易设置文字内边距的EdgeInsetsLabel
查看>>
[詹兴致矩阵论习题参考解答]习题1.3
查看>>
Android Fragment的使用
查看>>
沙朗javascript总结一下(一)---基础知识
查看>>
js深入研究之函数内的函数
查看>>
LeetCode:4_Median of Two Sorted Arrays | 求两个排序数组的中位数 | Hard
查看>>
python之commands模块
查看>>
android应用开发--------------看RadioGroup源代码,写相似单选选项卡的集成控件(如底部导航,tab等等)...
查看>>
LeetCode - Binary Tree Level Order Traversal
查看>>
FTP协议完全详解
查看>>
【C语言天天练(十五)】字符串输入函数fgets、gets和scanf
查看>>