diff options
Diffstat (limited to 'examples/widgets/itemviews')
24 files changed, 974 insertions, 55 deletions
diff --git a/examples/widgets/itemviews/address_book/adddialogwidget.py b/examples/widgets/itemviews/address_book/adddialogwidget.py index 276a8d2b4..ecb853e80 100644 --- a/examples/widgets/itemviews/address_book/adddialogwidget.py +++ b/examples/widgets/itemviews/address_book/adddialogwidget.py @@ -15,8 +15,8 @@ class AddDialogWidget(QDialog): name_label = QLabel("Name") address_label = QLabel("Address") - button_box = QDialogButtonBox(QDialogButtonBox.Ok | - QDialogButtonBox.Cancel) + button_box = QDialogButtonBox(QDialogButtonBox.Ok + | QDialogButtonBox.Cancel) self._name_text = QLineEdit() self._address_text = QTextEdit() diff --git a/examples/widgets/itemviews/address_book/address_book.py b/examples/widgets/itemviews/address_book/address_book.py index 2121f2783..af0cf3dee 100644 --- a/examples/widgets/itemviews/address_book/address_book.py +++ b/examples/widgets/itemviews/address_book/address_book.py @@ -2,6 +2,7 @@ # Copyright (C) 2022 The Qt Company Ltd. # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +from PySide6.QtCore import Slot from PySide6.QtGui import QAction from PySide6.QtWidgets import (QMainWindow, QFileDialog, QApplication) @@ -24,16 +25,19 @@ class MainWindow(QMainWindow): tool_menu = self.menuBar().addMenu("&Tools") # Populate the File menu - open_action = self.create_action("&Open...", file_menu, self.open_file) - save_action = self.create_action("&Save As...", file_menu, self.save_file) + self.open_action = self.create_action("&Open...", file_menu, self.open_file) + self.save_action = self.create_action("&Save As...", file_menu, self.save_file) file_menu.addSeparator() - exit_action = self.create_action("E&xit", file_menu, self.close) + self.exit_action = self.create_action("E&xit", file_menu, self.close) # Populate the Tools menu - add_action = self.create_action("&Add Entry...", tool_menu, self._address_widget.add_entry) - self._edit_action = self.create_action("&Edit Entry...", tool_menu, self._address_widget.edit_entry) + self.add_action = self.create_action( + "&Add Entry...", tool_menu, self._address_widget.add_entry) + self._edit_action = self.create_action( + "&Edit Entry...", tool_menu, self._address_widget.edit_entry) tool_menu.addSeparator() - self._remove_action = self.create_action("&Remove Entry", tool_menu, self._address_widget.remove_entry) + self._remove_action = self.create_action( + "&Remove Entry", tool_menu, self._address_widget.remove_entry) # Disable the edit and remove menu items initially, as there are # no items yet. @@ -60,11 +64,13 @@ class MainWindow(QMainWindow): # # In PySide6, these functions return a tuple: (filename, filter) + @Slot() def open_file(self): filename, _ = QFileDialog.getOpenFileName(self) if filename: self._address_widget.read_from_file(filename) + @Slot() def save_file(self): filename, _ = QFileDialog.getSaveFileName(self) if filename: diff --git a/examples/widgets/itemviews/address_book/addresswidget.py b/examples/widgets/itemviews/address_book/addresswidget.py index 7987ae3cd..cb2f46ea1 100644 --- a/examples/widgets/itemviews/address_book/addresswidget.py +++ b/examples/widgets/itemviews/address_book/addresswidget.py @@ -7,7 +7,7 @@ try: except ImportError: import pickle -from PySide6.QtCore import (Qt, Signal, QRegularExpression, QModelIndex, +from PySide6.QtCore import (Qt, Signal, Slot, QRegularExpression, QModelIndex, QItemSelection, QSortFilterProxyModel) from PySide6.QtWidgets import QTabWidget, QMessageBox, QTableView, QAbstractItemView @@ -35,6 +35,7 @@ class AddressWidget(QTabWidget): self.setup_tabs() + @Slot() def add_entry(self, name=None, address=None): """ Add an entry to the addressbook. """ if name is None and address is None: @@ -83,6 +84,7 @@ class AddressWidget(QTabWidget): table_view = self.currentWidget() table_view.resizeRowToContents(ix.row()) + @Slot() def edit_entry(self): """ Edit an entry in the addressbook. """ table_view = self.currentWidget() @@ -115,6 +117,7 @@ class AddressWidget(QTabWidget): ix = self._table_model.index(row, 1, QModelIndex()) self._table_model.setData(ix, new_address, Qt.EditRole) + @Slot() def remove_entry(self): """ Remove an entry from the addressbook. """ table_view = self.currentWidget() @@ -163,8 +166,9 @@ class AddressWidget(QTabWidget): proxy_model.setFilterKeyColumn(0) # Filter on the "name" column proxy_model.sort(0, Qt.AscendingOrder) - # This prevents an application crash (see: https://www.qtcentre.org/threads/58874-QListView-SelectionModel-selectionChanged-Crash) - viewselectionmodel = table_view.selectionModel() + # This prevents an application crash (see: + # https://www.qtcentre.org/threads/58874-QListView-SelectionModel-selectionChanged-Crash) # noqa: E501 + self.viewselectionmodel = table_view.selectionModel() table_view.selectionModel().selectionChanged.connect(self.selection_changed) self.addTab(table_view, group) diff --git a/examples/widgets/itemviews/address_book/tablemodel.py b/examples/widgets/itemviews/address_book/tablemodel.py index a0d63bbe2..3c1dbd4cc 100644 --- a/examples/widgets/itemviews/address_book/tablemodel.py +++ b/examples/widgets/itemviews/address_book/tablemodel.py @@ -105,5 +105,5 @@ class TableModel(QAbstractTableModel): """ if not index.isValid(): return Qt.ItemIsEnabled - return Qt.ItemFlags(QAbstractTableModel.flags(self, index) | - Qt.ItemIsEditable) + return Qt.ItemFlags(QAbstractTableModel.flags(self, index) + | Qt.ItemIsEditable) diff --git a/examples/widgets/itemviews/basicfiltermodel/basicsortfiltermodel.py b/examples/widgets/itemviews/basicfiltermodel/basicsortfiltermodel.py index a61a76cb8..a30b0abdf 100644 --- a/examples/widgets/itemviews/basicfiltermodel/basicsortfiltermodel.py +++ b/examples/widgets/itemviews/basicfiltermodel/basicsortfiltermodel.py @@ -4,7 +4,7 @@ import sys from PySide6.QtCore import (QDate, QDateTime, QRegularExpression, - QSortFilterProxyModel, QTime, Qt) + QSortFilterProxyModel, QTime, Qt, Slot) from PySide6.QtGui import QStandardItemModel from PySide6.QtWidgets import (QApplication, QCheckBox, QComboBox, QGridLayout, QGroupBox, QHBoxLayout, QLabel, QLineEdit, @@ -46,11 +46,11 @@ class Window(QWidget): self._filter_syntax_combo_box = QComboBox() self._filter_syntax_combo_box.addItem("Regular expression", - REGULAR_EXPRESSION) + REGULAR_EXPRESSION) self._filter_syntax_combo_box.addItem("Wildcard", - WILDCARD) + WILDCARD) self._filter_syntax_combo_box.addItem("Fixed string", - FIXED_STRING) + FIXED_STRING) self._filter_syntax_label = QLabel("Filter &syntax:") self._filter_syntax_label.setBuddy(self._filter_syntax_combo_box) @@ -102,6 +102,7 @@ class Window(QWidget): self._proxy_model.setSourceModel(model) self._source_view.setModel(model) + @Slot() def filter_reg_exp_changed(self): syntax_nr = self._filter_syntax_combo_box.currentData() pattern = self._filter_pattern_line_edit.text() @@ -117,9 +118,11 @@ class Window(QWidget): reg_exp.setPatternOptions(options) self._proxy_model.setFilterRegularExpression(reg_exp) + @Slot() def filter_column_changed(self): self._proxy_model.setFilterKeyColumn(self._filter_column_combo_box.currentIndex()) + @Slot() def sort_changed(self): if self._sort_case_sensitivity_check_box.isChecked(): case_sensitivity = Qt.CaseSensitive @@ -144,25 +147,25 @@ def create_mail_model(parent): model.setHeaderData(2, Qt.Horizontal, "Date") add_mail(model, "Happy New Year!", "Grace K. <grace@software-inc.com>", - QDateTime(QDate(2006, 12, 31), QTime(17, 3))) + QDateTime(QDate(2006, 12, 31), QTime(17, 3))) add_mail(model, "Radically new concept", "Grace K. <grace@software-inc.com>", - QDateTime(QDate(2006, 12, 22), QTime(9, 44))) + QDateTime(QDate(2006, 12, 22), QTime(9, 44))) add_mail(model, "Accounts", "pascale@nospam.com", - QDateTime(QDate(2006, 12, 31), QTime(12, 50))) + QDateTime(QDate(2006, 12, 31), QTime(12, 50))) add_mail(model, "Expenses", "Joe Bloggs <joe@bloggs.com>", - QDateTime(QDate(2006, 12, 25), QTime(11, 39))) + QDateTime(QDate(2006, 12, 25), QTime(11, 39))) add_mail(model, "Re: Expenses", "Andy <andy@nospam.com>", - QDateTime(QDate(2007, 1, 2), QTime(16, 5))) + QDateTime(QDate(2007, 1, 2), QTime(16, 5))) add_mail(model, "Re: Accounts", "Joe Bloggs <joe@bloggs.com>", - QDateTime(QDate(2007, 1, 3), QTime(14, 18))) + QDateTime(QDate(2007, 1, 3), QTime(14, 18))) add_mail(model, "Re: Accounts", "Andy <andy@nospam.com>", - QDateTime(QDate(2007, 1, 3), QTime(14, 26))) + QDateTime(QDate(2007, 1, 3), QTime(14, 26))) add_mail(model, "Sports", "Linda Smith <linda.smith@nospam.com>", - QDateTime(QDate(2007, 1, 5), QTime(11, 33))) + QDateTime(QDate(2007, 1, 5), QTime(11, 33))) add_mail(model, "AW: Sports", "Rolf Newschweinstein <rolfn@nospam.com>", - QDateTime(QDate(2007, 1, 5), QTime(12, 0))) + QDateTime(QDate(2007, 1, 5), QTime(12, 0))) add_mail(model, "RE: Sports", "Petra Schmidt <petras@nospam.com>", - QDateTime(QDate(2007, 1, 5), QTime(12, 1))) + QDateTime(QDate(2007, 1, 5), QTime(12, 1))) return model diff --git a/examples/widgets/itemviews/dirview/dirview.py b/examples/widgets/itemviews/dirview/dirview.py new file mode 100644 index 000000000..d1be6958e --- /dev/null +++ b/examples/widgets/itemviews/dirview/dirview.py @@ -0,0 +1,59 @@ +# Copyright (C) 2020 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import sys +from argparse import ArgumentParser, RawTextHelpFormatter + +from PySide6.QtWidgets import (QApplication, QFileSystemModel, + QFileIconProvider, QScroller, QTreeView) +from PySide6.QtCore import QDir + +"""PySide6 port of the widgets/itemviews/dirview example from Qt v6.x""" + + +if __name__ == "__main__": + app = QApplication(sys.argv) + + name = "Dir View" + argument_parser = ArgumentParser(description=name, + formatter_class=RawTextHelpFormatter) + argument_parser.add_argument("--no-custom", "-c", action="store_true", + help="Set QFileSystemModel.DontUseCustomDirectoryIcons") + argument_parser.add_argument("--no-watch", "-w", action="store_true", + help="Set QFileSystemModel.DontWatch") + argument_parser.add_argument("directory", + help="The directory to start in.", + nargs='?', type=str) + options = argument_parser.parse_args() + root_path = options.directory + + model = QFileSystemModel() + icon_provider = QFileIconProvider() + model.setIconProvider(icon_provider) + model.setRootPath("") + if options.no_custom: + model.setOption(QFileSystemModel.DontUseCustomDirectoryIcons) + if options.no_watch: + model.setOption(QFileSystemModel.DontWatchForChanges) + tree = QTreeView() + tree.setModel(model) + if root_path: + root_index = model.index(QDir.cleanPath(root_path)) + if root_index.isValid(): + tree.setRootIndex(root_index) + + # Demonstrating look and feel features + tree.setAnimated(False) + tree.setIndentation(20) + tree.setSortingEnabled(True) + availableSize = tree.screen().availableGeometry().size() + tree.resize(availableSize / 2) + tree.setColumnWidth(0, tree.width() / 3) + + # Make it flickable on touchscreens + QScroller.grabGesture(tree, QScroller.ScrollerGestureType.TouchGesture) + + tree.setWindowTitle(name) + tree.show() + + sys.exit(app.exec()) diff --git a/examples/widgets/itemviews/dirview/dirview.pyproject b/examples/widgets/itemviews/dirview/dirview.pyproject new file mode 100644 index 000000000..9470083c9 --- /dev/null +++ b/examples/widgets/itemviews/dirview/dirview.pyproject @@ -0,0 +1,3 @@ +{ + "files": ["dirview.py"] +} diff --git a/examples/widgets/itemviews/dirview/doc/dirview.rst b/examples/widgets/itemviews/dirview/doc/dirview.rst new file mode 100644 index 000000000..7044fdf58 --- /dev/null +++ b/examples/widgets/itemviews/dirview/doc/dirview.rst @@ -0,0 +1,5 @@ +Dir View Example +================ + +The Dir View example shows a tree view of the local file system. It uses the +QFileSystemModel class to provide file and directory information. diff --git a/examples/widgets/itemviews/editabletreemodel/mainwindow.py b/examples/widgets/itemviews/editabletreemodel/mainwindow.py index c6abfa568..1489bf28b 100644 --- a/examples/widgets/itemviews/editabletreemodel/mainwindow.py +++ b/examples/widgets/itemviews/editabletreemodel/mainwindow.py @@ -52,7 +52,7 @@ class MainWindow(QMainWindow): self.insert_child_action.setShortcut("Ctrl+N") self.insert_child_action.triggered.connect(self.insert_child) help_menu = menubar.addMenu("&Help") - about_qt_action = help_menu.addAction("About Qt", qApp.aboutQt) + about_qt_action = help_menu.addAction("About Qt", qApp.aboutQt) # noqa: F821 about_qt_action.setShortcut("F1") self.setWindowTitle("Editable Tree Model") diff --git a/examples/widgets/itemviews/editabletreemodel/treemodel.py b/examples/widgets/itemviews/editabletreemodel/treemodel.py index 58e405c12..a58572fca 100644 --- a/examples/widgets/itemviews/editabletreemodel/treemodel.py +++ b/examples/widgets/itemviews/editabletreemodel/treemodel.py @@ -2,14 +2,11 @@ # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause -from PySide6.QtCore import QModelIndex, Qt, QAbstractItemModel, Signal +from PySide6.QtCore import QModelIndex, Qt, QAbstractItemModel from treeitem import TreeItem class TreeModel(QAbstractItemModel): - # Define signals - dataChanged = Signal(QModelIndex, QModelIndex, object) - headerDataChanged = Signal(Qt.Orientation, int, int) def __init__(self, headers: list, data: str, parent=None): super().__init__(parent) @@ -154,9 +151,7 @@ class TreeModel(QAbstractItemModel): result: bool = self.root_item.set_data(section, value) if result: - # todo: Check if emit headerDataChanged signal is correct - # emit headerDataChanged(orientation, section, section) - self.headerDataChanged(orientation, section, section) + self.headerDataChanged.emit(orientation, section, section) return result diff --git a/examples/widgets/itemviews/fetchmore/fetchmore.py b/examples/widgets/itemviews/fetchmore/fetchmore.py index 08617edad..5150250e0 100644 --- a/examples/widgets/itemviews/fetchmore/fetchmore.py +++ b/examples/widgets/itemviews/fetchmore/fetchmore.py @@ -11,9 +11,8 @@ down the list to see the model being populated on demand. import sys -from PySide6.QtCore import (QAbstractListModel, QDir, QFileInfo, QLibraryInfo, +from PySide6.QtCore import (QAbstractListModel, QDir, QModelIndex, Qt, Signal, Slot) -from PySide6.QtGui import QPalette from PySide6.QtWidgets import (QApplication, QFileIconProvider, QListView, QPlainTextEdit, QSizePolicy, QVBoxLayout, QWidget) @@ -50,7 +49,7 @@ class FileListModel(QAbstractListModel): if role == Qt.BackgroundRole: batch = row // BATCH_SIZE - palette = qApp.palette() + palette = qApp.palette() # noqa: F821 return palette.base() if batch % 2 == 0 else palette.alternateBase() if role == Qt.DecorationRole: @@ -113,7 +112,7 @@ class Window(QWidget): self.setWindowTitle("Fetch More Example") - @Slot(str, int, int) + @Slot(str, int, int, int) def update_log(self, path, start, number, total): native_path = QDir.toNativeSeparators(path) last = start + number - 1 diff --git a/examples/widgets/itemviews/jsonmodel/jsonmodel.py b/examples/widgets/itemviews/jsonmodel/jsonmodel.py index c0adfc664..6e614c77f 100644 --- a/examples/widgets/itemviews/jsonmodel/jsonmodel.py +++ b/examples/widgets/itemviews/jsonmodel/jsonmodel.py @@ -3,7 +3,7 @@ import json import sys -from typing import Any, Iterable, List, Dict, Union +from typing import Any, List, Dict, Union from PySide6.QtWidgets import QTreeView, QApplication, QHeaderView from PySide6.QtCore import QAbstractItemModel, QModelIndex, QObject, Qt, QFileInfo @@ -182,10 +182,7 @@ class JsonModel(QAbstractItemModel): item = index.internalPointer() item.value = str(value) - if __binding__ in ("PySide", "PyQt4"): - self.dataChanged.emit(index, index) - else: - self.dataChanged.emit(index, index, [Qt.EditRole]) + self.dataChanged.emit(index, index, [Qt.EditRole]) return True diff --git a/examples/widgets/itemviews/spinboxdelegate/doc/spinboxdelegate.rst b/examples/widgets/itemviews/spinboxdelegate/doc/spinboxdelegate.rst new file mode 100644 index 000000000..12e505207 --- /dev/null +++ b/examples/widgets/itemviews/spinboxdelegate/doc/spinboxdelegate.rst @@ -0,0 +1,5 @@ +SpinBox Delegate Example +========================= + +A simple example that shows how a view can use a custom delegate to edit +data obtained from a model. diff --git a/examples/widgets/itemviews/spinboxdelegate/spinboxdelegate.py b/examples/widgets/itemviews/spinboxdelegate/spinboxdelegate.py new file mode 100644 index 000000000..577f0faa5 --- /dev/null +++ b/examples/widgets/itemviews/spinboxdelegate/spinboxdelegate.py @@ -0,0 +1,79 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import sys + +from PySide6.QtWidgets import (QApplication, QStyledItemDelegate, QSpinBox, + QTableView) +from PySide6.QtGui import QStandardItemModel, Qt +from PySide6.QtCore import QModelIndex + +"""PySide6 port of the widgets/itemviews/spinboxdelegate from Qt v6.x""" + + +#! [0] +class SpinBoxDelegate(QStyledItemDelegate): + """A delegate that allows the user to change integer values from the model + using a spin box widget. """ + +#! [0] + def __init__(self, parent=None): + super().__init__(parent) +#! [0] + +#! [1] + def createEditor(self, parent, option, index): + editor = QSpinBox(parent) + editor.setFrame(False) + editor.setMinimum(0) + editor.setMaximum(100) + return editor +#! [1] + +#! [2] + def setEditorData(self, editor, index): + value = index.model().data(index, Qt.EditRole) + editor.setValue(value) +#! [2] + +#! [3] + def setModelData(self, editor, model, index): + editor.interpretText() + value = editor.value() + model.setData(index, value, Qt.EditRole) +#! [3] + +#! [4] + def updateEditorGeometry(self, editor, option, index): + editor.setGeometry(option.rect) +#! [4] + + +#! [main0] +if __name__ == '__main__': + app = QApplication(sys.argv) + + model = QStandardItemModel(4, 2) + tableView = QTableView() + tableView.setModel(model) + + delegate = SpinBoxDelegate() + tableView.setItemDelegate(delegate) +#! [main0] + + tableView.horizontalHeader().setStretchLastSection(True) + +#! [main1] + for row in range(4): + for column in range(2): + index = model.index(row, column, QModelIndex()) + value = (row + 1) * (column + 1) + model.setData(index, value) +#! [main1] //# [main2] +#! [main2] + +#! [main3] + tableView.setWindowTitle("Spin Box Delegate") + tableView.show() + sys.exit(app.exec()) +#! [main3] diff --git a/examples/widgets/itemviews/spinboxdelegate/spinboxdelegate.pyproject b/examples/widgets/itemviews/spinboxdelegate/spinboxdelegate.pyproject new file mode 100644 index 000000000..70616905c --- /dev/null +++ b/examples/widgets/itemviews/spinboxdelegate/spinboxdelegate.pyproject @@ -0,0 +1,3 @@ +{ + "files": ["spinboxdelegate.py"] +} diff --git a/examples/widgets/itemviews/spreadsheet/doc/spreadsheet.png b/examples/widgets/itemviews/spreadsheet/doc/spreadsheet.png Binary files differnew file mode 100644 index 000000000..ae7dde24b --- /dev/null +++ b/examples/widgets/itemviews/spreadsheet/doc/spreadsheet.png diff --git a/examples/widgets/itemviews/spreadsheet/doc/spreadsheet.rst b/examples/widgets/itemviews/spreadsheet/doc/spreadsheet.rst new file mode 100644 index 000000000..c0839b232 --- /dev/null +++ b/examples/widgets/itemviews/spreadsheet/doc/spreadsheet.rst @@ -0,0 +1,10 @@ +Spreadsheet example +=================== + +The Spreadsheet example shows how a table view can be used to create a simple +spreadsheet application. Custom delegates are used to render different types of +data in distinctive colors. + +.. image:: spreadsheet.png + :width: 400 + :alt: Spreadsheet screenshot diff --git a/examples/widgets/itemviews/spreadsheet/main.py b/examples/widgets/itemviews/spreadsheet/main.py new file mode 100644 index 000000000..0ecc5ec23 --- /dev/null +++ b/examples/widgets/itemviews/spreadsheet/main.py @@ -0,0 +1,19 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import sys + +from PySide6.QtGui import QPixmap +from PySide6.QtWidgets import QApplication, QLayout + +from spreadsheet import SpreadSheet + +if __name__ == "__main__": + app = QApplication() + + sheet = SpreadSheet(10, 6) + sheet.setWindowIcon(QPixmap(":/images/interview.png")) + sheet.show() + sheet.layout().setSizeConstraint(QLayout.SetFixedSize) + + sys.exit(app.exec()) diff --git a/examples/widgets/itemviews/spreadsheet/spreadsheet.py b/examples/widgets/itemviews/spreadsheet/spreadsheet.py new file mode 100644 index 000000000..82ebe5ebb --- /dev/null +++ b/examples/widgets/itemviews/spreadsheet/spreadsheet.py @@ -0,0 +1,544 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +from PySide6.QtCore import QPoint, Qt, QCoreApplication, Slot +from PySide6.QtGui import QAction, QBrush, QPixmap, QColor, QPainter +from PySide6.QtWidgets import (QColorDialog, QComboBox, QDialog, QFontDialog, + QGroupBox, QHBoxLayout, QMainWindow, QLabel, + QLineEdit, QMessageBox, QPushButton, QToolBar, + QTableWidgetItem, QTableWidget, QVBoxLayout, QWidget) + +from spreadsheetdelegate import SpreadSheetDelegate +from spreadsheetitem import SpreadSheetItem + +from typing import Optional +from numbers import Number + + +class SpreadSheet(QMainWindow): + def __init__(self, rows: Number, cols: Number, parent: Optional[QWidget] = None) -> None: + super().__init__(parent) + + self._tool_bar = QToolBar(self) + self._color_action = QAction() + self._font_action = QAction() + self._first_separator = QAction() + self._cell_sum_action = QAction() + self._cell_add_action = QAction() + self._cell_sub_action = QAction() + self._cell_mul_action = QAction() + self._cell_div_action = QAction() + self._second_separator = QAction() + self._clear_action = QAction() + self._about_spreadsheet = QAction() + self._exit_action = QAction() + + # self._print_action = QAction() + + self._cell_label = QLabel(self._tool_bar) + self._table = QTableWidget(rows, cols, self) + self._formula_input = QLineEdit(self) + + self.addToolBar(self._tool_bar) + + self._cell_label.setMinimumSize(80, 0) + + self._tool_bar.addWidget(self._cell_label) + self._tool_bar.addWidget(self._formula_input) + + self._table.setSizeAdjustPolicy(QTableWidget.SizeAdjustPolicy.AdjustToContents) + for c in range(cols): + character = chr(ord('A') + c) + self._table.setHorizontalHeaderItem(c, QTableWidgetItem(character)) + + self._table.setItemPrototype(self._table.item(rows - 1, cols - 1)) + self._table.setItemDelegate(SpreadSheetDelegate()) + + self.create_actions() + self.update_color(None) + self.setup_menu_bar() + self.setup_contents() + self.setup_context_menu() + self.setCentralWidget(self._table) + + self.statusBar() + self._table.currentItemChanged.connect(self.update_status) + self._table.currentItemChanged.connect(self.update_color) + self._table.currentItemChanged.connect(self.update_line_edit) + self._table.itemChanged.connect(self.update_status) + self._formula_input.returnPressed.connect(self.return_pressed) + self._table.itemChanged.connect(self.update_line_edit) + + self.setWindowTitle("Spreadsheet") + + def create_actions(self) -> None: + self._cell_sum_action = QAction("Sum", self) + self._cell_sum_action.triggered.connect(self.action_sum) + + self._cell_add_action = QAction("&Add", self) + self._cell_add_action.setShortcut(Qt.CTRL | Qt.Key_Plus) + self._cell_add_action.triggered.connect(self.action_add) + + self._cell_sub_action = QAction("&Subtract", self) + self._cell_sub_action.setShortcut(Qt.CTRL | Qt.Key_Minus) + self._cell_sub_action.triggered.connect(self.action_subtract) + + self._cell_mul_action = QAction("&Multiply", self) + self._cell_mul_action.setShortcut(Qt.CTRL | Qt.Key_multiply) + self._cell_mul_action.triggered.connect(self.action_multiply) + + self._cell_div_action = QAction("&Divide", self) + self._cell_div_action.setShortcut(Qt.CTRL | Qt.Key_division) + self._cell_div_action.triggered.connect(self.action_divide) + + self._font_action = QAction("Font...", self) + self._font_action.setShortcut(Qt.CTRL | Qt.Key_F) + self._font_action.triggered.connect(self.select_font) + + self._color_action = QAction(QPixmap(16, 16), "Background &Color...", self) + self._color_action.triggered.connect(self.select_color) + + self._clear_action = QAction("Clear", self) + self._clear_action.setShortcut(Qt.Key_Delete) + self._clear_action.triggered.connect(self.clear) + + self._about_spreadsheet = QAction("About Spreadsheet", self) + self._about_spreadsheet.triggered.connect(self.show_about) + + self._exit_action = QAction("E&xit", self) + self._exit_action.triggered.connect(QCoreApplication.quit) + + self._first_separator = QAction(self) + self._first_separator.setSeparator(True) + + self._second_separator = QAction(self) + self._second_separator.setSeparator(True) + + def setup_menu_bar(self) -> None: + file_menu = self.menuBar().addMenu("&File") + # file_menu.addAction(self._print_action) + file_menu.addAction(self._exit_action) + + cell_menu = self.menuBar().addMenu("&Cell") + cell_menu.addAction(self._cell_add_action) + cell_menu.addAction(self._cell_sub_action) + cell_menu.addAction(self._cell_mul_action) + cell_menu.addAction(self._cell_div_action) + cell_menu.addAction(self._cell_sum_action) + cell_menu.addSeparator() + cell_menu.addAction(self._color_action) + cell_menu.addAction(self._font_action) + + self.menuBar().addSeparator() + + about_menu = self.menuBar().addMenu("&Help") + about_menu.addAction(self._about_spreadsheet) + + @Slot(QTableWidgetItem) + def update_status(self, item: QTableWidgetItem) -> None: + if item and item == self._table.currentItem(): + self.statusBar().showMessage(str(item.data(Qt.StatusTipRole)), 1000) + self._cell_label.setText( + "Cell: ({})".format( + SpreadSheetItem.encode_pos(self._table.row(item), self._table.column(item)) + ) + ) + + @Slot(QTableWidgetItem) + def update_color(self, item: QTableWidgetItem) -> None: + pix = QPixmap(16, 16) + col = QColor() + if item: + col = item.background().color() + if not col.isValid(): + col = self.palette().base().color() + + pt = QPainter(pix) + pt.fillRect(0, 0, 16, 16, col) + + lighter = col.lighter() + pt.setPen(lighter) + light_frame = [QPoint(0, 15), QPoint(0, 0), QPoint(15, 0)] + pt.drawPolyline(light_frame) + + pt.setPen(col.darker()) + darkFrame = [QPoint(1, 15), QPoint(15, 15), QPoint(15, 1)] + pt.drawPolyline(darkFrame) + + pt.end() + + self._color_action.setIcon(pix) + + @Slot(QTableWidgetItem) + def update_line_edit(self, item: QTableWidgetItem) -> None: + if item != self._table.currentItem(): + return + if item: + self._formula_input.setText(str(item.data(Qt.EditRole))) + else: + self._formula_input.clear() + + @Slot() + def return_pressed(self) -> None: + text = self._formula_input.text() + row = self._table.currentRow() + col = self._table.currentColumn() + item = self._table.item(row, col) + if not item: + self._table.setItem(row, col, SpreadSheetItem(text)) + else: + item.setData(Qt.EditRole, text) + self._table.viewport().update() + + @Slot() + def select_color(self) -> None: + item = self._table.currentItem() + col = item.background().color() if item else self._table.palette().base().color() + col = QColorDialog.getColor(col, self) + if not col.isValid(): + return + + selected = self._table.selectedItems() + if not selected: + return + + for i in selected: + if i: + i.setBackground(col) + + self.update_color(self._table.currentItem()) + + @Slot() + def select_font(self) -> None: + selected = self._table.selectedItems() + if not selected: + return + + ok = False + fnt = QFontDialog.getFont(ok, self.font(), self) + + if not ok: + return + for i in selected: + if i: + i.setFont(fnt) + + def run_input_dialog(self, title: str, c1Text: str, c2Text: str, opText: str, + outText: str, cell1: str, cell2: str, outCell: str) -> bool: + rows, cols = [], [] + for c in range(self._table.columnCount()): + cols.append(chr(ord('A') + c)) + for r in range(self._table.rowCount()): + rows.append(str(1 + r)) + + add_dialog = QDialog(self) + add_dialog.setWindowTitle(title) + + group = QGroupBox(title, add_dialog) + group.setMinimumSize(250, 100) + + cell1_label = QLabel(c1Text, group) + cell1_row_input = QComboBox(group) + c1_row, c1_col = SpreadSheetItem.decode_pos(cell1) + cell1_row_input.addItems(rows) + cell1_row_input.setCurrentIndex(c1_row) + + cell1_col_input = QComboBox(group) + cell1_col_input.addItems(cols) + cell1_col_input.setCurrentIndex(c1_col) + + operator_label = QLabel(opText, group) + operator_label.setAlignment(Qt.AlignHCenter) + + cell2_label = QLabel(c2Text, group) + cell2_row_input = QComboBox(group) + c2_row, c2_col = SpreadSheetItem.decode_pos(cell2) + cell2_row_input.addItems(rows) + cell2_row_input.setCurrentIndex(c2_row) + cell2_col_input = QComboBox(group) + cell2_col_input.addItems(cols) + cell2_col_input.setCurrentIndex(c2_col) + + equals_label = QLabel("=", group) + equals_label.setAlignment(Qt.AlignHCenter) + + out_label = QLabel(outText, group) + out_row_input = QComboBox(group) + out_row, out_col = SpreadSheetItem.decode_pos(outCell) + out_row_input.addItems(rows) + out_row_input.setCurrentIndex(out_row) + out_col_input = QComboBox(group) + out_col_input.addItems(cols) + out_col_input.setCurrentIndex(out_col) + + cancel_button = QPushButton("Cancel", add_dialog) + cancel_button.clicked.connect(add_dialog.reject) + + ok_button = QPushButton("OK", add_dialog) + ok_button.setDefault(True) + ok_button.clicked.connect(add_dialog.accept) + + buttons_layout = QHBoxLayout() + buttons_layout.addStretch(1) + buttons_layout.addWidget(ok_button) + buttons_layout.addSpacing(10) + buttons_layout.addWidget(cancel_button) + + dialog_layout = QVBoxLayout(add_dialog) + dialog_layout.addWidget(group) + dialog_layout.addStretch(1) + dialog_layout.addItem(buttons_layout) + + cell1_layout = QHBoxLayout() + cell1_layout.addWidget(cell1_label) + cell1_layout.addSpacing(10) + cell1_layout.addWidget(cell1_col_input) + cell1_layout.addSpacing(10) + cell1_layout.addWidget(cell1_row_input) + + cell2_layout = QHBoxLayout() + cell2_layout.addWidget(cell2_label) + cell2_layout.addSpacing(10) + cell2_layout.addWidget(cell2_col_input) + cell2_layout.addSpacing(10) + cell2_layout.addWidget(cell2_row_input) + + out_layout = QHBoxLayout() + out_layout.addWidget(out_label) + out_layout.addSpacing(10) + out_layout.addWidget(out_col_input) + out_layout.addSpacing(10) + out_layout.addWidget(out_row_input) + + v_layout = QVBoxLayout(group) + v_layout.addItem(cell1_layout) + v_layout.addWidget(operator_label) + v_layout.addItem(cell2_layout) + v_layout.addWidget(equals_label) + v_layout.addStretch(1) + v_layout.addItem(out_layout) + + if add_dialog.exec(): + cell1 = cell1_col_input.currentText() + cell1_row_input.currentText() + cell2 = cell2_col_input.currentText() + cell2_row_input.currentText() + outCell = out_col_input.currentText() + out_row_input.currentText() + return True + + return False + + @Slot() + def action_sum(self) -> None: + row_first = row_last = row_cur = 0 + col_first = col_last = col_cur = 0 + + selected = self._table.selectedItems() + + if selected is not None: + first = selected[0] + last = selected[-1] + row_first = self._table.row(first) + row_last = self._table.row(last) + col_first = self._table.column(first) + col_last = self._table.column(last) + + current = self._table.currentItem() + + if current: + row_cur = self._table.row(current) + col_cur = self._table.column(current) + + cell1 = SpreadSheetItem.encode_pos(row_first, col_first) + cell2 = SpreadSheetItem.encode_pos(row_last, col_last) + out = SpreadSheetItem.encode_pos(row_cur, col_cur) + + if self.run_input_dialog( + "Sum cells", "First cell:", "Last cell:", + f"{(chr(0x03a3))}", "Output to:", + cell1, cell2, out + ): + row, col = SpreadSheetItem.decode_pos(out) + self._table.item(row, col).setText(f"sum {cell1} {cell2}") + + def action_math_helper(self, title: str, op: str) -> None: + cell1 = "C1" + cell2 = "C2" + out = "C3" + + current = self._table.currentItem() + if current: + out = SpreadSheetItem.encode_pos(self._table.currentRow(), self._table.currentColumn()) + + if self.run_input_dialog(title, "Cell 1", "Cell 2", op, "Output to:", cell1, cell2, out): + row, col = SpreadSheetItem.decode_pos(out) + self._table.item(row, col).setText(f"{op} {cell1} {cell2}") + + @Slot() + def action_add(self) -> None: + self.action_math_helper("Addition", "+") + + @Slot() + def action_subtract(self) -> None: + self.action_math_helper("Subtraction", "-") + + @Slot() + def action_multiply(self) -> None: + self.action_math_helper("Multiplication", "*") + + @Slot() + def action_divide(self) -> None: + self.action_math_helper("Division", "/") + + @Slot() + def clear(self) -> None: + selected_items = self._table.selectedItems() + for item in selected_items: + item.setText("") + + def setup_context_menu(self) -> None: + self.addAction(self._cell_add_action) + self.addAction(self._cell_sub_action) + self.addAction(self._cell_mul_action) + self.addAction(self._cell_div_action) + self.addAction(self._cell_sum_action) + self.addAction(self._first_separator) + self.addAction(self._color_action) + self.addAction(self._font_action) + self.addAction(self._second_separator) + self.addAction(self._clear_action) + self.setContextMenuPolicy(Qt.ActionsContextMenu) + + def setup_contents(self) -> None: + title_background = QBrush(Qt.lightGray) + title_font = self._table.font() + title_font.setBold(True) + + # column 0 + self._table.setItem(0, 0, SpreadSheetItem("Item")) + self._table.item(0, 0).setBackground(title_background) + self._table.item(0, 0).setToolTip( + "This column shows the purchased item/service" + ) + self._table.item(0, 0).setFont(title_font) + + self._table.setItem(1, 0, SpreadSheetItem("AirportBus")) + self._table.setItem(2, 0, SpreadSheetItem("Flight (Munich)")) + self._table.setItem(3, 0, SpreadSheetItem("Lunch")) + self._table.setItem(4, 0, SpreadSheetItem("Flight (LA)")) + self._table.setItem(5, 0, SpreadSheetItem("Taxi")) + self._table.setItem(6, 0, SpreadSheetItem("Dinner")) + self._table.setItem(7, 0, SpreadSheetItem("Hotel")) + self._table.setItem(8, 0, SpreadSheetItem("Flight (Oslo)")) + self._table.setItem(9, 0, SpreadSheetItem("Total:")) + + self._table.item(9, 0).setFont(title_font) + self._table.item(9, 0).setBackground(title_background) + + # column 1 + self._table.setItem(0, 1, SpreadSheetItem("Date")) + self._table.item(0, 1).setBackground(title_background) + self._table.item(0, 1).setToolTip( + "This column shows the purchase date, double click to change" + ) + self._table.item(0, 1).setFont(title_font) + + self._table.setItem(1, 1, SpreadSheetItem("15/6/2006")) + self._table.setItem(2, 1, SpreadSheetItem("15/6/2006")) + self._table.setItem(3, 1, SpreadSheetItem("15/6/2006")) + self._table.setItem(4, 1, SpreadSheetItem("21/5/2006")) + self._table.setItem(5, 1, SpreadSheetItem("16/6/2006")) + self._table.setItem(6, 1, SpreadSheetItem("16/6/2006")) + self._table.setItem(7, 1, SpreadSheetItem("16/6/2006")) + self._table.setItem(8, 1, SpreadSheetItem("18/6/2006")) + + self._table.setItem(9, 1, SpreadSheetItem()) + self._table.item(9, 1).setBackground(title_background) + + # column 2 + self._table.setItem(0, 2, SpreadSheetItem("Price")) + self._table.item(0, 2).setBackground(title_background) + self._table.item(0, 2).setToolTip("This column shows the price of the purchase") + self._table.item(0, 2).setFont(title_font) + + self._table.setItem(1, 2, SpreadSheetItem("150")) + self._table.setItem(2, 2, SpreadSheetItem("2350")) + self._table.setItem(3, 2, SpreadSheetItem("-14")) + self._table.setItem(4, 2, SpreadSheetItem("980")) + self._table.setItem(5, 2, SpreadSheetItem("5")) + self._table.setItem(6, 2, SpreadSheetItem("120")) + self._table.setItem(7, 2, SpreadSheetItem("300")) + self._table.setItem(8, 2, SpreadSheetItem("1240")) + + self._table.setItem(9, 2, SpreadSheetItem()) + self._table.item(9, 2).setBackground(Qt.lightGray) + + # column 3 + self._table.setItem(0, 3, SpreadSheetItem("Currency")) + self._table.item(0, 3).setBackground(title_background) + self._table.item(0, 3).setToolTip("This column shows the currency") + self._table.item(0, 3).setFont(title_font) + + self._table.setItem(1, 3, SpreadSheetItem("NOK")) + self._table.setItem(2, 3, SpreadSheetItem("NOK")) + self._table.setItem(3, 3, SpreadSheetItem("EUR")) + self._table.setItem(4, 3, SpreadSheetItem("EUR")) + self._table.setItem(5, 3, SpreadSheetItem("USD")) + self._table.setItem(6, 3, SpreadSheetItem("USD")) + self._table.setItem(7, 3, SpreadSheetItem("USD")) + self._table.setItem(8, 3, SpreadSheetItem("USD")) + + self._table.setItem(9, 3, SpreadSheetItem()) + self._table.item(9, 3).setBackground(Qt.lightGray) + + # column 4 + self._table.setItem(0, 4, SpreadSheetItem("Ex. Rate")) + self._table.item(0, 4).setBackground(title_background) + self._table.item(0, 4).setToolTip("This column shows the exchange rate to NOK") + self._table.item(0, 4).setFont(title_font) + + self._table.setItem(1, 4, SpreadSheetItem("1")) + self._table.setItem(2, 4, SpreadSheetItem("1")) + self._table.setItem(3, 4, SpreadSheetItem("8")) + self._table.setItem(4, 4, SpreadSheetItem("8")) + self._table.setItem(5, 4, SpreadSheetItem("7")) + self._table.setItem(6, 4, SpreadSheetItem("7")) + self._table.setItem(7, 4, SpreadSheetItem("7")) + self._table.setItem(8, 4, SpreadSheetItem("7")) + + self._table.setItem(9, 4, SpreadSheetItem()) + self._table.item(9, 4).setBackground(title_background) + + # column 5 + self._table.setItem(0, 5, SpreadSheetItem("NOK")) + self._table.item(0, 5).setBackground(title_background) + self._table.item(0, 5).setToolTip("This column shows the expenses in NOK") + self._table.item(0, 5).setFont(title_font) + + self._table.setItem(1, 5, SpreadSheetItem("* C2 E2")) + self._table.setItem(2, 5, SpreadSheetItem("* C3 E3")) + self._table.setItem(3, 5, SpreadSheetItem("* C4 E4")) + self._table.setItem(4, 5, SpreadSheetItem("* C5 E5")) + self._table.setItem(5, 5, SpreadSheetItem("* C6 E6")) + self._table.setItem(6, 5, SpreadSheetItem("* C7 E7")) + self._table.setItem(7, 5, SpreadSheetItem("* C8 E8")) + self._table.setItem(8, 5, SpreadSheetItem("* C9 E9")) + + self._table.setItem(9, 5, SpreadSheetItem("sum F2 F9")) + self._table.item(9, 5).setBackground(title_background) + + @Slot() + def show_about(self) -> None: + html_text = ( + "<HTML>" + "<p><b>This demo shows use of <c>QTableWidget</c> with custom handling for" + " individual cells.</b></p>" + "<p>Using a customized table item we make it possible to have dynamic" + " output in different cells. The content that is implemented for this" + " particular demo is:" + "<ul>" + "<li>Adding two cells.</li>" + "<li>Subtracting one cell from another.</li>" + "<li>Multiplying two cells.</li>" + "<li>Dividing one cell with another.</li>" + "<li>Summing the contents of an arbitrary number of cells.</li>" + "</HTML>") + QMessageBox.about(self, "About Spreadsheet", html_text) diff --git a/examples/widgets/itemviews/spreadsheet/spreadsheetdelegate.py b/examples/widgets/itemviews/spreadsheet/spreadsheetdelegate.py new file mode 100644 index 000000000..57aba6f47 --- /dev/null +++ b/examples/widgets/itemviews/spreadsheet/spreadsheetdelegate.py @@ -0,0 +1,67 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +from PySide6.QtCore import (QAbstractItemModel, QDate, QModelIndex, QObject, + QStringListModel, Qt, Slot) +from PySide6.QtWidgets import (QCompleter, QDateTimeEdit, QLineEdit, + QStyleOptionViewItem, QStyledItemDelegate, QWidget) + +from typing import Optional + + +class SpreadSheetDelegate(QStyledItemDelegate): + def __init__(self, parent: Optional[QObject] = None) -> None: + super().__init__(parent) + + def create_editor(self, parent: QWidget, + option: QStyleOptionViewItem, + index: QModelIndex) -> QWidget: + if index.column() == 1: + editor = QDateTimeEdit(parent) + editor.setDisplayFormat("dd/M/yyyy") + editor.setCalendarPopup(True) + return editor + + editor = QLineEdit(parent) + + # create a completer with the strings in the column as model + allStrings = QStringListModel() + for i in range(1, index.model().rowCount()): + strItem = str(index.model().data(index.sibling(i, index.column()), Qt.EditRole)) + + if not allStrings.contains(strItem): + allStrings.append(strItem) + + autoComplete = QCompleter(allStrings) + editor.setCompleter(autoComplete) + editor.editingFinished.connect(SpreadSheetDelegate.commit_and_close_editor) + return editor + + @Slot() + def commit_and_close_editor(self) -> None: + editor = self.sender() + self.commitData.emit(editor) + self.closeEditor.emit(editor) + + def set_editor_data(self, editor: QWidget, index: QModelIndex) -> None: + edit = QLineEdit(editor) + if edit: + edit.setText(str(index.model().data(index, Qt.EditRole))) + return + + dateEditor = QDateTimeEdit(editor) + if dateEditor: + dateEditor.setDate( + QDate.fromString( + str(index.model().data(index, Qt.EditRole)), "d/M/yyyy")) + + def set_model_data(self, editor: QWidget, + model: QAbstractItemModel, index: QModelIndex) -> None: + edit = QLineEdit(editor) + if edit: + model.setData(index, edit.text()) + return + + dateEditor = QDateTimeEdit(editor) + if dateEditor: + model.setData(index, dateEditor.date().toString("dd/M/yyyy")) diff --git a/examples/widgets/itemviews/spreadsheet/spreadsheetitem.py b/examples/widgets/itemviews/spreadsheet/spreadsheetitem.py new file mode 100644 index 000000000..dc70da883 --- /dev/null +++ b/examples/widgets/itemviews/spreadsheet/spreadsheetitem.py @@ -0,0 +1,122 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +from typing import Any, Tuple +from PySide6.QtCore import QMetaType, Qt +from PySide6.QtWidgets import QTableWidget, QTableWidgetItem + + +class SpreadSheetItem(QTableWidgetItem): + is_resolving = False + + def __init_subclass__(cls) -> None: + return super().__init_subclass__() + + def data(self, role: int) -> Any: + if role == Qt.EditRole or role == Qt.StatusTipRole: + return self.formula() + + if role == Qt.DisplayRole: + return self.display() + + t = str(self.display()) + + if role == Qt.ForegroundRole: + try: + number = int(t) + color = Qt.red if number < 0 else Qt.blue + except ValueError: + color = Qt.black + return color + + if role == Qt.TextAlignmentRole: + if t and (t[0].isdigit() or t[0] == '-'): + return int(Qt.AlignRight | Qt.AlignVCenter) + + return super().data(role) + + def setData(self, role: int, value: Any) -> None: + super().setData(role, value) + if self.tableWidget(): + self.tableWidget().viewport().update() + + def display(self) -> QMetaType.Type.QVariant: + # avoid circular dependencies + if self.is_resolving: + return QMetaType.Type.QVariant + + self.is_resolving = True + result = self.compute_formula(self.formula(), self.tableWidget(), self) + self.is_resolving = False + return result + + def formula(self) -> None: + return str(super().data(Qt.DisplayRole)) + + def compute_formula(self, formula: str, widget: QTableWidget, this) -> QMetaType.Type.QVariant: + # check if the string is actually a formula or not + list_ = formula.split(' ') + if not list_ or not widget: + return formula # it is a normal string + + op = list_[0].lower() if list_[0] else "" + + first_row = -1 + first_col = -1 + second_row = -1 + second_col = -1 + + if len(list_) > 1: + SpreadSheetItem.decode_pos(list_[1]) + + if len(list_) > 2: + SpreadSheetItem.decode_pos(list_[2]) + + start = widget.item(first_row, first_col) + end = widget.item(second_row, second_col) + + first_val = int(start.text()) if start else 0 + second_val = int(end.text()) if start else 0 + + if op == "sum": + sum = 0 + for r in range(first_row, second_row + 1): + for c in range(first_col, second_col + 1): + table_item = widget.item(r, c) + if table_item and table_item != this: + sum += int(table_item.text()) + + result = sum + elif op == "+": + result = first_val + second_val + elif op == "-": + result = first_val - second_val + elif op == "*": + result = first_val * second_val + elif op == "/": + if second_val == 0: + result = "nan" + else: + result = first_val / second_val + elif op == "=": + if start: + result = start.text() + else: + result = formula + + return result + + def decode_pos(pos: str) -> Tuple[int, int]: + if (not pos): + col = -1 + row = -1 + else: + col = ord(pos[0].encode("latin1")) - ord('A') + try: + row = int(pos[1:]) - 1 + except ValueError: + row = -1 + return row, col + + def encode_pos(row: int, col: int) -> str: + return str(chr(col + ord('A'))) + str(row + 1) diff --git a/examples/widgets/itemviews/stardelegate/stardelegate.py b/examples/widgets/itemviews/stardelegate/stardelegate.py index 87bf1ff48..973eb14f6 100644 --- a/examples/widgets/itemviews/stardelegate/stardelegate.py +++ b/examples/widgets/itemviews/stardelegate/stardelegate.py @@ -111,15 +111,15 @@ if __name__ == "__main__": # Create and populate the tableWidget table_widget = QTableWidget(4, 4) table_widget.setItemDelegate(StarDelegate()) - table_widget.setEditTriggers(QAbstractItemView.DoubleClicked | - QAbstractItemView.SelectedClicked) + table_widget.setEditTriggers(QAbstractItemView.DoubleClicked + | QAbstractItemView.SelectedClicked) table_widget.setSelectionBehavior(QAbstractItemView.SelectRows) table_widget.setHorizontalHeaderLabels(["Title", "Genre", "Artist", "Rating"]) - data = [ ["Mass in B-Minor", "Baroque", "J.S. Bach", 5], - ["Three More Foxes", "Jazz", "Maynard Ferguson", 4], - ["Sex Bomb", "Pop", "Tom Jones", 3], - ["Barbie Girl", "Pop", "Aqua", 5] ] + data = [["Mass in B-Minor", "Baroque", "J.S. Bach", 5], + ["Three More Foxes", "Jazz", "Maynard Ferguson", 4], + ["Sex Bomb", "Pop", "Tom Jones", 3], + ["Barbie Girl", "Pop", "Aqua", 5]] for r in range(len(data)): table_widget.setItem(r, 0, QTableWidgetItem(data[r][0])) diff --git a/examples/widgets/itemviews/stardelegate/stareditor.py b/examples/widgets/itemviews/stardelegate/stareditor.py index 1b44164a8..296afa950 100644 --- a/examples/widgets/itemviews/stardelegate/stareditor.py +++ b/examples/widgets/itemviews/stardelegate/stareditor.py @@ -55,8 +55,7 @@ class StarEditor(QWidget): """ Calculate which star the user's mouse cursor is currently hovering over. """ - star = (x / (self.star_rating.sizeHint().width() / - self.star_rating.MAX_STAR_COUNT)) + 1 + star = (x / (self.star_rating.sizeHint().width() / self.star_rating.MAX_STAR_COUNT)) + 1 if (star <= 0) or (star > self.star_rating.MAX_STAR_COUNT): return -1 diff --git a/examples/widgets/itemviews/stardelegate/starrating.py b/examples/widgets/itemviews/stardelegate/starrating.py index 4d49f7405..28dbacd6f 100644 --- a/examples/widgets/itemviews/stardelegate/starrating.py +++ b/examples/widgets/itemviews/stardelegate/starrating.py @@ -23,13 +23,13 @@ class StarRating(object): self._star_polygon.append(QPointF(1.0, 0.5)) for i in range(1, 5): self._star_polygon.append(QPointF(0.5 + 0.5 * cos(0.8 * i * pi), - 0.5 + 0.5 * sin(0.8 * i * pi))) + 0.5 + 0.5 * sin(0.8 * i * pi))) # Create the diamond shape we'll show in the editor self._diamond_polygon = QPolygonF() diamond_points = [QPointF(0.4, 0.5), QPointF(0.5, 0.4), - QPointF(0.6, 0.5), QPointF(0.5, 0.6), - QPointF(0.4, 0.5)] + QPointF(0.6, 0.5), QPointF(0.5, 0.6), + QPointF(0.4, 0.5)] self._diamond_polygon.append(diamond_points) def sizeHint(self): |