QT QML学习(二)
QT QML学习(二)
学习资料:
1、W:\Download\565672 Qt Quick核心编程.pdf,安晓辉。
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的封装方法。

浙公网安备 33010602011771号