Я довольно смущен 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 я мог бы получить нужные данные в делегатах? Надеюсь, моя проблема достаточно ясна. Это мой первый пост здесь, поэтому я могу извиниться, если он длинный или не в форме. Я действительно надеюсь, кто-то может помочь мне с этой проблемой.
Ваш вопрос очень хорошо сформирован и имеет много информации, но заголовок плохой. Это облегчает людям возможность отвечать и находить ваше сообщение позже, если ваш заголовок больше связан с вашей проблемой, например: «Столбец всегда 0 в функции индекса», это всего лишь предложение, ваше сообщение в любом случае хорошее. Надеюсь, вы получите ответ – leparlon
Да, вы правы: QML использует только первый столбец модели. Однако вы можете предоставить модель с одной ролью на дорожку, которая содержит объект с данными. Еще одна идея - передать вашу модель через ['ProxyModel'] (http://doc.qt.io/qt-5/qabstractproxymodel.html), который переупорядочивает столбцы, поэтому у вас всегда есть столбец вашего желания будь первым. – derM
Даже в ['TableModel'] (http://doc.qt.io/qt-5/qml-qtquick-controls-tableview.html) QML используется только один столбец модели. Он использует одну «роль» в столбце первой модели для столбца представления. – derM