diff options
author | Friedemann Kleint <Friedemann.Kleint@qt.io> | 2022-08-09 11:30:47 +0200 |
---|---|---|
committer | Friedemann Kleint <Friedemann.Kleint@qt.io> | 2022-08-12 15:50:44 +0200 |
commit | 658f0bffaa7b84c1a1058c94e0175123283cfd81 (patch) | |
tree | 2e1abe7c0eb8d311729a6f8fde55b910faaad60b | |
parent | a324390fea8832db6ab806e5939a99f1feaf4796 (diff) |
Fix the modelview programming tutorial
Add some documentation examples and snippets to fix most outstanding
C++ to Python conversion errors.
The rest looks roughly ok and could be improved by further fixing up
the snippet conversion.
Task-number: PYSIDE-1984
Change-Id: I8c1bdcbc4a07847b2731ef7e2b9ba666cc00ccd6
Reviewed-by: Christian Tismer <tismer@stackless.com>
(cherry picked from commit 2d7a4fc710e59d0e766ffa101dba383eff46bb50)
6 files changed, 496 insertions, 1 deletions
diff --git a/sources/pyside6/doc/snippets/qtbase/examples/widgets/itemviews/spinboxdelegate/delegate_0.h.py b/sources/pyside6/doc/snippets/qtbase/examples/widgets/itemviews/spinboxdelegate/delegate_0.h.py new file mode 100644 index 000000000..de386a5ac --- /dev/null +++ b/sources/pyside6/doc/snippets/qtbase/examples/widgets/itemviews/spinboxdelegate/delegate_0.h.py @@ -0,0 +1,18 @@ +class SpinBoxDelegate(QStyledItemDelegate): + """A delegate that allows the user to change integer values from the model + using a spin box widget. """ + + def __init__(self, parent=None): + ... + + def createEditor(self, parent, option, index): + ... + + def setEditorData(self, editor, index): + ... + + def setModelData(self, editor, model, index): + ... + + def updateEditorGeometry(self, editor, option, index): + ... diff --git a/sources/pyside6/doc/snippets/qtbase/src/widgets/doc/snippets/stringlistmodel/model_0.h.py b/sources/pyside6/doc/snippets/qtbase/src/widgets/doc/snippets/stringlistmodel/model_0.h.py new file mode 100644 index 000000000..02a02aaf8 --- /dev/null +++ b/sources/pyside6/doc/snippets/qtbase/src/widgets/doc/snippets/stringlistmodel/model_0.h.py @@ -0,0 +1,24 @@ +class StringListModel(QAbstractListModel): + def __init__(self, strings, parent=None): + ... + + def rowCount(self, parent=QModelIndex()): + ... + + def data(self, index, role): + ... + + def headerData(self, section, orientation, role=Qt.DisplayRole): + ... + + def flags(self, index): + ... + + def setData(self, index, value, role=Qt.EditRole): + ... + + def insertRows(self, position, rows, parent): + ... + + def removeRows(self, position, rows, parent): + ... diff --git a/sources/pyside6/doc/tutorials/modelviewprogramming/qlistview-dnd.py b/sources/pyside6/doc/tutorials/modelviewprogramming/qlistview-dnd.py new file mode 100644 index 000000000..cebc99447 --- /dev/null +++ b/sources/pyside6/doc/tutorials/modelviewprogramming/qlistview-dnd.py @@ -0,0 +1,174 @@ +############################################################################# +## +## Copyright (C) 2022 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of the Qt for Python examples of the Qt Toolkit. +## +## $QT_BEGIN_LICENSE:BSD$ +## You may use this file under the terms of the BSD license as follows: +## +## "Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions are +## met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in +## the documentation and/or other materials provided with the +## distribution. +## * Neither the name of The Qt Company Ltd nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +## +## $QT_END_LICENSE$ +## +############################################################################# + +import sys + +from PySide6.QtWidgets import (QAbstractItemView, QApplication, QMainWindow, + QListView) +from PySide6.QtCore import (QByteArray, QDataStream, QIODevice, QMimeData, + QModelIndex, QStringListModel, Qt) + + +class DragDropListModel(QStringListModel): + """A simple model that uses a QStringList as its data source.""" + + def __init__(self, strings, parent=None): + super().__init__(strings, parent) + +#! [0] + + def canDropMimeData(self, data, action, row, column, parent): + if not data.hasFormat("application/vnd.text.list"): + return False + + if column > 0: + return False + + return True +#! [0] +#! [1] + def dropMimeData(self, data, action, row, column, parent): + if not self.canDropMimeData(data, action, row, column, parent): + return False + + if action == Qt.IgnoreAction: + return True +#! [1] + +#! [2] + begin_row = 0 + + if row != -1: + begin_row = row +#! [2] #! [3] + elif parent.isValid(): + begin_row = parent.row() +#! [3] #! [4] + else: + begin_row = self.rowCount(QModelIndex()) +#! [4] + +#! [5] + encoded_data = data.data("application/vnd.text.list") + stream = QDataStream(encoded_data, QIODevice.ReadOnly) + new_items = [] + while not stream.atEnd(): + new_items.append(stream.readQString()) +#! [5] + +#! [6] + self.insertRows(begin_row, len(new_items), QModelIndex()) + for text in new_items: + idx = self.index(begin_row, 0, QModelIndex()) + self.setData(idx, text) + begin_row += 1 + + return True +#! [6] + +#! [7] + def flags(self, index): + default_flags = super().flags(index) + if index.isValid(): + return Qt.ItemIsDragEnabled | Qt.ItemIsDropEnabled | default_flags + return Qt.ItemIsDropEnabled | default_flags +#! [7] + +#! [8] + def mimeData(self, indexes): + mime_data = QMimeData() + encoded_data = QByteArray() + stream = QDataStream(encoded_data, QIODevice.WriteOnly) + for index in indexes: + if index.isValid(): + text = self.data(index, Qt.DisplayRole) + stream.writeQString(text) + + mime_data.setData("application/vnd.text.list", encoded_data) + return mime_data +#! [8] + +#! [9] + def mimeTypes(self): + return ["application/vnd.text.list"] +#! [9] + +#! [10] + def supportedDropActions(self): + return Qt.CopyAction | Qt.MoveAction +#! [10] + + +class MainWindow(QMainWindow): + + def __init__(self, parent=None): + super().__init__(parent) + + file_menu = self.menuBar().addMenu("&File") + quit_action = file_menu.addAction("E&xit") + quit_action.setShortcut("Ctrl+Q") + +#! [mainwindow0] + self._list_view = QListView(self) + self._list_view.setSelectionMode(QAbstractItemView.ExtendedSelection) + self._list_view.setDragEnabled(True) + self._list_view.setAcceptDrops(True) + self._list_view.setDropIndicatorShown(True) +#! [mainwindow0] + + quit_action.triggered.connect(self.close) + + self.setup_list_items() + + self.setCentralWidget(self._list_view) + self.setWindowTitle("List View") + + def setup_list_items(self): + items = ["Oak", "Fir", "Pine", "Birch", "Hazel", "Redwood", "Sycamore", + "Chestnut", "Mahogany"] + model = DragDropListModel(items, self) + self._list_view.setModel(model) + + +if __name__ == '__main__': + app = QApplication(sys.argv) + window = MainWindow() + window.show() + sys.exit(app.exec()) diff --git a/sources/pyside6/doc/tutorials/modelviewprogramming/simplemodel-use.py b/sources/pyside6/doc/tutorials/modelviewprogramming/simplemodel-use.py new file mode 100644 index 000000000..503834aaf --- /dev/null +++ b/sources/pyside6/doc/tutorials/modelviewprogramming/simplemodel-use.py @@ -0,0 +1,81 @@ +############################################################################# +## +## Copyright (C) 2022 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of the Qt for Python examples of the Qt Toolkit. +## +## $QT_BEGIN_LICENSE:BSD$ +## You may use this file under the terms of the BSD license as follows: +## +## "Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions are +## met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in +## the documentation and/or other materials provided with the +## distribution. +## * Neither the name of The Qt Company Ltd nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +## +## $QT_END_LICENSE$ +## +############################################################################# + +import sys + +from PySide6.QtWidgets import (QApplication, QFileSystemModel, QLabel, + QVBoxLayout, QWidget) +from PySide6.QtGui import QPalette +from PySide6.QtCore import QDir, Qt + + +if __name__ == '__main__': + app = QApplication(sys.argv) + + window = QWidget() + layout = QVBoxLayout(window) + title = QLabel("Some items from the directory model", window) + title.setBackgroundRole(QPalette.Base) + title.setMargin(8) + layout.addWidget(title) + +#! [0] + model = QFileSystemModel() + model.setRootPath(QDir.currentPath()) + + def on_directory_loaded(directory): + parent_index = model.index(directory) + num_rows = model.rowCount(parent_index) +#! [1] + for row in range(num_rows): + index = model.index(row, 0, parent_index) +#! [1] +#! [2] + text = model.data(index, Qt.DisplayRole) +#! [2] + label = QLabel(text, window) + layout.addWidget(label) + + model.directoryLoaded.connect(on_directory_loaded) +#! [0] + + window.setWindowTitle("A simple model example") + window.show() + sys.exit(app.exec()) diff --git a/sources/pyside6/doc/tutorials/modelviewprogramming/stringlistmodel.py b/sources/pyside6/doc/tutorials/modelviewprogramming/stringlistmodel.py new file mode 100644 index 000000000..9d3631f62 --- /dev/null +++ b/sources/pyside6/doc/tutorials/modelviewprogramming/stringlistmodel.py @@ -0,0 +1,161 @@ +############################################################################# +## +## Copyright (C) 2022 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of the Qt for Python examples of the Qt Toolkit. +## +## $QT_BEGIN_LICENSE:BSD$ +## You may use this file under the terms of the BSD license as follows: +## +## "Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions are +## met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in +## the documentation and/or other materials provided with the +## distribution. +## * Neither the name of The Qt Company Ltd nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +## +## $QT_END_LICENSE$ +## +############################################################################# + +import sys + +from PySide6.QtWidgets import (QApplication, QListView) +from PySide6.QtCore import QAbstractListModel, QStringListModel, QModelIndex, Qt + + +#! [0] +class StringListModel(QAbstractListModel): + def __init__(self, strings, parent=None): + super().__init__(parent) + self._strings = strings + +#! [0] + def rowCount(self, parent=QModelIndex()): + """Returns the number of items in the string list as the number of rows + in the model.""" + return len(self._strings) +#! [0] + +#! [1] + def data(self, index, role): + """Returns an appropriate value for the requested data. + If the view requests an invalid index, an invalid variant is returned. + Any valid index that corresponds to a string in the list causes that + string to be returned.""" + row = index.row() + if not index.isValid() or row >= len(self._strings): + return None + if role != Qt.DisplayRole and role != Qt.EditRole: + return None + return self._strings[row] +#! [1] + +#! [2] + def headerData(self, section, orientation, role=Qt.DisplayRole): + """Returns the appropriate header string depending on the orientation of + the header and the section. If anything other than the display role is + requested, we return an invalid variant.""" + if role != Qt.DisplayRole: + return None + if orientation == Qt.Horizontal: + return f"Column {section}" + return f"Row {section}" +#! [2] + +#! [3] + def flags(self, index): + """Returns an appropriate value for the item's flags. Valid items are + enabled, selectable, and editable.""" + + if not index.isValid(): + return Qt.ItemIsEnabled + return super().flags(index) | Qt.ItemIsEditable +#! [3] + + #! [4] + def setData(self, index, value, role=Qt.EditRole): + """Changes an item in the string list, but only if the following conditions + are met: + + # The index supplied is valid. + # The index corresponds to an item to be shown in a view. + # The role associated with editing text is specified. + + The dataChanged() signal is emitted if the item is changed.""" + + if index.isValid() and role == Qt.EditRole: + self._strings[index.row()] = value + self.dataChanged.emit(index, index, {role}) + return True +#! [4] #! [5] + return False +#! [5] + +#! [6] + def insertRows(self, position, rows, parent): + """Inserts a number of rows into the model at the specified position.""" + self.beginInsertRows(QModelIndex(), position, position + rows - 1) + for row in range(rows): + self._strings.insert(position, "") + self.endInsertRows() + return True +#! [6] #! [7] +#! [7] + +#! [8] + def removeRows(self, position, rows, parent): + """Removes a number of rows from the model at the specified position.""" + self.beginRemoveRows(QModelIndex(), position, position + rows - 1) + for row in range(rows): + del self._strings[position] + self.endRemoveRows() + return True +#! [8] #! [9] +#! [9] + + +#! [main0] +if __name__ == '__main__': + app = QApplication(sys.argv) + +#! [main1] + numbers = ["One", "Two", "Three", "Four", "Five"] + model = StringListModel(numbers) +#! [main0] #! [main1] #! [main2] #! [main3] + view = QListView() +#! [main2] + view.setWindowTitle("View onto a string list model") +#! [main4] + view.setModel(model) +#! [main3] #! [main4] + + model.insertRows(5, 7, QModelIndex()) + for row in range(5, 12): + index = model.index(row, 0, QModelIndex()) + model.setData(index, f"{row+1}") + +#! [main5] + view.show() + sys.exit(app.exec()) +#! [main5] diff --git a/tools/snippets_translate/override.py b/tools/snippets_translate/override.py index f0f76fd2b..c9d5d149f 100644 --- a/tools/snippets_translate/override.py +++ b/tools/snippets_translate/override.py @@ -41,6 +41,7 @@ from pathlib import Path ROOT_PATH = Path(__file__).parents[2] EXAMPLES_PATH = ROOT_PATH / "examples" +TUTORIAL_EXAMPLES_PATH = ROOT_PATH / "sources" / "pyside6" / "doc" / "tutorials" _PYTHON_EXAMPLE_SNIPPET_MAPPING = { @@ -76,7 +77,9 @@ _PYTHON_EXAMPLE_SNIPPET_MAPPING = { (EXAMPLES_PATH / "widgets" / "tutorials" / "modelview" / "7_selections.py", "1"), ("qtbase/examples/widgets/tutorials/modelview/7_selections/mainwindow.cpp", "quoting modelview_b"): - (EXAMPLES_PATH / "widgets" / "tutorials" / "modelview" / "7_selections.py", "2") + (EXAMPLES_PATH / "widgets" / "tutorials" / "modelview" / "7_selections.py", "2"), + ("qtbase/src/widgets/doc/snippets/qlistview-dnd/mainwindow.cpp.cpp", "0"): + (TUTORIAL_EXAMPLES_PATH / "modelviewprogramming" / "qlistview-dnd.py", "mainwindow0") } @@ -87,6 +90,40 @@ def python_example_snippet_mapping(): global _python_example_snippet_mapping if not _python_example_snippet_mapping: result = _PYTHON_EXAMPLE_SNIPPET_MAPPING + + qt_path = "qtbase/src/widgets/doc/snippets/simplemodel-use/main.cpp" + pyside_path = TUTORIAL_EXAMPLES_PATH / "modelviewprogramming" / "stringlistmodel.py" + for i in range(3): + snippet_id = str(i) + result[(qt_path, snippet_id)] = pyside_path, snippet_id + + qt_path = "qtbase/src/widgets/doc/snippets/stringlistmodel/main.cpp" + pyside_path = TUTORIAL_EXAMPLES_PATH / "modelviewprogramming" / "stringlistmodel.py" + for i in range(6): + snippet_id = str(i) + result[(qt_path, snippet_id)] = pyside_path, f"main{snippet_id}" + + qt_path = "qtbase/examples/widgets/itemviews/spinboxdelegate/delegate.cpp" + pyside_path = (EXAMPLES_PATH / "widgets" / "itemviews" / "spinboxdelegate" + / "spinboxdelegate.py") + for i in range(5): + snippet_id = str(i) + result[(qt_path, snippet_id)] = pyside_path, snippet_id + + qt_path = "qtbase/src/widgets/doc/snippets/stringlistmodel/model.cpp" + pyside_path = (TUTORIAL_EXAMPLES_PATH / "modelviewprogramming" + / "stringlistmodel.py") + for i in range(10): + snippet_id = str(i) + result[(qt_path, snippet_id)] = pyside_path, snippet_id + + qt_path = "qtbase/src/widgets/doc/snippets/qlistview-dnd/model.cpp" + pyside_path = (TUTORIAL_EXAMPLES_PATH / "modelviewprogramming" + / "qlistview-dnd.py") + for i in range(11): + snippet_id = str(i) + result[(qt_path, snippet_id)] = pyside_path, snippet_id + _python_example_snippet_mapping = result return _python_example_snippet_mapping |