Шаг 165.
Библиотека Qt.
Создание главного меню

    На этом шаге рассмотрим создание главного меню.

    В библиотеке Qt меню реализуется классом QMenu. Этот класс определен в заголовочном файле QMenu. Основное назначение класса — это размещение команд в меню. Каждая команда представляет объект действия (класс QAction). Все действия или сами команды меню могут быть соединены со слотами для исполнения соответствующего кода при выборе команды пользователем. Например, если пользователь выделил команду меню, то и меню, и объекты действий отправляют сигнал hovered(), и если вам потребуется в этот момент выполнить какие-либо действия, то их нужно соединить с соответствующим слотом.

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


Рис.1. Анатомия меню

    Основной отправной точкой меню является меню верхнего уровня. Оно представляет собой постоянно видимый набор команд, которые, в свою очередь, могут быть выбраны при помощи указателя мыши или клавиш клавиатуры (Alt и клавиш управления курсором). Команды меню верхнего уровня предназначены для отображения выпадающих меню. Старайтесь логически группировать команды и объединять их в одном выпадающем меню, которое, в свою очередь, будет вызываться при выборе соответствующей команды меню верхнего уровня. Класс QMenuBar отвечает за меню верхнего уровня и определен в заголовочном файле QMenuBar.

    "Горячие" клавиши это по сути определенные комбинации клавиш, с помощью которых выполняется то же самое действие, что и при выборе соответствующей команды меню. Например, для отображения окна About QtQt), в примере, показанном на рис. 1, используется комбинация клавиш Ctrl+Q. Старайтесь использовать стандартные комбинации для "горячих" клавиш.

    По возможности, для всех пунктов меню должны быть определены клавиши быстрого вызова. Это позволит пользователю выбирать команды не только при помощи мыши, но и при помощи клавиатуры, нажав подчеркнутую букву (в названии команды) совместно с клавишей Alt. Например, для выбора команды Exit (Выход) нужно нажать Alt+E (см. рис. 1).

    Основные отличия подобного рода комбинаций для быстрого вызова от "горячих" клавиш состоят в следующем:

    Стрелка у команды SubMenu (Подменю) (см. рис. 1) говорит о том, что при выборе этой команды появится вложенное подменю, в нашем случае — Test (Тест). Вложенное подменю удобно для того, чтобы разгрузить меню, если оно содержит большое количество команд. Для удобства понимания программы рекомендуется, чтобы степень вложенности не превышала двух.

    Разделитель — это черта, которая отделяет одну группу команд от другой.

    Команда с флажком используется для управления режимами работы приложения. Установленный флажок сигнализирует об активированной команде меню.

    Пиктограмма команды отображает команду меню в графическом виде,который совпадает с кнопкой панели инструментов.

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

    Когда команда меню вызывает диалоговое окно, в ее название принято добавлять в конец троеточие. Это правило, правда, не распространяется на вызов простых окон сообщений.

    Большинство современных приложений с графическим пользовательским интерфейсом содержат меню, контекстное меню и панели инструментов. Меню позволяют пользователям исследовать возможности приложения и узнать новые способы работы, а контекстные меню и панели инструментов обеспечивают быстрый доступ к часто используемым функциям. На рис. 2 показано меню приложения Электронная таблица.


Рис.2. Меню приложения Электронная таблица

    Использование понятия "действия" упрощает программирование меню и панелей инструментов при помощи средств разработки Qt. Элемент action (действие) можно добавлять к любому количеству меню и панелей инструментов.

    Создание в Qt меню и панелей инструментов разбивается на следующие этапы:

    В приложении Электронная таблица действия создаются в createActions():

