Description
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
这个函数我没有找到实现,也没有仔细找,但是在 Qt
的 testlib
库中有一个类似的接口 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>
*设置窗体
设置窗体的逻辑也比较简单,就是创建一个widget
、Dialog
专门用来实现启动动画的逻辑。如下所示为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
做一些别的操作延时一下。目前笔者用过的最好的不是在 main
中 sleep
。而是执行以下 processEvent()
具体的代码就是在 Widget::show()
之前调用,如下所示:
// 这里的时间只是为了做一个判断,只调用一下 a.processEvents(); 应该也是可以的
QDateTime time = QDateTime::currentDateTime();
QDateTime currentTime = QDateTime::currentDateTime(); //记录当前时间
while (time.secsTo(currentTime) <= 5) //5为需要延时的秒数
{
currentTime = QDateTime::currentDateTime();
a.processEvents();
};
问题2
> Dialog
结束的时候界面关闭和主界面的展示会中断一下,强迫症难以接受这种突然闪现怎么办?
还是会到 finish()
。
在 closed()
的位置 sleep()
,让关闭的窗口稍微等一等,等主界面展示出来之后再关闭。1s
的时间足够主界面刷新出来了 。代码如下:
void Dialog::finish(QWidget *mainWin) // 这里已经是我们自定义的启动动画窗口 Dialog 了
{
if (mainWin) {
if (!mainWin->windowHandle())
mainWin->createWinId();
waitForWindowExposed(mainWin->windowHandle());
}
QThread::sleep(1); // sleep 的单位是秒
close();
}
完整代码
> widget
的代码不展示
main.cpp
#include "widget.h"
#include "dialog.h"
#include <QApplication>
#include <QLabel>
#include <QPixmap>
#include <QSplashScreen>
#include <QThread>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
#ifdef QT_DEBUG // 使用 QSplashScreen 的启动动画
QPixmap pixmap(":/test.gif");
QSplashScreen splash(pixmap,10);
splash.show();
QDateTime time = QDateTime::currentDateTime();
QDateTime currentTime = QDateTime::currentDateTime(); //记录当前时间
while (time.secsTo(currentTime) <= 5) //5为需要延时的秒数
{
currentTime = QDateTime::currentDateTime();
a.processEvents();
};
#endif
Dialog d; // 使用自定义的启动动画
d.show();
QDateTime time = QDateTime::currentDateTime();
QDateTime currentTime = QDateTime::currentDateTime(); //记录当前时间
while (time.secsTo(currentTime) <= 5) //5为需要延时的秒数
{
currentTime = QDateTime::currentDateTime();
a.processEvents();
};
Widget w; // 主程序
w.show();
#ifdef QT_DEBUG // 使用 QSplashScreen 的启动动画
splash.finish(&w);
#endif
d.finish(&w);
return a.exec();
}
dialog.h
#ifndef DIALOG_H
#define DIALOG_H
#include <QDialog>
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 "dialog.h"
#include "ui_dialog.h"
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
// setWindowFlags(Qt::FramelessWindowHint | Qt::Tool | Qt::WindowStaysOnTopHint | Qt::Window);
//setAttribute(Qt::WA_TranslucentBackground);
setStyleSheet("#Dialog{"
"border-image: url(:/bgimg_334.png);" // 自己添加一个图片到程序,不添加也不影响程序运行
"background-position: center;"
"backgroun d-repeat: no-repeat;"
"}");
}
Dialog::~Dialog()
{
delete ui;
}
void Dialog::finish(QWidget* mainWin)
{
if (mainWin) {
if (!mainWin->windowHandle())
mainWin->createWinId();
// QTest::qWaitForWindowExposed(mainWin->windowHandle());
}
close();
}
dialog.ui
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Dialog</class>
<widget class="QDialog" name="Dialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="2" column="0">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QStackedWidget" name="stackedWidget">
<widget class="QWidget" name="page">
<widget class="QWidget" name="widget" native="true">
<property name="geometry">
<rect>
<x>-101</x>
<y>-10</y>
<width>231</width>
<height>51</height>
</rect>
</property>
</widget>
</widget>
<widget class="QWidget" name="page_2"/>
</widget>
</item>
<item>
<widget class="QStackedWidget" name="stackedWidget_2">
<property name="currentIndex">
<number>1</number>
</property>
<widget class="QWidget" name="page_3"/>
<widget class="QWidget" name="page_4"/>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>Dialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>Dialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>
blog link Qt 启动动画