Qt C++ 实现无边框窗体自定义缩放和拖动

引言

在现代应用程序中,用户界面的个性化定制越来越重要。Qt C++作为强大的框架,为我们提供了丰富的工具来实现各种定制化的用户界面。本文将介绍如何在Qt C++中创建一个无边框窗体,并实现自定义缩放和拖动功能,让您的应用程序更加灵活和个性化。

创建无边框窗体

要创建无边框窗体,我们需要在窗体的构造函数中添加以下代码:

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent) {
    setWindowFlags(Qt::FramelessWindowHint); // 设置为无边框窗体
    setMouseTracking(true); // 设置鼠标追踪,以便在鼠标不按下时也能接收鼠标移动事件
}

通过setWindowFlags函数,我们将窗体的标志设置为Qt::FramelessWindowHint,这将隐藏窗体的边框,使其成为无边框窗体。另外,我们通过setMouseTracking函数设置鼠标追踪,这样即使鼠标不按下,窗体也能接收鼠标移动事件。

监听窗体和顶部栏,实现窗口缩放和拖动

为了实现窗体的拖动和缩放功能,我们需要使用事件过滤器来监听窗体和顶部栏的事件。在构造函数中添加以下代码:

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent) {
    // ...
    qApp->installEventFilter(this); // 监听整个应用程序的事件
    ui->header->installEventFilter(this); // 监听顶部栏的事件
}

通过以上代码,我们将事件过滤器安装在整个应用程序和顶部栏上,这样我们就可以捕获窗体和顶部栏的事件,并进行相应的处理。

实现拖动和缩放功能

现在我们已经设置了无边框窗体并监听了窗体和顶部栏的事件,接下来我们将实现拖动和缩放功能。在窗体类中添加以下事件过滤器:

bool MainWindow::eventFilter(QObject *watched, QEvent *event)
{
    if (event->type() == QEvent::MouseButtonPress) {
        QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
        if (mouseEvent->button() == Qt::LeftButton) {
            // 如果鼠标按下位置位于窗口边缘,记录鼠标按下位置和窗口当前大小
            if (watched == this && !isInFrameSection(mouseEvent->pos(), Qt::NoSection)) {
                dragStartPosition = mouseEvent->globalPos();
                windowGeometry = geometry();
                frameSection = getEdgeSection(mouseEvent->pos());
                isResizing = true;
                return true; // 截取并处理该事件
            }
            // 如果鼠标按下位置位于 spacer 区域,记录鼠标按下位置
            else if (watched == ui->header && ui->headerSpacer->geometry().contains(mouseEvent->pos())) {
                dragStartPosition = mouseEvent->globalPos() - frameGeometry().topLeft();
                isDragging = true;
                return true; // 截取并处理该事件
            }
        }
    }
    else if (event->type() == QEvent::MouseMove) {
        QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
        if (watched->isWindowType()) {
            setCursorStyle(mouseEvent->pos());
        }
        if (mouseEvent->buttons() & Qt::LeftButton) {
            // 如果正在缩放窗口,计算新的窗口大小并移动
            if (watched->isWindowType() && isResizing) {
                QPoint delta = mouseEvent->globalPos() - dragStartPosition;
                if (frameSection == Qt::LeftSection) {
                    move(windowGeometry.topLeft() + QPoint(delta.x(), 0));
                    resize(windowGeometry.size() + QSize(-delta.x(), 0));
                } else if (frameSection == Qt::RightSection) {
                    resize(windowGeometry.size() + QSize(delta.x(), 0));
                } else if (frameSection == Qt::TopSection) {
                    resize(windowGeometry.size() + QSize(0, -delta.y()));
                    move(windowGeometry.topLeft() + QPoint(0, delta.y()));
                } else if (frameSection == Qt::BottomSection) {
                    resize(windowGeometry.size() + QSize(0, delta.y()));
                } else if (frameSection == Qt::TopLeftSection) {
                    resize(windowGeometry.size() + QSize(-delta.x(), -delta.y()));
                    move(windowGeometry.topLeft() + QPoint(delta.x(), delta.y()));
                } else if (frameSection == Qt::BottomRightSection) {
                    resize(windowGeometry.size() + QSize(delta.x(), delta.y()));
                } else if (frameSection == Qt::BottomLeftSection) {
                    resize(windowGeometry.size() + QSize(-delta.x(), delta.y()));
                    move(windowGeometry.topLeft() + QPoint(delta.x(), 0));
                } else if (frameSection == Qt::TopRightSection) {
                    resize(windowGeometry.size() + QSize(delta.x(), -delta.y()));
                    move(windowGeometry.topLeft() + QPoint(0, delta.y()));
                }
                return true; // 截取并处理该事件
            }
            else if (isDragging) {
                // 如果正在拖动 spacer 区域,移动窗口
                QPoint newPos = mouseEvent->globalPos() - dragStartPosition;
                move(newPos);
                return true; // 截取并处理该事件
            }
        }
    }
    else if (event->type() == QEvent::MouseButtonRelease) {
        isDragging = false;
        isResizing = false;
    }

    return QWidget::eventFilter(watched, event); // 其他事件交给父类处理
}

