Creating a Main Window

For most main-window-style applications, the creation of the main window follows a similar pattern. We begin by creating and initializing some data structures, then we create a "central widget" which will occupy the main window's central area, and then we create and set up any dock windows. Next, we create "actions" and insert them into menus and toolbars. It is quite common to also read in the application's settings, and for applications that restore the

Figure 6.2 The Image Changer's modules, classes, and functions

user's workspace, to load the files that the application had open when it was last terminated.

The files that make up the Image Changer application are shown in Figure 6.2. The application's main window class is in the file chap06/imagechanger.pyw. The initializer is quite long, so we will look at it in pieces. But first we will look at the imports that precede the class definition.

import os import platform import sys from PyQt4.QtCore import * from PyQt4.QtGui import * import helpform import newimagedlg import qrc_resources

In this book, the practice is to import Python's standard modules, then third-party modules (such as PyQt), and then our own modules. We will discuss the items we use from the os and platform modules when we use them in the code. The sys module is used to provide sys.argv as usual. The helpform and newimagedlg modules provide the HelpForm and NewlmageDlg classes. We will discuss the qrc_resources module later on.

It is common for applications to have a version string, and conventional to call it_version_; we will use it in the application's about box.

Now we can look at the beginning of the MainWindow class. class MainWindow(QMainWindow):

def_init_(self, parent=None):

super(MainWindow, self)._init_(parent)

self.image = QImage() self.dirty = False self.filename = None self.mirroredvertically = False self.mirroredhorizontally = False

The initializer begins conventionally with the super() call. Next, we create a null QImage that we will use to hold the image the user loads or creates. A QImage is not a QObject subclass, so it does not need a parent; instead, we can leave its deletion to Python's normal garbage collection when the application terminates. We also create some instance variables. We use dirty as a Boolean flag to indicate whether the image has unsaved changes. The filename is initially set to None, which we use to signify that either there is no image, or there is a newly created image that has never been saved.

PyQt provides various mirroring capabilities, but for this example application we have limited ourselves to just three possibilities: having the image mirrored vertically, horizontally, or not at all. We need to keep track of the mirrored state so that we can keep the user interface in sync, as we will see when we discuss the mirroring actions.

self.imageLabel = QLabel() self.imageLabel.setMinimumSize(200, 200) self.imageLabel.setAlignment(Qt.AlignCenter) self.imageLabel.setContextMenuPolicy(Qt.ActionsContextMenu) self.setCentralWidget(self.imageLabel)

In some applications the central widget is a composite widget (a widget that is composed of other widgets, laid out just like those in a dialog), or an item-based widget (such as a list or table), but here a single QLabel is sufficient. A QLabel can display plain text, or HTML, or an image in any of the image formats that PyQt supports; later on we will see how to discover what these formats are, since they can vary. We have set a minimum size because initially the label has nothing to show, and would therefore take up no space, which would look peculiar. We have chosen to align our images vertically and horizontally centered.

PyQt offers many ways of creating context menus, but we are going to use the easiest and most common approach. First, we must set the context menu policy for the widget which we want to have a context menu. Then, we must add some actions to the widget—something we will do further on. When the user invokes the context menu, the menu will pop up, displaying the actions that were added.

Unlike dialogs, where we use layouts, in a main-window-style application we only ever have one central widget—although this widget could be composite, so there is no limitation in practice. We only need to call setCentralWidget() and we are done. This method both lays out the widget in the main window's central area, and reparents the widget so that the main window takes ownership of it.

Object Ownership sidebar

Window Title X

Menu Bar Toolbar Areas Dock Window Areas

Central Widget

Status Bar

Figure 6.3 QMainWindow's areas

Toolbars are suitable for holding toolbar buttons, and some other kinds of widgets such as comboboxes and spinboxes. For larger widgets, for tool palettes, or for any widget that we want the user to be able to drag out of the window to float freely as an independent window in its own right, using a dock window is often the right choice.

