One of the great strengths of PyQt is the use of layout
managers. Formerly, gui designers had to position and size every
element in their dialogs with pixel precision. Of course, this
meant that enlarging a window wouldn't show the user more data,
just a vast desert of boring grey pixels. Worse, when making a
window smaller, data would be obscured. Even worse, there are
still applications being made where you cannot resize the
windows at all.
Too large...
Too small.
It's easy to write applications as badly
behaved as this in PyQt— but where a Visual Basic
developer has to write a complex resize routine that
recalculates the size and position of each element, PyQt
developers can use Qt's advanced layout management
facilities.
Basically, this means that you create
several containers that hold your widgets, and those widgets
will resize together with the containers. The easiest way to
create a pleasing layout is by using the BlackAdder or Qt forms
designer, as this automatically uses sensible defaults.
There are three fundamental approaches to
layout management in PyQt: by stacking widgets or grouping them
in frames, by using the simple layout management provided by
QFrame and children, or by using the
advanced layout management QLayout
provides. In Qt 3.0 QLayout is even smart
enough to reverse the order of labels and entry widgets in
dialog boxes for right-to-left scripts.
Note: QMainWindow
provides its own layout management— it manages the size
and position of the menubar, toolbar or toolbars, statusbar
and the widget in the middle. If that widget is not composed
of several widgets, the management will be quite sufficient.
If there are several widgets constrained by a
QSplitter, the management will likewise
be sufficient, because in that case, the
QSplitter will be the central widget.
If you have a more complex assembly of widgets, you will have
to create a dummy central QWidget that
contains a layoutmanager that manages those widgets in a
pleasing way. You can also directly add a layout manager to
QMainWindow, but PyQt will natter about a layout manager being
added to a widget that already had one. It's not dangerous,
though. See Example 10-12 for an example
of such a dummy central widget.
Widget sizing: QSizePolicy
QWidget based
classes provide the layout management system with
size hints. This is a subtle system based
on a class named QSizePolicy. A
widget's size policy determines how small a widget can shrink,
how big it can grow and how big it really wants to be. Then
the layout manager negotiates with the widget though the use
of the sizeHint() about the size it will
get.
A widget can thus indicate whether it
prefers to stay a fixed horizontal or vertical size, or would
like to grow to occupy all available space. QSizePolicy
contains a horizontal size policy record and a vertical size
policy record. You can set the size policy programmatically,
but the setting is also available in the BlackAdder forms
creator.
The following size policies exist:
Fixed — the widget can't
shrink nor grow.
Minimum — the widget can't
shrink, and shouldn't grow.
Maximum — the widget can't
grow, but can shrink without any problem.
Preferred — the widget can
shrink, but shouldn't grow.
MinimumExpanding — the widget
can't shrink, but should be allowed to grow as much as
possible.
Expanding — the widget can
shrink, but should be allowed to grow as much as
possible.
Groups and frames
One way of getting automatic layout
management is by using QFrame, and its
children, like QGroupBox. We have
already seen such a frame in the radiobuttons example,
Example 10-9. The contents of the
frame will be managed automatically.
QFrame has
three interesting child classes: QGrid,
QHBox and
QGroupBox. There's also
QVBox, which descends from
QHBox.
Adding widgets to one of the
frame-based layout managers is simply a matter of creating the
widget with the layout manager as parent.
Those widgets will be resized according to the value their
sizeHint() returns.
QHBox
This a very, very simple class. A
QHBox aligns its children
horizontally, with a settable spacing between them.
QVBox
A QVBox layout
is possibly even simpler than the
QHBox layout: as the name implies, it
aligns its children vertically.
QGrid
It will come as no surprise that the
QGrid is a simple grid layout
manager — for more complicated layouts, with differently
sized columns, you really need
QGridLayout.
QGroupBox
A QGroupBox
gives you a frame (which can be drawn in as many flavors as
QFrame supports) and with a title
text which will appear on top of the frame. A
QGroupBox can hold child widgets.
Those widgets will be aligned horizontally, vertically or in
a grid. The grid can also be filled in columns (for
vertically oriented frames), or in strips (for horizontally
oriented frames).
QLayout
QLayout is
foundation of all complex Qt layout managers. Built on
QLayout, there are three
layoutmanagers: one for horizontal layouts, one for vertical
layouts and one for grids. It's also quite possible to build a
new layoutmanager on QLayout, one, for
instance, that manages playing cards on a stack, or perhaps
items in circle.
You can not only add widgets to layouts;
but also layouts. In this way, quite complex layouts can
achieved with very little pain.
All layout managers work by maintaining
a list of layoutitems. Those items can be
QLayoutItems,
QLayoutWidgets or
QSpacerItems. It's interesting to note
that QLayoutis a
QLayoutItem, and can thus be managed by
another layoutmanager.
Every layout item proxies for a widget.
A QSpacerItem is rather special, since
it doesn't represent a widget, but rather space. A
QSpacerItem ‘pushes' other
widgets, either horizontally or vertically. You can use them
to push all pushbuttons to the top of the dialog, instead of
having them spread out over the whole height by the layout
manager.
QBoxLayout and children
QBoxLayout is the
parent class of the horizontal and vertical box layout
managers — you will never use this class on its own, but
its useful to look at the methods it offers, because those are
inherited by QHBoxLayout and
QVBoxLayout
QGridLayout
While you can handle many layout
problems with combinations of horizontal and vertical box
layout managers, other problems are more suited for a
grid-based layout. The QGridLayout
class provides a flexible layout manager.
The grid managed by a
QGridLayout consists of cells laid out
in rows and columns: the grid cannot be as complicated as a
html table, but you can add widgets (or sub-layouts) that span
multiple rows and columns. Rows (or columns) can be given a
stretch factor and spacing.
Example 10-12. layout.py - two box layouts and adding and removing buttons
dynamically to a layout
This example shows that it is not only
possible to dynamically add widgets to a layout, but also to
remove them again. Removing means first severing the link from
the widget to the parent, and then deleting (using
del) all Python references to the widget.
When the last reference has been removed, the widget
disappears.
layout.py
setGeometry
You can use
setGeometry to set the size of every
individual widget yourself. There's another useful application
of setGeometry(), too: if you save the
size of the application window when the last window closes in
a configuration file, you can bring the window back to its
last size and position the next time the user starts opens it.
Example 10-13. geometry.py - setting the initial size of an
application