Dialogs and Standard Dialogs

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

...
    def slotFile(self):
        filename=QFileDialog.getOpenFileName("", "*.py", self, "FileDialog")
...
        

The Qt File dialog

QFontDialog

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

...
    def slotFont(self):
        (font, ok) = QFontDialog.getFont(self, "FontDialog")
...
          

The Qt font dialog

QColorDialog

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

...
    def slotColor(self):
        color=QColorDialog.getColor(QColor("linen"), self, "ColorDialog")
...
          

The Qt Color dialog

QInputDialog

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.