Creating Main Windows with Qt3
-
Subclassing QMainWindow
-
Creating Menus and Toolbars
-
Implementing the File Menu
-
Setting Up the Status Bar
-
Using Dialogs
-
Storing Settings
-
Multiple Documents
-
Splash Screens
This chapter will teach you how to create main windows using Qt. By the end, you will be able to build an application's entire user interface, complete with menus, toolbars, status bar, and as many dialogs as the application requires.
An application's main window provides the framework upon which the application's user interface is built. The main window for the Spreadsheet application shown in Figure 3.1 will form the basis of this chapter. The Spreadsheet application makes use of the Find, Go-to-Cell, and Sort dialogs that we created in Chapter 2.
Figure 3.1. Spreadsheet application
Behind most GUI applications lies a body of code that provides the underlying functionalityfor example, code to read and write files or to process the data presented in the user interface. In Chapter 4, we will see how to implement such functionality, again using the Spreadsheet application as our example.
Subclassing QMainWindow
An application's main window is created by subclassing QMainWindow. Many of the techniques we saw in Chapter 2 for creating dialogs are also relevant for creating main windows, since both QDialog and QMainWindow inherit from QWidget.
Main windows can be created using Qt Designer, but in this chapter we will use code to demonstrate how it's done. If you prefer the more visual approach, see the "Creating a Main Window Application" chapter in Qt Designer's manual.
The source code for the Spreadsheet application's main window is spread across mainwindow.h and mainwindow.cpp. Let's start with the header file:
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <qmainwindow.h> #include <qstringlist.h> class QAction; class QLabel; class FindDialog; class Spreadsheet; class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = 0, const char *name = 0); protected: void closeEvent(QCloseEvent *event); void contextMenuEvent(QContextMenuEvent *event);
We define the class MainWindow as a subclass of QMainWindow. It contains the Q_OBJECT macro because it provides its own signals and slots.
The closeEvent() function is a virtual function in QWidget that is automatically called when the user closes the window. It is reimplemented in MainWindow so that we can ask the user the standard question "Do you want to save your changes?" and to save user preferences to disk.
Similarly, the contextMenuEvent() function is called when the user right-clicks a widget or presses a platform-specific Menu key. It is reimplemented in MainWindow to pop up a context menu.
private slots: void newFile(); void open(); bool save(); bool saveAs(); void find(); void goToCell(); void sort(); void about();
Some menu options, like File|New and Help|About, are implemented as private slots in MainWindow. Most slots have void as their return value, but save() and saveAs() return a bool. The return value is ignored when a slot is executed in response to a signal, but when we call a slot as a function the return value is available to us just as it is when we call any ordinary C++ function.
void updateCellIndicators() void spreadsheetModified(); void openRecentFile(int param); private: void createActions(); void createMenus(); void createToolBars(); void createStatusBar(); void readSettings(); void writeSettings(); bool maybeSave(); void loadFile(const QString &fileName); void saveFile(const QString &fileName); void setCurrentFile(const QString &fileName); void updateRecentFileItems(); QString strippedName(const QString &fullFileName);
The main window needs some more private slots and several private functions to support the user interface.
Spreadsheet *spreadsheet; FindDialog *findDialog; QLabel *locationLabel; QLabel *formulaLabel; QLabel *modLabel; QStringList recentFiles; QString curFile; QString fileFilters; bool modified; enum { MaxRecentFiles = 5 }; int recentFileIds[MaxRecentFiles]; QPopupMenu *fileMenu; QPopupMenu *editMenu; QPopupMenu *selectSubMenu; QPopupMenu *toolsMenu; QPopupMenu *optionsMenu; QPopupMenu *helpMenu; QToolBar *fileToolBar; QToolBar *editToolBar; QAction *newAct; QAction *openAct; QAction *saveAct; ... QAction *aboutAct; QAction *aboutQtAct; }; #endif
In addition to its private slots and private functions, MainWindow also has lots of private variables. All of these will be explained as we use them.
We will now review the implementation:
#include <qaction.h> #include <qapplication.h> #include <qcombobox.h> #include <qfiledialog.h> #include <qlabel.h> #include <qlineedit.h> #include <qmenubar.h> #include <qmessagebox.h> #include <qpopupmenu.h> #include <qsettings.h> #include <qstatusbar.h> #include "cell.h" #include "finddialog.h" #include "gotocelldialog.h" #include "mainwindow.h" #include "sortdialog.h" #include "spreadsheet.h"
We include the header files for the Qt classes used in our subclass, and also some custom header files, notably finddialog.h, gotocelldialog.h, and sortdialog.h from Chapter 2.
MainWindow::MainWindow(QWidget *parent, const char *name) : QMainWindow(parent, name) { spreadsheet = new Spreadsheet(this); setCentralWidget(spreadsheet); createActions(); createMenus(); createToolBars(); createStatusBar(); readSettings(); setCaption(tr("Spreadsheet")); setIcon(QPixmap::fromMimeSource("icon.png")); findDialog = 0; fileFilters = tr("Spreadsheet files (*.sp)"); modified = false; }
In the constructor, we begin by creating a Spreadsheet widget and setting it to be the main window's central widget. The central widget occupies the area between the toolbars and the status bar. The Spreadsheet class is a QTable subclass with some spreadsheet capabilities, such as support for spreadsheet formulas. We will implement it in Chapter 4.
Figure 3.2. QMainWindow's constituent widgets
Then we call the private functions createActions(), createMenus(), create-ToolBars(), and createStatusBar() to create the rest of the main window. We also call the private function readSettings() to read the application's stored settings.
We set the window's icon to icon.png, a PNG file. Qt supports many image formats, including BMP, GIF,* JPEG, MNG, PNG, PNM, XBM, and XPM. Calling QWidget::setIcon() sets the icon shown in the top-left corner of the window. Unfortunately, there is no platform-independent way of setting the application icon that appears on the desktop. The procedure is explained at http://doc.trolltech.com/3.2/appicon.html.
GUI applications generally use many images, with some images being used in several different contexts. Qt has a variety of methods for providing images to the application. The most common are:
-
Storing images in files and loading them at run-time.
-
Including XPM files in the source code. (This works because XPMfiles are also valid C++ files.)
-
Using Qt's "image collection" mechanism.
Here we use the "image collection" approach because it is easier and more efficient than loading files at run-time, and it works with any supported file format. The images are stored in the source tree in a subdirectory called images. By adding the entry
IMAGES = images/icon.png images/new.png images/open.png ... images/find.png images/gotocell.png
to the application's .pro file, we tell uic to generate a C++ source code file that contains the data for all the specified images. The data is then compiled into the application's executable and can be retrieved using QPixmap::fromMime-Source(). This has the advantage that icons and other images cannot get lost; they are always in the executable.
If you use Qt Designer to create your main windows as well as your dialogs, you can also use it to handle your .pro file and to visually add images to the image collection.