Solved Passing QObjectList from C++ to QML and back
-
Hello.
I try to pass QList<QObject*> from C++ to QML, reorder it in GridLayout and pass it back.
I have done first two steps here. But I can't deal with last step.
I have tried to pass model back to C++ like this:
Button { onClicked: gridEx.receiveData(grid.inputModel) }
Then I have checked the QList after reordering in GridLayout but it is not changed.
So I will be grateful for any suggestion.
-
When you reorder how are changing ordering in qlist back in c++ ?
-
@dheerendra, sorry but I don't understand the question.
-
You said that you are reordering the elements. This is done at Qml side. How are you updating back at c++ side ?
-
This is the question. I can't to deal with this.
I pass the list like this:
QList<QObject*> dataList; dataList.append(new Block("俄", QColor("purple"))); .. engine.rootContext()->setContextProperty("inputModel", QVariant::fromValue(dataList));.
Then I use it in QML like this:
GridViewFluid { dataModel: inputModel }
Then I can manipulate (reorder all elements in GridView. But as far as I know Qt the data model is separated from view. Thus while reordering I don't change the model. Just the view. I have checked this in C++ side by printing the list after reordering like this:
// main.qml Button { onClicked: receiver.receiveData() } // gridviewextension.cpp extern QList<QObject*> dataList; void GridViewExtension::receiveData() { ... }
-
When you use QVariant::fromvalue it returns the copy of the original value. So if you do anything in view it will not change in c++ side. Simply put u can't achieve this in current scenario. If the model is exposed as in mv framework then we can do something.
-
@dheerendra said in Passing QObjectList from C++ to QML and back:
When you use QVariant::fromvalue it returns the copy of the original value.
Thank You! I didn't know this. I should read the documentation more careful.
If the model is exposed as in mv framework then we can do something.
How to achieve this? I have tried to pass model from QML to C++ without success like this:
// main.qml Button { onClicked: receiver.receiveData(grid.dataModel) } // gridviewextension.cpp void GridViewExtension::receiveData(QList<QObject*> dataList) { ... }
-
Finally I have coped with this task.
First of all I have created custom model like this:
class Block { public: Block(QString data = QString(), QColor color = QColor()) : m_data(data) , m_color(color) {} QString data() const { return m_data; } QColor color() const { return m_color; } private: QString m_data; QColor m_color; }; class BlockModel : public QAbstractListModel { Q_OBJECT public: enum BlockRoles { DataRole = Qt::UserRole + 1, ColorRole }; BlockModel(QObject *parent = nullptr) : QAbstractListModel (parent) {} void addBlock(const Block &block) { beginInsertRows(QModelIndex(), rowCount(), rowCount()); m_blocks << block; endInsertRows(); } int rowCount(const QModelIndex &parent = QModelIndex()) const { Q_UNUSED(parent); return m_blocks.count(); } QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const { if (index.row() < 0 || index.row() >= m_blocks.count()) return QVariant(); const Block &block = m_blocks[index.row()]; if (DataRole == role) return block.data(); else if (ColorRole == role) return block.color(); return QVariant(); } Q_INVOKABLE void move(int from, int to) { if (from == to || from < 0 || from >= m_blocks.count() || to < 0 || to >= m_blocks.count()) return; /* * Read carefully: * - https://doc.qt.io/qt-5/qabstractitemmodel.html#beginMoveRows * - https://bugreports.qt.io/browse/QTBUG-6940 */ int destinationChild = to; if (destinationChild - from == 1) ++destinationChild; beginMoveRows(QModelIndex(), from, from, QModelIndex(), destinationChild); auto b = m_blocks.takeAt(from); m_blocks.insert(to, b); endMoveRows(); } protected: QHash<int, QByteArray> roleNames() const { QHash<int, QByteArray> roles; roles[DataRole] = "data"; roles[ColorRole] = "color"; return roles; } private: QVector<Block> m_blocks; };
Then I have passe it without QVariant (many thanks to @dheerendra) like this:
BlockModel dataList; dataList.addBlock(Block("1", QColor("purple"))); dataList.addBlock(Block("2", QColor("cyan"))); dataList.addBlock(Block("3", QColor("magenta"))); dataList.addBlock(Block("4", QColor("chartreuse"))); QQmlApplicationEngine engine; engine.rootContext()->setContextProperty("inputModel", &dataList);
And last I have used it in my GridView like this:
GridView { property var dataModel: ListModel{} onDataModelChanged: visualModel.model = dataModel id: root height: parent.height - 10 width: parent.width - 10 x: 5 y: 5 cellHeight: getSize() cellWidth: cellHeight displaced: Transition { NumberAnimation { properties: "x,y"; easing.type: Easing.OutQuad } } model: DelegateModel { id: visualModel model: parent.dataModel delegate: MouseArea { property int visualIndex: DelegateModel.itemsIndex id: delegateRoot height: root.cellHeight width: root.cellWidth drag.target: block Rectangle { id: block objectName: "Block" anchors { horizontalCenter: parent.horizontalCenter verticalCenter: parent.verticalCenter } height: root.cellHeight - 8 width: root.cellWidth - 8 radius: width / 10 color: model.color Text { anchors.fill: parent fontSizeMode: Text.Fit minimumPointSize: 8 font.pointSize: 1000 text: model.data horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter color: "white" } Drag.active: delegateRoot.drag.active Drag.source: delegateRoot Drag.hotSpot.x: width / 2 Drag.hotSpot.y: width / 2 states: [ State { when: block.Drag.active ParentChange { target: block parent: root } AnchorChanges { target: block; anchors.horizontalCenter: undefined anchors.verticalCenter: undefined } } ] } DropArea { anchors { fill: parent; margins: 15 } onEntered: dataModel.move(drag.source.visualIndex, delegateRoot.visualIndex) } } } function getSize() { var minSize = 25 if (model.count <= 0 || height <= 0 || width <= 0) return minSize var size = minSize var p = 0 for (var rows = 1, columns = model.count; rows <= model.count; ++rows, columns = 1 + (model.count - 1) / rows) { var s = Math.min(height / rows, width / columns) if (s > size) size = s if (s < p) return size else p = s } return size } }