上述代码实现了事件过滤器,我们处理了鼠标点击、鼠标移动和鼠标释放事件。当用户点击顶部栏时,启用拖动功能,允许用户拖动窗体。当用户点击窗体边缘时,启用缩放功能,允许用户调整窗体的大小。

完善边缘区域判断功能

我们需要添加另外两个函数isInFrameSectiongetEdgeSection来判断鼠标当前位置位于窗体的哪个边缘区域,并根据边缘区域设置鼠标样式。

bool MainWindow::isInFrameSection(const QPoint &pos, Qt::WindowFrameSection edge) {
    QRect area = QRect(0, 0, width(), height()).adjusted(distance, distance, -distance, -distance);
    if  (edge == Qt::NoSection) { // center包含于edge包含于corner,判断顺序必须是 center>edge>corner
        return area.contains(pos);
    }
    if (edge == Qt::LeftSection || edge == Qt::TopLeftSection || edge == Qt::BottomLeftSection) {
        area.adjust(-distance * 2, 0, 0, 0); // 乘2是为了增加偏移量,超出窗体边缘,避免光标抖动
    }
    if (edge == Qt::RightSection || edge == Qt::TopRightSection || edge == Qt::BottomRightSection) {
        area.adjust(0, 0, distance * 2, 0);
    }
    if (edge == Qt::TopSection || edge == Qt::TopLeftSection || edge == Qt::TopRightSection) {
        area.adjust(0, -distance * 2, 0, 0);
    }
    if (edge == Qt::BottomSection || edge == Qt::BottomLeftSection || edge == Qt::BottomRightSection) {
        area.adjust(0, 0, 0, distance * 2);
    }
    return area.contains(pos);
}

Qt::WindowFrameSection MainWindow::getEdgeSection(const QPoint &pos) {
    QList<Qt::WindowFrameSection> sections = {Qt::NoSection, Qt::LeftSection, Qt::RightSection, Qt::TopSection, Qt::BottomSection,
            Qt::TopLeftSection, Qt::TopRightSection, Qt::BottomLeftSection, Qt::BottomRightSection};
    for (Qt::WindowFrameSection section : sections) {
        if (isInFrameSection(pos, section)) {
            return section;
        }
    }
    return Qt::NoSection;
}

通过上述代码,我们完成了边缘区域的判断功能。isInFrameSection函数根据edge参数的值调整了边缘区域的范围,并通过adjusted函数增加了偏移量,避免了鼠标在窗体边缘时的抖动问题。而getEdgeSection函数则用于获取当前鼠标位置所处的窗体边缘区域。

设置鼠标样式

通过辅助函数getEdgeSection,我们可以在MouseMove事件中调用setCursorStyle函数来设置鼠标样式,以便在拖动和缩放过程中提供直观的鼠标反馈。

void MainWindow::setCursorStyle(const QPoint &pos) {
    Qt::WindowFrameSection frameSection = getEdgeSection(pos);
    if (frameSection == Qt::NoSection) {
        setCursor(Qt::ArrowCursor);
    } else if (frameSection == Qt::LeftSection || frameSection == Qt::RightSection) {
        setCursor(Qt::SizeHorCursor);
    } else if (frameSection == Qt::TopSection || frameSection == Qt::BottomSection) {
        setCursor(Qt::SizeVerCursor);
    } else if (frameSection == Qt::TopLeftSection || frameSection == Qt::BottomRightSection) {
        setCursor(Qt::SizeFDiagCursor);
    } else if (frameSection == Qt::TopRightSection || frameSection == Qt::BottomLeftSection) {
        setCursor(Qt::SizeBDiagCursor);
    }
}

通过以上代码,我们完善了鼠标样式设置功能。在拖动和缩放过程中,根据鼠标所处的边缘区域,我们设置不同的鼠标样式来提供直观的用户反馈。

实现效果

附图一张,gif就不弄了。

总结

在本文中,我们使用Qt C++中创建了一个无边框窗体,并实现自定义缩放和拖动功能。我们利用Qt::FramelessWindowHint标志隐藏了窗体的边框,并通过事件过滤器监听了窗体和顶部栏的事件,从而实现了窗口的拖动和缩放功能。我们还通过辅助函数判断鼠标所处的边缘区域,并设置相应的鼠标样式,提供了直观的用户反馈。

最后,本人纯纯门外级,如果你有意见或想法,欢迎留言。祝大家心意顺遂,学有所成。

  • 1
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值