2017-02-16 9 views
3

QMLはかなり混乱しています。数週間後、私はQMLを使って動画のアノテーションのタイムラインを実装しようとしています。私はQMLが新しくなって以来、実際にそれを働かせることはできません。QAQueryItemModel with QtQuick:インデックスの列は常に0です

私はあなたの問題を解決しようとします。これは、タイムラインがどのように表示されるかの例です。 Timeline example 私は、異なるトラックを持っています。最初から最後までさまざまなアノテーションを保存しています。ビデオには、そのトラックのアノテーションが含まれています。たとえば、晴れた画像を含むビデオのすべてのシーンに注釈を付けると、すべての注釈ボックスは、ビデオが晴れた画像を持つシーンをマークします。

私はこの情報をXMLファイルなどで保存して取得する予定です。おそらく例は次のようになります。

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); 
     } 
    } 
} 

<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> 

私は後で使用できるモデルにデータを取得するには、私はこのような方法でファイルを解析しますTrackItemがその子を持つQListを保持している場合、QMapは格納されたデータと、おそらく親フォームタイプのTrackItemを持ちます。私のデータは親を持たないルートTrackItemオブジェクトを持つツリーのように見えます。データには長さとファイル名が格納され、子オブジェクトには異なるトラックのTrackItemが格納されています。 トラック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; 

データ機能は次のように見えます。

データ(constのQModelIndex &インデックス、int型の役割):

if (index.isValid()) { 
    TrackItem *item = static_cast<TrackItem*>(index.internalPointer()); 
    if (item) 
     return item; 
} 
return rootItem; 

とTrackItem ::データ(QStringのキー)がデータを返す:のgetItem(定数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(); 

TrackItemのQMap itemDataに格納されます。

インデックス(int型の列、int型の列、constの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ファイルがあります。このQMLファイルは、上記の例の外観を表す自分のQWidgetのコンストラクタで設定します。QWidgetから派生

TimelineWidgetコンストラクタ:

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を作成し、QMLコンテキストにcontectプロパティとして設定されているので、私は私のモデルを使用することができますQML。

私のタイムラインのQMLファイルは基本的に2つの列を含む矩形です。最初は、上記のコンテキストプロパティ "trackmodel"上のリピータを経由して、トラックの名前でトラックヘッドを作成します。私は基本的によりscrollViewの私のトラックの各作成2番目の列に

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ファイルになりました。各トラックは単に、新しい項目を作成しようとする矩形でもあり、注釈を表すはずです。ここでも私はデリゲートを使用しようとします。このDelegateModelと

Item { 
    Repeater { id: annotationRepeater; model: trackModel } 
} 

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は、トラック内の別のフレームまたは他のトラック全体に移動するための操作の可能性がある別のRectangleです。トリミングやその他の操作が可能です。

最後に私の問題に。私は上記の例のようにタイムラインを構築することができました。この例の黄色いボックスはgimpでペイントされています。 QModelIndexの作成方法がわからないため、注釈を表示できません。私はQAbstractItemModelのデータ関数に入るたびに、ルートの後のレイヤーからTrackItemを取得することができたので、トラックレイヤーだけでした。どのようにしてQtQuickはQAbstractItemModelと通信しますか?インデックス関数で行と列を取得し、各TrackItemに固有のインデックスを作成できると思って、データ関数内のgetItem関数を使って適切なTrackItemを取得できるようになりました。何らかの理由で列はインデックス関数で常に0です。どのレイヤー(ルート、トラック、またはアノテーション)を私のモデルに教えて、QMLでデリゲートで適切なデータを取得できるようにするか? 私の問題が十分にはっきりしていることを願っています。これは私の最初の投稿ですので、それが長いかどうかはフォームの場合は謝罪するかもしれません。私は本当に、誰かがこの問題で私を助けることができれば嬉しいです。

+2

あなたの質問は非常に整形されており、多くの情報を持っていますが、タイトルが悪いです。 「インデックス関数では列は常に0です。」これは単なる提案であり、あなたの投稿はとにかく良いです。 – leparlon

+1

はい、そうです。QMLはモデルの最初の列のみを使用します。ただし、トラックごとに1つの役割を持つモデルを提供することもできます。このモデルには、データを含むオブジェクトが含まれています。別の考え方は、あなたのモデルを['ProxyModel'](http://doc.qt.io/qt-5/qabstractproxymodel.html)に通して列を並べ替えることです。最初の人。 – derM

+0

QMLの['TableModel'](http://doc.qt.io/qt-5/qml-qtquick-controls-tableview.html)でも、モデルの1つの列だけが使用されます。これはビュー列ごとに最初のモデル列に1つの 'role'を使います。 – derM

答えて

0

「編集可能なツリーモデル」の例を再実装すると同じ問題が発生しました。私は私のモデルに2列を持っています。 2番目の列をクリックすると、selection.currentIndex.columnは常に0を返します。したがって、styleData.index.columnプロパティも返されました。しかし、styleData.columnは2番目の列をクリックしたときに私に1を与えました。そこで私はQMLで明示的に必要なインデックスを作成しました。その後

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

私はそれが何をすべきかを行うために私のsetData機能を持って - モデルの2列目の値を変更します。 これは一種のハックですが、まだ動作しています。私はそれが誰かにとって役に立つことを願っています。

P.S.編集可能なモデル例 と私の実装との唯一の大きな違いは、彼らは私がqmlRegisterTypeを通じて私のモデルを使用してい

view->setModel(model) 

によってC++側からモデルを使用していることです。それは私が考えることができる唯一の違いです。

関連する問題