目录
一.背景描述
在实际开发当中,很少会直接用Qt自带的边框,经常是去掉自带的边框,然后重新使用部件+底图来构建带有企业产品特征的边框。
然而去掉了Qt自带的边框后,随之也去掉了自带的窗口拉伸缩放功能,这就导致了在产品使用过程中失去了一定了灵活性。为此,需要我们重新实现该功能。在本文中将详细介绍如何通过代码来实现窗口的拉伸缩放功能。
二.实现原理
从Qt原生边框中自带的拉伸缩放功能使用中,我们可以大概猜测到其基本的实现原理,就是当鼠标靠近四边、四个角时,会对鼠标的形状进行改变,如果此时按下鼠标左键即可锁定需要调整的边框,在进行拖动拖动时便会对窗口的大小进行调整。同样,我们在进行重新实现拉伸缩放时,也是参照该逻辑进行实现。
如图(2),根据虚线将窗口划分为9个区域,当鼠标移动到各区域时,将修改鼠标图标,此时如果按下鼠标左键,将所动需要调整的区域边框,然后进行拖动以调整锁定边框,即实现窗口拉伸缩放功能。
三.具体实现
3.1 窗口布局调整
如果新建的是MainWindow类型的窗口,则先把自带的菜单栏、状态栏等去掉,即把图(3)中框选的部件Delete掉。
然后根据项目设计图自行添加控件进行布局,如图(3)所示。在本项目中主要讲解的都是自主实现窗口拉伸缩放功能,因此只进行了简单布局,大家可以根据各自项目需要进行布局。
注意:在去掉自带边框后至少需要手动实现一个关闭窗口的按钮,不然窗口无法正常关闭。
3.2 启用鼠标跟踪
在移动鼠标时需要实时判断鼠标所在区域,因此对该窗口及其相关子部件进行设置启用鼠标跟踪,否则将无法实时根据鼠标所在区域进行鼠标图标进行切换。
// 对窗口及其子部件进行启用鼠标跟踪
QObjectList child_object_list = ui.centralWidget->children();
for (auto child_object : child_object_list) {
if (child_object->isWidgetType()) { ((QWidget*)child_object)->setMouseTracking(true); }
}
ui.centralWidget->setMouseTracking(true);
setMouseTracking(true);
3.3 记录鼠标按下状态及所属区域
在按下鼠标时进行记录鼠标的状态,以及当前所在区域,以便后续进行边界拖动时根据不同情况来重新计算窗口位置大小。
// 记录鼠标左键按下状态及所属区域
is_left_button_pressed_ = true;
cur_region_index_ = CalculateBorderIndex(event->pos());
3.4 计算鼠标所属区域
根据鼠标所在位置进行边界归属计算,在本demo中的计算均留有余量,可避免鼠标拖动边界过程中发生鼠标形状对应不上情况。
CustomStretchWindow::BorderIndex CustomStretchWindow::CalculateBorderIndex(QPoint pos) {
BorderIndex region_index = BORDER_NONE;
int offset = 5;
auto x = pos.x();
auto y = pos.y();
auto w = this->width();
auto h = this->height();
if (y <= offset && x > offset && x < w - offset) {
region_index = BORDER_TOP;
}
else if (x <= offset && y > offset && y < h - offset) {
region_index = BORDER_LEFT;
}
else if (x >= w - offset && y > offset && y < h - offset) {
region_index = BORDER_RIGHT;
}
else if (y >= h - offset && x > offset && x < w - offset) {
region_index = BORDER_BOTTOM;
}
else if (x >= -offset && x < offset && y >= -offset && y < offset) {
region_index = BORDER_TOP_LEFT;
}
else if (x > w - offset && x <= w + offset && y >= -offset && y < offset) {
region_index = BORDER_TOP_RIGHT;
}
else if (x >= -offset && x < offset && y > h - offset && y <= h + offset) {
region_index = BORDER_BOTTOM_LEFT;
}
else if (x > w - offset && x <= w + offset && y > h - offset && y <= h + offset) {
region_index = BORDER_BOTTOM_RIGHT;
}
return region_index;
}
3.5 根据鼠标所属区域改变鼠标图标
当鼠标移动到不同区域时需要实时的进行鼠标图标切换,不然画面看着有点尬。
void CustomStretchWindow::SetCursorByRegoinIndex(BorderIndex region_index) {
switch (region_index) {
case BORDER_TOP:
case BORDER_BOTTOM:
this->setCursor(Qt::SizeVerCursor);
break;
case BORDER_LEFT:
case BORDER_RIGHT:
this->setCursor(Qt::SizeHorCursor);
break;
case BORDER_TOP_LEFT:
case BORDER_BOTTOM_RIGHT:
this->setCursor(Qt::SizeFDiagCursor);
break;
case BORDER_TOP_RIGHT:
case BORDER_BOTTOM_LEFT:
this->setCursor(Qt::SizeBDiagCursor);
break;
default:
this->setCursor(Qt::ArrowCursor);
break;
}
}
3.6 跟随鼠标的移动实时改变窗口尺寸
在鼠标拖动窗口边框时,需要根据不同的情况来重新计算窗口的尺寸和位置,拖动的是右、下相关边界时只需要改变尺寸即可,位置不用改变;但是当拖动的是左、上相关边界时就需要同时改变窗口的位置了,因为窗口起始点不一样了。
switch (cur_region_index_) {
case BORDER_TOP: {
wid.setTop(wid.bottom() - global_pos.y() > min_height ? global_pos.y() : wid.top());
setGeometry(wid);
} break;
case BORDER_TOP_LEFT: {
wid.setTop(wid.bottom() - global_pos.y() > min_height ? global_pos.y() : wid.top());
wid.setLeft(wid.right() - global_pos.x() > min_width ? global_pos.x() : wid.left());
setGeometry(wid);
} break;
case BORDER_TOP_RIGHT: {
wid.setTop(wid.bottom() - global_pos.y() > min_height ? global_pos.y() : wid.top());
wid.setRight(global_pos.x() - wid.left() > min_width ? global_pos.x() : wid.left() + min_width - 1);
setGeometry(wid);
} break;
case BORDER_LEFT: {
wid.setLeft(wid.right() - global_pos.x() > min_width ? global_pos.x() : wid.left());
setGeometry(wid);
} break;
case BORDER_RIGHT: {
width = this->width() - (this->width() - local_pos.x());
this->resize(width, height);
} break;
case BORDER_BOTTOM: {
height = this->height() - (this->height() - local_pos.y());
this->resize(width, height);
} break;
case BORDER_BOTTOM_LEFT: {
wid.setBottom(global_pos.y() - wid.top() > min_height ? global_pos.y() : wid.bottom());
wid.setLeft(wid.right() - global_pos.x() > min_width ? global_pos.x() : wid.left());
setGeometry(wid);
} break;
case BORDER_BOTTOM_RIGHT: {
width = this->width() - (this->width() - local_pos.x());
height = this->height() - (this->height() - local_pos.y());
this->resize(width, height);
} break;
default:
break;
}
以上即为去掉了Qt自带的边框后如何通过代码实现自定义的窗口拉伸功能的主要思路以及关键代码,有不解或说的不正确的地方可在下方评论区指出来,码字不易,如有不足之处请多担待。
四. 完整项目
如需下载本项目完整Demo,请点击下方链接:
https://download.csdn.net/download/tormi21c/85420901https://download.csdn.net/download/tormi21c/85420901 本项目是基于VS2019+QT5.15.2环境进行编写,大家可根据自身开发环境进行相应的修改,如有疑问可在评论区提出。
各位走过路过的大侠,如觉得对您有所帮助,赏个赞呗^_^,谢谢!如有不足之处请多包含!喜欢的可以收藏加关注,后续将持续更新更多精品内容!