欢迎光临
我们一直在努力

QSplitter 中添加 QWebEngineView 和其他 Widget 后布局没有均分的问题

1. 问题

一个大概率会出现的典型问题。问题的根源出在 QWebEngineView 上。 我们看一下示例代码:

MainWindow::MainWindow(QWidget *parent): QMainWindow(parent) {
...
m_textEdit = new QTextEdit(this);
m_webView = new QWebEngineView(this);
m_splitter = new QSplitter(this);
m_splitter->addWidget(m_textEdit);
m_splitter->addWidget(m_webView);
setCentralWidget(m_splitter);
...
}

运行结果:

2. 归因

这是一段非常普通的代码,在一个主窗体的构造函数里创建一个 QTextEdit,一个 QWebEngineView ,然后把它们添加到一个 QSplitter,如果是其他组件,出来的效果大概率是均分的,就是两个组件各占一半的窗口空间,因为 QSplitter 默认的分配策略就是均分,但如果有 QWebEngineView,就很有可能做不到了,就像上面一样。

猜测这和组件的初始默认推荐大小(sizeHint)有关,于是打印了两个组件的 sizeHint、minimumSize、minimumSizeHint:

MainWindow::MainWindow(QWidget *parent): QMainWindow(parent) {
...
m_textEdit = new QTextEdit(this);
m_webView = new QWebEngineView(this);
m_splitter = new QSplitter(this);
m_splitter->addWidget(m_textEdit);
m_splitter->addWidget(m_webView);

// size () 只有在控件 [ 可见/显示出来 ] 之后才有效
//qDebug() << "m_textEdit->size = " << m_textEdit->size();
//qDebug() << "m_webView->size = " << m_webView->size();

qDebug() << "m_textEdit->sizeHint = " << m_textEdit->sizeHint();
qDebug() << "m_webView->sizeHint = " << m_webView->sizeHint();

qDebug() << "m_textEdit->minimumSize = " << m_textEdit->minimumSize();
qDebug() << "m_webView->minimumSize = " << m_webView->minimumSize();

qDebug() << "m_textEdit->minimumSizeHint = " << m_textEdit->minimumSizeHint();
qDebug() << "m_webView->minimumSizeHint = " << m_webView->minimumSizeHint();

setCentralWidget(m_splitter);
...
}

输出结果:

m_textEdit->sizeHint = QSize(256, 192)
m_webView->sizeHint = QSize(100, 30)
m_textEdit->minimumSize = QSize(0, 0)
m_webView->minimumSize = QSize(0, 0)
m_textEdit->minimumSizeHint = QSize(69, 69)
m_webView->minimumSizeHint = QSize(0, 0)

根据 [ 官方文档 ] 的描述:The initial distribution of size between the widgets is determined by multiplying the initial size with the stretch factor,即:

Widget 在 QSplitter 中占据的空间是根据 Widget 本身的初始大小乘以伸展因子(stretch factor)来决定的

而在 MainWindow 的构建函数中获得的 size 是无实际意义的,实际上可用的是 sizeHint,从两个组件输出的 sizeHint 的值来看:256 : 100,而 QSplitter 的伸展因子(stretch factor)默认就是 1 : 1,计算下来和与应用实际展示的效果是一致的,这就解释了为什么两个窗口没有等分。为了验证这个分析结论,我们可以给 MainWindow 添加一个打印尺寸的方法:

void MainWindow::printSizes() {
qDebug() << "m_splitter->size = " << m_splitter->size();
qDebug() << "m_splitter->sizes = " << m_splitter->sizes();
qDebug() << "m_textEdit->size = " << m_textEdit->size();
qDebug() << "m_webView->size = " << m_webView->size();
}

放在 MainWindow::show 方法之后调用它,得到结果是:

m_splitter->size = QSize(356, 192)
m_splitter->sizes = QList(252, 99)
m_textEdit->size = QSize(252, 192)
m_webView->size = QSize(99, 192)

与我们的分析一致,所以,结论就是:

Widget 在 QSplitter 中占据的空间是根据 Widget 本身的初始大小(sizeHint)的比例来分配的

3. 方案

起初,我们可能会尝试显式地设置一下 QSplitter 的 Sizes 和 StretchFactor,就像下面这样:

MainWindow::MainWindow(QWidget *parent): QMainWindow(parent) {
...
m_textEdit = new QTextEdit(this);
m_webView = new QWebEngineView(this);
m_splitter = new QSplitter(this);
m_splitter->addWidget(m_textEdit);
m_splitter->addWidget(m_webView);
// 此处设置无效,后续会被重新覆盖
m_splitter->setSizes({100, 100});
// 并不需要,本来就是 1
m_splitter->setStretchFactor(0,1);
// 并不需要,本来就是 1
m_splitter->setStretchFactor(1,1);
setCentralWidget(m_splitter);
...
}

测试结果:无效。原因在于:在窗口对象的构造阶段,QSplitter 及其子控件(QTextEdit/QWebEngineView)的布局尚未完成,此时设置的尺寸会被后续的布局重计算覆盖。因此,我们需要推迟 QSplitter 重新进行布局计算的时机,这里有两终方法可供选择,比较推荐的是第二种。但我们先看一下第一种方案。

  • 方案一:延迟重置切分尺寸

可以在构造函数中插入一段延迟执行的代码,等窗体构造完成之后重置 QSplitter 的大小:

MainWindow::MainWindow(QWidget *parent): QMainWindow(parent) {
...
m_textEdit = new QTextEdit(this);
m_webView = new QWebEngineView(this);
m_splitter = new QSplitter(this);
m_splitter->addWidget(m_textEdit);
m_splitter->addWidget(m_webView);
setCentralWidget(m_splitter);
...
// 延迟到事件循环空闲(0ms)+ 等待 WebView 初始化(200ms 足够)
QTimer::singleShot(200, this, [this]() {
// 先确保 Splitter 有可用的总宽度
int totalWidth = m_splitter->width();
if (totalWidth <= 0) {
totalWidth = this->width() m_splitter->contentsMargins().left() m_splitter->contentsMargins().right();
}
// 等分尺寸(基于实际总宽度)
m_splitter->setSizes({totalWidth/2, totalWidth/2});
qDebug() << "最终生效的尺寸:" << m_splitter->sizes();
});
}

  • 方案二:事件驱动重置切分尺寸

延时处理总是给人一种“不牢靠”的感觉,还是建议通过事件驱动的方式来执行切分操作。这个事件就是当 GUI 渲染完毕后自动触发的 showEvent 方法:

void MainWindow::showEvent(QShowEvent *e) {
QMainWindow::showEvent(e);

qDebug() << "before:" << m_splitter->sizes();
// WidgetUtils::distributeWidgetsOfSplitter(m_splitter);
// 先确保 Splitter 有可用的总宽度
int totalWidth = m_splitter->width();
if (totalWidth <= 0) {
totalWidth = this->width() m_splitter->contentsMargins().left() m_splitter->contentsMargins().right();
}
// 等分尺寸(基于实际总宽度)
m_splitter->setSizes({totalWidth/2, totalWidth/2});
qDebug() << "after:" << m_splitter->sizes();
}

执行后的结果:

赞(0)
未经允许不得转载:171主机测评 » QSplitter 中添加 QWebEngineView 和其他 Widget 后布局没有均分的问题
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址