可靠地展示"请等待"在主Qt事件循环中执行冗长的阻塞操作时进行对话

时间:2014-03-26 18:55:57

标签: qt user-interface splash-screen synchronous

我有一个需要调用昂贵的非Qt函数的Qt应用程序(例如,解压缩~200MB的zip文件),并且因为我从主/ GUI线程调用该函数,所以Qt GUI冻结直到操作完成(即有时5-10秒)。

我知道避免这个问题的一种方法是从一个单独的线程中调用昂贵的函数,但是由于用户在解压缩完成之前可以做的事情不多,这似乎有点过分。我无法将processEvents()调用添加到昂贵的函数本身,因为该函数是非Qt感知代码库的一部分,我不想为它添加Qt依赖。

我唯一要改变的是,在GUI被阻止期间会出现一些“请稍候”类型的消息,这样用户就不会认为他的鼠标点击被忽略了。 / p>

我目前这样做:

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%的时间可以使用,但偶尔会出现飞溅小部件,或者它只显示为灰色矩形,并且“请稍候”文本不可见。

我的问题是,除了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);
}

1 个答案:

答案 0 :(得分:2)

转移到另一个线程是正确的方法,但对于简单的操作,没有管理线程的痛苦,实现这一点的方法要简单得多。

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 framework的文档。