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

問題はかなり深刻です。アニメーションを機能させるには、メイン スレッドでプロセス イベントを許可する必要があります。一方、初期化処理もメインスレッドで実行されます。
最初のアプローチはapp.processEvents()、できるだけ頻繁にそれらを追加することですが、これではフレーム レートが大幅に制限されてしまい、役に立たなくなります。

適切に修正するには、次の 2 つの重要な質問に答える必要があります。

  • 初期化中に何をしていますか?
  • この初期化を別のスレッドに移動できますか?

初期化中に大きなインデックスが作成されたアプリケーションを作成したら。これには約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スロットをメイン スレッドで実行するよう強制するデフォルト値であることに注意してください。

于 2013-08-20T16:04:16.193 に答える