8000 Qt 启动动画 · Issue #69 · holdyounger/ScopeBlog · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content
Qt 启动动画 #69
Open
Open
@holdyounger

Description

@holdyounger

Qt 启动动画

[toc]

QSplashScreen

设置图片

> Qt内置了用于程序启动的动画直接使用QSplashScreen即可。

QPixmap pixmap(":load.gif");     //读取图片
QSplashScreen splash(pixmap); //
splash.setWindowOpacity(0.8); // 设置窗口透明度
splash.show();
splash.showMessage("程序正在加载......", Qt::AlignCenter, Qt::red); //显示文字

// 可选项
#ifdeg DEBUG
QDateTime time = QDateTime::currentDateTime();
QDateTime currentTime = QDateTime::currentDateTime(); //记录当前时间

while (time.secsTo(currentTime) <= 5) // 5为需要延时的秒数
{
    currentTime = QDateTime::currentDateTime();
    a.processEvents();
};
#endif

widget w;
w.show();

splash.finish(&w); //在主体对象初始化完成后结束启动动画

以上代码放在程序的入口main函数中即可。 widget就是我们需要启动的程序。

自定义启动动画

> 自定义启动动画的方式网上有几种重写 QSplashScreen 的。笔者也尝试使用了一下,发现并不好用,于是按照一开始的方案,准备用一个 widget 去写启动动画。
>
> 参考 QSplashScreen 的方式,还是使用 finish(QWidget *mainWin) 的接口去作为窗体关闭的入口。

finish(Qwidget *mainWin)

关于 finished(QWidget *mainWin) 直接把 QSplashScreen 的源码搬过来即可。

> 源码一般在你使用的版本的 src 文件夹下:
>
> ${install dir}\5.9.9\Src\qtbase\src\widgets\widgets
>
> QSplashScreen 源码中的 finish() 如下所示:

/*!
    Makes the splash screen wait until the widget \a mainWin is displayed
    before calling close() on itself.
*/
// 这个接口我们需要复制到自定义的启动动画窗体中。
void QSplashScreen::finish(QWidget *mainWin)
{
    if (mainWin) {
        if (!mainWin->windowHandle())
            mainWin->createWinId();
        waitForWindowExposed(mainWin->windowHandle());
    }
    close();
}

上述代码,唯一的问题在于 waitForWindowExposed 这个函数我没有找到实现,也没有仔细找,但是在 Qttestlib 库中有一个类似的接口 QTest::qwaitForWindowExposed() 两个接口的作用应该是一致的。上述代码中 waitForWindowExposed(mainWin->windowHandle()); 经笔者测试哈(不一定准确),即使去掉也不会影响 finish() 接口的作用。^2022年7月27日08:39:40^ 去掉还是会有影响的,相当于判断窗口的逻辑没了。关于 waitForWi CEF8 ndowExposed() 在 Qt源码中的实现,笔者在调试程序的时候发现去掉这行代码,怎么看启动动画和运行的程序之间的衔接都不太合理,遂决定找一下这个 waitForWindowExposed() 的源码,这一找,真的是远在天边,近在眼前。函数的实现就在 qsplashscreen.cpp 中。这里贴一下其源码。

// A copy of Qt Test's qWaitForWindowExposed() and qSleep().
inline static bool waitForWindowExposed(QWindow *window, int timeout = 1000)
{
    enum { TimeOutMs = 10 };
    QElapsedTimer timer;
    timer.start();
    while (!window->isExposed()) {
        const int remaining = timeout - int(timer.elapsed());
        if (remaining <= 0)
            break;
        QCoreApplication::processEvents(QEventLoop::AllEvents, remaining);
        QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
#if defined(Q_OS_WINRT)
        WaitForSingleObjectEx(GetCurrentThread(), TimeOutMs, false);
#elif defined(Q_OS_WIN)
        Sleep(uint(TimeOutMs));
#else
        struct timespec ts = { TimeOutMs / 1000, (TimeOutMs % 1000) * 1000 * 1000 };
        nanosleep(&ts, NULL);
#endif
    }
    return window->isExposed();
}

如果要使用你源码中的实现,则需要引入两个头问题。

#include <QElapsedTimer>
#include <QtGui/QWindow>

*设置窗体

设置窗体的逻辑也比较简单,就是创建一个widgetDialog专门用来实现启动动画的逻辑。如下所示为Qt程序的入口函数:

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
	Dialog d; // 启动动画的窗台
    d.show();

    Widget w;
    w.show();

	d.finish(&w); // 复制的 QSplashScreen 中的 finished()
	return a.exec();
}

其中的 Dialog d 就是我们创建的一个自定义的启动动画窗口,至于窗口内要实现什么,依据你个人的业务和需求去实现即可。

问题1

> 上文中的 Dialog 还没有主界面就展示了,或者 Dialog 展示了但是没有画面。

关于这个问题,我认为是主界面的刷新太快了(大家可以调试代码去观察一下现象),也就是 finish 相当于是瞬间调用了。导致 Dialog<u>没有展示和来得及刷新</u> 。这个问题网友也给出了方案,让 main 做一些别的操作延时一下。目前笔者用过的最好的不是在 mainsleep 。而是执行以下 processEvent()

