2017-02-16 14 views
3

QML로 인해 상당히 혼란 스럽습니다. 몇 주 후에 QML로 동영상에 주석을 달기위한 타임 라인을 구현하려고 시도했는데 QML을 처음 접했기 때문에 실제로 작동시키지 못했습니다.QAQualityItemModel 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는 루트 개체를 부모로 갖고 있으며 트랙 이름 만 저장합니다. 각 트랙에는 startData와 endData가 자식으로 저장된 주석이 있습니다.

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에는 현재 다음과 같은 역할이 있습니다.

roleNames()는 다음과 같이

QHash<int, QByteArray> roles; 
roles[NameRole] = "name"; 
roles[StartFrameRole] = "startFrame"; 
roles[EndFrameRole] = "endFrame"; 
return roles; 

데이터 기능을 보인다.

데이터 (CONST이 & 인덱스, INT 역할 QModelIndex) :

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

데이터를 반환 TrackItem :: 데이터 (QString 키)의 getItem과

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

을 (CONST는 & 인덱스를 QModelIndex) 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 측면에서 timeline이라는 QML 파일을 가지고 있습니다.이 QML 파일은 위 예제의 모양을 나타내는 자체 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를 작성하고 QML 컨텍스트에 CONTECT 특성으로 설정, 그래서 나는 내 모델을 사용할 수 있습니다

TimelineWidget 생성자는 QWidget에서 파생 된 QML.

기본적으로 내 타임 라인 QML 파일은 두 개의 열이 포함 된 사각형입니다. 첫 번째로 위에서 만든 컨텍스트 속성 "trackmodel"을 통해 리피터를 통해 트랙 이름이있는 트랙 헤드를 만듭니다. 두 번째 열에서

Repeater { 
    id: headerRepeater 
    model: trackmodel 
    TrackHead { 
     label: model.name 
     width: headerWidth 
     height: 50 
     selected: false 
    } 
} 

나는 본질적으로있는 ScrollView 내 각 트랙을 만드는 것보다 : 여기

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이며 트리밍 및 다른 것들을 트리밍합니다.

마침내 내 문제. 위의 예처럼 타임 라인을 만들 수 있습니다. 예제의 노란색 상자는 김프에서 칠해집니다. QModelIndex가 어떻게 생성되는지 이해할 수 없기 때문에 주석을 표시 할 수 없습니다. 매번 QAbstractItemModel의 데이터 함수로 들어갈 때마다 루트 레이어 다음에 트랙 항목 만 가져올 수 있으므로 트랙 레이어 만 사용할 수 있습니다. QtQuick은 QAbstractItemModel과 어떤 식으로 통신합니까? 인덱스 함수에서 행과 열을 가져 와서 각 TrackItem에 대해 고유 한 인덱스를 만들 수 있다고 생각하여 데이터 함수의 getItem 함수를 사용하여 적절한 TrackItem을 가져올 수있었습니다. 인덱스 함수에서 어떻게 든 column은 항상 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)조차도 모델의 한 컬럼만을 사용합니다. 뷰 컬럼 당 첫 번째 모델 컬럼에 하나의'role'을 사용합니다. – derM

답변

0

"Editable Tree model"예제를 다시 구현할 때 동일한 문제가 발생했습니다. 내 모델에는 2 개의 칼럼이 있습니다. 두 번째 열을 클릭하면 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.편집 가능한 모델 예를 내 구현의 유일한 큰 차이가 나는 qmlRegisterType를 통해 내 모델을 사용하고

view->setModel(model) 

에 의해 C++ 측면에서 모델을 사용하는 것입니다. 내가 생각할 수있는 유일한 차이점입니다.