1

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

将插槽添加到启动画面:

    void doRefresh()
    {
        repaint();
    }

但这也没有用。doRefresh 没有被调用。似乎 QTimer 还需要一个已经存在的事件循环。

4

2 回答 2

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。

于 2013-08-20T12:14:02.017 回答
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::finishclose那么QSignalMapper可以提供帮助。
请注意,connect 的最后一个参数是默认值Qt::AutoConnection,它将强制插槽在主线程中运行。

于 2013-08-20T16:04:16.193 回答