具体的代码就是在 Widget::show() 之前调用,如下所示:

// 这里的时间只是为了做一个判断,只调用一下 a.processEvents(); 应该也是可以的
QDateTime time = QDateTime::currentDateTime();
QDateTime currentTime = QDateTime::currentDateTime();   //记录当前时间
while (time.secsTo(currentTime) &lt;= 5)                   //5为需要延时的秒数
{
    currentTime = QDateTime::currentDateTime();
    a.processEvents();
};

问题2

> Dialog 结束的时候界面关闭和主界面的展示会中断一下,强迫症难以接受这种突然闪现怎么办?

还是会到 finish()

closed() 的位置 sleep(),让关闭的窗口稍微等一等,等主界面展示出来之后再关闭。1s 的时间足够主界面刷新出来了 。代码如下:

void Dialog::finish(QWidget *mainWin) // 这里已经是我们自定义的启动动画窗口 Dialog 了
{
    if (mainWin) {
        if (!mainWin-&gt;windowHandle())
            mainWin-&gt;createWinId();
        waitForWindowExposed(mainWin-&gt;windowHandle());
    }
    QThread::sleep(1); // sleep 的单位是秒
    close();
}

完整代码

> widget 的代码不展示

main.cpp

#include &quot;widget.h&quot;
#include &quot;dialog.h&quot;

#include &lt;QApplication&gt;
#include &lt;QLabel&gt;
#include &lt;QPixmap&gt;
#include &lt;QSplashScreen&gt;
#include &lt;QThread&gt;

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

#ifdef QT_DEBUG // 使用 QSplashScreen 的启动动画
    QPixmap pixmap(&quot;:/test.gif&quot;);
    QSplashScreen splash(pixmap,10);
    splash.show();
    QDateTime time = QDateTime::currentDateTime();
        QDateTime currentTime = QDateTime::currentDateTime();   //记录当前时间
        while (time.secsTo(currentTime) &lt;= 5)                   //5为需要延时的秒数
        {
            currentTime = QDateTime::currentDateTime();
            a.processEvents();
        };
#endif
    
    Dialog d; // 使用自定义的启动动画
    d.show();

    QDateTime time = QDateTime::currentDateTime();
    QDateTime currentTime = QDateTime::currentDateTime();   //记录当前时间
    while (time.secsTo(currentTime) &lt;= 5)                   //5为需要延时的秒数
    {
        currentTime = QDateTime::currentDateTime();
        a.processEvents();
    };

    Widget w; // 主程序
    w.show();

#ifdef QT_DEBUG // 使用 QSplashScreen 的启动动画
	splash.finish(&amp;w);
#endif

   d.finish(&amp;w);
   return a.exec();
}

dialog.h

#ifndef DIALOG_H
#define DIALOG_H

#include &lt;QDialog&gt;

namespace Ui {
class Dialog;
}

class Dialog : public QDialog
{
    Q_OBJECT

public:
    explicit Dialog(QWidget *parent = nullptr);
    ~Dialog();

    void finish(QWidget *mainWin);
private:
    Ui::Dialog *ui;
};

#endif // DIALOG_H

dialog.cpp

#include &quot;dialog.h&quot;
#include &quot;ui_dialog.h&quot;

Dialog::Dialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::Dialog)
{
    ui-&gt;setupUi(this);
   // setWindowFlags(Qt::FramelessWindowHint | Qt::Tool | Qt::WindowStaysOnTopHint | Qt::Window);
    //setAttribute(Qt::WA_TranslucentBackground);
    setStyleSheet(&quot;#Dialog{&quot;
                  &quot;border-image: url(:/bgimg_334.png);&quot; // 自己添加一个图片到程序,不添加也不影响程序运行
                  &quot;background-position: center;&quot;
                  &quot;backgroun d-repeat: no-repeat;&quot;
                  &quot;}&quot;);
}

Dialog::~Dialog()
{
    delete ui;
}

void Dialog::finish(QWidget* mainWin)
{
    if (mainWin) {
        if (!mainWin-&gt;windowHandle())
            mainWin-&gt;createWinId();
    //	QTest::qWaitForWindowExposed(mainWin-&gt;windowHandle());
    }
    close();
}

dialog.ui

