Есть ли способ убедиться, что QSplashScreen был перекрашен на экране? - PullRequest
0 голосов
/ 27 апреля 2018

У меня проблема в том, что в Linux с Xorg (Ubuntu 14.04) и Qt 5.5.1 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. Это работает для меня, но я не уверен, что это будет работать всегда, например. на занятом / медленном процессоре или в любых других условиях (магическое значение 30 итераций было найдено опытным путем).

Другим, довольно навязчивым способом, было бы сделать всю необходимую загрузку в другом потоке, только чтобы убедиться, что QSplashScreen в основном потоке имеет активную очередь сообщений. Из-за необходимости значительно переделать основную программу, это выглядит не слишком удачным решением.

Итак, есть ли способ убедиться, что QSplashScreen был перекрашен, чтобы его окно не содержало мусора, и только затем продолжить длительную блокировку процесса загрузки?


* Я обнаружил это, когда переместил окно за заставкой

Ответы [ 2 ]

0 голосов
/ 29 апреля 2018

Решение довольно простое: продолжайте цикл обработки событий, пока окно не будет перекрашено. Это должно быть сделано без какого-либо вращения, т. Е. Вы не должны использовать никаких явных таймаутов.

#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"
0 голосов
/ 27 апреля 2018

Один из способов избежать непостижимого априорного магического таймаута - дождаться точного события: события рисования. На 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();
}
...