| |
Разработка графического интерфейса с помощью библиотеки Qt3 | ||
---|---|---|
Пред. | Глава 3. Создание главного окна приложения | След. |
Здесь мы расскажем о принципах работы с диалогами в Qt -- о том как они создаются, инициализируются, запускаются и как от них получить выбор, сделанный пользователем. Здесь мы будем использовать диалоги "Find", "Go-to-Cell" и "Sort", созданные нами в Главе 2. Кроме того, мы создадим простенький диалог "About" ("О программе").
Начнем с диалога "Find". Так как мы хотим, чтобы пользователь имел возможность переключаться между главным окном приложения и окном диалога, необходимо, чтобы диалоговое окно было НЕМОДАЛЬНЫМ. Немодальным называется такое окно, которое работает независимо от остальных окон приложения.
Как правило, при создании немодальных диалогов, их сигналы подключаются к слотам, которые обеспечивают реакцию на действия пользователя.
void MainWindow::find() { if (!findDialog) { findDialog = new FindDialog(this); connect(findDialog, SIGNAL(findNext(const QString &, bool)), spreadsheet, SLOT(findNext(const QString &, bool))); connect(findDialog, SIGNAL(findPrev(const QString &, bool)), spreadsheet, SLOT(findPrev(const QString &, bool))); } findDialog->show(); findDialog->raise(); findDialog->setActiveWindow(); }Диалог "Find" предназначен для выполнения поиска некоторого значения в таблице. Слот find() вызывается, когда пользователь выбирает пункт меню "Edit|Find", и предназначен для вывода диалогового окна на экран. С этого момента возможны три сценария дальнейшего развития событий:
Пользователь вызвал диалог впервые.
Диалог вызывался ранее, но после этого пользователь закрыл его.
Диалог вызывался ранее и не закрывался (окно диалога видно на экране).
Далее вызываются show(), raise() и setActiveWindow(), которые выводят окно диалога на экран, поверх других окон приложения, и активизируют его. Метод show() делает окно диалога видимым, но оно может уже присутствовать на экране -- в этом случае функция show() ничего не делает. Так как нам необходимо вывести диалог поверх других окон и активизировать его, мы должны вызвать raise() и setActiveWindow(). В качестве альтернативы можно предложить следующий код:
if (findDialog->isHidden()) { findDialog->show(); } else { findDialog->raise(); findDialog->setActiveWindow(); }но он более медлительный.
Перейдем к диалогу "Go-to-Cell". В этом случае нет необходимости переключаться между окном приложения и окном диалога. Отсюда следует, что окно диалога "Go-to-Cell" должно быть МОДАЛЬНЫМ. Модальным называется такое окно, которое блокирует возможность взаимодействия пользователя с другими окнами приложения до тех пор, пока не будет закрыто модальное окно. Все диалоги нашего приложения, за исключением "Find", будут модальными.
Немодальные диалоги вызываются при помощи функции show() (если перед этим не вызывалась функция setModal(), которая делает окно модальным). Модальные диалоги вызываются функцией exec(). Как правило, для модальных диалогов не требуется устанавливать соединения между сигналами и слотами.
void MainWindow::goToCell() { GoToCellDialog dialog(this); if (dialog.exec()) { QString str = dialog.lineEdit->text(); spreadsheet->setCurrentCell(str.mid(1).toInt() - 1, str[0].upper().unicode() - 'A' ); } }Функция QDialog::exec() возвращает true, если результат диалога принимается пользователем, и false -- в противном случае. (Помните? В главе 2 мы соединяли сигнал кнопки OK со слотом accept(), а сигнал от кнопки Cancel со слотом reject().) Если пользователь нажмет кнопку OK, то мы выполним переход к заданной ячейке, если Cancel -- exec() вернет false и мы не будем ничего предпринимать.
Функция QTable::setCurrentCell() принимает два аргумента: номер строки и номер колонки. В нашем приложении, адрес A1, например, соответствует ячейке (0, 0) в таблице, а адрес B27 -- ячейке (26, 1). Чтобы получить номер строки, из QString, возвращаемой QLabel::text(), извлекается ее часть, с помощью QString::mid() и затем преобразуется в целое число с помощью QString::toInt(). После этого, из полученного числа вычитается единица (поскольку нумерация строк в QTable начинается с 0). Чтобы получить номер колонки, из кода символа колонки мы просто вычитаем код символа "A".
В отличие от диалога "Find", экземпляр диалога "Go-to-Cell" создается на стеке. Это общепринятая практика для модальных диалогов, вызываемых из разного рода меню, поскольку они становятся не нужны после их использования.
А теперь перейдем к диалогу сортировки. Этот диалог так же является модальным и позволяет отсортировать выделенный дипазон ячеек по заданным колонкам. На рисунке 3.14 показан пример сортировки по колонкам B (первичный ключ) и A (вторичный ключ) в порядке возрастания.
Рисунок 3.14. Сортировка выбранного диапазона ячеек.void MainWindow::sort() { SortDialog dialog(this); QTableSelection sel = spreadsheet->selection(); dialog.setColumnRange( A + sel.leftCol(), A + sel.rightCol()); if (dialog.exec()) { SpreadsheetCompare compare; compare.keys[0] = dialog.primaryColumnCombo->currentItem(); compare.keys[1] = dialog.secondaryColumnCombo->currentItem() - 1; compare.keys[2] = dialog.tertiaryColumnCombo->currentItem() - 1; compare.ascending[0] = (dialog.primaryOrderCombo->currentItem() == 0); compare.ascending[1] = (dialog.secondaryOrderCombo->currentItem() == 0); compare.ascending[2] = (dialog.tertiaryOrderCombo->currentItem() == 0); spreadsheet->sort(compare); } }Алгоритм функции sort():
Диалог создается на стеке и инициализируется.
Диалог запускается вызовом exec()
Если пользователь нажал кнопку OK, то из виджетов диалога извлекается необходимая информация и выполняется сортировка.
Реализация sort() очень чувствительна к дизайну диалога "Sort", точнее -- эта "чувствительность" связана с выпадающими списками и элементами списков "None". Если вы измените диалог, то скорее всего вам придется изменить и код функции. Пока этот диалог вызывается из одного места в программе -- обслуживание его не так трудоемко. Но как только вы попытаетесь вызывать диалог из разных точек в программе, то обслуживание всех изменений, вносимых в него, может превратиться в кошмарный сон.
Чтобы избежать подобных трудностей, можно порекомендовать сделать диалог более "интеллектуальным", который сам будет создавать экземпляр класса SpreadsheetCompare и передавать его в вызывающую функцию. В этом случае функция sort() могла бы выглядеть так:
void MainWindow::sort() { SortDialog dialog(this); QTableSelection sel = spreadsheet->selection(); dialog.setColumnRange( 'A' + sel.leftCol(), 'A' + sel.rightCol()); if (dialog.exec()) spreadsheet->performSort(dialog.comparisonObject()); }Такой подход применяется к слабосвязанным компонентам и практически всегда оправдан в тех случаях, когда один и тот же диалог вызывается более чем из одного места в программе.
Более радикальный подход -- передать диалогу указатель на Spreadsheet и позволить ему напрямую работать с таблицей. Это несколько снижает универсальность диалога, так как он теперь будет "привязан" к определенному типу виджета, но значительно упрощает код за счет отказа от функции SortDialog::setColumnRange(). В этом случае, код функции MainWindow::sort() приобретает такой вид:
void MainWindow::sort() { SortDialog dialog(this); dialog.setSpreadsheet(spreadsheet); dialog.exec(); }Этот подход является полной противоположностью. Теперь уже не программа должна "знать" архитектуру и алгоритм работы диалога, а диалог должен "знать" об архитектуре вызывающей программы. Такой подход может оказаться оправданным, когда диалогу необходимо предоставить возможность оперативного изменения данных. Но и в этом случае код программы крайне чувствителен к реализации диалога.
Некоторые разработчики выбирают один из описанных вариантов и применяют только его. В этом есть свои преимущества, поскольку все диалоги имеют одинаковое строение, хотя при этом могут оказаться не использованными преимущества других подходов. В любом случае, решение о том, какой из подходов должен использоваться, необходимо принимать в каждом конкретном приложении, на основе требований, предъявляемых к диалогам.
В завершение этого раздела мы создадим простенький диалог -- окно, содержащее сведения о программе и разработчике. Такой диалог можно создать самому, аналогично рассмотренным ранее диалогам "Find" или "Sort", но Qt предоставляет более простое решение.
void MainWindow::about() { QMessageBox::about(this, tr("About Spreadsheet"), tr("<h2>Spreadsheet 1.0</h2>" "<p>Copyright © 2003 Software Inc." "<p>Spreadsheet is a small application that " "demonstrates <b>QAction</b>, <b>QMainWindow</b>, " "<b>QMenuBar</b>, <b>QStatusBar</b>, " "<b>QToolBar</b>, and many other Qt classes.")); }Вызывается диалог функцией QMessageBox::about(). Очень похоже на функцию QMessageBox::warning(), за одним маленьким исключением: вместо стандартной иконки "warning", используется иконка приложения.
Рисунок 3.15. Диалог с информацией о программе.
До сих пор мы использовали ряд, очень удобных в обращении, статических функций-членов из классов QMessageBox и QFileDialog. Эти функции "на лету" создают диалоги, инициализируют их и вызывают функцию exec(). Но можно, хотя это и менее удобно, самому создать QMessageBox или QFileDialog, подобно любому другому виджету, и явно вызвать exec() или даже show().
Пред. | В начало | След. |
Настройка строки состояния. | На уровень выше | Сохранение пользовательских настроек приложения. |
Закладки на сайте Проследить за страницей |
Created 1996-2024 by Maxim Chirkov Добавить, Поддержать, Вебмастеру |