All Qt widgets and all visible components are founded upon
QWidget — this monster class provides all
event handling, all style handling and countless other chores.
To help with the handling of these tasks, there are other
classes, such as QPixmap,
QColor, QFont or
QStyle.
QWidget can be useful to build your
own widgets on, provided you are prepared to do all your own
painting — this includes buffering in case your widget gets
a paintEvent call! Consider the next
snippet, which is an extension of the event1.py example:
Example 10-4. event2.py - using QWidget to create a custom,
double-buffered drawing widget.
By drawing to QPixmap instead of
to QWidget, and
blitting the contents of that pixmap to the
widget, the drawing will be kept. Note also how much smoother
the drawing feels, despite the extra work the script has to do.
This technique is called double buffering, and is the alpha
and the omega of graphics programming. Of course, there's still
a small problem with resizing... In fact, if you want to build
your own widgets from the ground up using QWidget, you're always
in for more work than you reckoned with.
QColor
The QColor class
represents any color that can be used in PyQt. You can
instantiate a new color either by using an RGB
(red-green-blue) value, an HSV (hue-saturation-value) value,
or a name. The X11 system used on Unix provides a database
full of rather poetic color names like ‘Old Lace',
‘Royal Blue' and ‘Peach Puff' —you can use
these names instead of hexadecimal numbers. The Windows
version of PyQt has a copy of this database, so it's quite
portable. If you replace the
resizeEvent() in the event2.py example
with the following code, you'll see the effect:
Example 10-5. snippet from event3.py - a peach puff drawing
board
A final note on colors: the way you set
the colors of a widget have been changed between Qt2 and Qt3.
Where you first used
setBackgroundColor(), you'd now use
setEraseColor(). Yes, there is a logic
behind this change of name, but it is very specious, and the
change broke almost all my code. The erase color is the color
that Qt uses to clear away, or erase, all the pixels that had
been painted just before they are painted again in a
paint event.
When you're designing complex widgets,
you will want to investigate
setBackgroundMode and the
BackgroundMode flags.
QPixmap, QBitmap and QImage
We have already been using a
QPixMap to double buffer the
scribblings in the previous two examples.
QPixmap is not the only image class
PyQt offers: there's also QBitmap,
which is just like QPixmap, but for
black and white images only, and
QImage. Where
QPixmap and
QBitmap are optimized for drawing (and
then showing on screen or on a printer),
QImage is optimized for reading and
writing (together with the QImageIO
class), and for manipulating the actual pixels of an image.
There's another image-related class,
QPicture, which can be used to record
drawing operations and replay them later. The recorded paint
events can then be stored in a file and reloaded later on.
Those files are called meta-files —
but they're in a special Qt format. In Qt 3,
QPicture also supports the standard
scalable vector graphics format, svg. If you want to create a
complex vector-drawing application you'd be well advised to
stick to this standard.
QPainter
A QPainter object
is used to efficiently paint on any paintdevice using a
variety of primitive graphics, such as simple dots or lines,
bezier curves, polygons, strings of text (using a particular
font) or pixmaps. Drawings can be modified, for instance by
shearing or rotating, and parts can be erased or clipped. We
have already used a QPainter to draw the scribbly lines in
previous examples.
Paint devices can be:
pictures:
QPicture
pixmaps:
QPixmap
printers:
QPrinter
widgets : QWidget (and
all children of QWidget)
What can be drawn on one device, can
be drawn on all devices, so it's uncommonly easy to print on
paper what can be drawn on screen. Copying batches of pixels
from one paint device to another is blindingly fast if you use
the bitBlt global function, as we did above for our
double-buffered graphics editor.
Note that you cannot create
any paint device until you have created a
QApplication. This includes
QPixmaps. The following variant on
action.py won't work, even though it seems a
good idea to pre-create the pixmap,
instead of converting the xpm data on constructing the
QAction:
Example 10-6. fragment from action2.py - You cannot create a
QPixmap before a QApplication
#
# action2.py
#
import sys
from qt import *
connectIcon=QPixmap(["16 14 5 1",
" c None",
". c black",
"X c gray50",
"o c red",
"O c yellow",
" ",
" . ",
" X .X ",
" XooX . ",
" Xoooo .X ",
" XooooooX ",
" XooooooX ",
" XoooooX. ",
" XooooX. ",
" XOXXXX. ",
" XOXX... ",
" XOXX ",
" XX ",
" X "
])
class MainWindow(QMainWindow):
def __init__(self, *args):
apply(QMainWindow.__init__, (self, ) + args)
...
self.action.setIconSet(QIconSet(connectIcon))
...
Running this gives the following result:
boudewijn@maldar:~ > python action2.py
QPaintDevice: Must construct a QApplication before a QPaintDevice
Aborted
Chapter 21 deals with painters and paintdevices
in quite a lot of detail, while Chapter 24 deals
with printing to paper.
QFont
There is no other area where there are so
many and profound differences between operating systems as
there is with fonts. And if you take into account the
difference in font handling between printers and screens, you
will get a feeling for how difficult it is to get proper and
dependable cross-platform multi-lingual font support in a
toolkit.
Fortunately, Qt's font support has
steadily improved, and is now at the point where, provided
good quality fonts are available on a system, it can offer the
same excellent screen and printer support
on all platforms.
The first issue is the font used for
drawing labels and other application texts — sometimes
called the system font. This naturally differs for each
system: Windows uses Arial these days, while KDE uses
Helvetica, CDE Times and OS X a bold Helvetica. Furthermore,
the system font is also often customized by the user. Text in
one font takes more room than text in another font —
possibly giving ugly display errors. By using Qt's layout
managers, instead of positioning widgets with pixel-precision
yourself, you will have little trouble dealing with the
geometry differences between Window's Arial font and KDE's
Helvetica standard — all controls will reflow
neatly.
For handling fonts in your application
you can work with QFont. Qt builds its
own database of available fonts from whatever the system
provides. You can then access these fonts in a
system-independent manner, without having to juggle X11 font
resource names yourself.
QFont provides all
necessary functions to select encodings (or scripts in Qt3),
font families, styles and sizes. There's also a standard
dialog available, QFontDialog that you
can use to let the user select a certain font.
There are serious differences between the
font system in Qt2 and Qt3. In Qt2, you need to determine
which character set encoding you need; and you can only use
the character set encodings that the particular font supports.
For instance, if your font supports the KOI8 Cyrillic
encoding, then that is the encoding you can use. The font you
request has a one-to-one relation with the font files on your
system.
In Qt3, you select fonts by name, style
and script (like Cyrillic), and Qt will select the closest
fitting font. If your widget needs to present text on screen
that uses characters that cannot be retrieved from the
selected font, Qt will query all other fonts on your system,
and assemble a composite, synthetic font that includes all
characters you need. You lose some control but you gain a
correct representation of all possible texts— you can
use any font for any text in any script.
If you want to set a certain font for the
entire application, you can use the
QApplication.setFont class function.
Likewise, everything that descends from
QWidget also has a
setFont() function.
You can use QFontInfo to
determine the exact font Qt uses for a certain QFont — but
this might be quite slow. An important use of
QFontInfo with Qt3 is to determine
whether the font you get was exactly the font you asked for.
For instance, if you desire a Bembo font, which might not be
present on your system, you could get something closeish: a
Times New Roman. Especially for drawing and dtp applications
it's important to be sure which font is actually used.
QFontMetrics can
be used to determine metrics information about a font. For
instance, how high the ascenders and descenders are, and how
wide the widest character is. This is useful if you need to
determine how much space a line of text takes when printed on
paper.