From 9f2a9aba3aff73e31ea15eb4a7a04b0e50f4ee4e Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Fri, 5 Jan 2018 15:58:35 +0100 Subject: Move examples from submodule to pyside-setup Move PySide2 examples that are owned by the Qt Company to a new examples directory. Done-with: Venugopal Shivashankar Task-number: PYSIDE-363 Change-Id: I14099764d9eef2bc35e067086121427955862e3a Reviewed-by: Alexandru Croitor --- .../itemviews/addressbook/adddialogwidget.py | 103 +++++++++ .../widgets/itemviews/addressbook/addressbook.py | 131 +++++++++++ .../widgets/itemviews/addressbook/addresswidget.py | 249 +++++++++++++++++++++ .../widgets/itemviews/addressbook/newaddresstab.py | 94 ++++++++ .../widgets/itemviews/addressbook/tablemodel.py | 147 ++++++++++++ 5 files changed, 724 insertions(+) create mode 100644 examples/widgets/itemviews/addressbook/adddialogwidget.py create mode 100644 examples/widgets/itemviews/addressbook/addressbook.py create mode 100644 examples/widgets/itemviews/addressbook/addresswidget.py create mode 100644 examples/widgets/itemviews/addressbook/newaddresstab.py create mode 100644 examples/widgets/itemviews/addressbook/tablemodel.py (limited to 'examples/widgets/itemviews/addressbook') diff --git a/examples/widgets/itemviews/addressbook/adddialogwidget.py b/examples/widgets/itemviews/addressbook/adddialogwidget.py new file mode 100644 index 000000000..c0dcaf6c0 --- /dev/null +++ b/examples/widgets/itemviews/addressbook/adddialogwidget.py @@ -0,0 +1,103 @@ +#!/usr/bin/python + +############################################################################# +## +## Copyright (C) 2011 Arun Srinivasan +## Copyright (C) 2016 The Qt Company Ltd. +## Contact: http://www.qt.io/licensing/ +## +## This file is part of the PySide 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$ +## +############################################################################# + +from PySide2.QtCore import Qt +from PySide2.QtWidgets import (QDialog, QLabel, QTextEdit, QLineEdit, + QDialogButtonBox, QGridLayout, QVBoxLayout) + +class AddDialogWidget(QDialog): + """ A dialog to add a new address to the addressbook. """ + + def __init__(self, parent=None): + super(AddDialogWidget, self).__init__(parent) + + nameLabel = QLabel("Name") + addressLabel = QLabel("Address") + buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | + QDialogButtonBox.Cancel) + + self.nameText = QLineEdit() + self.addressText = QTextEdit() + + grid = QGridLayout() + grid.setColumnStretch(1, 2) + grid.addWidget(nameLabel, 0, 0) + grid.addWidget(self.nameText, 0, 1) + grid.addWidget(addressLabel, 1, 0, Qt.AlignLeft | Qt.AlignTop) + grid.addWidget(self.addressText, 1, 1, Qt.AlignLeft) + + layout = QVBoxLayout() + layout.addLayout(grid) + layout.addWidget(buttonBox) + + self.setLayout(layout) + + self.setWindowTitle("Add a Contact") + + buttonBox.accepted.connect(self.accept) + buttonBox.rejected.connect(self.reject) + + # These properties make using this dialog a little cleaner. It's much + # nicer to type "addDialog.address" to retrieve the address as compared + # to "addDialog.addressText.toPlainText()" + @property + def name(self): + return self.nameText.text() + + @property + def address(self): + return self.addressText.toPlainText() + + +if __name__ == "__main__": + import sys + from PySide2.QtWidgets import QApplication + + app = QApplication(sys.argv) + + dialog = AddDialogWidget() + if (dialog.exec_()): + name = dialog.name + address = dialog.address + print("Name:" + name) + print("Address:" + address) diff --git a/examples/widgets/itemviews/addressbook/addressbook.py b/examples/widgets/itemviews/addressbook/addressbook.py new file mode 100644 index 000000000..f8927be5e --- /dev/null +++ b/examples/widgets/itemviews/addressbook/addressbook.py @@ -0,0 +1,131 @@ +#!/usr/bin/python + +############################################################################# +## +## Copyright (C) 2011 Arun Srinivasan +## Copyright (C) 2016 The Qt Company Ltd. +## Contact: http://www.qt.io/licensing/ +## +## This file is part of the PySide 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$ +## +############################################################################# + +from PySide2.QtWidgets import (QMainWindow, QAction, QFileDialog, QApplication) + +from addresswidget import AddressWidget + + +class MainWindow(QMainWindow): + + def __init__(self, parent=None): + super(MainWindow, self).__init__(parent) + + self.addressWidget = AddressWidget() + self.setCentralWidget(self.addressWidget) + self.createMenus() + self.setWindowTitle("Address Book") + + def createMenus(self): + # Create the main menuBar menu items + fileMenu = self.menuBar().addMenu("&File") + toolMenu = self.menuBar().addMenu("&Tools") + + # Populate the File menu + openAction = self.createAction("&Open...", fileMenu, self.openFile) + saveAction = self.createAction("&Save As...", fileMenu, self.saveFile) + fileMenu.addSeparator() + exitAction = self.createAction("E&xit", fileMenu, self.close) + + # Populate the Tools menu + addAction = self.createAction("&Add Entry...", toolMenu, self.addressWidget.addEntry) + self.editAction = self.createAction("&Edit Entry...", toolMenu, self.addressWidget.editEntry) + toolMenu.addSeparator() + self.removeAction = self.createAction("&Remove Entry", toolMenu, self.addressWidget.removeEntry) + + # Disable the edit and remove menu items initially, as there are + # no items yet. + self.editAction.setEnabled(False) + self.removeAction.setEnabled(False) + + # Wire up the updateActions slot + self.addressWidget.selectionChanged.connect(self.updateActions) + + def createAction(self, text, menu, slot): + """ Helper function to save typing when populating menus + with action. + """ + action = QAction(text, self) + menu.addAction(action) + action.triggered.connect(slot) + return action + + # Quick gotcha: + # + # QFiledialog.getOpenFilename and QFileDialog.get.SaveFileName don't + # behave in PySide2 as they do in Qt, where they return a QString + # containing the filename. + # + # In PySide2, these functions return a tuple: (filename, filter) + + def openFile(self): + filename, _ = QFileDialog.getOpenFileName(self) + if filename: + self.addressWidget.readFromFile(filename) + + def saveFile(self): + filename, _ = QFileDialog.getSaveFileName(self) + if filename: + self.addressWidget.writeToFile(filename) + + def updateActions(self, selection): + """ Only allow the user to remove or edit an item if an item + is actually selected. + """ + indexes = selection.indexes() + + if len(indexes) > 0: + self.removeAction.setEnabled(True) + self.editAction.setEnabled(True) + else: + self.removeAction.setEnabled(False) + self.editAction.setEnabled(False) + + +if __name__ == "__main__": + """ Run the application. """ + import sys + app = QApplication(sys.argv) + mw = MainWindow() + mw.show() + sys.exit(app.exec_()) diff --git a/examples/widgets/itemviews/addressbook/addresswidget.py b/examples/widgets/itemviews/addressbook/addresswidget.py new file mode 100644 index 000000000..7ecd42e7d --- /dev/null +++ b/examples/widgets/itemviews/addressbook/addresswidget.py @@ -0,0 +1,249 @@ +#!/usr/bin/python + +############################################################################# +## +## Copyright (C) 2011 Arun Srinivasan +## Copyright (C) 2016 The Qt Company Ltd. +## Contact: http://www.qt.io/licensing/ +## +## This file is part of the PySide 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$ +## +############################################################################# + +try: + import cpickle as pickle +except ImportError: + import pickle + +from PySide2.QtCore import (Qt, Signal, QRegExp, QModelIndex, + QItemSelection, QItemSelectionModel, QSortFilterProxyModel) +from PySide2.QtWidgets import (QWidget, QTabWidget, QMessageBox, QTableView, + QAbstractItemView) + +from tablemodel import TableModel +from newaddresstab import NewAddressTab +from adddialogwidget import AddDialogWidget + + +class AddressWidget(QTabWidget): + """ The central widget of the application. Most of the addressbook's + functionality is contained in this class. + """ + + selectionChanged = Signal(QItemSelection) + + def __init__(self, parent=None): + """ Initialize the AddressWidget. """ + super(AddressWidget, self).__init__(parent) + + self.tableModel = TableModel() + self.newAddressTab = NewAddressTab() + self.newAddressTab.sendDetails.connect(self.addEntry) + + self.addTab(self.newAddressTab, "Address Book") + + self.setupTabs() + + def addEntry(self, name=None, address=None): + """ Add an entry to the addressbook. """ + if name is None and address is None: + addDialog = AddDialogWidget() + + if addDialog.exec_(): + name = addDialog.name + address = addDialog.address + + address = {"name": name, "address": address} + addresses = self.tableModel.addresses[:] + + # The QT docs for this example state that what we're doing here + # is checking if the entered name already exists. What they + # (and we here) are actually doing is checking if the whole + # name/address pair exists already - ok for the purposes of this + # example, but obviously not how a real addressbook application + # should behave. + try: + addresses.remove(address) + QMessageBox.information(self, "Duplicate Name", + "The name \"%s\" already exists." % name) + except ValueError: + # The address didn't already exist, so let's add it to the model. + + # Step 1: create the row + self.tableModel.insertRows(0) + + # Step 2: get the index of the newly created row and use it. + # to set the name + ix = self.tableModel.index(0, 0, QModelIndex()) + self.tableModel.setData(ix, address["name"], Qt.EditRole) + + # Step 3: lather, rinse, repeat for the address. + ix = self.tableModel.index(0, 1, QModelIndex()) + self.tableModel.setData(ix, address["address"], Qt.EditRole) + + # Remove the newAddressTab, as we now have at least one + # address in the model. + self.removeTab(self.indexOf(self.newAddressTab)) + + # The screenshot for the QT example shows nicely formatted + # multiline cells, but the actual application doesn't behave + # quite so nicely, at least on Ubuntu. Here we resize the newly + # created row so that multiline addresses look reasonable. + tableView = self.currentWidget() + tableView.resizeRowToContents(ix.row()) + + def editEntry(self): + """ Edit an entry in the addressbook. """ + tableView = self.currentWidget() + proxyModel = tableView.model() + selectionModel = tableView.selectionModel() + + # Get the name and address of the currently selected row. + indexes = selectionModel.selectedRows() + + for index in indexes: + row = proxyModel.mapToSource(index).row() + ix = self.tableModel.index(row, 0, QModelIndex()) + name = self.tableModel.data(ix, Qt.DisplayRole) + ix = self.tableModel.index(row, 1, QModelIndex()) + address = self.tableModel.data(ix, Qt.DisplayRole) + + # Open an addDialogWidget, and only allow the user to edit the address. + addDialog = AddDialogWidget() + addDialog.setWindowTitle("Edit a Contact") + + addDialog.nameText.setReadOnly(True) + addDialog.nameText.setText(name) + addDialog.addressText.setText(address) + + # If the address is different, add it to the model. + if addDialog.exec_(): + newAddress = addDialog.address + if newAddress != address: + ix = self.tableModel.index(row, 1, QModelIndex()) + self.tableModel.setData(ix, newAddress, Qt.EditRole) + + def removeEntry(self): + """ Remove an entry from the addressbook. """ + tableView = self.currentWidget() + proxyModel = tableView.model() + selectionModel = tableView.selectionModel() + + # Just like editEntry, but this time remove the selected row. + indexes = selectionModel.selectedRows() + + for index in indexes: + row = proxyModel.mapToSource(index).row() + self.tableModel.removeRows(row) + + # If we've removed the last address in the model, display the + # newAddressTab + if self.tableModel.rowCount() == 0: + self.insertTab(0, self.newAddressTab, "Address Book") + + def setupTabs(self): + """ Setup the various tabs in the AddressWidget. """ + groups = ["ABC", "DEF", "GHI", "JKL", "MNO", "PQR", "STU", "VW", "XYZ"] + + for group in groups: + proxyModel = QSortFilterProxyModel(self) + proxyModel.setSourceModel(self.tableModel) + proxyModel.setDynamicSortFilter(True) + + tableView = QTableView() + tableView.setModel(proxyModel) + tableView.setSortingEnabled(True) + tableView.setSelectionBehavior(QAbstractItemView.SelectRows) + tableView.horizontalHeader().setStretchLastSection(True) + tableView.verticalHeader().hide() + tableView.setEditTriggers(QAbstractItemView.NoEditTriggers) + tableView.setSelectionMode(QAbstractItemView.SingleSelection) + + # This here be the magic: we use the group name (e.g. "ABC") to + # build the regex for the QSortFilterProxyModel for the group's + # tab. The regex will end up looking like "^[ABC].*", only + # allowing this tab to display items where the name starts with + # "A", "B", or "C". Notice that we set it to be case-insensitive. + reFilter = "^[%s].*" % group + + proxyModel.setFilterRegExp(QRegExp(reFilter, Qt.CaseInsensitive)) + proxyModel.setFilterKeyColumn(0) # Filter on the "name" column + proxyModel.sort(0, Qt.AscendingOrder) + + # This prevents an application crash (see: http://www.qtcentre.org/threads/58874-QListView-SelectionModel-selectionChanged-Crash) + viewselectionmodel = tableView.selectionModel() + tableView.selectionModel().selectionChanged.connect(self.selectionChanged) + + self.addTab(tableView, group) + + # Note: the QT example uses a QDataStream for the saving and loading. + # Here we're using a python dictionary to store the addresses, which + # can't be streamed using QDataStream, so we just use cpickle for this + # example. + def readFromFile(self, filename): + """ Read contacts in from a file. """ + try: + f = open(filename, "rb") + addresses = pickle.load(f) + except IOError: + QMessageBox.information(self, "Unable to open file: %s" % filename) + finally: + f.close() + + if len(addresses) == 0: + QMessageBox.information(self, "No contacts in file: %s" % filename) + else: + for address in addresses: + self.addEntry(address["name"], address["address"]) + + def writeToFile(self, filename): + """ Save all contacts in the model to a file. """ + try: + f = open(filename, "wb") + pickle.dump(self.tableModel.addresses, f) + + except IOError: + QMessageBox.information(self, "Unable to open file: %s" % filename) + finally: + f.close() + + +if __name__ == "__main__": + import sys + from PySide2.QtWidgets import QApplication + + app = QApplication(sys.argv) + addressWidget = AddressWidget() + addressWidget.show() + sys.exit(app.exec_()) diff --git a/examples/widgets/itemviews/addressbook/newaddresstab.py b/examples/widgets/itemviews/addressbook/newaddresstab.py new file mode 100644 index 000000000..82dcff441 --- /dev/null +++ b/examples/widgets/itemviews/addressbook/newaddresstab.py @@ -0,0 +1,94 @@ +#!/usr/bin/python + +############################################################################# +## +## Copyright (C) 2011 Arun Srinivasan +## Copyright (C) 2016 The Qt Company Ltd. +## Contact: http://www.qt.io/licensing/ +## +## This file is part of the PySide 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$ +## +############################################################################# + +from PySide2.QtCore import (Qt, Signal) +from PySide2.QtWidgets import (QWidget, QLabel, QPushButton, QVBoxLayout) + +from adddialogwidget import AddDialogWidget + +class NewAddressTab(QWidget): + """ An extra tab that prompts the user to add new contacts. + To be displayed only when there are no contacts in the model. + """ + + sendDetails = Signal(str, str) + + def __init__(self, parent=None): + super(NewAddressTab, self).__init__(parent) + + descriptionLabel = QLabel("There are no contacts in your address book." + "\nClick Add to add new contacts.") + + addButton = QPushButton("Add") + + layout = QVBoxLayout() + layout.addWidget(descriptionLabel) + layout.addWidget(addButton, 0, Qt.AlignCenter) + + self.setLayout(layout) + + addButton.clicked.connect(self.addEntry) + + def addEntry(self): + addDialog = AddDialogWidget() + + if addDialog.exec_(): + name = addDialog.name + address = addDialog.address + self.sendDetails.emit(name, address) + + +if __name__ == "__main__": + + def printAddress(name, address): + print("Name:" + name) + print("Address:" + address) + + import sys + from PySide2.QtWidgets import QApplication + + app = QApplication(sys.argv) + newAddressTab = NewAddressTab() + newAddressTab.sendDetails.connect(printAddress) + newAddressTab.show() + sys.exit(app.exec_()) diff --git a/examples/widgets/itemviews/addressbook/tablemodel.py b/examples/widgets/itemviews/addressbook/tablemodel.py new file mode 100644 index 000000000..24316f981 --- /dev/null +++ b/examples/widgets/itemviews/addressbook/tablemodel.py @@ -0,0 +1,147 @@ +#!/usr/bin/python + +############################################################################# +## +## Copyright (C) 2011 Arun Srinivasan +## Copyright (C) 2016 The Qt Company Ltd. +## Contact: http://www.qt.io/licensing/ +## +## This file is part of the PySide 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$ +## +############################################################################# + +from PySide2.QtCore import (Qt, QAbstractTableModel, QModelIndex) + +class TableModel(QAbstractTableModel): + + def __init__(self, addresses=None, parent=None): + super(TableModel, self).__init__(parent) + + if addresses is None: + self.addresses = [] + else: + self.addresses = addresses + + def rowCount(self, index=QModelIndex()): + """ Returns the number of rows the model holds. """ + return len(self.addresses) + + def columnCount(self, index=QModelIndex()): + """ Returns the number of columns the model holds. """ + return 2 + + def data(self, index, role=Qt.DisplayRole): + """ Depending on the index and role given, return data. If not + returning data, return None (PySide equivalent of QT's + "invalid QVariant"). + """ + if not index.isValid(): + return None + + if not 0 <= index.row() < len(self.addresses): + return None + + if role == Qt.DisplayRole: + name = self.addresses[index.row()]["name"] + address = self.addresses[index.row()]["address"] + + if index.column() == 0: + return name + elif index.column() == 1: + return address + + return None + + def headerData(self, section, orientation, role=Qt.DisplayRole): + """ Set the headers to be displayed. """ + if role != Qt.DisplayRole: + return None + + if orientation == Qt.Horizontal: + if section == 0: + return "Name" + elif section == 1: + return "Address" + + return None + + def insertRows(self, position, rows=1, index=QModelIndex()): + """ Insert a row into the model. """ + self.beginInsertRows(QModelIndex(), position, position + rows - 1) + + for row in range(rows): + self.addresses.insert(position + row, {"name":"", "address":""}) + + self.endInsertRows() + return True + + def removeRows(self, position, rows=1, index=QModelIndex()): + """ Remove a row from the model. """ + self.beginRemoveRows(QModelIndex(), position, position + rows - 1) + + del self.addresses[position:position+rows] + + self.endRemoveRows() + return True + + def setData(self, index, value, role=Qt.EditRole): + """ Adjust the data (set it to ) depending on the given + index and role. + """ + if role != Qt.EditRole: + return False + + if index.isValid() and 0 <= index.row() < len(self.addresses): + address = self.addresses[index.row()] + if index.column() == 0: + address["name"] = value + elif index.column() == 1: + address["address"] = value + else: + return False + + self.dataChanged.emit(index, index, 0) + return True + + return False + + def flags(self, index): + """ Set the item flags at the given index. Seems like we're + implementing this function just to see how it's done, as we + manually adjust each tableView to have NoEditTriggers. + """ + if not index.isValid(): + return Qt.ItemIsEnabled + return Qt.ItemFlags(QAbstractTableModel.flags(self, index) | + Qt.ItemIsEditable) -- cgit v1.2.3