С вызова функции QApplication::exec() начинается главный цикл обработки событий. Сначала Qt запускает несколько событий, чтобы отобразить и перерисовать виджеты. После этого в цикле постоянно выполняется проверка поступления новых событий и их передача виджетам приложения.
Во время обработки одного события, в очередь могут поступать другие события. Если обработка какого либо события занимает продолжительное время, то это может отрицательно сказаться на времени отклика пользовательского интерфейса. Например, ни одно событие, поступившее от оконной системы во время сохранения файла на диск, не будет обработано до тех пор, пока файл не будет сохранен полностью. В течение времени, необходимого для сохранения файла, приложение никак не реагирует на запросы оконной системы.
Как одно из возможных решений данной проблемы -- создавать многопоточные приложения, в которых один поток будет отвечать за пользовательский интерфейс, а другой -- за дисковые операции (или любые другие действия, выполняющиеся продолжительное время). В этом случае приложение будет исправно откликаться на действия пользователя даже во время выполнения длительной обработки данных. Этот подход мы будем обсуждать в Главе 17.
Более простое решение -- вызывать QApplication::processEvents() как можно чаще, во время длительных операций. Эта функция выполняет обработку событий, ожидающих в очереди, и затем возвращает управление в вызвавшую функцию. Фактически, QApplication::exec() -- это не более чем цикл while, в котором вызывается функция processEvents().
Ниже приводится пример того, как можно сократить время отклика приложения Spreadsheet, во время сохранения большого файла на диск (см. оригинальную версию):
bool Spreadsheet::writeFile(const QString &fileName)
{
QFile file(fileName);
...
for (int row = 0; row < NumRows; ++row) {
for (int col = 0; col < NumCols; ++col) {
QString str = formula(row, col);
if (!str.isEmpty())
out << (Q_UINT16)row << (Q_UINT16)col << str;
}
qApp->processEvents();
}
return true;
}
Однако в таких случаях существует одна опасность: пользователь
может закрыть приложение до того, как файл будет сохранен, или даже
может повторно вызывать процедуру сохранения файла. Эта проблема
решается довольно просто -- нужно заменить вызов
qApp->processEvents();
на
qApp->eventLoop()->processEvents(QEventLoop::ExcludeUserInput);
который заставит Qt игнорировать события от мыши и
клавиатуры.Зачастую возникает необходимость вывести окно диалога, демонстрирующего ход выполнения длительной операции. Для подобных целей предназначен QProgressDialog, который имеет индикатор хода выполнения. У него так же имеется кнопка Cancel, с помощью которой пользователь может прервать операцию. Ниже представлен измененный вариант функции, которая демонстрирует пользователю ход операции сохранения файла:
bool Spreadsheet::writeFile(const QString &fileName)
{
QFile file(fileName);
...
QProgressDialog progress(tr("Saving file..."), tr("Cancel"),
NumRows);
progress.setModal(true);
for (int row = 0; row < NumRows; ++row) {
progress.setProgress(row);
qApp->processEvents();
if (progress.wasCanceled()) {
file.remove();
return false;
}
for (int col = 0; col < NumCols; ++col) {
QString str = formula(row, col);
if (!str.isEmpty())
out << (Q_UINT16)row << (Q_UINT16)col << str;
}
}
return true;
}
На этот раз функция создает QProgressDialog, которому передает значение переменной
NumRows, как общее число шагов. Затем, перед
сохранением каждой строки, вызывается setProgress(), которая обновляет индикатор хода
операции. Процент выполнения вычисляется компонентом QProgressDialog самостоятельно. Затем вызывается
QApplication::processEvents(), чтобы
обновить изображение на экране, а заодно и проверить -- не нажал ли
пользователь на кнопку Cancel. Если
кнопка Cancel была нажата, то операция
сохранения прерывается и файл удаляется.Мы не вызываем метод show() диалога, потому что он самостоятельно выполняет это действие. Если операция выполняется достаточно быстро, возможно потому что файл получился очень коротким, или потому что компьютер обладает очень высокой производительностью, QProgressDialog обнаружит это и вообще не будет выводить себя на экран.
Есть еще один способ выполнения длительных операций. Он сильно отличается от того, что был описан выше. Вместо того, чтобы в процессе длительных операций предусматривать обработку пользовательского интерфейса, можно наоборот, производить длительные операции, когда приложение простаивает. Этот способ пригоден в тех случаях, когда операция может быть безопасно прервана и затем опять продолжена.
В Qt такой вариант может быть реализован с помощью специального таймера -- таймера с нулевым интервалом. Такие таймеры генерируют события, когда очередь событий пуста. Ниже приводится пример реализации обработчика событий от такого таймера:
void Spreadsheet::timerEvent(QTimerEvent *event)
{
if (event->timerId() == myTimerId) {
while (step < MaxStep && !qApp->hasPendingEvents()) {
performStep(step);
++step;
}
} else {
QTable::timerEvent(event);
}
}
Если функция hasPendingEvents()
возвращает true, обработка данных
приостанавливается и управление передается обратно в Qt. Обработка
будет продолжена, когда Qt обслужит все события в очереди.
Пред. | В начало | След. |
Установка фильтров событий. | На уровень выше | Двух- и трехмерная графика. |