Dock windows are windows that can appear in the dock areas shown in Figure 6.3. They have a small caption, and restore and close buttons, and they can be dragged from one dock area to another, or float freely as independent toplevel windows in their own right. When they are docked they automatically provide a splitter between themselves and the central area, and this makes them easy to resize.

In PyQt, dock windows are instances of the QDockWidget class. We can add a single widget to a dock widget, just as we can have a single widget in a main window's central area, and in the same way this is no limitation, since the widget added can be a composite.

logDockWidget = QDockWidget("Log", self) logDockWidget.setObjectName("LogDockWidget") logDockWidget.setAllowedAreas(Qt.LeftDockWidgetArea|

Qt.RightDockWidgetArea) self.listWidget = QListWidget() logDockWidget.setWidget(self.listWidget) self.addDockWidget(Qt.RightDockWidgetArea, logDockWidget)

Dock widgets are not put into a layout, so when we create them, in addition to providing their window caption, we must give them a parent. By setting a parent, we ensure that the dock widget does not go out of scope and get garbage-

Object collected by Python at the wrong time. Instead, the dock widget will be deleted

Owner-- when its parent, the top-level window (the main window), is deleted.

ship sidebar Every PyQt object can be given an object name, although up to now we have

119^ never done so. Object names can sometimes be useful in debugging, but we have set one here because we want PyQt to save and restore the dock widget's size and position, and since there could be any number of dock widgets, PyQt uses the object name to distinguish between them.

By default, dock widgets can be dragged into any dock area and are movable, floatable, and closable. Since our dock widget is going to be used to store a list—a widget that is usually tall and narrow—it only makes sense for it to be in the left or right dock areas (or to float), so we use setAUowedAreas() to restrict the areas. Dock widgets also have a setFeatures() method which is used to control whether the dock widget can be moved, floated, or closed, but we do not need to use it here because the defaults are fine.

Once the dock widget has been set up, we create the widget it will hold, in this case a list widget. Then we add the widget to the dock widget, and the dock widget to the main window. We did not have to give the list widget a parent because when it is added to the dock widget the dock widget takes ownership of it.

self.printer = None

We want users to be able to print out their images. To do this we need to create a QPrinter object. We could create the printer whenever we need it and leave it to be garbage-collected afterward. But we prefer to keep an instance variable, initially set to None. The first time the user asks to print we will create a QPrinter and assign it to our printer variable. This has two benefits. First, we create the printer object only when it is needed, and second, because we keep a reference to it, it stays around—and keeps all its previous state such as the user's choice of printer, paper size, and so on.

self.sizeLabel = QLabel()

self.sizeLabel.setFrameStyle(QFrame.StyledPanel|QFrame.Sunken) status = self.statusBar() status.setSizeGripEnabled(False) status.addPermanentWidget(self.sizeLabel) status.showMessage("Ready", 5000)

For the application's status bar, we want the usual message area on the left, and a status indicator showing the width and height of the current image. We do this by creating a QLabel widget and adding it to the status bar. We also switch off the status bar's size grip since that seems inappropriate when we have an indicator label that shows the image's dimensions. The status bar itself is created for us the first time we call the QMainWindow's statusBar() method. If we call the status bar's showMessage() method with a string, the string will be displayed in the status bar, and will remain on display until either another showMessage()

call supplants it or until clearMessage() is called. We have used the two-argument form, where the second argument is the number of milliseconds (5 000, i.e., 5 seconds), that the message should be shown for; after this time the status bar will clear itself.

So far we have seen how to create the main window's central widget, create a dock widget, and set up the status bar. Now we are almost ready to create the menus and toolbars, but first we must understand what PyQt actions are, and then take a brief detour to learn about resources.

Actions and Key Sequences

Qt's designers recognized that user interfaces often provide several different ways for the user to achieve the same thing. For example, creating a new file in many applications can be done via the File—>New menu option, or by clicking the New File toolbar button, ¡7-^, or by using the Ctrl+N keyboard shortcut. In general, we do not care how the user asked to perform the action, we only care what action they asked to be done. PyQt encapsulates user actions using the QAction class. So, for example, to create a "file new" action we could write code like this:

fileNewAction = QAction(QIcon("images/filenew.png"), "&New", self)

