[toc]
QSplashScreen
设置图片
Qt内置了用于程序启动的动画直接使用QSplashScreen即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 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 ) { 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()
如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 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 去掉还是会有影响的,相当于判断窗口的逻辑没了。关于 waitForWindowExposed()
在 Qt源码中的实现,笔者在调试程序的时候发现去掉这行代码,怎么看启动动画和运行的程序之间的衔接都不太合理,遂决定找一下这个 waitForWindowExposed()
的源码,这一找,真的是远在天边,近在眼前。函数的实现就在 qsplashscreen.cpp
中。这里贴一下其源码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 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 (); }
如果要使用你源码中的实现,则需要引入两个头问题。
1 2 #include <QElapsedTimer> #include <QtGui/QWindow>
*设置窗体
设置窗体的逻辑也比较简单,就是创建一个widget
、Dialog
专门用来实现启动动画的逻辑。如下所示为Qt程序的入口函数:
1 2 3 4 5 6 7 8 9 10 11 12 int main (int argc, char *argv[]) { QApplication a (argc, argv) ; Dialog d; d.show (); Widget w; w.show (); d.finish (&w); return a.exec (); }
其中的 Dialog d
就是我们创建的一个自定义的启动动画窗口,至于窗口内要实现什么,依据你个人的业务和需求去实现即可。
问题1
上文中的 Dialog
还没有主界面就展示了,或者 Dialog
展示了但是没有画面。
关于这个问题,我认为是主界面的刷新太快了(大家可以调试代码去观察一下现象),也就是 finish
相当于是瞬间调用了。导致 Dialog
没有展示和来得及刷新 。这个问题网友也给出了方案,让 main
做一些别的操作延时一下。目前笔者用过的最好的不是在 main
中 sleep
。而是执行以下 processEvent()
具体的代码就是在 Widget::show()
之前调用,如下所示:
1 2 3 4 5 6 7 8 QDateTime time = QDateTime::currentDateTime (); QDateTime currentTime = QDateTime::currentDateTime (); while (time.secsTo (currentTime) <= 5 ) { currentTime = QDateTime::currentDateTime (); a.processEvents (); };
问题2
Dialog
结束的时候界面关闭和主界面的展示会中断一下,强迫症难以接受这种突然闪现怎么办?
还是会到 finish()
。
在 closed()
的位置 sleep()
,让关闭的窗口稍微等一等,等主界面展示出来之后再关闭。1s
的时间足够主界面刷新出来了 。代码如下:
1 2 3 4 5 6 7 8 9 10 void Dialog::finish (QWidget *mainWin) { if (mainWin) { if (!mainWin->windowHandle ()) mainWin->createWinId (); waitForWindowExposed (mainWin->windowHandle ()); } QThread::sleep (1 ); close (); }
完整代码
widget
的代码不展示
main.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 #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 QPixmap pixmap (":/test.gif" ) ; QSplashScreen splash (pixmap,10 ) ; splash.show (); QDateTime time = QDateTime::currentDateTime (); QDateTime currentTime = QDateTime::currentDateTime (); while (time.secsTo (currentTime) <= 5 ) { currentTime = QDateTime::currentDateTime (); a.processEvents (); };#endif Dialog d; d.show (); QDateTime time = QDateTime::currentDateTime (); QDateTime currentTime = QDateTime::currentDateTime (); while (time.secsTo (currentTime) <= 5 ) { currentTime = QDateTime::currentDateTime (); a.processEvents (); }; Widget w; w.show ();#ifdef QT_DEBUG splash.finish (&w);#endif d.finish (&w); return a.exec (); }
dialog.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 #ifndef DIALOG_H #define DIALOG_H #include <QDialog> namespace Ui {class Dialog ; }class Dialog : public QDialog { Q_OBJECTpublic : explicit Dialog (QWidget *parent = nullptr ) ; ~Dialog (); void finish (QWidget *mainWin) ;private : Ui::Dialog *ui; };#endif
dialog.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 #include "dialog.h" #include "ui_dialog.h" Dialog::Dialog (QWidget *parent) : QDialog (parent), ui (new Ui::Dialog) { ui->setupUi (this ); 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 (); } close (); }
dialog.ui
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 <?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>