У меня есть простое расширение экрана-заставки, которое должно показывать небольшую анимацию (анимированный 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 также требует уже существующего цикла событий.
Предполагая, что заставка отображается перед вызовом функции 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.
Проблема довольно глубокая. Чтобы анимация работала, вы должны разрешить события процесса в главном потоке. С другой стороны, процесс инициализации также выполняется в основном потоке.
Первый подход — это добавить те 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
который заставит слоты запускаться в основном потоке.