无标题栏窗口的实现拖动,和边的拉伸功能

本文的功能实现,参考了链接:https://blog.csdn.net/Ternence_God/article/details/100150377,图片也是来自其。

引言

创建的程序采用系统窗口,直接可以进行拖动,向八个方向拉伸,一旦设置窗口无标题栏,即:setWindowFlags(Qt::FramelessWindowHint);后,窗口的拖动,拉伸功能便需要重新实现。

效果

由于暂时还没有向平台上传视频,所以咱没有插入视频来直观的显示程序运行的效果。后期补上。

实现

开发环境

在这里插入图片描述

下面以一个小的demo来实现窗口的拖动和拉伸功能。

思路

窗口的拖动

鼠标按下后记住当前鼠标按下点的位置,这个点相对于屏幕而言,同时获取窗口左上角的位置,当鼠标按下移动后,获取当前移动到的点的位置,也是相对于桌面而言的坐标,用移动后的位置减去刚开始鼠标按下的点得到偏移量,用鼠标左上角的点加上偏移量便是移动后窗口的位置,调用move()函数将窗口移动到指定点,实现窗口在鼠标按下不放可拖动。这里不用考虑偏移量是加上还是减去,因为记录了鼠标刚开始按下的点之后,向右移,偏移量变为正值,向左移偏移量便为负值。

窗口的拉伸

由于我们获取窗口的上下左右四个边以及四个角的坐标来判断当鼠标进入指定边或者角时,两者的坐标并不相同,而是有一定的偏差,这是采用划分区域来实现鼠标进入窗口置顶的边或角,从而按照不同的边、角置顶鼠标的样式,保存原来窗口的位置大小,记录鼠标变样后按下点的坐标,和鼠标移动后点的坐标,求得偏移量,从而计算各个边角的位置,从新设置窗口的位置大小。且需要记录窗口被拉伸后鼠标的位置,防止下一次操作直接从上一次拉伸后不释放坐标直接拉伸。
区域划分
在这里插入图片描述

项目结构

在这里插入图片描述
基类选择QDialog类,其它都是默认,然后创建项目。

具体代码

.pro,.ui文件采用自动生成的,不做任何改变。
widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();
protected:
    enum mouseArea{
        topLeftCorner = 11,
        topBorder = 12,
        topRightCorner = 13,
        leftBorder = 21,
        midArea = 22,
        rightBorder = 23,
        leftBottomConrner = 31,
        bottomBorder = 32,
        rightBottomConrner = 33,
    };
    enum winDivideArea{
        oneArea = 1,
        twoArea,
        threeArea,
    };

    void mousePressEvent(QMouseEvent *event);
    void mouseMoveEvent(QMouseEvent *event);
    void mouseReleaseEvent(QMouseEvent *event);
    void setMouseCursorStyle(QMouseEvent *event);//设置鼠标光标的样式
    int determineArea(QPoint pos);//判断点pos所在的区域
    void mouseStrechDrag(QMouseEvent *event,QPoint &offset);//鼠标拉伸拖动
private:
    Ui::Widget *ui;
    QPoint      m_pressPos;//鼠标按下点的位置
    QPoint      m_topLeftPos;//鼠标左上角的位置
    QPoint      m_lastPos;//上一次拉伸点的位置
    const int   m_floatValue;//鼠标所在位置的浮动值
    int         m_region;//鼠标所在的区域
};
#endif // WIDGET_H

widget.cpp

#include "widget.h"
#include "ui_widget.h"
#include <QMouseEvent>
#include <QDebug>

Widget::Widget(QWidget *parent)
    : QWidget(parent),m_floatValue(10)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    setWindowFlags(Qt::FramelessWindowHint);
    setMouseTracking(true);
}

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

void Widget::mousePressEvent(QMouseEvent *event)
{
    if (event->button() == Qt::LeftButton) {
//        m_pressPos = event->globalPos();//获取在屏幕坐标系统中的位置
//        m_topLeftPos = geometry().topLeft();//获取窗口左上角的位置,原本采取左上角坐标+偏移量
        m_lastPos = event->globalPos();
        m_pressPos = pos();
        m_region = determineArea(event->pos());//在父窗口中的位置

//        qDebug()<<QStringLiteral("鼠标位置:")<<m_pressPos<<QStringLiteral("位置:")<<event->pos()
//        <<QStringLiteral("窗口左上角的位置:")<<m_topLeftPos;//在父窗口中的位置
    }
    QWidget::mousePressEvent(event);
}

void Widget::mouseMoveEvent(QMouseEvent *event)
{
    if (isMaximized())
    {
        return;
    }
    setMouseCursorStyle(event);
    if (event->buttons() == Qt::LeftButton) {
        QPoint offset = event->globalPos() - m_lastPos;
        if (m_region == midArea) {
            move(m_pressPos + offset);
        }else {
            mouseStrechDrag(event,offset);
        }
    }
    QWidget::mouseMoveEvent(event);
}

void Widget::mouseReleaseEvent(QMouseEvent *event)
{
    QWidget::mouseReleaseEvent(event);
}

