Qt界面中的子窗口实现鼠标拖动边缘改变大小以及移动(完整demo代码)

发布于:2024-07-05 ⋅ 阅读:(21) ⋅ 点赞:(0)

目录

效果

拖拽

移动​编辑

实现

 DragResizeWgt类.h文件

DragResizeWgt类.cpp文件 

使用

 testwidget窗口.ui文件

testwidget窗口.h文件

testwidget窗口.cpp文件

参考


效果

想要的效果就是类似于QT IDE中的效果,可以拖动边缘改变大小,用户自身可以调整一些窗口的布局,方便使用,如下所示:

demo完成后,经测试达到的效果,如下所示:

拖拽

移动

如果你想要的是这样的效果,无需多言,上代码。

实现

这是一个自定义的窗口类,需要重写窗口的一些函数,如下所示:

    void mousePressEvent(QMouseEvent *event);
    void mouseMoveEvent(QMouseEvent *event);
    void mouseReleaseEvent(QMouseEvent *event);
    void paintEvent(QPaintEvent *event);
    void resizeEvent(QResizeEvent *event);

 DragResizeWgt类.h文件

#ifndef DRAGRESIZEWGT_H
#define DRAGRESIZEWGT_H

#include <QWidget>
#include <QMouseEvent>
#include <QPoint>
#include <QScreen>
#include <QPainter>
#include <QResizeEvent>
#include <QEvent>
namespace Ui {
class DragResizeWgt;
}

class DragResizeWgt : public QWidget
{
    Q_OBJECT
    enum DIRECTION{
        nodir,
        top = 0x01,
        bottom = 0x02,
        left = 0x04,
        right = 0x08,
        topLeft = 0x01 | 0x04,
        topRight = 0x01 | 0x08,
        bottomLeft = 0x02 | 0x04,
        bottomRight = 0x02 | 0x08};
public:
    explicit DragResizeWgt(QWidget *parent = nullptr);
    ~DragResizeWgt();
signals:
    void sizeChange();
public:
    void mousePressEvent(QMouseEvent *event);
    void mouseMoveEvent(QMouseEvent *event);
    void mouseReleaseEvent(QMouseEvent *event);
    void paintEvent(QPaintEvent *event);
    void resizeEvent(QResizeEvent *event);
    bool eventFilter(QObject *watch, QEvent *event);
    void setEdgeBeReSized(const bool& resizeableTop, const bool& resizeableBottom, const bool& resizeableRight, const bool& resizeableLeft);
    void setIsRepositioning(const bool& reposition);
private:
    void checkEdge();
private:
    Ui::DragResizeWgt *ui;

    QPoint m_startCursor;

    int m_nLeftOff = 0;//鼠标开始拖拽时子窗口左边相对父窗口左边的距离
    int m_nRightOff = 0;//鼠标开始拖拽时子窗口右边相对父窗口左边的距离
    int m_nTopOff = 0;//鼠标开始拖拽时子窗口上边相对父窗口上边的距离
    int m_nBottomOff = 0;//鼠标开始拖拽时子窗口下边相对父窗口上边的距离

    QPoint dragPosition;   //鼠标拖动的位置
    int    edgeMargin = 4;     //鼠标检测的边缘距离
    DIRECTION resizeDir; //更改尺寸的方向

    bool m_resizing;
    bool m_repositioning;
    //帮助判断是否需要激活边框可变化大小
    bool m_resizeableTop;
    bool m_resizeableBottom;
    bool m_resizeableRight;
    bool m_resizeableLeft;
};

#endif // DRAGRESIZEWGT_H

DragResizeWgt类.cpp文件 

#include "DragResizeWgt.h"
#include "ui_DragResizeWgt.h"
#include <QDebug>
#include "Windows.h"
#include <QMouseEvent>
#include <QObject>
#include <QWidget>
#include <QVBoxLayout>
#include <QLabel>
#define min(a,b) ((a)<(b)? (a) :(b))
#define max(a,b) ((a)>(b)? (a) :(b))

