2017-02-16 14 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; 

Итак, теперь мы становимся ближе к моей проблеме. Я сделал свою собственную реализацию QAbstractItemModel для связи с моим представлением QtQuick. Моя собственная QAbstractItemModel в настоящее время имеет следующие роли.

roleNames():

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

Функции данных выглядит следующим образом.

данных (Const 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 (Const QModelIndex & индекса):

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

и TrackItem :: данные (ключевыми QString) возвращающийся данные хранится в элементе данных QMap объекта TrackItem.

индекс (интермедиат строка, столбец 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, называемый временной шкалой, который я установил в конструкторе моего собственного 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 
    } 
} 

Во второй колонке я по существу не создавать каждый из моих треков в 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" 
    } 
} 

Итак, теперь к файлу QML Track. Каждый трек также представляет собой просто прямоугольник, в котором я пытаюсь создать новые элементы, которые должны представлять аннотации. Здесь я также пытаюсь использовать делегат.

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

Поэтому на данном этапе я стараюсь, чтобы захватить информацию из QAbstractItemModel через роли и endFrame начального кадра, чтобы вычислить длину каждой аннотации. Аннотации, чем просто другой прямоугольник с некоторыми возможностями манипуляции, чтобы переместить их в другой кадр на треке или на весь другой трек, обрезать их и некоторые другие вещи.

Теперь, наконец, к моей проблеме. Я мог бы построить график времени, как в приведенном выше примере. Желтые ящики в примере, где окрашены в gimp. Я не могу получить аннотации, потому что я не понимаю, как создается QModelIndex. Каждый раз, когда я вхожу в функцию данных QAbstractItemModel, я могу получить только TrackItems из слоя после корня, так что просто слой трека. Как QtQuick взаимодействует с QAbstractItemModel? Я думал, что получаю строку и столбец в функции индекса и могу создавать уникальные индексы для каждого TrackItem, чтобы я мог захватить правильный TrackItem с помощью функции getItem в моей функции данных. Столбец почему-то всегда равен 0 в функции индекса. Как сообщить моей модели о том, на каком слое (корень, трек или аннотация) я, так что в QML я мог бы получить нужные данные в делегатах? Надеюсь, моя проблема достаточно ясна. Это мой первый пост здесь, поэтому я могу извиниться, если он длинный или не в форме. Я действительно надеюсь, кто-то может помочь мне с этой проблемой.

+2

Ваш вопрос очень хорошо сформирован и имеет много информации, но заголовок плохой. Это облегчает людям возможность отвечать и находить ваше сообщение позже, если ваш заголовок больше связан с вашей проблемой, например: «Столбец всегда 0 в функции индекса», это всего лишь предложение, ваше сообщение в любом случае хорошее. Надеюсь, вы получите ответ – leparlon

+1

Да, вы правы: QML использует только первый столбец модели. Однако вы можете предоставить модель с одной ролью на дорожку, которая содержит объект с данными. Еще одна идея - передать вашу модель через ['ProxyModel'] (http://doc.qt.io/qt-5/qabstractproxymodel.html), который переупорядочивает столбцы, поэтому у вас всегда есть столбец вашего желания будь первым. – derM

+0

Даже в ['TableModel'] (http://doc.qt.io/qt-5/qml-qtquick-controls-tableview.html) QML используется только один столбец модели. Он использует одну «роль» в столбце первой модели для столбца представления. – derM

ответ

0

Я наткнулся на ту же проблему, повторив пример «Редактируемая модель дерева». У меня 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) 

После этого я получил свою функцию УстановитьДанной, чтобы делать то, что он должен сделать - изменить значение во втором столбце модели. Это своего рода хак, но все же он работает. Надеюсь, это может быть полезно для кого-то.

P.S.Единственное существенное различие между редактируемой например моделью и моей реализацией является то, что они используют модель из стороны C++ с помощью

view->setModel(model) 

Я использую свою модель через qmlRegisterType. Это единственное различие, о котором я могу думать.