我有一个Qt应用程序,需要调用一个昂贵的非Qt函数(例如解压缩一个~200MB的zip文件),因为我从主/GUI线程调用该函数,Qt GUI冻结直到操作完成(即有时为5-10秒)。
我知道避免这个问题的一种方法是从一个单独的线程中调用昂贵的函数,但是因为在解压缩完成之前用户可以做的事情并不多,所以这似乎是多余的。我不能将processEvents()调用添加到昂贵的函数本身,因为该函数是非Qt感知代码库的一部分,我不想添加Qt依赖于它。我唯一想改变的是,在GUI被阻塞的时候出现一个小的"请等待"类型的消息,这样用户就不会认为他的鼠标点击被忽略了。
我现在是这样做的:
BusySplashWidget * splash = new BusySplashWidget("Please wait…", this);
splash->show();
qApp->processEvents(); // make sure that the splash is actually visible at this point?
ReadGiantZipFile(); // this can take a long time to return
delete splash;
这在95%的情况下是有效的,但偶尔splash widget不出现,或者它只显示为一个灰色矩形,并且"Please wait"文本不可见。
我的问题是,除了qApp->processEvents()之外,是否还有其他一些调用,我也应该这样做,以保证splash小部件在冗长的操作开始之前完全可见?(我想我可以调用qApp->processEvents()一遍又一遍的100mS,或其他东西,以说服Qt,我真的很认真,但我想避免基于巫术的编程,如果可能的话;)
如果它很重要,下面是我如何实现我的BusySplashWidget构造函数:
BusySplashWidget :: BusySplashWidget(const QString & t, QWidget * parent) : QSplashScreen(parent)
{
const int margin = 5;
QFontMetrics fm = fontMetrics();
QRect r(0,0,margin+fm.width(t)+margin, margin+fm.ascent()+fm.descent()+1+margin);
QPixmap pm(r.width(), r.height());
pm.fill(white);
// these braces ensure that ~QPainter() executes before setPixmap()
{
QPainter p(&pm);
p.setPen(black);
p.drawText(r, Qt::AlignCenter, t);
p.drawRect(QRect(0,0,r.width()-1,r.height()-1));
}
setPixmap(pm);
}
转移到另一个线程是正确的方法,但是对于简单的操作,有一种简单得多的方法可以完成此操作,而无需管理线程的痛苦。
BusySplashWidget splash("Please wait…", this);
QFutureWatcher<void> watcher;
connect(&watcher, SIGNAL(finished()), &splash, SLOT(quit()));
QFuture<void> future = QtConcurrent::run(ReadGiantZipFile);
watcher.setFuture(future);
splash.exec(); // use exec() instead of show() to open the dialog modally
有关QtConcurrent
框架的更多信息,请参阅文档