DragResizeWgt::DragResizeWgt(QWidget *parent) :
    QWidget(parent),ui(new Ui::DragResizeWgt),
    resizeDir(nodir), m_resizing(false),m_repositioning(false),
    m_resizeableTop(true), m_resizeableBottom(true),
    m_resizeableRight(true), m_resizeableLeft(true)
{
    ui->setupUi(this);
    this->setObjectName("DragResizeWgt");

    //注意:一定要设置一个最小的宽高奥,要不会被拖到看不见,默认最小(0,0)
    setMinimumSize(100, 100);

    //一定一定要有这句话奥,这是能捕捉到这个窗口鼠标事件的关键,还有一点需要特别注意,如果这个窗口上会叠加其他的widget或者控件,对应的子UI也需要调用setMouseTracking这个函数
    this->setMouseTracking(true);

    //鼠标事件过滤器,来处理父窗口与子窗口的事件关系,不能互相干扰,后续会十分用得到
    setAttribute(Qt::WA_NoMousePropagation);
    installEventFilter(this);
    // QVBoxLayout *layout = new QVBoxLayout(this);
    // QLabel *label = new QLabel("Resizable and Draggable Widget", this);
    // layout->addWidget(label);

    edgeMargin = 4;        //设置检测边缘为4
    resizeDir = nodir;   //初始化检测方向为无

    setEdgeBeReSized(false,true,true,false);
    // setEdgeBeReSized(true,true,true,true);
    setLayout(new QHBoxLayout);
}

DragResizeWgt::~DragResizeWgt() = default;

void DragResizeWgt::mousePressEvent(QMouseEvent *event)
{
    if (event->button() == Qt::LeftButton)  //每当按下鼠标左键就记录一下位置
    {
        dragPosition = event->globalPos() - frameGeometry().topLeft();  //获得鼠标按键位置相对窗口左上面的位置
        m_startCursor = event->globalPos();

        m_nLeftOff = frameGeometry().left();
        m_nRightOff = frameGeometry().right() + 1;
        m_nTopOff = frameGeometry().top();
        m_nBottomOff = frameGeometry().bottom() + 1;
        qDebug() << "mousePressEvent" << dragPosition << m_startCursor << m_nLeftOff << m_nRightOff << m_nTopOff << m_nBottomOff;
    }
}

void DragResizeWgt::mouseMoveEvent(QMouseEvent *event)
{
    if (event->buttons() & Qt::LeftButton)//如果左键是按下的
    {
        if(resizeDir == nodir)//鼠标未放置在边缘处,进行窗口整体拖动处理
        {
            if(m_repositioning)
            {
                move(event->globalPos() - dragPosition);
            }
        }
        else//拖拽边缘,根据拖拽方向进行大小调整
        {

            int ptop,pbottom,pleft,pright;
            ptop = m_nTopOff;
            pbottom = m_nBottomOff;
            pleft = m_nLeftOff;
            pright = m_nRightOff;
            //这里获取父窗口的大小,如果没有则获取全局可用的屏幕大小
            QRect parentRect = parentWidget() ? parentWidget()->rect() : QApplication::primaryScreen()->availableGeometry();
            if(resizeDir & top)//拖拽顶部上下变化
            {
                //计算根据当前鼠标位置与拖拽偏移量计算当前top的位置
                ptop = m_nTopOff-(m_startCursor.ry()- event->globalY());
                qDebug() << "#########top position" << ptop << m_nTopOff << m_startCursor.ry() << event->globalY();
                if(ptop < 0)
                {
                    ptop = parentRect.height() - this->maximumHeight();
                }
                else{
                    if(this->height() <= minimumHeight())//进行极端高度最小的处理
                    {
                        ptop = min(m_nBottomOff-minimumHeight(),ptop);
                        qDebug() << "this->height() >= minimumHeight()" << ptop  << m_nBottomOff << minimumHeight();
                    }
                    else if(this->height() >= maximumHeight())//进行极端高度最大的处理
                    {
                        ptop = max(m_nBottomOff-maximumHeight(),ptop);
                        qDebug() << "this->height() >= maximumHeight()" << ptop << m_nBottomOff << maximumHeight();
                    }
                }
            }
            else if(resizeDir & bottom)//拖拽底部上下变化
            {
                //计算根据当前鼠标位置与拖拽偏移量计算当前bottom的位置
                pbottom = m_nBottomOff +(event->globalY()-m_startCursor.ry());
                if(pbottom > parentRect.bottom())
                {
                    pbottom = this->maximumHeight();
                }else{
                    if(this->height()<minimumHeight())//进行极端高度最小的处理
                    {
                        pbottom = m_nTopOff+minimumHeight();
                    }
                    else if(this->height()>maximumHeight())//进行极端高度最大的处理
                    {
                        pbottom = m_nTopOff+maximumHeight();
                    }
                }
            }

            if(resizeDir & left)//拖拽左侧左右变化
            {
                //计算根据当前鼠标位置与拖拽偏移量计算当前left的位置
                pleft = m_nLeftOff-(m_startCursor.rx() - event->globalX());
                if(pleft < 0)
                {
                    pleft = 0;
                }else{
                    if(this->width()<= minimumWidth())//进行极端宽度最小的处理
                    {
                        pleft = min(pleft,m_nRightOff- minimumWidth());
                    }
                    else if(this->width() >= maximumWidth())//进行极端宽度最大的处理
                    {
                        pleft = max(m_nRightOff- maximumWidth(),pleft);
                    }
                }
            }
            else if(resizeDir & right)//拖拽右侧左右变化
            {
                //计算根据当前鼠标位置与拖拽偏移量计算当前right的位置
                pright = m_nRightOff + (event->globalX()-m_startCursor.rx());
                if(pright > parentRect.right())
                {
                    pright = parentRect.right();
                }
                if(this->width()<minimumWidth())//进行极端宽度最小的处理
                {
                    pright = m_nLeftOff+minimumWidth();
                }
                else if(this->width()> this->maximumWidth())//进行极端宽度最大的处理
                {
                    pright = m_nLeftOff + this->maximumWidth();
                }
            }
            setGeometry(pleft,ptop,pright-pleft,pbottom-ptop);
            emit sizeChange();
        }

    }
    else checkEdge();
}

