• qt 拖拽 修改大小(二)


    最近项目需要实现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属性,但是窗口的放大和缩小使用另一种方式解决。

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

    代理头文件

      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所示。

    图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
    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     else
    64 
    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             else
    136 
    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             else
    152 
    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         else
    174 
    175         {
    176 
    177             //mWidget->setGeometry(newRect);
    178 
    179             mWidget->move(newRect.topLeft());
    180 
    181             mWidget->resize(newRect.size());
    182 
    183         }
    184 
    185     }
    186 
    187     else
    188 
    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     else
    228 
    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         else
    256 
    257         {
    258 
    259             mWidget->move(globalMousePos - mDragPos);
    260 
    261         }
    262 
    263     }
    264 
    265 }
    View Code
     
    如果您觉得文章不错,不妨给个打赏,写作不易,感谢各位的支持。您的支持是我最大的动力,谢谢!!! 

     

      


    很重要--转载声明

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

  • 相关阅读:
    Navicat Premium 15 永久激活版安装教程
    win10安装redis
    Linux下安装Go环境
    IoT platforms that deserves to be noticed
    TortoiseGit配置
    impot和require区别
    websocket搭建简单的H264实时视频流播放
    应用索引技术优化SQL 语句(Part 3)
    应用索引技术优化SQL 语句(Part 2)
    应用索引技术优化SQL 语句(Part 1)
  • 原文地址:https://www.cnblogs.com/swarmbees/p/5621567.html
Copyright © 2020-2023  润新知