Bill Gates is apocryphically reported to
have once shouted ‘Why must everyone in my company write
his own file-open code? Go and build something that works for
every application!". And thus the Windows
standard file open dialog was born. This dialog has gone through
several versions, necessitating continuous rewriting. All the
same, the idea was a good idea, and Qt implements several
standard dialogs that look and feel just like the Windows common
dialogs, but are a lot easier to program. We'll implement
common dialog PyQt lacks, for searching and replacing, in
the Section called Non-modal: Search and
replace in Chapter 19, Using Dialog
Windows.
QDialog
QDialog is the
parent of all dialog classes. A dialog window is a window that
pops up over the application window. These can be modal (where
it will block the rest of the application) or modeless (where
the user can continue working in the main screen of the
application). Dialogs are commonly closed with OK or Cancel
buttons. There is no reason to make a dialog a fixed size; you
can give it a QSizeGrip, and if you use
QLayout layout management, the contents
will be resized quite nicely. A modal dialog has its own
exec_loop; a modeless dialog can be
constructed, shown, and hidden, but is part of its parents
event loop.
Of course, there are many other
occasions where you will want to create custom dialog boxes.
PyQt provides for plain dialog boxes, expanding dialog boxes,
tabbed dialog boxes and wizards.
QMessageBox
A QMessageBox is a very simple
standard dialog class. Message boxes are always modal, and can
be used to inform, warn or frighten the user. Message texts
should preferably short, specific, and as non-threatening as
possible.
Example 10-14. dialogs.py - opening message and default dialogs boxes
#
# dialogs.py
#
import sys
from qt import *
class MainWindow(QMainWindow):
def __init__(self, *args):
apply(QMainWindow.__init__, (self, ) + args)
self.setCaption("Network Client")
self.actionInformation=QAction(self, "Information")
self.actionInformation.setText("Informational Message")
self.actionInformation.setMenuText("&Information")
self.actionInformation.setStatusTip("Show an informational mesagebox.")
self.connect(self.actionInformation,
SIGNAL("activated()"),
self.slotInformation)
self.actionWarning=QAction(self, "Warning")
self.actionWarning.setText("Warning Message")
self.actionWarning.setMenuText("&Warning")
self.actionWarning.setStatusTip("Show a warning mesagebox.")
self.connect(self.actionWarning,
SIGNAL("activated()"),
self.slotWarning)
self.actionCritical=QAction(self, "Critical")
self.actionCritical.setText("Critical Message")
self.actionCritical.setMenuText("&Critical")
self.actionCritical.setStatusTip("Show an informational mesagebox.")
self.connect(self.actionCritical,
SIGNAL("activated()"),
self.slotCritical)
self.actionAbout=QAction(self, "About")
self.actionAbout.setText("About")
self.actionAbout.setMenuText("&About")
self.actionAbout.setStatusTip("Show an about box.")
self.connect(self.actionAbout,
SIGNAL("activated()"),
self.slotAbout)
self.actionAboutQt=QAction(self, "AboutQt")
self.actionAboutQt.setText("About Qt Message")
self.actionAboutQt.setMenuText("About &Qt")
self.actionAboutQt.setStatusTip("Show an about box for Qt.")
self.connect(self.actionAboutQt,
SIGNAL("activated()"),
self.slotAboutQt)
self.actionFile=QAction(self, "OpenFile")
self.actionFile.setText("Open File")
self.actionFile.setMenuText("&Open")
self.actionFile.setStatusTip("Open a file.")
self.connect(self.actionFile,
SIGNAL("activated()"),
self.slotFile)
self.actionFont=QAction(self, "Font")
self.actionFont.setText("Select a font")
self.actionFont.setMenuText("&Font")
self.actionFont.setStatusTip("Select a font")
self.connect(self.actionFont,
SIGNAL("activated()"),
self.slotFont)
self.actionColor=QAction(self, "Color")
self.actionColor.setText("Select a color")
self.actionColor.setMenuText("&Color")
self.actionColor.setStatusTip("Select a color")
self.connect(self.actionColor,
SIGNAL("activated()"),
self.slotColor)
# Statusbar
self.statusBar=QStatusBar(self)
# Define menu
self.messageMenu=QPopupMenu()
self.actionInformation.addTo(self.messageMenu)
self.actionWarning.addTo(self.messageMenu)
self.actionCritical.addTo(self.messageMenu)
self.dialogMenu=QPopupMenu()
self.actionFile.addTo(self.dialogMenu)
self.actionFont.addTo(self.dialogMenu)
self.actionColor.addTo(self.dialogMenu)
self.helpMenu=QPopupMenu()
self.actionAbout.addTo(self.helpMenu)
self.actionAboutQt.addTo(self.helpMenu)
self.menuBar().insertItem("&Messages", self.messageMenu)
self.menuBar().insertItem("&Standard dialogs", self.dialogMenu)
self.menuBar().insertItem("&Help", self.helpMenu)
def slotInformation(self):
QMessageBox.information(self,
"Information",
"A plain, informational message")
def slotWarning(self):
QMessageBox.warning(self,
"Warning",
"What you are about to do will do some serious harm .")
def slotCritical(self):
QMessageBox.critical(self,
"Critical",
"A critical error has occurred.\nProcessing will be stopped!")
def slotAbout(self):
QMessageBox.about(self,
"About me",
"A demo of message boxes and standard dialogs.")
def slotAboutQt(self):
QMessageBox.aboutQt(self)
def slotFile(self):
filename=QFileDialog.getOpenFileName("", "*.py", self, "FileDialog")
def slotFont(self):
(font, ok) = QFontDialog.getFont(self, "FontDialog")
def slotColor(self):
color=QColorDialog.getColor(QColor("linen"), self, "ColorDialog")
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)
Giving the user some information.
A gentle warning
A dire warning
About your application
About Qt
QTabDialog
One of the best ways to organize a
multitude of options is to group them together and show the
user only the pertinent set, hiding the rest between tabs.
Usability studies have shown that a moderate number of tabs,
presented in a single row showing all available tabs at one
time, promotes the greatest usability. Twenty tabs in three
rows confuse the user; one scrolling row of twenty tabs
irritates the user. I have once used tabs within tabs myself,
but it's not something I'd recommend.
QWizard
Complex, infrequent actions are
eminently suited to the wizard approach. A wizard is a set of
pages that guide the user through a certain path. The user
need not visit all pages, and there might be more than one
possible path. Avoid using wizards where tab pages might be
more suited (when there are many options but no clear
progression through the steps of a complex action).
QFileDialog
The first of the Qt standard dialogs is the
QFileDialog. The file dialog can be
extended with custom icons, toolbuttons and extra widgets.
In its default format it is
extremely easy to use: just call one of the predefined class
methods that return the name of a directory or file, such as
getOpenFileName() or
getOpenFileNames().
Example 10-15. fragment from dialogs.py - opening a file dialog
A useful dialog,
QFontDialog lets the user select a font
by giving parameters for font name, style, size, effects and
script — this last parameter being the encoding of the
font, such as Unicode. Just as with
QFileDialog,
QFontDialog provides a set of class
methods that return the selected value, in this case a tuple
containing a QFont object and a boolean
value that indicates whether OK or Cancel was pressed..
Of course, with Qt3, you no longer set
the desired encoding, but rather the script - Greek, Tamil, or
whatever you want.
Example 10-16. fragment from dialogs.py - opening a font dialog
QColorDialog
provides a standard dialog for color selection. An interesting
addition to this class is that you ask it to store a set of
custom colors. This set will be kept during the lifetime of
the application, and you can store those colors in a
configuration file and restore them when the app is restarted.
You can ask the color dialog either for a
QColor object, or for a set of RGB
values, encapsulated in a QRgb object.
In contrast with QFileDialog, which is
extensible, or QFontDialog, which
really suffices, QColorDialog provides
just barely enough for simple color selection, but won't do
for more complex graphics applications (with which you might
want to implement something that works with HSV values, or
with a color wheel).
Example 10-17. fragment from dialogs.py - opening a color dialog
You can use
QInputDialog to ask the user for a
simple, single value. This value can be of the following type:
text, integer, double, or an item from a listbox. Frankly,
I've never had a need for these. The open remote location
dialog in browsers like Opera or Netscape are a common
example.
QProgressDialog
The
QProgressDialog is a useful little
dialog that can be used to inform the user that a certain
action will be taking a lot of time. If the operation of the
dialog is meant to block the whole application, use a modal
QprogressDialog. If the operation won't
block the entire application, then it's possible to use a
modeless QProgressDialog, but it may be
more effective to use a QProgressBar in
the statusbar of the application.
QProgressDialog is based on the
QSemiModal class.