void Widget::setMouseCursorStyle(QMouseEvent *event)
{
    int region = determineArea(event->pos());//这里的区域必须为局部变量,否则会出现拉伸中出现区域变更,导致功能紊乱
    switch (region) {
    case topLeftCorner:
    case rightBottomConrner:
        setCursor(Qt::SizeFDiagCursor);//设置光标的形状使用setCursor(),使用setShape()设置不上,并不知为啥
        break;
    case topBorder:
    case bottomBorder:
        setCursor(Qt::SizeVerCursor);
        break;
    case topRightCorner:
    case leftBottomConrner:
        setCursor(Qt::SizeBDiagCursor);
        break;
    case leftBorder:
    case rightBorder:
        setCursor(Qt::SizeHorCursor);
        break;
    case midArea:
        setCursor(Qt::ArrowCursor);
        break;
    default:
        break;
    }
}

int Widget::determineArea(QPoint pos)
{
    int xArea = 0;
    int yArea = 0;
    int areaFlag = 0;
    if (pos.x() < m_floatValue) {
        xArea = oneArea;
    }else if (pos.x() > width() - m_floatValue) {
        xArea = threeArea;
    }else {
        xArea = twoArea;
    }
    if (pos.y() < m_floatValue) {
        yArea = oneArea;
    }else if (pos.y() > height() - m_floatValue) {
        yArea = threeArea;
    }else {
        yArea = twoArea;
    }
    return areaFlag = yArea * 10 + xArea;
}

void Widget::mouseStrechDrag(QMouseEvent *event,QPoint &offset)
{
    QRect rect = geometry();

    qDebug()<<QStringLiteral("鼠标移动的位置:")<<event->globalPos()<<QStringLiteral("按下点位置:")<<m_pressPos
    <<QStringLiteral("偏移量:")<<event->globalPos()-m_pressPos;
    switch (m_region) {
    case topLeftCorner:
        rect.setTopLeft(rect.topLeft() + offset);
        break;
    case rightBottomConrner:
        rect.setBottomRight(rect.bottomRight() + offset);
        break;
    case topBorder:
        rect.setTop(rect.top() + offset.y());
        break;
    case bottomBorder:
        rect.setBottom(rect.bottom() + offset.y());
        break;
    case topRightCorner:
        rect.setTopRight(rect.topRight() + offset);
        break;
    case leftBottomConrner:
        rect.setBottomLeft(rect.bottomLeft() + offset);
        break;
    case leftBorder:
        rect.setLeft(rect.left() + offset.x());
        break;
    case rightBorder:
        rect.setRight(rect.right() + offset.x());
        break;
    default:
        break;
    }
    //设置拉伸的最小宽度和高度
    if (rect.width() < 200 || rect.height() < 100) {
        return ;
    }
    setGeometry(rect);
    m_lastPos = event->globalPos();
}

//void Widget::setMouseCursorStyle(QMouseEvent *event)
//{
//    //转换后存在误差,鼠标在左下角获取的位置与左下角的位置不一样,因而无法实现下述方法
//    qDebug()<<QStringLiteral("鼠标在父窗口的位置:")<<mapFromParent(event->globalPos())<<
//    QStringLiteral("鼠标的位置:")<<event->pos()<<QStringLiteral("左上角")<<geometry().topLeft()
//    <<QStringLiteral("右上角")<<geometry().topRight()<<QStringLiteral("左下角")<<geometry().bottomLeft()
//    <<QStringLiteral("右下角")<<geometry().bottomRight()<<QStringLiteral("左边")<<geometry().left()
//    <<QStringLiteral("右边")<<geometry().right()<<QStringLiteral("上边")<<geometry().top()
//    <<QStringLiteral("下边")<<geometry().bottom()<<QStringLiteral("转换后左下角")<<mapFromParent(geometry().bottomLeft());//获取位置相同,都是鼠标在父窗口中的位置
//    if (event->pos().x() > geometry().left() &&event->pos().x() < geometry().right()
//    && event->pos().y() > geometry().top() && event->pos().y() < geometry().bottom())
//    {//中间
//        cursor().setShape(Qt::ArrowCursor);
//    }else if (event->pos() == mapFromParent(geometry().topLeft())) {//左上角
//        qDebug()<<QStringLiteral("鼠标在左上角");
//        cursor().setShape(Qt::SizeFDiagCursor);
//    }else if (event->pos() == mapFromParent(geometry().topRight())) {//右上角
//        cursor().setShape(Qt::SizeBDiagCursor);
//    }else if (event->pos() == geometry().bottomLeft()) {//左下角
//        cursor().setShape(Qt::SizeBDiagCursor);
//    }else if (event->pos() == geometry().bottomRight()) {//右下角
//        cursor().setShape(Qt::SizeFDiagCursor);
//    }else if (event->pos().x() == geometry().left()) {//左边
//        cursor().setShape(Qt::SizeHorCursor);
//    }else if (event->pos().x() == geometry().right()) {//右边
//        cursor().setShape(Qt::SizeHorCursor);
//    }else if (event->pos().y() == geometry().top()) {//上边
//        cursor().setShape(Qt::SizeVerCursor);
//    }else if (event->pos().y() == geometry().bottom()) {//下边
//        cursor().setShape(Qt::SizeVerCursor);
//    }
//}

main.cpp文件也采用默认生成的文件。
这样就可以编译,运行,查看程序的运行效果了。

注意点

  1. 理解思路,将窗口划分为9个区域,进行编号,之所以要划分区域是因为上面被注释掉的代码在运行时会发现鼠标即使到了窗口的指定边,但是与获取的指定边的坐标总有偏差。
  2. 需要在构造函数中设置鼠标跟踪,要不不能实现鼠标放置在指定边时出现特定的鼠标样式,只在鼠标点击后才能出现特定 的鼠标样式。
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值