void DragResizeWgt::mouseReleaseEvent(QMouseEvent * event)
{
    // Q_UNUSED(event);
    if(resizeDir != nodir)//还原鼠标样式
    {
        checkEdge();
        emit sizeChange();
    }
}

void DragResizeWgt::paintEvent(QPaintEvent *event)
{
    // QPainter painter(this);
    // painter.fillRect(rect(), Qt::white);
    QWidget::paintEvent(event);
}

void DragResizeWgt::resizeEvent(QResizeEvent *event)
{
    QWidget::resizeEvent(event);
    // emit sizeChange();
}

bool DragResizeWgt::eventFilter(QObject *watch, QEvent *event)
{
    //一定要加这一句,不然父窗口的event事件也会出发窗口大小的改变信号
    if(watch == parentWidget() && event->type() == QEvent::MouseButtonRelease)
    {
        event->ignore();
        return true;
    }
    // if(watch == parentWidget() && event->type() == QEvent::Resize)
    // {
    //     event->ignore();
    //     return true;
    // }
    return QWidget::eventFilter(watch, event);
}

void DragResizeWgt::setEdgeBeReSized(const bool &resizeableTop, const bool &resizeableBottom, const bool &resizeableRight, const bool &resizeableLeft)
{
    m_resizeableTop = resizeableTop;
    m_resizeableBottom =  resizeableBottom;
    m_resizeableRight = resizeableRight;
    m_resizeableLeft = resizeableLeft;
}

void DragResizeWgt::setIsRepositioning(const bool &reposition)
{
    m_repositioning = reposition;
}

void DragResizeWgt::checkEdge()
{
    QPoint pos = this->mapFromGlobal(QCursor::pos());//开始拖拽时点击控件的什么位置

    int diffLeft = pos.rx();
    int diffRight = this->width() - diffLeft;
    int diffTop = pos.ry();
    int diffBottom = this->height() - diffTop;
    QCursor tempCursor;                                    //获得当前鼠标样式,注意:只能获得当前鼠标样式然后再重新设置鼠标样式
    tempCursor = cursor();                                 //因为获得的不是鼠标指针,所以不能这样用:cursor().setXXXXX

    // qDebug() << "diffLeft" << diffLeft << "diffRight" << diffRight << "diffTop" << diffTop << "diffBottom" << diffBottom ;
    if(diffTop < edgeMargin && m_resizeableTop)
    {                              //根据 边缘距离 分类改变尺寸的方向
        if(diffLeft < edgeMargin  && m_resizeableTop && m_resizeableLeft)
        {
            resizeDir = topLeft;
            tempCursor.setShape(Qt::SizeFDiagCursor);
        }
        else if(diffRight < edgeMargin && m_resizeableTop && m_resizeableRight)
        {
            resizeDir = topRight;
            tempCursor.setShape(Qt::SizeBDiagCursor);
        }
        else
        {
            resizeDir = top;
            tempCursor.setShape(Qt::SizeVerCursor);
        }
    }
    else if(diffBottom < edgeMargin && m_resizeableBottom)
    {
        if(diffLeft < edgeMargin && m_resizeableBottom && m_resizeableLeft)
        {
            resizeDir = bottomLeft;
            tempCursor.setShape(Qt::SizeBDiagCursor);
        }
        else if(diffRight < edgeMargin && m_resizeableBottom && m_resizeableRight)
        {
            resizeDir = bottomRight;
            tempCursor.setShape(Qt::SizeFDiagCursor);
        }
        else
        {
            resizeDir = bottom;
            tempCursor.setShape(Qt::SizeVerCursor);
        }
    }
    else if(diffLeft < edgeMargin && m_resizeableLeft)
    {
        resizeDir = left;
        tempCursor.setShape(Qt::SizeHorCursor);
    }
    else if(diffRight < edgeMargin && m_resizeableRight)
    {
        resizeDir = right;
        tempCursor.setShape(Qt::SizeHorCursor);
    }
    else
    {
        resizeDir = nodir;
        tempCursor.setShape(Qt::ArrowCursor);
    }

    setCursor(tempCursor);
}



