通俗易懂玩QT:自定义窗口移动与拉伸的实现(内附主要源代码)

QT自定义窗口移动与拉伸的实现(内附主要源代码)

国庆没啥事,研究了一下 Qt 实现自定义窗口,参考了两位博主的文章,自己做了点修改,修复了一些 Bug(有可能是我没按大佬的思路来产生的),目前还存在的 Bug 是最大化窗口被移动了以后,需要点击两次窗口还原按钮才能还原窗口,不想再耗费时间再纠结这个问题了,后面有需要的话再来改就完事,下面是主要源码:

zoomMove.h

enum {
    EDGENULL = 0,
    TOPLEFT,
    TOPRIGHT,
    BOTTOMRIGHT,
    BOTTOMLEFT,
    TOP,
    RIGHT,
    BOTTOM,
    LEFT
};

class ZoomMove : public QWidget {
    Q_OBJECT

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

private:
    bool key_down,      //0:松开, 1:按下
         zoom,          //0:无操作, 1:拉扯
         move;          //0:无操作, 1:移动

    int is_on_edge,     //0:不沾边, 1:上边, 2:右上方 , 3:右边 , 4:右下方 , 5:下边 , 6:左下方 , 7:左边 , 8:左上方
        left,
        top,
        right,
        bottom,
        min_width,
        min_height;

    QPoint last_point,      //当前窗口的左上方顶点
           current_point,   //当前鼠标相对于屏幕的位置,不能将 current_point 与 point 看作同一坐标点,后者没有前者稳定
           point,           //当前鼠标在屏幕中的位置
           temp_point;      //用于存放窗口的四个顶点

    QWidget *widget;

    void updateCursor();                            //更新鼠标图标
    void mouseIsOnEdge(QPoint point, QRect rect);   //判断鼠标是否在边停靠
    void resizeWidget(QPoint p);                    //根据拉伸重置窗口大小
    void widgetEventHandler(QEvent *e);             //窗口事件处理
    void mouseHoverEventHandler(QHoverEvent *e);    //鼠标停靠窗口
    void mouseMoveEventHandler(QMouseEvent *e);     //鼠标移动
    void mousePressEventHandler(QMouseEvent *e);    //鼠标被按下
    void mouseReleaseEventHandler(QMouseEvent *e);  //鼠标被松开

protected:
    virtual bool eventFilter(QObject *obj, QEvent *e);
};

zoomMove.cpp

ZoomMove::ZoomMove(QWidget *parent):
    QWidget(parent) {
    key_down =      //默认鼠标为松开状态
    zoom =          //默认无拉扯操作
    move = 0;       //默认无移动操作
    is_on_edge = 0; //默认鼠标没有在边停靠

    parent -> installEventFilter(this); //开启事件过滤
}

ZoomMove::~ZoomMove() {
    widget -> removeEventFilter(this);  //关闭事件过滤
}

void ZoomMove::updateCursor() {
    switch(is_on_edge) {
    case TOPLEFT:     //左上方
    case BOTTOMRIGHT:     //右下方
        widget -> setCursor(Qt::SizeFDiagCursor);   //左上-右下
        break;
    case TOPRIGHT:     //右上方
    case BOTTOMLEFT:     //左下方
        widget -> setCursor(Qt::SizeBDiagCursor);   //右上-左下
        break;
    case TOP:     //上边
    case BOTTOM:     //下边
        widget -> setCursor(Qt::SizeVerCursor);     //上-下
        break;
    case RIGHT:     //右边
    case LEFT:     //左边
        widget -> setCursor(Qt::SizeHorCursor);     //左-右
        break;
    case EDGENULL:
    default:
        widget -> setCursor(Qt::ArrowCursor);       //箭头
        break;
    }
}

void ZoomMove::mouseIsOnEdge(QPoint point, QRect rect) {
    if((point.x() - rect.x() < 5) && (point.y() - rect.y() < 5)) {
        is_on_edge = TOPLEFT; //左上方
    }
    else if((rect.x() + rect.width() - point.x() < 5) && (point.y() - rect.y() < 5)) {
        is_on_edge = TOPRIGHT; //右上方
    }
    else if((rect.x() + rect.width() - point.x() < 5) && (rect.y() + rect.height() - point.y() < 5)) {
        is_on_edge = BOTTOMRIGHT; //右下方
    }
    else if((point.x() - rect.x() < 5) && (rect.y() + rect.height() - point.y() < 5)) {
        is_on_edge = BOTTOMLEFT; //左下方
    }
    else if(point.y() - rect.y() < 5) {
        is_on_edge = TOP; //上边
    }
    else if(rect.x() + rect.width() - point.x() < 5) {
        is_on_edge = RIGHT; //右边
    }
    else if(rect.y() + rect.height() - point.y() < 5) {
        is_on_edge = BOTTOM; //下边
    }
    else if(point.x() - rect.x() < 5) {
        is_on_edge = LEFT; //左边
    }
    else {
        is_on_edge = EDGENULL; //不沾边
    }

    zoom = is_on_edge == EDGENULL ? 0 : 1;
}

