Qt for Python中SplashScreen的用法

做桌面程序的时候,大家一般都会想在程序启动时怎么可以更友好,特别是python启动慢的时候,一直等着跟个傻子一样。这时候就用到Qt提供的控件QSplashScreen,除了能提升程序响应(避免因加载东西过多导致程序半天起不来),还能显得更专业。

Qt Widgets中的QSplashScreen,就是专门干这个事情的。在Qt Quick程序中我们同样可以用这个类,方法是先启动一个QSplashScreen,然后用QQuickView加载你的主QML文件,加载完毕后,关掉QSplashScreen。但该方法必须带上Qt5Widgets这个模块,dll个头不小,release版的都要4M多。

那如何用Qt Quick来实现Splash Screen呢?下面就是我们现在用的方案。

第一种方案:在python中定义SplashScreen

我现在是这样的,觉得暂时没有问题,速度上也影响不大,但是不知道是不是真的合理

    app = QApplication(sys.argv)
    splash = QSplashScreen(QPixmap("logo//logo.jpg?x-oss-process=image/watermark,g_center,image_YXJ0aWNsZS9wdWJsaWMvd2F0ZXJtYXJrLnBuZz94LW9zcy1wcm9jZXNzPWltYWdlL3Jlc2l6ZSxQXzQwCg==,t_20"))
    splash.show()
    splash.showMessage("正在加载图片资源……", Qt.AlignLeft, Qt.red)
    time.sleep(1)
    splash.showMessage("正在加载音频资源……", Qt.AlignLeft, Qt.red)
    time.sleep(1)
    splash.showMessage("正在加载摄像头资源……", Qt.AlignLeft, Qt.red)
    time.sleep(1)

    engine = QQmlApplicationEngine()
    engine.load(os.path.join(os.path.dirname(__file__), "main.qml"))
    splash.close()
    sys.exit(app.exec_())

第二种方案:

定义Splash Screen

首先当然是定义我们的Splash Screen。现在我们用一个QML文件来实现。创建一个QML文件,取名Splash.qml,派生自Window,内部内容可以自由发挥,一般主要部件是一个Image,然后你可以放上Text等其他控件来显示程序启动进度。

需要注意的是:

  1. 背景color属性要设置为transparent,这样才能够将图片中的alpha通道的作用体现出来;
  2. flags设置为Qt.SplashScreen | Qt.WindowStaysOnTopHint,因为Splash Screen一定是显示在程序最前面的;
  3. xy要根据当前屏幕尺寸以及我们图片尺寸进行适配计算,好让我们的Splash Screen刚好显示在屏幕正中间;
  4. 别忘了导入Window模块,即import QtQuick.Window 2.3
  5. 为了更好的体验,一般主程序显示出来后Splash Screen还要待1s,这需要设置一个定时器。

下面就是Splash.qml

import QtQuick 2.7
import QtQuick.Window 2.3
import QtQuick.Controls 2.2

Window {
    id: splash
    color: "transparent"
    title: "Splash Window"
    modality: Qt.ApplicationModal
    flags: Qt.SplashScreen | Qt.WindowStaysOnTopHint
    x: (Screen.width - splashImage.width) / 2
    y: (Screen.height - splashImage.height) / 2
    width: splashImage.width
    height: splashImage.height

	property alias timer: timer

    Image {
        id: splashImage
        source: "qrc:/res/splashscreen.png?x-oss-process=image/watermark,g_center,image_YXJ0aWNsZS9wdWJsaWMvd2F0ZXJtYXJrLnBuZz94LW9zcy1wcm9jZXNzPWltYWdlL3Jlc2l6ZSxQXzQwCg==,t_20"
    }
	Text{
		id: textCtrl
		width: contentWidth
		height: contentHeight
		anchors{left: splashImage.left; bottom: splashImage.bottom}
		font{pointSize: 30}
	}

    Timer {
		id: timer
        interval: 1000; running: false; repeat: false
        onTriggered: {
			splash.visible = false;
        }
    }

	function delay(){
		timer.start();
	}

    Component.onCompleted: visible = true
}

显示Splash Screen并异步加载主窗体

接下去要显示Splash Screen了。回归初心,我们做这个Splash Screen是为了在主程序加载的时候显示给用户看,让用户知道“稍等,我们的程序已经在拼命启动了”。所以还需要异步加载主窗体。这个通过使用Loader加载主窗体QML文件,并将它的asynchronous设置为true来实现的。main.qml示例代码如下:

import QtQuick 2.7
import QtQuick.Window 2.3
import QtQuick.Controls 2.2

QtObject {
	id: root

    property QtObject splashScreen: Splash{}

	property var loader: Loader{
        asynchronous: true
        source: "qrc:/MainView.qml"
        active: false
        onLoaded: {
            splashScreen.delay();
        }
    }

	Component.onCompleted:{
        loader.active = true;
    }
}

注意,加载器Loader在定义时active属性要设置为false,表示我们不希望它一开始就自动加载。我们希望Splash Screen以最快的速度显示,而Loader同步加载的话肯定会对此有影响。

Component.onCompleted会在我们的root对象构造完成时调用,此时根据Qt QML的文档,root的所有属性都应该构造完毕,包括我们的Splash Screen$splashScreen和加载器loader,说明此时Splash Screen已经显示出来了,然后我们将loaderactive属性设为true,OK,真正开始主程序的异步加载。

当加载完毕后,Loader会发送loaded信号。我们在响应函数onLoaded中调用$splashScreen.delay()即可。

主窗体

主窗可以是ApplicationWindow或者Window

import QtQuick 2.6
import QtQuick.Window 2.3
import QtQuick.Controls 2.2

ApplicationWindow {
	id: window
    visible: true
    width: 800
    height: 600
    title: qsTr("Splash Demo")
    flags: Qt.Window | Qt.FramelessWindowHint

    Button{
        anchors{top: parent.top; right: parent.right;margins: 5}
        text: "X"
        width: 50
        height: 50
        onClicked: Qt.quit();
    }

    Text{
        text: qsTr("Test window");
        anchors.centerIn: parent
        font.pointSize: 30
    }
    Component.onCompleted: window.show()
}

注意两点:

  1. flags属性要带上Qt.FramelessWindowHint,否则会发现启动的时候,主窗体会先显示在Splash Screen之前、再退到Splash Screen之后的问题;
  2. Component.onCompleted里手动调用show()显示出来,否则在某些平台上会有主窗体不显示的问题。

加载起始QML文件

最后一步,其实是最开始的时候,就是在main函数中加载我们的main.qml。主要代码如下:

  QGuiApplication app(argc, argv);
  auto engine = new QQmlApplicationEngine();
  engine->load(QUrl(QStringLiteral("qrc:/main.qml")));
  

QGuiApplication意味着我们发布时只需要带上Qt5Core和Qt5Gui就可以了,不需要Qt5Widgets。

问题

目前的实现也有些问题。只有当主窗体为Frameless时才能保证Splash Screen一直在程序最前面,否则会短暂地被主窗体抢前。但无边框的窗体就没有了各操作系统基于窗体的边框效果,例如阴影、Aero等。网上有针对该问题的解决方法,但我没有尝试。

声明:本内容为作者独立观点,不代表电子星球立场。未经允许不得转载。授权事宜与稿件投诉,请联系:editor@netbroad.com
觉得内容不错的朋友,别忘了一键三连哦!
赞 3
收藏 2
关注 17
成为作者 赚取收益
全部留言
0/200
成为第一个和作者交流的人吧