使用

新建一个主界面窗口testwidget,用于创建主界面包含QWidget和DragResizeWgt共同存在的情况

其中widget_6为底部窗口,widget_5为顶部窗口

注意,我这里右边的窗口类中,我将widget_6和widget提升为了DragResizeWgt窗口类。

 testwidget窗口.ui文件

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>testwidget</class>
 <widget class="QWidget" name="testwidget">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>662</width>
    <height>549</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>Form</string>
  </property>
  <layout class="QVBoxLayout" name="verticalLayout_2">
   <property name="spacing">
    <number>0</number>
   </property>
   <property name="leftMargin">
    <number>0</number>
   </property>
   <property name="topMargin">
    <number>0</number>
   </property>
   <property name="rightMargin">
    <number>0</number>
   </property>
   <property name="bottomMargin">
    <number>0</number>
   </property>
   <item>
    <widget class="QWidget" name="widget_8" native="true">
     <layout class="QVBoxLayout" name="verticalLayout">
      <property name="spacing">
       <number>0</number>
      </property>
      <property name="leftMargin">
       <number>0</number>
      </property>
      <property name="topMargin">
       <number>0</number>
      </property>
      <property name="rightMargin">
       <number>0</number>
      </property>
      <property name="bottomMargin">
       <number>0</number>
      </property>
      <item>
       <widget class="QWidget" name="widget_5" native="true">
        <property name="minimumSize">
         <size>
          <width>0</width>
          <height>50</height>
         </size>
        </property>
        <property name="maximumSize">
         <size>
          <width>16777215</width>
          <height>50</height>
         </size>
        </property>
       </widget>
      </item>
      <item>
       <widget class="QWidget" name="widget_7" native="true">
        <property name="styleSheet">
         <string notr="true">background-color: rgb(170, 85, 255);</string>
        </property>
        <layout class="QHBoxLayout" name="horizontalLayout_2">
         <property name="spacing">
          <number>0</number>
         </property>
         <property name="leftMargin">
          <number>0</number>
         </property>
         <property name="topMargin">
          <number>0</number>
         </property>
         <property name="rightMargin">
          <number>0</number>
         </property>
         <property name="bottomMargin">
          <number>0</number>
         </property>
         <item>
          <widget class="DragResizeWgt" name="widget" native="true">
           <property name="styleSheet">
            <string notr="true"/>
           </property>
          </widget>
         </item>
         <item>
          <widget class="QWidget" name="widget_4" native="true">
           <property name="styleSheet">
            <string notr="true">background-color: rgb(0, 0, 127);</string>
           </property>
          </widget>
         </item>
        </layout>
       </widget>
      </item>
     </layout>
    </widget>
   </item>
   <item>
    <widget class="DragResizeWgt" name="widget_6" native="true">
     <property name="styleSheet">
      <string notr="true">background-color: rgb(255, 85, 127);</string>
     </property>
     <layout class="QHBoxLayout" name="horizontalLayout">
      <property name="spacing">
       <number>0</number>
      </property>
     </layout>
    </widget>
   </item>
  </layout>
 </widget>
 <customwidgets>
  <customwidget>
   <class>DragResizeWgt</class>
   <extends>QWidget</extends>
   <header>dragresizewgt.h</header>
   <container>1</container>
  </customwidget>
 </customwidgets>
 <resources/>
 <connections/>
</ui>

testwidget窗口.h文件

#ifndef TESTWIDGET_H
#define TESTWIDGET_H

#include <QWidget>
#include <QMouseEvent>
namespace Ui {
class testwidget;
}

