Application classes

Most gui applications consist of a main window embellished with toolbars, a menu bar, and a statusbar, and have a hole in the middle. The hole in the middle can be filled with a specialized widget, or it may be a sort of desktop-in-a- desktop, with its own windows, and with sub-windows that dock themselves to the sides of the main window. Often, and application includes a few secondary windows, dialogs and a number of small popup-windows that warn or inform the user.

We have already worked with a few of these components. There is QApplication, the base of every PyQt application. QApplication wants to receive the command-line arguments to determine the look and feel of the application, as shown in the Section called As simple as they come in Chapter 6.

Then there's the main window — as shown in the Section called A better Hello World in Chapter 6, you can have an unlimited number of main windows, and not all those main windows have to be of the same class, as long as they inherit QMainWindow.

We have yet to add the frills to the main window. PyQt makes it quite easy to do this. The best way of adding menu options and toolbar buttons is to create a QAction for each action. QAction is a class that brings together user interface information about a certain action the user can undertake in your application.

For instance, if you're developing a network client application, one of the actions could be the command to log in. Associated with this command is a short help text that appears as a tooltip, a longer help text that might appear in the status bar, an icon that is used in the toolbar, a short text for use in the menu, and an accelerator key that is used from the keyboard. The log in action can be enabled or disabled (when the network is down, for instance). You do not want to distribute all this functionality all over your application.

A QAction ties everything related to an action together, and can be added to toolbars and menus. When performed, a QAction emits an activated() signal. The following is a simple example with an action, a menubar, a toolbar and a statusbar:

Example 10-2. action.py - Using a QAction to group data associated with user commands

#
# action.py
#

import sys
from qt import *

connectIcon=["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.setCaption("Network Client")

        # Define action
        self.action=QAction(self, "login")
        self.action.setText("Log in")
        self.action.setMenuText("&Login")
        self.action.setToolTip("Login to the central server")
        self.action.setWhatsThis("Logs in to the central server.")
        self.action.setStatusTip("Log in to the central server.")
        self.action.setAccel(Qt.CTRL + Qt.Key_L)
        self.action.setIconSet(QIconSet(QPixmap(connectIcon)))
        self.connect(self.action,
                     SIGNAL("activated()"),
                     self.slotAction)


        # Statusbar
        self.statusBar=QStatusBar(self)

        # Define menu
        self.menu=QPopupMenu()
        self.action.addTo(self.menu)
        self.menuBar().insertItem("&File", self.menu)

        # Define toolbar
        self.toolBar=QToolBar(self, 'Main')
        self.action.addTo(self.toolBar)

        # Set a central widget
        self.editor=QMultiLineEdit(self)
        self.setCentralWidget(self.editor)

    def slotAction(self):
        QMessageBox.information(self,
                                "Network Client",
                                "Connecting to server...")

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)
      

action.py

When, in Chapter 24, we reach the pinnacle of development of Kalam, the extensible Unicode editor, you will have become very familiar with QAction.

Multiple document windows with QWorkspace

The MDI (multiple document interface) is a paradigm made popular by Microsoft, in which one application window contains several document windows. For certain classes of application, such as programming editors, this is a very comfortable paradigm, but most users tend to get very confused when confronted with windows that don't show up in their taskbar. In fact, a large percentage of users have trouble when there is more than one window on their desktop.

However, the functionality is available, and it might be useful for your application. Let's take our high-powered graphics editor, from the event1.py example, and give the user ten windows to scribble in. All that is needed is it to add the Painting to a QWorkspace object, instead of setting it as the central widget of the MainWindow.

Realistically, you'll want to offer menu options for selecting, tiling and cascading the windows. QWorkSpace provides a tile() and a cascade() slot for these purposes, as well as a windowList that returns a list of all windows. While it is a bad idea to limit your users to a small maximum number of documents, if you let them open more, you should provide a separate window with a full list. Having more than ten windows to select from in a menu makes working difficult.

Example 10-3. fragment from mdi.py - ten little scribbling windows

...
class MainWindow(QMainWindow):

    def __init__(self, *args):
        apply(QMainWindow.__init__, (self,) + args)
        self.setCaption("MDI Scribbler")
        self.workspace=QWorkspace(self, "workspace")
        self.winlist=[]
        for i in range(10):
            win=Painting(self.workspace)
            win.resize(100,100)
            win.setCaption("Window " + str(i))
            self.winlist.append(win)
        self.setCentralWidget(self.workspace)
...
        

mdi.py - ten little scribbling windows.