void MainWindow::createActions()
{
   /*создание действия Новый, которое имеет клавишу быстрого выбора пункта
   меню, родительское окно (главное окно)*/
   newAction = new QAction(tr("&Новый"), this);
   //пиктограмму
   newAction->setIcon(QIcon(":/images/new.png"));
   //клавишу быстрого вызова команды
   /*Большинство оконных систем имеют стандартизованные клавиши быстрого
   вызова определенных действий. Например, действие New (Создать)
   вызывается в Windows, KDE и GNOME нажатием Ctrl+N,
   а в Mac OS X - Comnand+N. С помощью соответствующего
   перечислимого значения QKeySequence::StandardKey мы сделаем так,
   что Qt будет правильно использовать клавиши быстрого вызова той
   платформы, на которой запущено приложение*/
   newAction->setShortcut(QKeySequence::New);
   //и сообщение в строке состояния
   newAction->setStatusTip(tr("Создание нового файла электронной таблицы"));
   /*подсоединяем к сигналу этого действия triggered() закрытый слот
   главного окна newFile(). Это соединение гарантирует, что при выборе
   пользователем пункта меню File/New (Файл/Новый), при нажатии им
   соответствующей кнопки на панели инструментов или при нажатии клавиш
   Ctrl+N будет вызван слот newFile()*/
   connect(newAction, SIGNAL(triggered()), this, SLOT(newFile()));

    Создание действий Open (Открыть), Save (Сохранить) и Save As (Сохранить как) очень похоже на создание действия New (Новый), поэтому вы сможете выполнить их самостоятельно.

    Переходим к строке recently opened files (недавно открытые файлы) меню Файл:

/*заполняем действиями массив recentFileActions. Каждое действие скрыто и
подключается к слоту openRecentFile(). Далее мы покажем, как действия в списке
недавно используемых файлов сделать видимыми, чтобы можно было ими воспользоваться*/
for (int i = 0; i < MaxRecentFiles; ++i) {
    recentFileActions[i] = new QAction(this);
    recentFileActions[i]->setVisible(false);
    connect(recentFileActions[i], SIGNAL(triggered()),
            this, SLOT(openRecentFile()));
}

    Действие Exit (Выход) несколько отличается от действий, которые мы видели до сих пор.

exitAction = new QAction(tr("&Выход"), this);
/*не существует стандартизованной клавиши быстрого вызова для завершения работы
приложения, так что мы указываем последовательность клавиш явным образом*/
exitAction->setShortcut(tr("Ctrl+Q"));
exitAction->setStatusTip(tr("Выйти из приложения"));
/*подключаемся к слоту close() окна, который предоставлен Qt*/
connect(exitAction, SIGNAL(triggered()), this, SLOT(close()));

    Теперь перейдем к действию Select All (Выделить все):

selectAllAction = new QAction(tr("Вс&е"), this);
selectAllAction->setShortcut(QKeySequence::SelectAll);
selectAllAction->setStatusTip(tr("Выделить все ячейки в электронной таблице"));
/*cлот selectAll() обеспечивается в QAbstractItemView, который является одним
из базовых классов QTableWidget, поэтому нам самим не надо его реализовывать*/
connect(selectAllAction, SIGNAL(triggered()), spreadsheet, SLOT(selectAll()));

    Давайте теперь перейдем к действию Show Grid (Показать сетку) из меню Options (Настройки):

/*Действие ShowGrid является включаемым. Включаемые действия обозначаются флажком
в меню и реализуются как кнопки-переключатели на панели инструментов.
Когда это действие включено, на компоненте Spreadsheet отображается сетка.
При запуске приложения мы инициализируем это действие в соответствии со значениями,
которые принимаются по умолчанию компонентом Spreadsheet, и поэтому работа этого
переключателя будет с самого начала синхронизирована.*/
showGridAction = new QAction(tr("&Показать сетку"), this);
showGridAction->setCheckable(true);
showGridAction->setChecked(spreadsheet->showGrid());
showGridAction->setStatusTip(tr("Показать/скрыть сетку таблицы"));
/*Затем мы соединяем сигнал toggled(bool) действия Show Grid со слотом
setShowGrid(bool) компонента Spreadsheet, который наследуется от QTableWidget.
После добавления этого действия к меню или панели инструментов пользователь
сможет включать и выключать сетку*/
connect(showGridAction, SIGNAL(toggled(bool)),
            spreadsheet, SLOT(setShowGrid(bool)));

    Действия-переключатели Show Grid (Показать сетку) и Auto-Recalculate (Автопересчет) работают независимо. Кроме того, Qt обеспечивает возможность определения взаимоисключающих действий путем применения своего собственного класса QActionGroup.

    Для действия About QtQt) мы используем слот aboutQt() объекта QApplication, который доступен через глобальную переменную qApp.

