一、简述
大家好,我是前行中的小猪,今天给大家讲解的是布局的相关用法分析以及如何对控件进行重布局,先从助手文档带大家了解一下用法,然后再通过类似视频监控分屏切换的示例给大家简单分析一下,在一个控件中通过菜单切换实现不同的布局,我们先看一下效果。
效果图1:
一般视频监控这种场景会连接多颗摄像头去进行监控,这个时候就需要多屏操作,一般情况会连接多个显示器,但是奈何像学校,商场,小区需要几十上百个摄像头进行对整片区域的监控,这不是靠几个屏幕能看的过来的,这个时候就需要进行分区显示,比如一个屏幕显示单个、4个、6个、9个、16个、21个、32等等,无疑是希望在几块屏幕上能够看到更多区域的视频,同时可以进行切换,方便管理。
1.1 Qt助手中setLayout方法介绍
图2:
从上图2中,我们看到Qt文档对于setLayout方法的解说,具体作用就是给某个部件设置布局管理器。
1.11 文档中需要注意的两点
- 如果当前部件已经设置过布局,当你再次对此部件设置新的布局是不会生效的,你必须先删除之前已经设置的布局,新布局才会生效。
- 如果当前布局已经设置给了控件A,那么控件B对此布局再调用setLayout方法,将会把控件A的布局重新设置给控件B。
我们先来看看第一点,结合下方的测试代码,如果widget已经设置了hLayout1布局,如果再设置hLayout2布局,最终结果是hLayout1生效,hLayout2不生效,并且运行到这段代码的时候编译器会有错误提示,告诉你当前部件已经配置一个布局了,人家这是一夫一妻制,多了就不合法了。
QWidget* widget = new QWidget;
QHBoxLayout* hLayout1 = new QHBoxLayout;
widget->setLayout(hLayout1);
QHBoxLayout* hLayout2 = new QHBoxLayout;
widget->setLayout(hLayout2);
====================================================
我们再来看看第二点,继续结合下方的测试代码,大家快速过一遍,然后翻到下方的效果图。
m_contentLabel = new QLabel("我是测试控件");
m_contentLabel->setAlignment(Qt::AlignCenter);
m_leftWgt = new QWidget;
m_leftWgt->setStyleSheet(".QWidget{background:red;}");
QHBoxLayout* hLayout = new QHBoxLayout(m_leftWgt);
hLayout->addWidget(m_contentLabel);
hLayout->setMargin(0);
m_rightWgt = new QWidget;
m_rightWgt->setStyleSheet(".QWidget{background:yellow;}");
m_pBtnSwitch = new QToolButton;
m_pBtnSwitch->setText("切换布局");
QGridLayout* gLayout = new QGridLayout(this);
gLayout->addWidget(m_leftWgt, 0, 0);
gLayout->addWidget(m_rightWgt, 0, 1);
gLayout->addWidget(m_pBtnSwitch, 1, 0);
connect(m_pBtnSwitch, &QToolButton::clicked, this, [=] {
QLayout* layout = m_leftWgt->layout();
m_rightWgt->setLayout(layout);
});
上方的代码中,我们定义了左右两个widget,我们将布局先设置给了左边的widget,在按钮的槽函数中我们将此布局再传递给右边的控件,从下方的效果图中我们看到布局(及布局上添加的部件)是可以成功地从控件A转移到控件B上,但前提是控件B没有设置过布局。
这就好比,控件A在生活中对布局对象不好,那行吧,两个人过不下去,那就离吧,然后布局遇到了控件B,觉得控件B人不错,就决定嫁给控件B,但是要想合法,那么控件B不能存在婚姻关系(也就是说控件B没有设置过布局),所以布局对象就可以名正言顺地带着他所拥有的一切转移给控件B,不知道这个比喻大家还能理解O(∩_∩)O。
效果图3:
1.12 给部件设置布局的两种方式:
方式一:主动式
给部件对象直接调用setLayout方法指定布局(文档中给出了示例)
QWidget* parentWidget = new QWidget;
QPushButton* pBtn = new QPushButton;
QVBoxLayout *layout = new QVBoxLayout;
layout->addWidget(QPushButton);
parentWidget->setLayout(layout);
方式二:被动式
直接将需要设置布局的控件对象放到布局对象的构造中(文档中最后一句话给了提示,通常我也喜欢用这种方式,毕竟省了一行代码O(∩_∩)O)
QWidget* parentWidget = new QWidget;
QPushButton* pBtn = new QPushButton;
QVBoxLayout *layout = new QVBoxLayout(parentWidget);
layout->addWidget(QPushButton);
1.13 - - 问题来了- -
如果我们给控件设置了布局,那如何清空某个控件的布局呢?
方法一:
既然我们可以使用setLayout方法设置布局,能不能用它清空布局呢,我们来传个空指针可以不?
答案是不可以,这里假设m_leftWgt已经设置过布局,这里我们给他传个空指针,调用完之后,实际上之前的布局依然存在,并且编译器再次给出提示 QWidget::setLayout: Cannot set layout to 0,就是你不能给他传个空指针。
m_leftWgt->setLayout(nullptr);
QLayout* layout = m_leftWgt->layout();
if (layout != nullptr){
int count = layout->count();
qDebug() << count;
qDebug() << "布局仍然存在";
}
else{
qDebug() << "布局已经删除";
}
// 结果: 布局仍然存在
方法二:
这里我们假设m_leftWgt已经设置过布局。
这种方法就强势一点,直接获取当前控件的布局对象,然后直接删除(为了严谨一点,这里还是要判断下指针是否为空),通过测试,这种方法是可以成功的。
QLayout* layout = m_leftWgt->layout();
if (layout != nullptr)
delete layout;
layout = m_leftWgt->layout();
if (layout != nullptr){
int count = layout->count();
qDebug() << count;
qDebug() << "布局仍然存在";
}
else{
qDebug() << "布局已经删除";
}
// 结果: 布局已经删除
通过这种方法,布局虽然删除了,但是布局上的添加的子控件依然认为m_leftWgt控件为父控件,也就是我们给布局添加的子控件依然依附于m_leftWgt控件,那么怎么完全删除布局及布局上所添加的所有子控件呢?具体解决方案将在下一篇博文中再详细讲解。
效果图4:
二、尾
本篇文章详细讲解了setLayout的一些相关用法,通过助手中给出的文档说明,带着大家进行了一些拓展分析,下篇文章将继续带着大家分析遗留的问题以及给大家带来视频监控分屏切换的示例代码,欢迎一起交流讨论,如果对文章中提及的点有疑问或者发现有错误的地方欢迎指出,也十分欢迎感兴趣的小伙伴一起交流。