void ZoomMove::resizeWidget(QPoint p) {
    min_width = widget -> minimumWidth();
    min_height = widget -> minimumHeight();

    QRect frame = widget -> frameGeometry(); //获取窗口的几何框架

    switch(is_on_edge) {    //根据边框的位置改变窗口的形状
        case TOPLEFT:     //左上方
            temp_point = frame.topLeft();
            if(frame.bottomRight().x() - p.x() > min_width) { temp_point.setX(p.x()); }
            if(frame.bottomRight().y() - p.y() > min_height) { temp_point.setY(p.y()); }
            if(frame.bottomRight().x() - p.x() <= min_width && frame.bottomRight().y() - p.y() <= min_height) {
                temp_point.setX(frame.bottomRight().x() - min_width);
                temp_point.setY(frame.bottomRight().y() - min_height);
            }
            frame.setTopLeft(temp_point);
            break;

        case TOPRIGHT:     //右上方
            temp_point = frame.topRight();
            if(p.x() - frame.bottomLeft().x() > 0) { temp_point.setX(p.x()); }
            if(frame.bottomLeft().y() - p.y() > min_height) { temp_point.setY(p.y()); }
            if(p.x() - frame.bottomLeft().x() <= 0 && frame.bottomLeft().y() - p.y() <= min_height) {
                temp_point.setX(p.x());
                temp_point.setY(frame.bottomLeft().y() - min_height);
            }
            frame.setTopRight(temp_point);
            break;

        case BOTTOMRIGHT:   //右下方
            temp_point = frame.bottomRight();
            if(p.x() - frame.topLeft().x() > 0) { temp_point.setX(p.x()); }
            if(p.y() - frame.topLeft().y() > 0) { temp_point.setY(p.y()); }
            if(p.x() - frame.topLeft().x() <= 0 && p.y() - frame.topLeft().y() <= 0) {
                temp_point.setX(p.x());
                temp_point.setY(p.y());
            }
            frame.setBottomRight(temp_point);
            break;

        case BOTTOMLEFT:    //左下方
            temp_point = frame.bottomLeft();
            if(frame.topRight().x() - p.x() > min_width) { temp_point.setX(p.x()); }
            if(p.y() - frame.topRight().y() > 0) { temp_point.setY(p.y()); }
            if(frame.topRight().x() - p.x() <= min_width && p.y() - frame.topRight().y() <= 0) {
                temp_point.setX(frame.topRight().x() - min_width);
                temp_point.setY(p.y());
            }
            frame.setBottomLeft(temp_point);
            break;

        case TOP:           //上边
            temp_point = frame.topRight();
            if(frame.bottomLeft().y() - p.y() > min_height) { temp_point.setY(p.y()); }
            frame.setTopRight(temp_point);
            break;

        case RIGHT:         //右边
            temp_point = frame.bottomRight();
            if(p.x() - frame.topLeft().x() > 0) { temp_point.setX(p.x()); }
            frame.setBottomRight(temp_point);
            break;

        case BOTTOM:        //下边
            temp_point = frame.bottomLeft();
            if(p.y() - frame.topRight().y() > 0) { temp_point.setY(p.y()); }
            frame.setBottomLeft(temp_point);
            break;

        case LEFT:          //左边
            temp_point = frame.topLeft();
            if(frame.bottomRight().x() - p.x() > min_width) { temp_point.setX(p.x()); }
            frame.setTopLeft(temp_point);
            break;

        default:
            break;
    }

    widget -> setGeometry(frame);    //给窗口设置新的几何框架
}

void ZoomMove::widgetEventHandler(QEvent *e) {
    switch(e -> type()) {
    case QEvent::HoverMove:
        mouseHoverEventHandler(static_cast<QHoverEvent *>(e));
        break;
    case QEvent::MouseMove:
        mouseMoveEventHandler(static_cast<QMouseEvent *>(e));
        break;
    case QEvent::MouseButtonPress:
        mousePressEventHandler(static_cast<QMouseEvent *>(e));
        break;
    case QEvent::MouseButtonRelease:
        mouseReleaseEventHandler(static_cast<QMouseEvent *>(e));
        break;
    default:
        break;
    }
}

void ZoomMove::mouseHoverEventHandler(QHoverEvent *e) {
    if(!key_down){      //在窗口被拉扯或移动的过程中,停止对鼠标的悬浮监听
        point = widget -> mapToGlobal(e -> pos());

        mouseIsOnEdge(point, widget -> frameGeometry());
        updateCursor();
    }
}

void ZoomMove::mouseMoveEventHandler(QMouseEvent *e) {
    Q_UNUSED(e);

    if(key_down) {
        current_point = e -> globalPos();

        //current_point - last_point 即窗口的左上角顶点
        if(move && last_point.y() < 65) {
            widget -> move(current_point - last_point);
        }

        if(zoom) {
            resizeWidget(current_point);
        }
    }
}

void ZoomMove::mousePressEventHandler(QMouseEvent *e) {
    if(e -> button() == Qt::LeftButton) {
        key_down = 1;
        move = (!zoom && !move) & 1;
        last_point = e -> pos();
    }
}

void ZoomMove::mouseReleaseEventHandler(QMouseEvent *e) {
    if(e -> button() == Qt::LeftButton) {
        key_down =      //按键松开
        move = 0;       //更正移动状态
    }
}

bool ZoomMove::eventFilter(QObject *obj, QEvent *e) {
    switch(e -> type()) {
    case QEvent::MouseMove:
    case QEvent::MouseButtonPress:
    case QEvent::MouseButtonRelease:
    case QEvent::HoverMove:
        widget = static_cast<QWidget *>(obj);
        widgetEventHandler(e);
        return true;
    default:
        return QObject::eventFilter(obj, e);
    }
}

在 QMainWindow 或 QWidget 中初始化该类即可实现自定义窗口的拉伸与移动功能。

new ZoomMove(this);

参考博文:
前行中的小猪:Qt 之 自定义窗口标题栏 之窗口拉伸
一去丶二三里:Qt 之自定义界面(窗体缩放-跨平台终极版)

学习分享,一起成长!以上为小编的经验分享,若存在不当之处,请批评指正!

  • 6
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我是混子我怕谁

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值