&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;ui version=&quot;4.0&quot;&gt;
 &lt;class&gt;Dialog&lt;/class&gt;
 &lt;widget class=&quot;QDialog&quot; name=&quot;Dialog&quot;&gt;
  &lt;property name=&quot;geometry&quot;&gt;
   &lt;rect&gt;
    &lt;x&gt;0&lt;/x&gt;
    &lt;y&gt;0&lt;/y&gt;
    &lt;width&gt;400&lt;/width&gt;
    &lt;height&gt;300&lt;/height&gt;
   &lt;/rect&gt;
  &lt;/property&gt;
  &lt;property name=&quot;windowTitle&quot;&gt;
   &lt;string&gt;Dialog&lt;/string&gt;
  &lt;/property&gt;
  &lt;layout class=&quot;QGridLayout&quot; name=&quot;gridLayout&quot;&gt;
   &lt;item row=&quot;2&quot; column=&quot;0&quot;&gt;
    &lt;widget class=&quot;QDialogButtonBox&quot; name=&quot;buttonBox&quot;&gt;
     &lt;property name=&quot;orientation&quot;&gt;
      &lt;enum&gt;Qt::Horizontal&lt;/enum&gt;
     &lt;/property&gt;
     &lt;property name=&quot;standardButtons&quot;&gt;
      &lt;set&gt;QDialogButtonBox::Cancel|QDialogButtonBox::Ok&lt;/set&gt;
     &lt;/property&gt;
    &lt;/widget&gt;
   &lt;/item&gt;
   &lt;item row=&quot;1&quot; column=&quot;0&quot;&gt;
    &lt;widget class=&quot;QFrame&quot; name=&quot;frame&quot;&gt;
     &lt;property name=&quot;frameShape&quot;&gt;
      &lt;enum&gt;QFrame::StyledPanel&lt;/enum&gt;
     &lt;/property&gt;
     &lt;property name=&quot;frameShadow&quot;&gt;
      &lt;enum&gt;QFrame::Raised&lt;/enum&gt;
     &lt;/property&gt;
     &lt;layout class=&quot;QGridLayout&quot; name=&quot;gridLayout_2&quot;&gt;
      &lt;item row=&quot;0&quot; column=&quot;0&quot;&gt;
       &lt;layout class=&quot;QVBoxLayout&quot; name=&quot;verticalLayout&quot;&gt;
        &lt;item&gt;
         &lt;widget class=&quot;QStackedWidget&quot; name=&quot;stackedWidget&quot;&gt;
          &lt;widget class=&quot;QWidget&quot; name=&quot;page&quot;&gt;
           &lt;widget class=&quot;QWidget&quot; name=&quot;widget&quot; native=&quot;true&quot;&gt;
            &lt;property name=&quot;geometry&quot;&gt;
             &lt;rect&gt;
              &lt;x&gt;-101&lt;/x&gt;
              &lt;y&gt;-10&lt;/y&gt;
              &lt;width&gt;231&lt;/width&gt;
              &lt;height&gt;51&lt;/height&gt;
             &lt;/rect&gt;
            &lt;/property&gt;
           &lt;/widget&gt;
          &lt;/widget&gt;
          &lt;widget class=&quot;QWidget&quot; name=&quot;page_2&quot;/&gt;
         &lt;/widget&gt;
        &lt;/item&gt;
        &lt;item&gt;
         &lt;widget class=&quot;QStackedWidget&quot; name=&quot;stackedWidget_2&quot;&gt;
          &lt;property name=&quot;currentIndex&quot;&gt;
           &lt;number&gt;1&lt;/number&gt;
          &lt;/property&gt;
          &lt;widget class=&quot;QWidget&quot; name=&quot;page_3&quot;/&gt;
          &lt;widget class=&quot;QWidget&quot; name=&quot;page_4&quot;/&gt;
         &lt;/widget&gt;
        &lt;/item&gt;
       &lt;/layout&gt;
      &lt;/item&gt;
     &lt;/layout&gt;
    &lt;/widget&gt;
   &lt;/item&gt;
  &lt;/layout&gt;
 &lt;/widget&gt;
 &lt;resources/&gt;
 &lt;connections&gt;
  &lt;connection&gt;
   &lt;sender&gt;buttonBox&lt;/sender&gt;
   &lt;signal&gt;accepted()&lt;/signal&gt;
   &lt;receiver&gt;Dialog&lt;/receiver&gt;
   &lt;slot&gt;accept()&lt;/slot&gt;
   &lt;hints&gt;
    &lt;hint type=&quot;sourcelabel&quot;&gt;
     &lt;x&gt;248&lt;/x&gt;
     &lt;y&gt;254&lt;/y&gt;
    &lt;/hint&gt;
    &lt;hint type=&quot;destinationlabel&quot;&gt;
     &lt;x&gt;157&lt;/x&gt;
     &lt;y&gt;274&lt;/y&gt;
    &lt;/hint&gt;
   &lt;/hints&gt;
  &lt;/connection&gt;
  &lt;connection&gt;
   &lt;sender&gt;buttonBox&lt;/sender&gt;
   &lt;signal&gt;rejected()&lt;/signal&gt;
   &lt;receiver&gt;Dialog&lt;/receiver&gt;
   &lt;slot&gt;reject()&lt;/slot&gt;
   &lt;hints&gt;
    &lt;hint type=&quot;sourcelabel&quot;&gt;
     &lt;x&gt;316&lt;/x&gt;
     &lt;y&gt;260&lt;/y&gt;
    &lt;/hint&gt;
    &lt;hint type=&quot;destinationlabel&quot;&gt;
     &lt;x&gt;286&lt;/x&gt;
     &lt;y&gt;274&lt;/y&gt;
    &lt;/hint&gt;
   &lt;/hints&gt;
  &lt;/connection&gt;
 &lt;/connections&gt;
&lt;/ui&gt;

blog link Qt 启动动画

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      0