Модальное диалоговое окно блокирует родительский виджет. Блокировка снимается только при закрытии диалогового окна. Такое поведение оказывается проще при реализации, поскольку оно ограничивает свободу пользователя и снижает количество возможных проблем. Однако пользователи могут счесть это недостатком.
Немодальное диалоговое окно обладает в точности противоположным поведением по сравнению с модальным. Пользователь может взаимодействовать и с родительским виджетом, и с диалоговым окном, переключаясь между ними. Управлять подобной логикой взаимодействия сложнее. Но в замен пользователь получает свободу, которой ему могло не хватать в модальном варианте.
Выбор типа диалогового окна основывается на здравом смысле и относится к этапу юзабилити-тестирования. Мы этого вопроса касаться не станем. Рассмотрим лишь технику создания и тех, и других диалоговых окон с помощью Qt.
Реализуем простой класс диалогового окна. Именно его мы попробуем вызывать в модальном и немодальном режимах.
class DemoDialog : public QDialog {
Q_OBJECT
public:
DemoDialog( QWidget* parent = 0 );
~DemoDialog();
QString getInput() const;
signals:
void applied();
private:
QLineEdit* m_edit;
};
DemoDialog::DemoDialog( QWidget* parent ) : QDialog( parent ) {
QBoxLayout* layout = new QHBoxLayout;
m_edit = new QLineEdit;
layout->addWidget( m_edit );
QPushButton* okBtn = new QPushButton( "OK" );
connect( okBtn, SIGNAL( clicked() ), SLOT( accept() ) );
layout->addWidget( okBtn );
QPushButton* applyBtn = new QPushButton( "Apply" );
connect( applyBtn, SIGNAL( clicked() ), SIGNAL( applied() ) );
layout->addWidget( applyBtn);
QPushButton* cancelBtn = new QPushButton( "Cancel" );
connect( cancelBtn, SIGNAL( clicked() ), SLOT( reject() ) );
layout->addWidget( cancelBtn );
setLayout( layout );
}
DemoDialog::~DemoDialog() {
}
QString DemoDialog::getInput() const {
return m_edit->text();
}

На нашем диалоговом окне мы разместили одно поле ввода и три кнопки: OK, Apply и Cancel.
Кнопка OK предназначена для подтверждения ввода с последующим закрытием окна. Этого мы добились, связав сигнал щелчка по кнопке со стандартным слотом QDialog::accept().
Кнопка Apply так же предназначена для подтверждения ввода. Но предполагает продолжение работы с диалоговым окном. Для реализации такой логики мы предусмотрели сигнал applied().
Последняя кнопка Cancel просто закрывает диалоговое окно. Больше ничего не происходит. Введенные данные отклоняются. Для этого мы используем слот QDialog::reject().
Других кодов в QDialog не предусмотрено (кроме Accepted и Rejected). Поэтому существует слот QDialog::done( int ). Он закрывает окно и возвращает в качестве результата вызова указанное целочисленное значение.
Нажатие клавиши Esc в диалоговом окне приводит к вызову QDialog::reject(), а Enter связывается с кнопкой по умолчанию. Такой кнопкой становится либо первая добавленная кнопка (в нашем случае OK), либо кнопка, для которой вызвана функция btn->setDefault( true ).
Чтобы извлечь введенное значение в текстовое поле, мы предусмотрели константную функцию-член getInput().
Реализуем виджет, который будет использовать наше диалоговое окно:
class MainWidget : public QWidget {
Q_OBJECT
public:
MainWidget( QWidget* parent = 0 );
~MainWidget();
private slots:
void onModalDemo();
void onNonModalDemo();
void onApplied();
private:
QLineEdit* m_edit;
DemoDialog* m_nonModalDlg;
};
MainWidget::MainWidget( QWidget* parent ) : QWidget( parent ), m_nonModalDlg( NULL ) {
QBoxLayout* layout = new QHBoxLayout;
m_edit = new QLineEdit;
m_edit->setReadOnly( true );
layout->addWidget( m_edit );
QPushButton* btnModal = new QPushButton( "ModalDemo" );
layout->addWidget( btnModal );
connect( btnModal, SIGNAL( clicked() ), SLOT( onModalDemo() ) );
QPushButton* btnNonModal = new QPushButton( "NonModalDemo" );
layout->addWidget( btnNonModal );
connect( btnNonModal, SIGNAL( clicked() ), SLOT( onNonModalDemo() ) );
setLayout( layout );
}
MainWidget::~MainWidget() {
}
void MainWidget::onModalDemo() {
DemoDialog dlg( this );
connect( &dlg, SIGNAL( applied() ), SLOT( onApplied() ) );
switch( dlg.exec() ) {
case QDialog::Accepted:
qDebug() << "Accepted";
m_edit->setText( dlg.getInput() );
break;
case QDialog::Rejected:
qDebug() << "Rejected";
break;
default:
qDebug() << "Unexpected";
}
}
void MainWidget::onNonModalDemo() {
// TO DO
}
void MainWidget::onApplied() {
if( DemoDialog* dlg = qobject_cast< DemoDialog* >( sender() ) ) {
m_edit->setText( dlg->getInput() );
}
}

