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

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

qt 拖拽 修改大小(二)

图1 windows橡皮筋

在使用Qt窗口时,如果需要支持windows系统这种方式的拖拽,不能够使用setGeometry该函数来移动或者放大缩小窗口,而需要 重写QWidget::nativeEvent这个方法,该方法是在消息进入qt事件循环之前调用的,也就是说该方法会在mouseEvent等方法之前 调用,nativeEvent方法的实现请看另一篇文章qt 拖拽 修改大小,不过在我使用的过程中,使用了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 QObject
140 
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:true
158 
159     void setWidgetMovable(bool movable);
160 
161     bool isWidgetMovable();
162 
163     //大小可变 default:true
164 
165     void setWidgetResizable(bool resizable);
166 
167     bool isWidgetResizable();
168 
169     // 橡胶式窗口移动 default:false
170 
171     void useRubberBandOnMove(bool use);
172 
173     bool isUsingRubberBandOnMove();
174 
175     //橡胶式修改大小 default:false
176 
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所示。

qt 拖拽 修改大小(二)

图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_WIN
12 
13     setWindowFlags(windowFlags() | Qt::FramelessWindowHint);
14 
15 }
View Code

相关文章: