Thursday, August 13, 2009

Shrinking Qt widgets to minimum needed size

Shrinking a Qt widgets to the minimum size they can become because of size constraints or child widgets is not a difficult task. In fact, it's simply one call to resize(0, 0) that does just that. Because even resizes by the programmer are bound to the usual constraints, this resizes to the smallest possible size.


But when trying this in practical situations, it might become tricky. Shrinking your widgets means it becomes smaller. A widget (or dialog, or window) usually becomes smaller after removing or changing stuff. You will notice, however, that after removing a widget, a call to resize(0, 0) will not resize your widget to the size it would occupy without the removed widget, but instead it behaves like the widget was still there. This is because the widget is not completely removed until the next processing of the message loop. There are two ways to make certain that the message loop is being processed. One is calling QApplication::processEvents(). The other is using a QTimer::singleShot() and doing the task you want to do in the provided slot. I've tried both in regards to shrinking the widget and noticed that using a singleShot is more flicker-free than calling processEvents(), at least on the Windows platform.


I've created a demo-application to visualize the problem and check which method works better on a certain platform. Here are some screenshots:

Initial window
This is the window after opening the application. You can choose between directly shrinking the widget after removing the last button, using a singleShot() and using processEvents().


Added two buttons
Two buttons have been added, the dialog has automatically grown.


Removed button, shrinked directly
The last button has been removed, resize(0, 0) has been called directly after removing the widget. You can see that the size is not appropriate, the space the button took is still there. Notice the wider gaps between the widgets.


Removed button, shrinked in singleShot
The last button has been removed, resize(0, 0) has been called in a slot that was activated by a QTimer using a singleShot(). The size of the window is correct, no flicker was visible.


Removed button, called processEvents first
The last button has been removed, QApplication::processEvents() has been called before calling resize(0, 0). The size of the window is correct, but there was flicker visible.


You can download the source-code of the demo-appliction here and try it out yourself.

9 comments:

  1. Hey, would it be possible to tag your Qt articles so there would be a qt rss feed that I could add to planetqt?

    ReplyDelete
  2. Yup! Will do that for this and other related blog posts.
    I'm always too lazy for tagging ;-)

    ReplyDelete
  3. There's also QWidget::adjustSize() and QLayout::setSizeConstraint(QLayout::SetFixedSize). ;)

    ReplyDelete
  4. Following what J-P Nurmi suggests, there's a nice example at
    http://wiki.qtcentre.org/index.php?title=Expanding_dialog

    cheers!

    ReplyDelete
  5. Hello,
    Thank you very much for your code. On my Kubuntu Linux machine both singleShot and processEvents flicker (there is no difference in the amount of flicker). But adding the following line in the constructor removes the flicker:
    m_layout.setAlignment(Qt::AlignTop);

    ReplyDelete
  6. Hey! great blog , your information tips and tricks,which you provided really gud ...this is gud way to provide the information..nice work

    gaurvi
    orangy tech

    ReplyDelete
  7. You can reduce flickering by keeping a spacer with expanding size policy.

    ReplyDelete