有没有办法真正确保QSplashScreen已经在屏幕上重新绘制了?

Is there any way to make really sure QSplashScreen has been repainted on the screen?

本文关键字:新绘制 绘制 屏幕 确保 QSplashScreen 有没有      更新时间:2023-10-16

我有一个问题,在带有Xorg(Ubuntu 14.04)和Qt 5.5.1的Linux上,QSplashScreen直到我进入事件循环才被绘制。即使我多次调用QApplication::processEvents(),即使在 1000 次调用之后,它仍然没有被绘制,尽管窗口已经在屏幕上,保留了应用程序启动前存在的原始像素,因此实际上是不可见的*。从这个答案中,我得到了一个调用QApplication::processEvents()定时循环的想法,如下所示:

#include <QThread>
#include <QApplication>
#include <QSplashScreen>
int main(int argc, char** argv)
{
QApplication a(argc,argv);
QSplashScreen splash;
splash.show();
splash.showMessage("Loading...");
// The hack to try to ensure that splash screen is repainted
for(int i=0;i<30;++i)
{
QThread::usleep(1e3);
a.processEvents();
}
QThread::usleep(5e6); // simulate slow loading process
splash.showMessage("Finished");
return a.exec();
}

上面的代码主动休眠 30 毫秒,试图重新绘制QSplashScreen。这对我有用,但我不确定它是否总是可以工作,例如在繁忙/慢速的 CPU 或任何其他条件下(30 次迭代的神奇值是根据经验发现的)。

另一种通常相当侵入性的方法是在另一个线程中进行所有必要的加载,只是为了确保主线程中的QSplashScreen确实具有活动的消息队列。由于需要大量重做主程序,这看起来不太好的解决方案。

那么,有没有办法确保QSplashScreen已被重新粉刷,使其窗口不包含垃圾,然后才能进行长时间的阻塞加载过程?


*当我在启动画面后面移动一个窗口时,我发现了这一点

避免不可知的先验魔术超时的一种方法是等待一个确切的事件:绘画事件。在 X11 上,它似乎有延迟。要完成此等待,我们必须对QSplashScreen进行子类化并覆盖QSplashScreen::paintEvent(),如下所示:

#include <QThread>
#include <QApplication>
#include <QSplashScreen>
class MySplashScreen : public QSplashScreen
{
bool painted=false;
void paintEvent(QPaintEvent* e) override
{
QSplashScreen::paintEvent(e);
painted=true;
}
public:
void ensureFirstPaint() const
{
while(!painted)
{
QThread::usleep(1e3);
qApp->processEvents();
}
}
};
int main(int argc, char** argv)
{
QApplication a(argc,argv);
MySplashScreen splash;
splash.show();
splash.showMessage("Loading...");
splash.ensureFirstPaint();
QThread::usleep(5e6); // simulate slow loading process
splash.showMessage("Finished");
return a.exec();
}

解决方案相当简单:保持事件循环运行,直到重新绘制窗口。这应该在没有任何旋转的情况下完成,即您不应该使用任何显式超时。

#include <QtWidgets>
class EventSignaler : public QObject {
Q_OBJECT
QEvent::Type m_type;
protected:
bool eventFilter(QObject *src, QEvent *ev) override {
if (ev->type() == m_type)
emit hasEvent(src);
return false;
}
public:
EventSignaler(QEvent::Type type, QObject *object) :
QObject(object), m_type(type) {
object->installEventFilter(this);
}
Q_SIGNAL void hasEvent(QObject *);
};
int execUntilPainted(QWidget *widget) {
EventSignaler painted{QEvent::paint, widget};
QObject::connect(&painted, &EventSignaler::hasEvent, qApp, &QCoreApplication::quit);
return qApp->exec();
}
int main(int argc, char **argv) {
QApplication app{argc, argv};
MySplashScreen splash;
EventSignaler painted{QEvent::Paint, &splash};
splash.show();
splash.showMessage("Loading...");
execUntilPainted(&splash);
QThread::sleep(5); // simulate slow loading process
splash.showMessage("Finished");
return app.exec();
}
#include "main.moc"