2017-02-16 61 views
3

我很困惑於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中我可以在代表中獲取正確的數據? 我希望我的問題足夠清楚。這是我在這裏的第一篇文章,所以我可能會道歉,如果它是長或變形。我真的希望有人能幫助我解決這個問題。

+2

你的問題格式很好,有很多信息,但標題不好。它讓人們更容易回答並在以後找到你的帖子,如果你的標題更多的是與你的問題有關,如: 「索引函數中的列總是0」這只是一個建議,你的帖子無論如何都是好的。希望你得到你的答案 – leparlon

+1

是的,你是對的:QML只使用模型的第一列。然而,您可能會爲每個軌道提供一個角色的模型,其中包含具有數據的對象。另一個想法是,將模型傳遞給重新對列進行排序的['ProxyModel'](http://doc.qt.io/qt-5/qabstractproxymodel.html),所以你的願望總是以成爲第一個。 – derM

+0

即使QML的['TableModel'](http://doc.qt.io/qt-5/qml-qtquick-controls-tableview.html)僅使用模型的一列。它在每個視圖列的第一個模型列中使用一個「角色」。 – derM

回答

0

當重新實現「可編輯樹模型」示例時,我偶然發現了同樣的問題。我的模特里有2個colums。當我點擊第二列時,selection.currentIndex.column總是返回0,styleData.index.column屬性也一樣。但是當我點擊第二列時,styleData.column給了我1。所以我構建了我在QML中明確需要的索引。

var currentIndex = myTreeModel.index(styleData.index.row, styleData.column, styleData.index.parent) 
var success = myTreeModel.setData(currentIndex , loaderEditor.item.text, 2) 

之後,我得到我的setData函數來做它應該做的 - 在模型的第二列中更改一個值。 這是一種黑客,但它仍在工作。我希望這對某些人有用。

P.S.可編輯的典範 和我的執行之間的唯一顯著不同的是,他們使用的是從C++方面的模型通過

view->setModel(model) 

我使用過qmlRegisterType我的模型。這是我能想到的唯一區別。