您的位置:首页 > 其它

7-3 系统繁忙时的响应(Staying Responsive During Intensive Processing)

2007-05-29 20:56 316 查看
当我们调用QApplication::exec()时,Qt就开始了事件循环。启动时,Qt发出显示和绘制事件,把控件显示出来。然后,事件循环就开始了,不停检查是否有事件发生,然后把事件分派到程序中的QObject对象。

一个事件正在处理时,其他的事件已经产生并加入到Qt的事件队列中,如果我们在处理某一个事件时花费了很多事件,这期间用户界面就不会有任何响应。例如,在程序保存文件时,窗口产生的事件就不会处理,只有在保存结束后才能处理。在保存的过程中,应用程序也不会处理窗口的绘制事件。

解决这个问题的方法是多线程:一个线程处理用户界面,另一个线程进行文件保存或者其他耗时的操作。这样,程序的用户界面就会在文件保存期间保持响应。在第18章会介绍这种方法。

还有一个简单的方法是在保存文件的过程中多次调用QApplication::processEvents()。调用时Qt就会处理暂停的事件,然后返回继续保存文件。其实,QApplication::exec()也是一个调用processEvents()的while循环。下面的例子是Spreadsheet在保存文件时用processEvents()响应用户界面:
bool Spreadsheet::writeFile(const QString &fileName)
{
QFile file(fileName);
...
for (int row = 0; row < RowCount; ++row) {
for (int column = 0; column < ColumnCount; ++column) {
QString str = formula(row, column);
if (!str.isEmpty())
out << quint16(row) << quint16(column) << str;
}
qApp->processEvents();
}
return true;
}
但是这样做有一个危险,如果用户在保存文件期间关闭了主窗口,或者又点击了一次File|Save菜单,很容易造成死循环。解决的方法是把代码qApp->processEvents()用qApp->processEvents(QEventLoop::ExcludeUserInputEvents);代替,这样,Qt就会不处理键盘和鼠标事件。
应用程序在进行长时间的操作时,经常使用QProgressDialog,提示用户正在进行的操作的完成情况。QProgressDialog还提供了一个Cancel按钮,允许用户取消当前的操作。下面的代码是Spreadsheet保存文件时使用QProgressDialog的代码:
bool Spreadsheet::writeFile(const QString &fileName)
{
QFile file(fileName);
...
QProgressDialog progress(this);
progress.setLabelText(tr("Saving %1").arg(fileName));
progress.setRange(0, RowCount);
progress.setModal(true);
for (int row = 0; row < RowCount; ++row) {
progress.setValue(row);
qApp->processEvents();
if (progress.wasCanceled()) {
file.remove();
return false;
}
for (int column = 0; column < ColumnCount; ++column) {
QString str = formula(row, column);
if (!str.isEmpty())
out << quint16(row) << quint16(column) << str;
}
}
return true;
}
首先,创建一个QProgressDialog,设置NumRows做为步骤的总数。然后,保存一行以后,调用setValue()更新进度条的状态。QProgressDialog根据当前步骤数和总步骤数自动计算完成的百分比。调用QApplication::processEvents()处理可能发生的绘制事件,用户点击事件,或者键盘事件,如果用户点击了Cancel按钮,则取消保存操作,删除正在保存的文件。

我们没有调用QProgressDialog的show()函数,因为QProgressDialog会自己处理。如果需要保存的文件很小,所需时间很短,QProgressDialog能够发觉这个情况,不显示进度条。

除了使用多线程和QProgressDialog,还有一种完全不同的方法处理这种耗时较长的操作:在程序空闲时进行这类操作,而不是等待用户的请求才做。但是程序空闲的时间无法预计,这种方法的条件是所进行的操作能够安全中止和继续。具体实现是,启动一个0毫秒的计时器。只要程序中没有其他须处理的事件,这个事件就会触发。下面的timeEvent()函数就是这个方法的实现:
void Spreadsheet::timerEvent(QTimerEvent *event)
{
if (event->timerId() == myTimerId) {
while (step < MaxStep && !qApp->hasPendingEvents()) {
performStep(step);
++step;
}
} else {
QTableWidget::timerEvent(event);
}
}

如果hasPendingEvents()返回true,暂停操作,让Qt控制程序运行。当Qt没有需要处理的事件时,操作继续。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: