diff options
Diffstat (limited to 'examples/qml/editingmodel')
-rw-r--r-- | examples/qml/editingmodel/MovingRectangle.qml | 78 | ||||
-rw-r--r-- | examples/qml/editingmodel/doc/editingmodel.rst | 16 | ||||
-rw-r--r-- | examples/qml/editingmodel/doc/qabstractlistmodelqml.png | bin | 0 -> 45810 bytes | |||
-rw-r--r-- | examples/qml/editingmodel/main.py | 21 | ||||
-rw-r--r-- | examples/qml/editingmodel/main.pyproject | 3 | ||||
-rw-r--r-- | examples/qml/editingmodel/main.qml | 106 | ||||
-rw-r--r-- | examples/qml/editingmodel/model.py | 156 |
7 files changed, 380 insertions, 0 deletions
diff --git a/examples/qml/editingmodel/MovingRectangle.qml b/examples/qml/editingmodel/MovingRectangle.qml new file mode 100644 index 000000000..b99a5f4dc --- /dev/null +++ b/examples/qml/editingmodel/MovingRectangle.qml @@ -0,0 +1,78 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + + +import QtQuick +import QtQuick.Controls + +Rectangle { + id: root + property int modelIndex + property Item dragParent + property Item sizeParent + property alias text: zone.text + property alias bgColor: root.color + + anchors { + horizontalCenter: parent.horizontalCenter + verticalCenter: parent.verticalCenter + } + color: backgroundColor + anchors.fill: sizeParent + border.color: "yellow" + border.width: 0 + TextArea { + id: zone + anchors.centerIn: parent + text: display + onTextChanged: model.edit = text + } + + MouseArea { + id: zoneMouseArea + anchors.fill: parent + + acceptedButtons: Qt.MiddleButton + onClicked: function(mouse) { + if (mouse.button == Qt.MiddleButton) + lv.model.remove(index) + else + mouse.accepted = false + } + } + DragHandler { + id: dragHandler + xAxis { + + enabled: true + minimum: 0 + maximum: lv.width - droparea.width + } + yAxis.enabled: false + acceptedButtons: Qt.LeftButton + } + Drag.active: dragHandler.active + Drag.source: root + Drag.hotSpot.x: width / 2 + + states: [ + State { + when: dragHandler.active + ParentChange { + target: root + parent: root.dragParent + } + + AnchorChanges { + target: root + anchors.horizontalCenter: undefined + anchors.verticalCenter: undefined + } + PropertyChanges { + target: root + opacity: 0.6 + border.width: 3 + } + } + ] +} diff --git a/examples/qml/editingmodel/doc/editingmodel.rst b/examples/qml/editingmodel/doc/editingmodel.rst new file mode 100644 index 000000000..2b45b23f0 --- /dev/null +++ b/examples/qml/editingmodel/doc/editingmodel.rst @@ -0,0 +1,16 @@ +QAbstractListModel in QML +========================= + +.. tags:: Android + +This example shows how to add, remove and move items inside a QML +ListView, but showing and editing the data via roles using a +QAbstractListModel from Python. + +You can add new elements and reset the view using the two top buttons, +remove elements by 'middle click' the element, and move the elements +with a 'left click' plus dragging the item around. + +.. image:: qabstractlistmodelqml.png + :width: 400 + :alt: QAbstractListModel/ListView Screenshot diff --git a/examples/qml/editingmodel/doc/qabstractlistmodelqml.png b/examples/qml/editingmodel/doc/qabstractlistmodelqml.png Binary files differnew file mode 100644 index 000000000..6e181fba1 --- /dev/null +++ b/examples/qml/editingmodel/doc/qabstractlistmodelqml.png diff --git a/examples/qml/editingmodel/main.py b/examples/qml/editingmodel/main.py new file mode 100644 index 000000000..5240a9de0 --- /dev/null +++ b/examples/qml/editingmodel/main.py @@ -0,0 +1,21 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import sys +from pathlib import Path + +from PySide6.QtCore import QUrl +from PySide6.QtGui import QGuiApplication +from PySide6.QtQml import QQmlApplicationEngine + +from model import BaseModel # noqa: F401 + +if __name__ == "__main__": + app = QGuiApplication(sys.argv) + engine = QQmlApplicationEngine() + qml_file = Path(__file__).parent / "main.qml" + engine.load(QUrl.fromLocalFile(qml_file)) + + if not engine.rootObjects(): + sys.exit(-1) + sys.exit(app.exec()) diff --git a/examples/qml/editingmodel/main.pyproject b/examples/qml/editingmodel/main.pyproject new file mode 100644 index 000000000..71272a973 --- /dev/null +++ b/examples/qml/editingmodel/main.pyproject @@ -0,0 +1,3 @@ +{ + "files": ["model.py","main.qml","main.py","MovingRectangle.qml"] +} diff --git a/examples/qml/editingmodel/main.qml b/examples/qml/editingmodel/main.qml new file mode 100644 index 000000000..2318ae8b3 --- /dev/null +++ b/examples/qml/editingmodel/main.qml @@ -0,0 +1,106 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Controls +import QtQuick.Window +import BaseModel + +Window { + title: "Moving Rectangle" + width: 800 + height: 480 + visible: true + id: mainWindow + + Column { + spacing: 20 + anchors.fill: parent + id: mainColumn + Text { + padding: 20 + font.pointSize: 10 + width: 600 + wrapMode: Text.Wrap + text: "This example shows how to add, remove and move items inside a QML ListView.\n +It shows and edits data via roles using QAbstractListModel on the Python side.\n +Use the 'Middle click' on top of a rectangle to remove an item.\n +'Left click' and drag to move the items." + } + + Button { + anchors { + left: mainColumn.left + right: mainColumn.right + margins: 30 + } + text: "Reset view" + onClicked: lv.model.reset() + } + + Button { + anchors { + left: mainColumn.left + right: mainColumn.right + margins: 30 + } + text: "Add element" + onClicked: lv.model.append() + } + + ListView { + id: lv + anchors { + left: mainColumn.left + right: mainColumn.right + margins: 30 + } + + height: 200 + model: BaseModel {} + orientation: ListView.Horizontal + displaced: Transition { + NumberAnimation { + properties: "x,y" + easing.type: Easing.OutQuad + } + } + delegate: DropArea { + id: droparea + width: ratio * lv.width + height: lv.height + + onEntered: function (drag) { + let dragindex = drag.source.modelIndex + if (index === dragindex) + return + lv.model.move(dragindex, index) + } + + MovingRectangle { + modelIndex: index + dragParent: lv + sizeParent: droparea + } + } + + MouseArea { + id: lvMousearea + anchors.fill: lv + z: -1 + } + Rectangle { + id: lvBackground + anchors.fill: lv + anchors.margins: -border.width + color: "white" + border.color: "black" + border.width: 5 + z: -1 + } + Component.onCompleted: { + lv.model.reset() + } + } + } +} diff --git a/examples/qml/editingmodel/model.py b/examples/qml/editingmodel/model.py new file mode 100644 index 000000000..02a1e5717 --- /dev/null +++ b/examples/qml/editingmodel/model.py @@ -0,0 +1,156 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + + +from PySide6.QtCore import QAbstractListModel, QByteArray, QModelIndex, Qt, Slot +from PySide6.QtGui import QColor +from PySide6.QtQml import QmlElement + +# To be used on the @QmlElement decorator +# (QML_IMPORT_MINOR_VERSION is optional) +QML_IMPORT_NAME = "BaseModel" +QML_IMPORT_MAJOR_VERSION = 1 + + +@QmlElement +class BaseModel(QAbstractListModel): + + RatioRole = Qt.UserRole + 1 + + def __init__(self, parent=None): + super().__init__(parent=parent) + self.db = [] + + def rowCount(self, parent=QModelIndex()): + return len(self.db) + + def roleNames(self): + default = super().roleNames() + default[self.RatioRole] = QByteArray(b"ratio") + default[Qt.BackgroundRole] = QByteArray(b"backgroundColor") + return default + + def data(self, index, role: int): + if not self.db: + ret = None + elif not index.isValid(): + ret = None + elif role == Qt.DisplayRole: + ret = self.db[index.row()]["text"] + elif role == Qt.BackgroundRole: + ret = self.db[index.row()]["bgColor"] + elif role == self.RatioRole: + ret = self.db[index.row()]["ratio"] + else: + ret = None + return ret + + def setData(self, index, value, role): + if not index.isValid(): + return False + if role == Qt.EditRole: + self.db[index.row()]["text"] = value + return True + + @Slot(result=bool) + def append(self): + """Slot to append a row at the end""" + return self.insertRow(self.rowCount()) + + def insertRow(self, row): + """Insert a single row at row""" + return self.insertRows(row, 0) + + def insertRows(self, row: int, count, index=QModelIndex()): + """Insert n rows (n = 1 + count) at row""" + + self.beginInsertRows(QModelIndex(), row, row + count) + + # start database work + if len(self.db): + newid = max(x["id"] for x in self.db) + 1 + else: + newid = 1 + for i in range(count + 1): # at least one row + self.db.insert( + row, {"id": newid, "text": "new", "bgColor": QColor("purple"), "ratio": 0.2} + ) + # end database work + self.endInsertRows() + return True + + @Slot(int, int, result=bool) + def move(self, source: int, target: int): + """Slot to move a single row from source to target""" + return self.moveRow(QModelIndex(), source, QModelIndex(), target) + + def moveRow(self, sourceParent, sourceRow, dstParent, dstChild): + """Move a single row""" + return self.moveRows(sourceParent, sourceRow, 0, dstParent, dstChild) + + def moveRows(self, sourceParent, sourceRow, count, dstParent, dstChild): + """Move n rows (n=1+ count) from sourceRow to dstChild""" + + if sourceRow == dstChild: + return False + + elif sourceRow > dstChild: + end = dstChild + + else: + end = dstChild + 1 + + self.beginMoveRows(QModelIndex(), sourceRow, sourceRow + count, QModelIndex(), end) + + # start database work + pops = self.db[sourceRow: sourceRow + count + 1] + if sourceRow > dstChild: + self.db = ( + self.db[:dstChild] + + pops + + self.db[dstChild:sourceRow] + + self.db[sourceRow + count + 1:] + ) + else: + start = self.db[:sourceRow] + middle = self.db[dstChild: dstChild + 1] + endlist = self.db[dstChild + count + 1:] + self.db = start + middle + pops + endlist + # end database work + + self.endMoveRows() + return True + + @Slot(int, result=bool) + def remove(self, row: int): + """Slot to remove one row""" + return self.removeRow(row) + + def removeRow(self, row, parent=QModelIndex()): + """Remove one row at index row""" + return self.removeRows(row, 0, parent) + + def removeRows(self, row: int, count: int, parent=QModelIndex()): + """Remove n rows (n=1+count) starting at row""" + self.beginRemoveRows(QModelIndex(), row, row + count) + + # start database work + self.db = self.db[:row] + self.db[row + count + 1:] + # end database work + + self.endRemoveRows() + return True + + @Slot(result=bool) + def reset(self): + self.beginResetModel() + self.resetInternalData() # should work without calling it ? + self.endResetModel() + return True + + def resetInternalData(self): + self.db = [ + {"id": 3, "bgColor": QColor("red"), "ratio": 0.15, "text": "first"}, + {"id": 1, "bgColor": QColor("blue"), "ratio": 0.1, "text": "second"}, + {"id": 2, "bgColor": QColor("green"), "ratio": 0.2, "text": "third"}, + ] |