All basic screen components are available in PyQt:
buttons, frames, edit controls, listboxes and comboboxes. All
these widgets can be drawn in any number of styles, and you
can even define your own style. Note that the window
titlebar and borders are not defined by the widget style, but
by the system you are running. The borders in these
screenshots are from the KDE System++ style.
Basic widgets in the CDE style
Basic widgets in the motif style
Basic widgets in the motif plus style
Basic widgets in the platinum style
Basic widgets in the SGI style
Basic widgets in the Windows style
QFrame
Frames are used to group other widgets
— either visibly (for instance by drawing a nice bevel
around them), or invisibly (by managing the geometry of those
widgets. PyQt offers all the usual options, from panels to
ridges to bevels, with horizontal and vertical lines thrown in
for good measure.
QPushButton
Pushbuttons are the mainstay of gui programming. They can be
adorned with text or with a picture, but not both (you need a
QToolButton for that).
QPushButtons are based on an
abstraction of all button functionality, namely
QButton, which is also the parent of
QCheckBox,
QRadioButton and
QToolButton. In honor of QPushButton's
central importance, I want to present a ‘Hello'
application with four buttons, each in a different style. This
also shows a frame.
Example 10-7. buttons.py - Four pushbuttons saying
‘hello'.
Labels are ubiquitous in a gui
application — and the PyQt QLabel
offers much more than just plain-text labels for use in dialog
boxes. PyQt labels can also contain rich text or a
QMovie, such as an animated GIF or PNG.
Through the setBuddy method, a
QLabel can be associated with another
control. If any character in the label text is prefixed by an
ampersand — & — that character will be shown
underlined, and by pressing alt-character, the user can jump
to the control associated with the label through the buddy
property.
Example 10-8. label.py - a label associated with an
edit control
If you press alt-e after starting the
label.py script (which is on the CD-ROM),
you'll see the cursor appearing in the edit field.
You might wonder why the cursor is not the control
that accepts user input when you start the script —
this is a property of PyQt. On starting an application,
the main window has the focus, not the controls
associated with it. If you want to make the user's life
easier, call setFocus() on the main
widget in the __init__ of the main
window:
#
# label2.py
#
import sys
from qt import *
class dlgLabel(QDialog):
def __init__(self,parent = None,name = None,modal = 0,fl = 0):
QDialog.__init__(self,parent,name,modal,fl)
self.setCaption("label dialog")
if name == None:
self.setName("dlgLabel")
self.layout=QHBoxLayout(self)
self.layout.setSpacing(6)
self.layout.setMargin(11)
self.label=QLabel("&Enter some text", self)
self.edit=QLineEdit(self)
self.label.setBuddy(self.edit)
self.layout.addWidget(self.label)
self.layout.addWidget(self.edit)
self.edit.setFocus()
if __name__ == '__main__':
app = QApplication(sys.argv)
QObject.connect(app, SIGNAL('lastWindowClosed()'),
app, SLOT('quit()'))
win = dlgLabel()
app.setMainWidget(win)
win.show()
app.exec_loop()
A label is a QWidget; it is thus quite
possible to handle key and mouse events in a label. You might,
for instance, want to make a clickable label that looks like
an URL.
QRadioButton
Radio buttons always remind of my tax
return forms - check one and only
one out of a certain number of choices.
Radio buttons should not be used if there are more than five
choices at most, and you would be well advised to limit
yourself to no more than three. A constellation of radio
buttons has the advantage that all options are visible at the
same time, but it takes a lot of screen space. Do not use
checkboxes instead of radio buttons for exclusive choices.
People will get confused.
Radio buttons include their labels
— and these labels can again be marked with an ampersand
(&) for easy selection. In order to force the ‘one
and only one' choice, combine the mutually exclusive radio
buttons in a QButtonGroup, or one of
the descendants: QVButtonGroup for
vertical layouts (recommended) or
QHButtonGroup for horizontal layouts
(these look rather weird). A radiobutton can be initialized
with setChecked().
Example 10-9. radio.py - a group of mutually exclusive
options
Checkboxes are another of those gui
features that makes one think of bureaucratic forms. Check any
that apply... Checkboxes are as easy to use as radiobuttons,
and come with a label attached, like radiobuttons. A variation
which makes for instant user confusion is the tri-state
checkbox, in which there's a checked,
unchecked and a doesn't
apply state — the doesn't apply state is
usually rendered to look just like a completely disabled
checkbox. However, it is sometimes necessary to introduce this
behavior. Imagine a dialog box that allows the user to set a
filter:
A dialog with a tri-state
checkbox in the ‘doesn't
apply' state.
Now the user has three choices: either
look for solvent persons, for insolvent persons or for both.
The Platinum style makes it very clear which state the
checkbox is in, compared to, for instance, the Windows
style.
QListBox
Listboxes are simple containers where a
variable number of strings can be added. You can allow the
user to select no item, one item, a range of items or a
discontinuous set of items. You can enter texts or pixmaps in
a listbox, but the listbox, like the listview and the
combobox, doesn't let you associate arbitrary data with the
items inside it.
This is something you may often want
— you let the user select a certain object by clicking
on an item in the listbox, and you want that object to be
available in your application, not the string or picture that
represents the object, or the index of the item in the
listbox. You can achieve this by coding a small associative
listbox:
Example 10-10. listbox.py - A listbox where data can be
associated with an entry
#
# listbox.py
#
# listbox with key-to-index and index-to-key mapping
#
import sys
from qt import *
class AssociativeListBox(QListBox):
def __init__(self, *args):
apply(QListBox.__init__,(self,)+args)
self.text2key = {}
self.key2text = {}
self.connect(self, SIGNAL("selected(int)"),
self.slotItemSelected)
def insertItem(self, text, key):
QListBox.insertItem(self, text)
self.text2key [self.count() - 1] = key
self.key2text [key]=self.count() - 1
def currentKey(self):
return self.text2key[self.currentItem()]
def setCurrentItem(self, key):
if self.key2text.has_key(key):
QListBox.setCurrentItem(self, self.key2text[key])
def slotItemSelected(self, index):
key=self.currentKey()
self.emit(PYSIGNAL("itemSelected"),
(key, self.currentText()) )
def removeItem(self, index):
del self.text2key[self.currentItem()]
del self.key2text[index]
QListView.removeItem(self, index)
class MainWindow(QMainWindow):
def __init__(self, *args):
apply(QMainWindow.__init__, (self,) + args)
self.listbox=AssociativeListBox(self)
self.listbox.insertItem("Visible text 1", "key1")
self.listbox.insertItem("Visible text 2", "key2")
self.listbox.insertItem("Visible text 3", "key3")
self.setCentralWidget(self.listbox)
self.connect(self.listbox,PYSIGNAL( "itemSelected"), self.printSelection)
def printSelection(self, key, text):
print "Associated with", key, "is", text
def main(args):
app=QApplication(args)
win=MainWindow()
win.show()
app.connect(app, SIGNAL("lastWindowClosed()")
, app
, SLOT("quit()")
)
app.exec_loop()
if __name__=="__main__":
main(sys.argv)
listbox.py
Of course, the same trick is needed to get something
useful out of a QComboBox or a
QListView.
QComboBox
A QComboBox offers almost the same
functionality as a QListBox, but folds
out and in, preserving screen space. The contents of a
combobox can be read-only or editable, and you can check the
correctness of user input using a
QValdidator object.
QLineEdit
This is a simple one-line edit control, familiar to gui
users everywhere. It supports copy, cut and paste and redo,
too. There's a special mode for password boxes.
QMultiLineEdit
QMultiLineEdit provides a very
simple multi-line editor. This class does not, in any way,
support rich text — all text is in the same font, same
size and same color. You can enable word-wrap, but that's
about it. There are no limits on the amount of text it can
handle (as was the case with the old Windows edit control,
which had a limit of about 32 kb), but with megabytes of data,
it will become decidedly slow.
With Qt3 this class has become obsolete.
You're supposed to use the new, advanced
QTextEdit, instead. After creating a
QTextEdit object, you'd set the
textformat to plain with
QTextEdit.setFormat(Qt.PlainText); no
user would notice the difference.
QTextEdit and
QMultiLineEdit are quite different to
the programmer, though, and you can still use
QMultiLineEdit if you need
compatibility with older versions of Qt.
QPopupMenu
One of the most useful things you can
offer a user in any document-based application is a context
menu — press the mouse-button anywhere in the document
and a list of useful options pop up. PyQt's
QPopupMenu can be used both as a
stand-alone popup menu, and within a menu bar. Menu items can
have a shortcut key associated with them, an accelerator and a
small icon. The most useful way of adding items to a menu is
by defining a QAction. You can nest
menus, and make ‘tear-off' menus, where the user can
click on a ‘tear-off handle' which puts the menu in a
window of its own.
QProgressBar
QProgressBar gives
you a horizontal progressbar — it's quite simple, even
though it can be set to use one of several different styles.
There's also QProgressDialog which can
be used for lengthy actions that completely block access to
the application. Since PyQt doesn't really support
multi-threading, it's probably best to stick with the blocking
dialog.
If you want to get fancy, you can, with a
bit of juggling, get an approximation to threading by using a
QTimer. Then it's best to place the
progress bar in the statusbar of your application, instead of
a separate non-modal progress dialog.
the Section called A painting example in Chapter 21 gives an example of the
use of a timer.
QSlider and other small fry
There are several other, simple
user-interface widgets: QDial,
QLCDNumber,
QScrollBar,
QSizeGrip,
QSpinBox and
QToolButton. These widgets are seldom
used, mostly because they are rather overspecialized.
QDial is a
potentio-meter like knob. Twiddling it demands a fair
proficiency with the mouse, and the keyboard interface isn't
immediately obvious. See Example 7-5
for an example of using QDial.
QLCDNumber is a
kind of label which can display numbers in an lcd-like format.
It's mostly interesting for historical reasons — the
first version was written for the Sinclair ZX-Spectrum, a 1.9
MHz Z80 computer with a rubber keyboard and 48 Kb of
ram.
QScrollBar looks
to be quite useful, because, on the face of it, any gui
application is full of scrollbars. But those scrollbars come
automatically with the edit-controls, listboxes and other
scrollable widgets, and the QScrollBar
is seldom used in isolation, and then mostly for the same
purpose as QDial— as a range
control. If you want to use it for scrolling a section on
screen, use the QScrollView class
instead.
The QSizeGrip is
extremely obscure, being at its peak form
only in statusbars of resizable windows. And those
QStatusBars can take care of their
sizegrips themselves.
QSpinBox is another range control.
It's often used to let the user select a size for a font
— she can either type the size directly, or use the
little arrows to choose a larger or smaller size. Spinboxes
are often quite fiddly to use.
QToolButton is a special button
that carries more often a picture than a text — it's
mostly used in toolbars, where you can add buttons without
explicitly creating instances of this class.