Глава 3. Создание главного окна приложения

В этой главе мы рассмотрим процесс создания главного окна приложения. По прочтении ее, вы сможете построить интерфейс приложения, который будет содержать меню, панели инструментов, строку состояния и набор дополнительных диалогов.

Рисунок 3.1. Приложение -- электронная таблица.


Главное окно -- это своего рода каркас, на который "натягивается" весь пользовательский интерфейс приложения. Здесь мы рассмотрим пример создания главного окна электронной таблицы. Внешний вид нашего будущего приложения приводится на рисунке 3.1. В этом приложении мы будем использовать диалоги "Find", "Go-to-Cell" и "Sort", которые были созданы нами в Главе 2.

Внутри большинства приложений с графическим интерфейсом "прячется" код, который обеспечивает основные функциональные возможности программы, например, работа с файлами или обработка данных, представляемых пользовательским интерфейсом. В Главе 4 мы покажем -- как реализуется такого рода функциональность, на примере нашей электронной таблицы.

3.1. Создание класса-наследника от QMainWindow.

Главное окно любого приложения -- это класс-наследник QMainWindow. Большинство приемов, используемых при создания диалогов и о которых мы говорили в Главе 2, вполне применимы и при создании главного окна приложения.

Главное окно может быть создано в Qt Designer, но мы все будем делать "вручную", чтобы продемонстрировать процесс создания главного окна во всех деталях. Если вы предпочитаете визуальное проектирование -- прочитайте главу "Creating a Main Window Application" в справочном руководстве к Qt Designer.

Исходные тексты главного окна будут располагаться в двух файлах: mainwindow.cpp и mainwindow.cpp. Начнем с файла заголовка:

#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); Это определение класса MainWindow -- наследника QMainWindow. Оно содержит макрос Q_OBJECT, поскольку реализует свои собственные сигналы и слоты.

Функция closeEvent(), в классе QWidget, объявлена как виртуальная. Она автоматически вызывается перед завершением приложения. Мы перекрываем ее в MainWindow для того, чтобы иметь возможность спросить у пользователя -- желает ли он сохранить произведенные изменения, а также для того, чтобы сохранить на диск пользовательские настройки.

Аналогично, функция contextMenuEvent() вызывается, когда пользователь щелкает правой кнопкой мыши по виджету. Мы перекрываем ее в MainWindow для того, чтобы вывести контекстное меню.

private slots: void newFile(); void open(); bool save(); bool saveAs(); void find(); void goToCell(); void sort(); void about(); Реализация действий некоторых пунктов меню, таких как File|New и Help|About, выполнена в виде приватных слотов MainWindow. Большинство слотов имеют тип void, но слоты save() и saveAs возвращают результат типа bool. Значение, возвращаемое слотом, игнорируется в случае вызова по сигналу, но когда слот вызывается как обычная функция, то мы получаем от него возвращаемое значение, которое можем использовать для своих нужд. 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); Дополнительные приватные функции, необходимые для обслуживания пользовательского интерфейса. 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 Кроме функций, класс главного окна имеет ряд скрытых переменных. Все они будут описаны по мере необходимости.

Теперь перейдем к реализации:

#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" Здесь подключаются заголовки всех классов Qt, которые используются в приложении, а также заголовок класса главного окна и ряд других заголовочных файлов, таких как finddialog.h, gotocelldialog.h и sortdialog.h, которые мы создали в предыдущей главе. 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; } Конструктор начинается с создания виджета Spreadsheet, который будет центральным виджетом главного окна. Центральный виджет занимает все пространство, находящееся между панелью инструментов (toolbar) и строкой состояния (statusbar). Класс Spreadsheet является потомком класса QTable и добавляет некоторые свойства, характерные для электронных таблиц. Среди них можно назвать поддержку формул, которая будет реализована в Главе 4.

Рисунок 3.2. Раскладка виджетов в главном окне.


Далее вызываются приватные функции createActions(), createMenus(), createToolBars() и createStatusBar(), которые создают остальную часть главного окна. Для восстановления пользовательских настроек вызывается функция readSettings().

В качестве иконки приложения устанавливается icon.png. Qt поддерживает различные форматы графических файлов, включая BMP, GIF [1] , JPEG, MNG, PNG, PNM, XBM и XPM. Вызов QWidget::setIcon() выводит иконку в верхний левый угол окна. К сожалению, отсутствует платформо-независимый способ помещения иконки на рабочий стол.

Программы с графическим интерфейсом обычно используют достаточно большое число изображений. Qt, в свою очередь, предоставляет значительное число методов работы с изображениями в приложениях. Наиболее употребимые из них:

В данном примере мы будем использовать механизм "коллекции изображений", поскольку работать с ним намного проще, чем загружать файлы в процессе исполнения. К тому же он может взаимодействовать со всеми, поддерживаемыми библиотекой, графическими форматами. Все изображения мы будем хранить в каталоге images. Чтобы создать файл на языке C++ (он будет создан утилитой uic), который будет хранить наши изображения, добавим следующие строки в файл .pro:

IMAGES = images/icon.png \ images/new.png \ images/open.png \ ... images/find.png \ images/gotocell.png Изображения будут помещены в исполняемый файл приложения и могут быть получены вызовом QPixmap::fromMimeSource(). Преимущество такого способа организации хранения изображений заключается в том, что они никогда не потеряются, поскольку находятся внутри исполняемого файла.

Если главное окно создается в Qt Designer, то вы можете использовать визуальные средства, предоставляемые построителем, для вставки изображений в коллекцию.

Примечания

[1]

Если вы проживаете в стране, чье законодательство признает патенты на программное обеспечение и где компания Unisys имеет зарегистрированный патент на алгоритм сжатия LZW, то Unisys может потребовать от вас приобрести лицензию на использование GIF. По этой причине, поддержка формата GIF в Qt по-умолчанию запрещена. Мы полагаем, что срок действия этого патента по всему миру истечет к концу 2004 года.