Source code for edifice.base_components.table_grid_view

from __future__ import annotations

import logging
from typing import TYPE_CHECKING

from edifice.qt import QT_VERSION

if QT_VERSION == "PyQt6" and not TYPE_CHECKING:
    from PyQt6.QtWidgets import QGridLayout, QWidget
else:
    from PySide6.QtWidgets import QGridLayout, QWidget

from .base_components import CommandType, Element, PropsDict, QtWidgetElement, _get_widget_children, _WidgetTree

logger = logging.getLogger("Edifice")


[docs] class TableGridRow(QtWidgetElement): """ Row Element of a :class:`TableGridView`. This Element’s parent must be a :class:`TableGridView`. This Element may have children of any type of :class:`Element`. This Element has no Qt underlying, and no props. """
[docs] def __init__(self): super().__init__()
def _qt_update_commands( self, widget_trees: dict[Element, _WidgetTree], # noqa: ARG002 newprops: PropsDict, # noqa: ARG002 ) -> list[CommandType]: # This element has no Qt underlying so it has nothing to do except store # the children of the row. # The _qt_update_commands for TableGridView will render the children. return []
[docs] class TableGridView(QtWidgetElement[QWidget]): """Table-style grid layout displays its children as aligned rows of columns. * Underlying Qt Layout `QGridLayout <https://doc.qt.io/qtforpython-6/PySide6/QtWidgets/QGridLayout.html>`_ This component has similar behavior to an `HTML table <https://developer.mozilla.org/en-US/docs/Web/HTML/Element/table>`_. Each column will be the width of its widest child. Each row will be the height of its tallest child. The only type of child :class:`Element` allowed in a :class:`TableGridView` is :class:`TableGridRow`. Each :class:`TableGridRow` establishes a row in the table, and may have children of any type of :class:`Element`. .. code-block:: python with TableGridView(): with TableGridRow(): Label(text="row 0 column 0") Label(text="row 0 column 1") with TableGridRow(): Label(text="row 1 column 0") with VBoxView(): Label(text="row 1 column 1") If the :class:`TableGridRow` s are added and removed dynamically then it’s a good idea to :func:`Element.set_key` each row. Args: row_stretch: *n*:sup:`th` row stretch size in proportion to the *n*:sup:`th` :code:`int` in this list. See `setRowStretch <https://doc.qt.io/qtforpython-6/PySide6/QtWidgets/QGridLayout.html#PySide6.QtWidgets.QGridLayout.setRowStretch>`_ column_stretch: *n*:sup:`th` column stretch size in proportion to the *n*:sup:`th` :code:`int` in this list. See `setColumnStretch <https://doc.qt.io/qtforpython-6/PySide6/QtWidgets/QGridLayout.html#PySide6.QtWidgets.QGridLayout.setColumnStretch>`_ row_minheight: *n*:sup:`th` row minimum height is the *n*:sup:`th` :code:`int` in this list. See `setRowMinimumHeight <https://doc.qt.io/qtforpython-6/PySide6/QtWidgets/QGridLayout.html#PySide6.QtWidgets.QGridLayout.setRowMinimumHeight>`_ column_minwidth: *n*:sup:`th` column minimum width is the *n*:sup:`th` :code:`int` in this list. See `setColumnMinimumWidth <https://doc.qt.io/qtforpython-6/PySide6/QtWidgets/QGridLayout.html#PySide6.QtWidgets.QGridLayout.setColumnMinimumWidth>`_ """
[docs] def __init__( self, row_stretch: tuple[int, ...] = (), column_stretch: tuple[int, ...] = (), row_minheight: tuple[int, ...] = (), column_minwidth: tuple[int, ...] = (), **kwargs, ): super().__init__(**kwargs) self._register_props( { "row_stretch": row_stretch, "column_stretch": column_stretch, "row_minheight": row_minheight, "column_minwidth": column_minwidth, }, ) self.underlying = None self._old_children: dict[QtWidgetElement, tuple[int, int]] = {} """Like _LinearView._widget_children""" self._row_stretch = row_stretch self._column_stretch = column_stretch self._row_minheight = row_minheight self._column_minwidth = column_minwidth
def _initialize(self): self.underlying = QWidget() self.underlying_layout = QGridLayout() self.underlying.setObjectName(str(id(self))) self.underlying.setLayout(self.underlying_layout) self.underlying_layout.setContentsMargins(0, 0, 0, 0) self.underlying_layout.setSpacing(0) def _clear(self): while self.underlying_layout.takeAt(0) is not None: pass def _add_child(self, child_component: QtWidgetElement, row: int, column: int): # https://doc.qt.io/qtforpython-6/PySide6/QtWidgets/QGridLayout.html#PySide6.QtWidgets.QGridLayout.addWidget assert child_component.underlying is not None self.underlying_layout.addWidget(child_component.underlying, row, column) if len(self._row_stretch) > row: self.underlying_layout.setRowStretch(row, self._row_stretch[row]) if len(self._column_stretch) > column: self.underlying_layout.setColumnStretch(column, self._column_stretch[column]) if len(self._row_minheight) > row: self.underlying_layout.setRowMinimumHeight(row, self._row_minheight[row]) if len(self._column_minwidth) > column: self.underlying_layout.setColumnMinimumWidth(column, self._column_minwidth[column]) def _delete_child(self, child_component: QtWidgetElement, row: int, column: int): if self.underlying_layout is None: logger.warning("_delete_child No underlying_layout " + str(self)) else: if (layoutitem := self.underlying_layout.itemAtPosition(row, column)) is None: logger.warning("_delete_child itemAtPosition failed " + str((row, column)) + " " + str(self)) else: if (w := layoutitem.widget()) is None: logger.warning("_delete_child widget is None " + str((row, column)) + " " + str(self)) else: w.deleteLater() self.underlying_layout.removeItem(layoutitem) def _soft_delete_child(self, child_component: QtWidgetElement, row: int, column: int): if self.underlying_layout is None: logger.warning("_delete_child No underlying_layout " + str(self)) else: if (layoutitem := self.underlying_layout.itemAtPosition(row, column)) is None: logger.warning("_delete_child itemAtPosition failed " + str((row, column)) + " " + str(self)) else: self.underlying_layout.removeItem(layoutitem) def _qt_update_commands( self, widget_trees: dict[Element, _WidgetTree], newprops, ): if self.underlying is None: self._initialize() assert self.underlying is not None # The following is equivalent to _LinearView._recompute_children(). # # The direct children of this Element are TableGridRow, but # the TableGridRow doesn't have a Qt instantiation so we # want to treat the TableGridRow children as the children of # the TableGridView. children = _get_widget_children(widget_trees, self) new_children: dict[QtWidgetElement, tuple[int, int]] = {} children_of_rows: list[QtWidgetElement] = [] for row, c in enumerate(children): children_of_row = _get_widget_children(widget_trees, c) children_of_rows.extend(children_of_row) for col, child in enumerate(children_of_row): new_children[child] = (row, col) old_deletions = self._old_children.items() - new_children.items() new_additions = new_children.items() - self._old_children.items() commands: list[CommandType] = [] for w, (row, column) in old_deletions: if w in new_children: commands.append(CommandType(self._soft_delete_child, w, row, column)) else: commands.append(CommandType(self._delete_child, w, row, column)) for w, (row, column) in new_additions: commands.append(CommandType(self._add_child, w, row, column)) self._old_children = new_children if "row_stretch" in newprops: commands.append(CommandType(self._set_row_stretch, newprops.row_stretch)) if "column_stretch" in newprops: commands.append(CommandType(self._set_column_stretch, newprops.column_stretch)) if "row_minheight" in newprops: commands.append(CommandType(self._set_row_minheight, newprops.row_minheight)) if "column_minwidth" in newprops: commands.append(CommandType(self._set_column_minwidth, newprops.column_minwidth)) commands.extend( super()._qt_update_commands_super(widget_trees, newprops, self.underlying, self.underlying_layout), ) return commands def _set_row_stretch(self, row_stretch: tuple[int, ...]): self._row_stretch = row_stretch for i, stretch in enumerate(row_stretch[: self.underlying_layout.rowCount()]): self.underlying_layout.setRowStretch(i, stretch) def _set_column_stretch(self, column_stretch: tuple[int, ...]): self._column_stretch = column_stretch for i, stretch in enumerate(column_stretch[: self.underlying_layout.columnCount()]): self.underlying_layout.setColumnStretch(i, stretch) def _set_row_minheight(self, row_minheight: tuple[int, ...]): self._row_minheight = row_minheight for i, minheight in enumerate(row_minheight[: self.underlying_layout.rowCount()]): self.underlying_layout.setRowMinimumHeight(i, minheight) def _set_column_minwidth(self, column_minwidth: tuple[int, ...]): self._column_minwidth = column_minwidth for i, minwidth in enumerate(column_minwidth[: self.underlying_layout.columnCount()]): self.underlying_layout.setColumnMinimumWidth(i, minwidth)