QT QML学习(二)

QT QML学习(二)

学习资料:
1、W:\Download\565672 Qt Quick核心编程.pdf,安晓辉。

安晓辉的CSDN

2、在IMX6.0,飞凌嵌入式开发板上验证学习。GST API,界面框架用QT5 QML。
编译环境,ubuntu18.04。
qt5官方源码工程:/opt/Qt5.9.4/5.9.4/Src/qtmultimedia/examples/multimedia/video/qmlvideo

QML官方实例

本地视频播放实例

源码路径:/opt/Qt5.9.4/5.9.4/Src/qtmultimedia/examples/multimedia/video/qmlvideo

1、main.cpp

    QQuickView viewer;
    viewer.setSource(QUrl("qrc:///qml/qmlvideo/main.qml"));
    QObject::connect(viewer.engine(), SIGNAL(quit()), &viewer, SLOT(close()));
    
        QQuickItem *rootObject = viewer.rootObject();
    rootObject->setProperty("source1", url1);
    QMetaObject::invokeMethod(rootObject, "init");//c++中使用qml的init函数

2、main.qml

import QtQuick 2.0

Rectangle {
...
	    Loader {
        id: performanceLoader

        Connections {
            target: inner
            onVisibleChanged:
                if (performanceLoader.item)
                    performanceLoader.item.enabled = !inner.visible
            ignoreUnknownSignals: true
        }

        function init() {
            var enabled = root.perfMonitorsLogging || root.perfMonitorsVisible
            source = enabled ? "../performancemonitor/PerformanceItem.qml" : ""
        }

        onLoaded: {
            item.parent = root
            item.anchors.fill = root
            item.logging = root.perfMonitorsLogging
            item.displayed = root.perfMonitorsVisible
            item.enabled = false
            item.init()
        }
    }
    
    Rectangle {
        id: inner
        ...
        SceneSelectionPanel {
        ...
        }
	}
...
}

其中Item为Loader的属性之一,

item : object
This property holds the top-level object that is currently loaded.
Since QtQuick 2.0, Loader can load any object type.
init为Loader定义的函数。


3、SceneSelectionPanel.qml

mport QtQuick 2.0