Виджет содержит текстовое поле и две кнопки. Первая предназначена для вызова модального диалогового окна, а вторая - для немодального. Сосредоточимся на первой.
Модальное диалоговое окно обеспечивает блокировку вызывающей функции с помощью вызова exec() (как у QApplication). Это дает нам полное право создавать такой диалог в виде локальной переменной. Обращаю внимание, что в конструкторе диалогового окна мы передаем указать на виджет. Это обеспечивает необходимую привязку.
Мы должны не забыть привязать сигнал applied() нашего диалогового окна к специально созданному слоту onApplied(). В нем мы просто устанавливаем значение введенной строки в качестве значения текстового поля виджета.
путевка в пицунду. встроенный шкаф купе.
Момент отображения диалогового окна соответствует строке с вызовом dlg.exec(). В качестве ответа возвращается код завершения работы окна. Единственный интересный для нас случай: QDialog::Accepted. Для него мы делаем то же самое, что и в слоте onApplied().
Не забудьте убедиться, что родительский виджет становится неактивным, когда модальное диалоговое окно отображается.
Добавим реализацию слота onNonModalDemo() для вызова нашего диалогового окна в немодальном режиме:
void MainWidget::onNonModalDemo() {
if( !m_nonModalDlg ) {
m_nonModalDlg = new DemoDialog( this );
connect( m_nonModalDlg, SIGNAL( applied() ), SLOT( onApplied() ) );
connect( m_nonModalDlg, SIGNAL( accepted() ), SLOT( onApplied() ) );
}
m_nonModalDlg->show();
}
Следует заметить, что для немодального диалогового окна мы завели отдельное поле класса в виджете. Это необходимо, поскольку немодальное окно не блокирует выполнение слота, поэтому локальная переменная оказалась бы уничтожена.
Для обработки ответа QDialog::Accepted мы делаем привязку к сигналу диалогового окна accepted(). Есть аналогичный сигнал для отрицательного ответа rejected(). А также для нашего собственного кода результата (на случай вызова слота done( int ) ): finished( int ).
Важным отличием является то, что отображаем диалог мы не с помощью exec(), а путем вызова show().
Освобождать указатель на немодальное диалоговое окно нам не нужно. Это произойдет автоматически при вызове деструктора родительского виджета. В этот же момент диалоговое окно закроется.
Если запустить немодальное диалоговое окно, то родительский виджет остается доступен. Например, мы можем открыть после этого еще и модальное диалоговое окно. Заметим, что в этом случае блокируется и родительский виджет, и немодальное диалоговое окно.
Крайне не рекомендую применять диалоговые окна для вывода результатов выполнения операций или сообщений об ошибках (хотя для обработки серьезных сбоев можно и подумать). В этом случае они только мешают и раздражают пользователей. Их лучше всего использовать для вынесения второстепенных функций приложения, которые просто не умещаются на форме главного виджета. Все остальное зависит от конкретных задач и целевой аудитории.