Отображение QMovie в QSplashScreen

У меня есть простое расширение экрана-заставки, которое должно показывать небольшую анимацию (анимированный GIF), пока пользователь ждет главного окна. Проблема в том, что вместо всех отображается только первый кадр:

class SplashScreen : public QSplashScreen
{
Q_OBJECT

public:
explicit SplashScreen(const QPixmap& pixmap, const QString& animation, Qt::WindowFlags flags = 0);

protected:
void paintEvent(QPaintEvent* event);

signals:
void frameChanged();

private slots:
void convertFrameChanged(int)
{
repaint();
}

private:
QMovie movie;

};SplashScreen::SplashScreen(const QPixmap& pixmap, const QString& animation, Qt::WindowFlags flags)
: QSplashScreen(pixmap, flags),
movie(animation)
{
movie.start();
connect(&(movie), SIGNAL(frameChanged(int)), this, SLOT(convertFrameChanged(int)));
}

void SplashScreen::paintEvent(QPaintEvent* event)
{
QSplashScreen::paintEvent(event);

QPixmap frame = movie.currentPixmap();
QRect rect = frame.rect();
rect.moveCenter(this->rect().center());
if (rect.intersects(event->rect()))
{
QPainter painter(this);
painter.drawPixmap(rect.left(), rect.top(), frame);
}
}

РЕДАКТИРОВАТЬ:

Попытался вызвать перерисовку с QTimer в конструкторе SplashScreen:

QTimer *timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(doRefresh()));
timer->start(1000);

Добавлен слот на заставку:

    void doRefresh()
{
repaint();
}

Но это тоже не сработало. doRefresh не вызывается. Похоже, QTimer также требует уже существующего цикла событий.

1

Решение

Предполагая, что заставка отображается перед вызовом функции QApplication :: exec (), вполне вероятно, что события не обрабатываются, поэтому вам необходимо вызвать processEvents для объекта QApplication.

Обратите внимание на пример справки: —

int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QPixmap pixmap(":/splash.png");
QSplashScreen splash(pixmap);
splash.show();
app.processEvents(); // need to process events manually
...
QMainWindow window;
window.show();
splash.finish(&window);
return app.exec();

}

В этой ситуации обсуждается вызов метода lift () на заставке, с QTimer, чтобы гарантировать, что он остается на вершине, но для работы QTimer требуется вызов processEvents.

Как говорится в Qt, справка об отключении заставки при нажатии кнопки мыши: —

Поскольку заставка обычно отображается до начала цикла обработки событий, необходимо периодически вызывать QApplication :: processEvents () для получения щелчков мыши.

То же самое относится и к анимированному GIF.

1

Другие решения

Проблема довольно глубокая. Чтобы анимация работала, вы должны разрешить события процесса в главном потоке. С другой стороны, процесс инициализации также выполняется в основном потоке.
Первый подход — это добавить те app.processEvents() как можно чаще, но это настолько сильно ограничит частоту кадров, что станет бесполезным.

Чтобы исправить это, у вас есть ответ на два важных вопроса:

  • что вы делаете во время инициализации?
  • и можно ли эту инициализацию перенести в отдельный поток?

Когда-то у меня было приложение, в котором во время инициализации создавался большой индекс. Это заняло около 40 секунд, так что довольно много. Я переместил индекс здания в отдельный поток, используя QtConcurrent::run(this, &MyClass::MyMethodToBuildIndex) и показывая некоторый прогресс, периодически излучая сигналы от MyClass::MyMethodToBuildIndex (автоматическое подключение по умолчанию сделало весь трюк).
Если у вас есть аналогичная инициализация времени журнала, это решение включит анимацию вашего QMovie Из коробки проблема будет заключаться в умном соединении сигнала и слотов, поэтому окна будут отображаться и скрываться с надлежащим временем.


Это довольно просто сделать. Детали зависят от того, как вы разработали свое приложение. Я обычно создаю отдельный класс с бизнес-логикой. Если этот класс содержит некоторые тяжелые вычисления, сделанные во время инициализации, то это может быть сделано что-то вроде:

int main(int argc, char *argv[])
{
QApplication app(argc, argv);
SplashScreen splash();
splash.show();
app.processEvents(); // need to process events manually

QMainWindow window;
SomeLogicClass data;
window.setData(&data);

connect(&data, SIGNAL(initializationProgressChanged(int)),
&spash, SLOT(setProgress(int)));

// show main window when initialization is finished
connect(&data, SIGNAL(initializationFinished()),
&window, SLOT(show()));

// close splash when initialization is finished
connect(&data, SIGNAL(initializationFinished()),
&spash, SLOT(close()));

// this line I usually hide behind some method like: startBackgroundInitialization
QtCuncurent::run(&data, &SomeLogicClass::heavyInitialization);

return app.exec();
}

Если вам нужно использовать QSplashScreen::finish вместо close затем QSignalMapper может помочь.
Обратите внимание, что последний аргумент connect является значением по умолчанию Qt::AutoConnection который заставит слоты запускаться в основном потоке.

1

По вопросам рекламы [email protected]