我很困惑於QML。自從幾個星期以來,我嘗試使用QML在視頻中實現註釋內容的時間軸,但我無法真正實現它,因爲我對QML很陌生。QAbstractItemModel與QtQuick:列在索引中始終爲0
我試着讓你解決我的問題。這是一個例子,時間軸的外觀應該如何: Timeline example 我得到了不同的軌道,其中我存儲了不同的註釋,這些註釋只是表示從開始到結束點視頻包含給定軌道的註釋。例如,如果我爲包含晴朗圖像的視頻中的所有場景進行註釋,則每個註釋框都會標記視頻中有陽光圖像的場景。
我打算通過XML文件保存和獲取這些信息。一個可能的例子是:
<root length="800" filename="tralala.mp4">
<track name="sunny">
<annotation start="20" end="50"/>
<annotation start="70" end="120"/>
...
</track>
<track name="cloudy">
...
</track>
</root>
獲取數據到模型中,我可以在以後使用,我分析的文件,像這樣的方法:)
readModelFromXML(:
QFile xmlFile(_filename);
xmlFile.open(QIODevice::ReadOnly);
xml.setDevice(&xmlFile);
TrackItem* root;
while(!xml.atEnd() && !xml.hasError())
{
QXmlStreamReader::TokenType token = xml.readNext();
if(token == QXmlStreamReader::StartDocument)
continue;
if(token == QXmlStreamReader::StartElement)
{
if(xml.name() == "root")
{
QMap<QString, QVariant> itemData;
itemData["length"] = xml.attributes().value("length").toInt();
itemData["filename"] = xml.attributes().value("filename").toString();
root = new TrackItem(itemData);
}
else if(xml.name() == "track")
{
QMap<QString, QVariant> itemData;
itemData["name"] = xml.attributes().value("name").toString();
TrackItem* track = new TrackItem(itemData, root);
root->insertChildren(root->childCount(), track);
}
else if(xml.name() == "annotation")
{
QMap<QString, QVariant> itemData;
itemData["start"] = xml.attributes().value("start").toInt();
itemData["end"] = xml.attributes().value("end").toInt();
TrackItem* parent = root->child(root->childCount() - 1);
TrackItem* annotation = new TrackItem(itemData, parent);
parent->insertChildren(parent->childCount(), annotation);
}
}
}
如果TrackItem擁有QList及其子級,則包含存儲數據的QMap以及可能的父級表單類型TrackItem。因此,我的數據看起來很像一個帶有沒有父對象的根TrackItem對象的樹,因爲它存儲了長度和文件名的數據,並且它的子對象具有不同軌道的TrackItems。 跟蹤TrackItems比將根對象作爲其父項並且僅存儲軌道的名稱。每條曲目的開始和結束點都存儲爲itemData作爲子節點。
TrackItem.h:
public:
explicit TrackItem(QMap<QString, QVariant> &data, TrackItem *parent = 0);
~TrackItem();
some functions for getting childs, inserting childs and so on
private:
QList<TrackItem*> childItems;
QMap<QString, QVariant> itemData;
TrackItem *parentItem;
所以,現在我們正在越來越接近我的問題。我爲自己的QtQuick視圖做了自己的QAbstractItemModel實現。我自己的QAbstractItemModel目前有以下角色。
角色名():
QHash<int, QByteArray> roles;
roles[NameRole] = "name";
roles[StartFrameRole] = "startFrame";
roles[EndFrameRole] = "endFrame";
return roles;
的數據的功能如下所示。
數據(常量QModelIndex &指數,詮釋角色):
if (!index.isValid())
return QVariant();
TrackItem *item = getItem(index);
if (role == NameRole)
return item->data("name");
else if (role == StartFrameRole)
return item->data("start");
else if (role == EndFrameRole)
return item->data("end");
return QVariant();
用的getItem(常量QModelIndex &指數):
if (index.isValid()) {
TrackItem *item = static_cast<TrackItem*>(index.internalPointer());
if (item)
return item;
}
return rootItem;
和TrackItem ::數據(QString的鍵)返回數據存儲在TrackItem的QMap itemData中。
指數(INT行,INT列,常量QModelIndex &父):
if (parent.isValid() && parent.column() != 0)
return QModelIndex();
TrackItem *parentItem = getItem(parent);
TrackItem *childItem = parentItem->child(row);
if (childItem)
return createIndex(row, column, childItem);
else
return QModelIndex();
所以在指數我嘗試創建TrackItems的指標。
這對C++方面有很大的幫助。現在我將介紹我的QML代碼,並跟隨我的問題。所以在程序的QML方面,我有一個稱爲時間線的QML文件,我在我自己的QWidget的構造函數中設置了它,它代表了上面示例的外觀。
TimelineWidget構造從QWidget中派生:
sharedEngine_ = new QQmlEngine(this);
quickWidget_ = new QQuickWidget(sharedEngine_, this);
QQmlContext *context = quickWidget_->rootContext();
context->setContextProperty("timeline", this);
model_ = new TrackModel(":/resources/example.txt", context);
context->setContextProperty("trackmodel", model_);
quickWidget_->setSource(QUrl::fromLocalFile("qml/timeline.qml"));
ui->layout->addWidget(quickWidget_);
正如你所看到的,在這一點上我也正在創建化QAbstractItemModel,並將其設置爲contect屬性的QML上下文,所以我可以用我的模型QML。
所以基本上我的時間線QML文件是一個包含兩列的矩形。在第一個中,我通過上面的上下文屬性「trackmodel」通過中繼器創建了帶有軌道名稱的軌道頭。
Repeater {
id: headerRepeater
model: trackmodel
TrackHead {
label: model.name
width: headerWidth
height: 50
selected: false
}
}
在第二列中我基本上不是在滾動視圖創建我的每個曲目:
Item {
width: tracksContainer.width + headerWidth
height: headers.height + 30
Column {
id: tracksContainer
Repeater {
id: tracksRepeater
model: trackDelegateModel
}
}
}
這裏我用一個DelegateModel中,我嘗試建立單獨的軌道。
DelegateModel {
id: trackDelegateModel
model: trackmodel
Track {
model: trackmodel
trackId: index
height: 50
width: timelineLength
...
also here are some "slots"
}
}
所以現在到Track QML文件。每個軌道也是一個簡單的矩形,我嘗試創建新的項目,這應該表示註釋。在這裏我也嘗試使用委託。
Item {
Repeater { id: annotationRepeater; model: trackModel }
}
與此DelegateModel:
DelegateModel {
id: trackModel
Annotation {
myModel: model
trackIndex: trackId
height: 15
width: model.endFrame - model.startFrame
x: model.startFrame
y: 17.5
...
like before here are also some "slots"
}
}
所以在這一點上,我試圖抓住通過startFrame將和endFrame角色從化QAbstractItemModel信息來計算每個註釋的長度。 Annotation不僅僅是另一個矩形,它具有一些操作可能性,可以將它們移動到軌道中的另一個框架或整個其他軌道,修剪它們和其他一些東西。
現在終於到了我的問題。我可以像上面的例子那樣構建時間線。示例中的黃色框在gimp中繪製。我無法看到註釋,因爲我不明白QModelIndex是如何創建的。每次我進入QAbstractItemModel的數據函數時,我只能從根層後面的層獲取TrackItems,所以只需要軌道層。 QtQuick以何種方式與QAbstractItemModel進行通信?我認爲我得到了索引函數中的一行和一列,並且可以爲每個TrackItem創建唯一的索引,以便我可以在數據函數中使用getItem函數獲取正確的TrackItem。不知何故,索引函數中的列始終爲0。我如何告訴我的模型在哪一層(根,軌道或註釋)上,以便在QML中我可以在代表中獲取正確的數據? 我希望我的問題足夠清楚。這是我在這裏的第一篇文章,所以我可能會道歉,如果它是長或變形。我真的希望有人能幫助我解決這個問題。
你的問題格式很好,有很多信息,但標題不好。它讓人們更容易回答並在以後找到你的帖子,如果你的標題更多的是與你的問題有關,如: 「索引函數中的列總是0」這只是一個建議,你的帖子無論如何都是好的。希望你得到你的答案 – leparlon
是的,你是對的:QML只使用模型的第一列。然而,您可能會爲每個軌道提供一個角色的模型,其中包含具有數據的對象。另一個想法是,將模型傳遞給重新對列進行排序的['ProxyModel'](http://doc.qt.io/qt-5/qabstractproxymodel.html),所以你的願望總是以成爲第一個。 – derM
即使QML的['TableModel'](http://doc.qt.io/qt-5/qml-qtquick-controls-tableview.html)僅使用模型的一列。它在每個視圖列的第一個模型列中使用一個「角色」。 – derM