Rectangle {
    id: root
    property int itemHeight: 25
    property string sceneSource: ""

    ListModel {
        id: videolist
        ListElement { name: "Multi"; source: "SceneMulti.qml" }
        ListElement { name: "Video"; source: "VideoBasic.qml" }
        ...
	ListModel {
        id: cameralist
        ListElement { name: "Camera"; source: "CameraBasic.qml" }
        ListElement { name: "Drag"; source: "CameraDrag.qml" }
        ...
        }
		...
    Component {
        id: rightDelegate
        Item {
            width: root.width / 2
            height: 0.8 * itemHeight

            Button {
                anchors.fill: parent
                anchors.margins: 5
                anchors.leftMargin: 2.5
                anchors.bottomMargin: 0
                text: name
                onClicked: root.sceneSource = source//设置主界面应该加载的页面
            }
        }
    }

注意MVC的设计结构,以点击Video为测试例子。
VideoBasic.qml---->SceneBasic.qml---->Scene.qml---->Rectangle的层层抽象


4、VideoBasic.qml

import QtQuick 2.0

SceneBasic {
    contentType: "video"
}

整个文件只设置了一个自定义的属性。
SceneBasic.qml

Scene {
    id: root
    property string contentType
    property bool autoStart: false
    property bool started: false

    Content {
        id: content
        autoStart: parent.autoStart
        contentType: parent.contentType//上层向下层,层层传递的属性设置
        onVideoFramePainted: root.videoFramePainted()//下层向上层,层层传递的信号
        ...
        }

由以上文件的设计可以发现,设置是由UI上层逐层向下的设置的,UI为最上层的表示设置一路到底层。信号是由底层逐层向上发送的,最低层的信号主动触发一路通知上层事件的发生。
此qml又包含了两个自定义的类型:Scene和Content。


5、Scene.qml

import QtQuick 2.0

Rectangle {
    id: root
    color: "black"
    property alias buttonHeight: closeButton.height
    property string source1
    property string source2
    property int contentWidth: 640 / 2
    property real volume: 0.25
    property int margins: 5
    property QtObject content

    signal close
    signal videoFramePainted

    Button {
        id: closeButton
        anchors {
            top: parent.top
            right: parent.right
            margins: root.margins
        }
        width: Math.max(parent.width, parent.height) / 12
        height: Math.min(parent.width, parent.height) / 12
        z: 2.0
        bgColor: "#212121"
        bgColorSelected: "#757575"
        textColorSelected: "white"
        text: "Back"
        onClicked: root.close()
    }
}

内容比较少除了显示样式和自定义属性外就只有一个返回按钮。
其中property QtObject content属性
其中signal videoFramePainted信号
这两个值得关注


6、Content.qml

此为内容的核心,业务都抽象在这个文件中了。

Rectangle {
    id: root
    border.color: "white"
    border.width: showBorder ? 1 : 0
    color: "transparent"
    property string contentType // "camera" or "video"
    property string source
    ...
	Loader {
        id: contentLoader
    }
    //动态显示帧率的小窗
	Loader {
        id: frameRateLoader
        source: root.showFrameRate ? "../frequencymonitor/FrequencyItem.qml" : ""
        onLoaded: {
            item.parent = root
            item.anchors.top = root.top
            item.anchors.right = root.right
            item.anchors.margins = 10
        }
    }
    ...
    //支持video和camera两种业务,可惜开发板上camera业务gst元件不支持。此用例只支持video的相关按键
	function initialize() {
        if ("video" == contentType) {
            contentLoader.source = "VideoItem.qml"
            if (Loader.Error == contentLoader.status) {
                contentLoader.source = "VideoDummy.qml"
                dummy = true
            }
            contentLoader.item.volume = volume
        } else if ("camera" == contentType) {
            contentLoader.source = "CameraItem.qml"
            if (Loader.Error == contentLoader.status) {
                contentLoader.source = "CameraDummy.qml"
                dummy = true
            }
        } else {
            console.log("[qmlvideo] Content.initialize: error: invalid contentType")
        }
        ...
    function start() {
        if (contentLoader.item) {
            if (root.contentType == "video")
                contentLoader.item.mediaSource = root.source
            contentLoader.item.start()
            root.started = true
        }
    }
    ...

此qml又指向了新的自定义文件VideoItem.qml


7、VideoItem.qml

import QtQuick 2.0
import QtMultimedia 5.0

VideoOutput {
    id: root
    height: width
    source: mediaPlayer

    property alias duration: mediaPlayer.duration
    property alias mediaSource: mediaPlayer.source
    property alias metaData: mediaPlayer.metaData
    property alias playbackRate: mediaPlayer.playbackRate
    property alias position: mediaPlayer.position
    property alias volume: mediaPlayer.volume

    signal sizeChanged
    signal fatalError

    onHeightChanged: root.sizeChanged()

    MediaPlayer {
        id: mediaPlayer
        autoLoad: false
        loops: Audio.Infinite

        onError: {
            if (MediaPlayer.NoError != error) {
                console.log("[qmlvideo] VideoItem.onError error " + error + " errorString " + errorString)
                root.fatalError()
            }
        }
    }

    function start() { mediaPlayer.play() }
    function stop() { mediaPlayer.stop() }
    function seek(position) { mediaPlayer.seek(position); }
}

MediaPlayer类型正是本项目中需要使用的类型,在IMX6.0开发板上其底层依赖为GST元件。MediaPlayer中QT 应该做了差异化的封装区分了不同硬件平台环境。
下一节分析MediaPlayer的底层实现,以借鉴GST的封装方法。

posted @ 2021-02-02 16:24  wuya178  阅读(254)  评论(0)    收藏  举报