aboutQtAction = new QAction(tr("О &Qt"), this);
aboutQtAction->setStatusTip(tr("Показывает сведения о Qt"));
connect(aboutQtAction, SIGNAL(triggered()), qApp, SLOT(aboutQt()));

    Действия нами созданы, и теперь мы можем перейти к построению системы меню с этими действиями.

void MainWindow::createMenus()
{
    /*В Qt все меню являются экземплярами класса QMenu.
    Функция addMenu() создает виджет QMenu с заданным текстом и добавляет
    его в строку меню. Функция QMainWindow::menuBar() возвращает указатель
    на QMenuBar. Строка меню создается при первом вызове menuBar()*/
    //Сначала мы создаем меню File и
    fileMenu = menuBar()->addMenu(tr("&Файл"));
    //затем добавляем к нему действия New,
    fileMenu->addAction(newAction);
    //Open,
    fileMenu->addAction(openAction);
    //Save и
    fileMenu->addAction(saveAction);
    //Save As
    fileMenu->addAction(saveAsAction);
    /*Мы вставляем разделитель для визуального выделения группы взаимосвязанных
    пунктов меню. Мы сохранили указатель на один из разделителей.
    Это позволяет нам  скрывать этот разделитель (если файлы не использовались)
    или показывать его, поскольку мы не хотим отображать два разделителя,
    когда между ними ничего нет*/
    separatorAction = fileMenu->addSeparator();
    //используем цикл for для добавления (первоначально скрытых)
    //действий из массива recentFileActions,
    for (int i = 0; i < MaxRecentFiles; ++i)
        fileMenu->addAction(recentFileActions[i]);
    fileMenu->addSeparator();
    //а в конце добавляем действие exitAction
    fileMenu->addAction(exitAction);
    /*Теперь мы создаем меню Edit (Правка), добавляя действия при помощи
    QMenu::addAction(), как мы это делали для меню File*/
    editMenu = menuBar()->addMenu(tr("&Правка"));
    editMenu->addAction(cutAction);
    editMenu->addAction(copyAction);
    editMenu->addAction(pasteAction);
    editMenu->addAction(deleteAction);
    /*В меню Edit (правка) включается подменю. Это подменю (как и меню,
    к которому оно принадлежит) является экземпляром класса QPopupMenu.
    Мы просто создаем подменю путем указания this в качестве его родителя и вставляем
    его в то место меню Edit, где собираемся его расположить. */
    /*Добавляем подменю в нужную позицию при помощи QMenu::addMenu().
    Подменю, как и меню, к которому оно относится, имеет тип QMenu*/
    selectSubMenu = editMenu->addMenu(tr("&Выделение"));
    selectSubMenu->addAction(selectRowAction);
    selectSubMenu->addAction(selectColumnAction);
    selectSubMenu->addAction(selectAllAction);

    editMenu->addSeparator();
    editMenu->addAction(findAction);
    editMenu->addAction(goToCellAction);
    /*Подобным же образом мы создаем меню Tools*/
    toolsMenu = menuBar()->addMenu(tr("&Инструменты"));
    toolsMenu->addAction(recalculateAction);
    toolsMenu->addAction(sortAction);
    /*создаем меню Options*/
    optionsMenu = menuBar()->addMenu(tr("&Настройки"));
    optionsMenu->addAction(showGridAction);
    optionsMenu->addAction(autoRecalcAction);
    /*вставляем разделитель между меню Options и Help.
    В системе Motif и CDE этот разделитель сдвигает меню Help вправо;
    в других случаях этот разделитель игнорируется*/
    menuBar()->addSeparator();
    /*создаем меню Help*/
    helpMenu = menuBar()->addMenu(tr("&Помощь"));
    helpMenu->addAction(aboutAction);
    helpMenu->addAction(aboutQtAction);
} 

    На следующем шаге рассмотрим создание контекстного меню.




Предыдущий шаг Содержание Следующий шаг