Qt中的样式表、小部件隐藏和大小

5

样式表存在一个大问题。

如果您通过设置最大和最小值(固定大小)或其他方式通过样式表设置大小,然后尝试使用小部件的size()获取它们的大小,如果小部件从未显示过,则获取的大小会很奇怪。

但是您也无法从样式表中获取它们的大小,因此在调整大小或获取位置之前,您必须先显示所有小部件。

在我的情况下,我从窗口开始。但是,此窗口下面的小部件稍后将出现,并且它们必须在出现之前放置在正确的位置。 我有浮动小部件,它们并不作为QMainWindow的子级集成,所以我需要手动移动它们并将它们定位...但是,如果我不知道它们的大小,我该怎么做呢?

您知道任何其他获取存储在样式表中的大小的方法吗?


我猜如果解决方案是一个不带参数的单个方法调用,那么这不是一个大问题 :) 这可能与文档或PEBKAC有关,但与样式表无关 :) - Kuba hasn't forgotten Monica
这个平台是什么(操作系统,编译器),Qt 的确切版本是什么,以及给 configure 命令传递了哪些选项?我真的很难复现它。 - Kuba hasn't forgotten Monica
Windows 64位,Visual Studio 2008(使用哪个编译器?O_o)。Qt4.7。 - Darkgaze
当然有一种方法可以获取样式表的大小,你可以解析样式表自己,或者使用私有的Qt头文件来访问样式表的内部。但这是完全没有必要的,只是你在代码中遇到 bug 的一种解决方法。 - Kuba hasn't forgotten Monica
在显示小部件之前,size()将始终无效。您是否检查QWidget属性(如sizeHintminimumSizemaximumSizeminimumSizeHint)是否为您返回了正确的值? - Kamil Klimek
显示剩余2条评论
1个回答

7
我目前知道三种解决方案:
  1. 优化小部件:调用widget->ensurePolished()

  2. 通过样式优化小部件:调用qApp->style()->polish(widget)

  3. 发送已发布到小部件的事件:QCoreApplication::sendPostedEvents(widget, 0)在显示小部件之前。使用Qt 5,您不需要第二个参数,因为它现在有一个默认值0。

另一个解决问题的方法是根本不编写任何同步代码。要在首次显示时布置顶级小部件:
  1. Install an event filter on the widget.

  2. When the show event arrives on the widget:

    1. Uninstall the event filter from the widget.

    2. Deliver all of the window's events until none are left:

      while (widget->d_ptr->postedEvents)
        QCoreApplication::sendPostedEvents(widget, 0);
      

      You're now guaranteed that the widget is visible and has the correct size.

    3. Use the widget's geometry and move it. If you need information from all the widgets to make that decision, store it in some data structure and process it only when all the widgets are there.

如果您希望防止小部件在错误位置暂时可见,您可能希望在调整大小事件上采取行动。布局和样式系统将适当地调整顶层小部件的大小以适应其内容。当没有更多事件要传递给该对象并从调用sendPostedEvents返回时,您应卸载事件过滤器并仅处理最近的几何形状。
以下演示了强制抛光方法,并适用于Qt 4.8.5和5.2.0。我已在OS X上的64位构建和Windows上的32位构建上进行了测试。
请注意,l1和l2都没有显示,但它们都报告了正确的大小。 l3被显示,但在显示之前查询时,它会报告错误的大小。 screenshot
#include <QApplication>
#include <QLabel>
#include <QStyle>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QString style("QLabel { min-height:100px; max-height:100px; min-width: 300px; max-width:300px }");
    a.setStyleSheet(style);
    QLabel l1, l2, l3;
    l1.ensurePolished(); // equivalent first/second methods
    QCoreApplication::sendPostedEvents(&l2, 0); // third method
    l3.setText(QString("l1: %1 x %2 l2: %3 x %4 l3: %5 x %6")
               .arg(l1.width()).arg(l1.height())
               .arg(l2.width()).arg(l2.height())
               .arg(l3.width()).arg(l3.height()));
    l3.setAlignment(Qt::AlignCenter);
    l3.show();
    return a.exec();
}

@darkgaze,你误解了文档的意思。在这种情况下,Qt 可以在两个时间点对小部件进行重要操作:构造时和调用 show() 时。它是单线程代码,在调用 size() 时不会为您执行任何操作!因此,该函数只能在调用 show() 时调用,但是在它尚未可见之前show() 在小部件可见之前执行了许多操作。文档的真正意思是:我们将在您调用 show() 时调用它,但在电子击中屏幕荧光之前 :) - Kuba hasn't forgotten Monica
我已经执行了它,而且它工作正常。但是我的项目有将近100个类,很难找出问题所在。我在开头使用了一个样式表,但在我所做的事情中,它并不起作用。 :-( - Darkgaze
1
@darkgaze 嗯,我不能替你进行调试。在软件开发中解决问题需要将其缩小到最小可重现的情况。你可以轻松地复制项目并开始排除不相关的内容。它应该能够正常工作,所以你的问题不在这里,而是在代码的其他地方,可能与样式表等无关。 - Kuba hasn't forgotten Monica
是的。那可能是某个隐藏功能......让我检查一下那段新代码! 谢谢。 - Darkgaze
1
@darkgaze 是的。文档中没有说明 setStyleSheet 立即生效。所有的保证都是在事件循环排完队列后,设置样式的影响会产生并且可见。在 Qt 中,更新显示是异步从事件循环中完成的。由于您无法按需绘制,因此样式机制假装您可以按需绘制是毫无意义的。样式的可视和不可视效果在异步设置相关事件被从队列中排出时同时设置。 - Kuba hasn't forgotten Monica
显示剩余9条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接