class testwidget : public QWidget
{
    Q_OBJECT

public:
    explicit testwidget(QWidget *parent = nullptr);
    ~testwidget();

public:
    void resizeEvent(QResizeEvent *event);
private:
    Ui::testwidget *ui;
};

#endif // TESTWIDGET_H

testwidget窗口.cpp文件

#include "testwidget.h"
#include "qdebug.h"
#include "ui_testwidget.h"
#include "dragresizewgt.h"
#include <QPushButton>
testwidget::testwidget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::testwidget)
{
    ui->setupUi(this);
    // DragResizeWgt *w = new DragResizeWgt(this);
    // w->resize(400,400);
    // w->setStyleSheet("background-color: rgb(255, 255, 0);");
    // w->show();
    // this->setMaximumSize(1920,1080);
    this->setMouseTracking(true);
    this->setMinimumSize(480,270);

    ui->widget->setEdgeBeReSized(false,false,true,false);
    // ui->widget->setWgtMaxmumSize(300,800);
    ui->widget->setMinimumSize(100,0);
    // ui->widget_4->setEdgeBeReSized(false,false,false,true);
    ui->widget_6->setEdgeBeReSized(true,false,false,false);
    ui->widget_6->setMinimumSize(0,100);
    // ui->widget_6->setWgtMaxmumSize(1000,800);
    // ui->widget->setStyleSheet("background-color: rgb(20, 20, 255);");
    QWidget *childWidget3 = new QWidget;
    childWidget3->setMouseTracking(true);
    childWidget3->setStyleSheet("background-color: rgb(255, 12, 128);");
    childWidget3->setMouseTracking(true);
    ui->widget->layout()->addWidget(childWidget3);
    ui->widget->layout()->setContentsMargins(0,0,0,0);
    ui->widget->layout()->setSpacing(0);
    connect(ui->widget, &DragResizeWgt::sizeChange, [=](){
        qDebug() << "this->width()" << this->width() << "ui->widget->width()"<< ui->widget->width();
        int remainingWidth = this->width() -  ui->widget->width();
        ui->widget_4->setFixedWidth(remainingWidth);
    });
    connect(ui->widget_6, &DragResizeWgt::sizeChange, [=](){
        qDebug() << "this->width()" << this->height() << "ui->widget->height()"<< ui->widget_6->height();
        int remainingHeight= this->height() -  ui->widget_6->height();
        ui->widget_8->setFixedHeight(remainingHeight);
        ui->widget_4->setFixedHeight(remainingHeight);
    });
    QWidget *childWidget1 = new QWidget;
    QVBoxLayout *layout = new QVBoxLayout(this);
    QPushButton *label = new QPushButton("Resizable and Draggable Widget", childWidget1);
    layout->addWidget(label);
    childWidget1->setLayout(layout);
    connect(label,  &QPushButton::clicked, [](){
        qDebug() << "pushbutton clicked";
    });
    childWidget1->setMouseTracking(true);
    childWidget1->setStyleSheet("background-color: rgb(85, 85, 255);");
    QWidget *childWidget2 = new QWidget;
    childWidget2->setMouseTracking(true);
    childWidget2->setStyleSheet("background-color: rgb(85, 20, 255);");
    ui->widget_6->layout()->addWidget(childWidget1);
    ui->widget_6->layout()->addWidget(childWidget2);
    ui->widget_6->layout()->setContentsMargins(0,0,0,0);
    ui->widget_6->layout()->setSpacing(0);


    ui->widget->setIsRepositioning(true);
}

testwidget::~testwidget()
{
    delete ui;
}

// void testwidget::mousePressEvent(QMouseEvent *event)
// {

// }

// void testwidget::mouseReleaseEvent(QMouseEvent *event)
// {

// }

void testwidget::resizeEvent(QResizeEvent *event)
{
    qDebug() << "resizeEvent";
    ui->widget_6->setMaximumSize(this->width(),this->height()*0.7);
    ui->widget_8->setMinimumWidth(0);
    ui->widget_8->setMaximumWidth(this->width());
    ui->widget_8->setMinimumHeight(0);
    ui->widget_8->setMaximumHeight(this->height() - ui->widget_6->height());
    ui->widget_4->setMaximumWidth(this->width()*0.9);
    ui->widget->setMaximumSize(this->width()*0.9,this->height() - ui->widget_5->height());
}

最后在main函数中show()

    testwidget w;
    w.show();

参考

源于对这篇文章的参考,调试与修改

https://blog.csdn.net/weixin_40425059/article/details/116495403

如有什么别的问题可以留言,谢谢。


网站公告

今日签到

点亮在社区的每一天
去签到