赞
踩
在现代应用程序中,用户界面的个性化定制越来越重要。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); // 其他事件交给父类处理
- }

上述代码实现了事件过滤器,我们处理了鼠标点击、鼠标移动和鼠标释放事件。当用户点击顶部栏时,启用拖动功能,允许用户拖动窗体。当用户点击窗体边缘时,启用缩放功能,允许用户调整窗体的大小。
我们需要添加另外两个函数isInFrameSection
和getEdgeSection
来判断鼠标当前位置位于窗体的哪个边缘区域,并根据边缘区域设置鼠标样式。
- 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
标志隐藏了窗体的边框,并通过事件过滤器监听了窗体和顶部栏的事件,从而实现了窗口的拖动和缩放功能。我们还通过辅助函数判断鼠标所处的边缘区域,并设置相应的鼠标样式,提供了直观的用户反馈。
最后,本人纯纯门外级,如果你有意见或想法,欢迎留言。祝大家心意顺遂,学有所成。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。