引言
在现代应用程序中,用户界面的个性化定制越来越重要。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
标志隐藏了窗体的边框,并通过事件过滤器监听了窗体和顶部栏的事件,从而实现了窗口的拖动和缩放功能。我们还通过辅助函数判断鼠标所处的边缘区域,并设置相应的鼠标样式,提供了直观的用户反馈。
最后,本人纯纯门外级,如果你有意见或想法,欢迎留言。祝大家心意顺遂,学有所成。