From eae788072fff001028e0c8f04babd61325125281 Mon Sep 17 00:00:00 2001 From: Adrian Herrmann Date: Mon, 11 Jul 2022 11:29:35 +0200 Subject: PySide6-examples: SpreadSheet example Port pre-existing SpreadSheet example to PySide. Pick-to: 6.2 6.3 Change-Id: I32d014fbd5e3e406672b6bec2465c6e41c3a6580 Reviewed-by: Cristian Maureira-Fredes --- .../itemviews/spreadsheet/spreadsheetitem.py | 122 +++++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 examples/widgets/itemviews/spreadsheet/spreadsheetitem.py (limited to 'examples/widgets/itemviews/spreadsheet/spreadsheetitem.py') 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) -- cgit v1.2.3