fileNewAction.setShortcut(QKeySequence.New)

helpText = "Create a new image"

fileNewAction.setToolTip(helpText)

fileNewAction.setStatusTip(helpText)

self.connect(fileNewAction, SIGNAL("triggered()"), self.fileNew)

This assumes that we have a suitable icon and a fileNew() method. The ampersand in the menu item's text means that the menu item will appear as New (except on Mac OS X or unless the windowing system is set to suppress underlines), and that keyboard users will be able to invoke it by pressing Alt+F,N, assuming that the File menu's text is "&File" so that it appears as File. Alternatively, the user could use the shortcut that was created by setShortcut(), and simply press Ctrl+N instead.

Many key sequences are standardized, some even across different windowing systems. For example, Windows, KDE, and GNOME all use Ctrl+N for "new" and Ctrl+S for "save". Mac OS X is similar, with Command+N and Command+S for these actions. The QKeySequence class in PyQt 4.2 provides constants for the standardized key sequences, such as QKeySequence.New. This is especially useful when the standardized key sequences differ across windowing systems, or where more than one key sequence is associated with an action. For example, if we set a shortcut to QKeySequence.Paste, PyQt will trigger a "paste" action in response to Ctrl+V or Shift+Ins on Windows; Ctrl+V, Shift+Ins, or F18 on KDE and GNOME; and Command+V on Mac OS X.

For key sequences that are not standardized (or if we want backward compatibility with earlier PyQt releases), we can provide the shortcut as a string;

Table 6.1 Selected QAction Methods

Object Ownership sidebar

Syntax a.data()

a.setData(v)

a.isChecked()

a.setChecked(b)

a.isEnabled()

a.setEnabled(b)

a.setSeparator(b)

a.setShortcut(k)

a.setStatusTip(s)

a.setText(s)

a.setToolTip(s)

a.setWhatsThis(s)

a.toggled(b)

a.triggered(b)

Description

Returns QAction a's user data as a QVariant

Sets QAction a's user data to QVariant v

Returns True if QAction a is checked

Checks or unchecks QAction a depending on bool b

Returns True if QAction a is enabled

Enables or disables QAction a depending on bool b

Sets QAction a to be a normal action or a separator depending on bool b

Sets QAction a's keyboard shortcut to QKeySequence k

Sets QAction a's status tip text to string s

Sets QAction a's text to string s

Sets QAction a's tooltip text to string s

Sets QAction a's What's This? text to string s

This signal is emitted when QAction a's checked status changes; bool b is True if the action is checked This signal is emitted when QAction a is invoked; the optional bool b is True if QAction a is checked for example, setShortcut("Ctrl+Q"). This book uses the standardized key sequences that are available, and otherwise falls back to using strings.

Notice that we give the QAction a parent of self (the form in which the action is applicable). It is important that every QObject subclass (except top-level windows) has a parent; for widgets this is usually achieved by laying them out, but for a pure data object like a QAction, we must provide the parent explicitly.

Once we have created the action, we can add it to a menu and to a toolbar like this:

fileMenu.addAction(fileNewAction) fileToolbar.addAction(fileNewAction)

Now whenever the user invokes the "file new" action (by whatever means), the fileNew() method will be called.

Was this article helpful?

0 0
Tuberminator

Tuberminator

The main focus of this report is to show how to get involved in video marketing on the run, how to rank quickly on YouTube and Google using FREE semi-automatic tools and services. QUICKLY AND FREE. I will show methods and techniques I use to rank my videos, as well as free resources and tools to make video clips, to get backlinks and free traffic.

Get My Free Ebook


Responses

  • lydia
    How to align PyQt4 labels with different text sizelabel text in PyQt4?
    9 years ago
  • vincenza lombardi
    How to mirror widget pyqt?
    8 years ago
  • ghenet
    What is dock window in pyqt4?
    8 years ago
  • Gerald
    How to create window in pyqt?
    7 years ago
  • gabriele faust
    How invoke a printer dialog box using QPrinter with PyQT?
    3 years ago

Post a comment