在QSplashScreen中显示QMovie

时间:2013-08-20 12:00:49

标签: c++ qt splash-screen

我有一个简单的启动画面扩展,在用户等待主窗口时应该显示一些动画(动画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);
    }
}

编辑:

试图在SplashScreen构造函数中使用QTimer调用重绘:

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

在Splashscreen中添加了插槽:

    void doRefresh()
    {
        repaint();
    }

但这也行不通。不调用doRefresh。似乎QTimer还需要一个已读的现有事件循环。

2 个答案:

答案 0 :(得分:1)

假设在调用QApplication :: exec()函数之前显示启动画面,则可能没有处理事件,因此您需要在QApplication对象上调用processEvents。

请注意帮助示例: -

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();

}

在这种情况下,它讨论了在启动画面上调用raise(),使用QTimer确保它保持在最顶层,但需要processEvents调用才能使QTimer运行。

正如Qt帮助在点击鼠标按钮时解除闪屏一样: -

由于启动画面通常在事件循环开始运行之前显示,因此需要定期调用QApplication :: processEvents()来接收鼠标点击。

同样可能适用于GIF动画。

答案 1 :(得分: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,它将强制在主线程中运行插槽。

相关问题