Advertisement
Guest User

Untitled

a guest
Jul 28th, 2021
68
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 8.24 KB | None | 0 0
  1. from __future__ import annotations
  2.  
  3. import string
  4. import sys
  5. from enum import IntEnum
  6. from random import choices
  7. from typing import List, Optional, Tuple, Union, cast
  8.  
  9. from PySide2.QtCore import (
  10.     QAbstractItemModel,
  11.     QModelIndex,
  12.     QObject,
  13.     QSortFilterProxyModel,
  14.     Qt,
  15.     Signal,
  16. )
  17. from PySide2.QtWidgets import QApplication, QTreeView
  18.  
  19.  
  20. class Node:
  21.     """Node inside the tree model."""
  22.  
  23.     def __init__(self, name: Optional[str]) -> None:
  24.         """Create a new Node."""
  25.         self._name = name
  26.         self._children: List[Node] = []
  27.         self._parent: Optional[Node] = None
  28.         self._check_state: Qt.CheckState = Qt.CheckState.Unchecked
  29.  
  30.     @property
  31.     def name(self) -> Optional[str]:
  32.         """Return the name of the Node."""
  33.         return self._name
  34.  
  35.     @property
  36.     def child_count(self) -> int:
  37.         """Return the number of children for the Node."""
  38.         return len(self._children)
  39.  
  40.     def child(self, row: int) -> Node:
  41.         """Return the child for the given row."""
  42.         return self._children[row]
  43.  
  44.     @property
  45.     def children(self) -> List[Node]:
  46.         """Return the list of children."""
  47.         return self._children
  48.  
  49.     @property
  50.     def parent(self) -> Optional[Node]:
  51.         """Return the Node's parent element."""
  52.         return self._parent
  53.  
  54.     @parent.setter
  55.     def parent(self, parent: Node) -> None:
  56.         """Set the Node's parent element."""
  57.         self._parent = parent
  58.  
  59.     @property
  60.     def row(self) -> int:
  61.         """Return the row number of the Node."""
  62.         if not self.parent:
  63.             return -1
  64.         return self.parent.children.index(self)
  65.  
  66.     def add_child(self, child: Node) -> None:
  67.         """Add the given child to the Node's children."""
  68.         child.parent = self
  69.         self._children.append(child)
  70.  
  71.     def remove_child(self, child: Node) -> None:
  72.         """Remove the given child to the Node's children."""
  73.         self._children.remove(child)
  74.         child.parent = None
  75.  
  76.     @property
  77.     def check_state(self) -> Qt.CheckState:
  78.         """Return the check state of the Node."""
  79.         return self._check_state
  80.  
  81.     @check_state.setter
  82.     def check_state(self, check_state: Qt.CheckState) -> None:
  83.         """Set the check state of the Node."""
  84.         self._check_state = check_state
  85.  
  86.  
  87. class Model(QAbstractItemModel):
  88.     """Model for data in a QTreeView."""
  89.  
  90.     add_filter = Signal(tuple, name="add_filter")
  91.     remove_filter = Signal(tuple, name="remove_filter")
  92.  
  93.     class Header(IntEnum):
  94.         """Header definitions."""
  95.  
  96.         NAME = 0
  97.  
  98.     def __init__(self, parent: Optional[QObject] = None) -> None:
  99.         """Create a new Model to show data in a QTreeView."""
  100.         super().__init__(parent)
  101.         self._header_labels = {
  102.             Model.Header.NAME: self.tr("Name"),
  103.         }
  104.         self._root = Node(None)
  105.         self._path_cache: List[Tuple[str, ...]] = []
  106.         self._blocked = False
  107.  
  108.     def flags(self, index: QModelIndex) -> Qt.ItemFlags:
  109.         """Return the flags for the given index and model."""
  110.         if index.isValid():
  111.             return super().flags(index) | Qt.ItemFlag.ItemIsUserCheckable
  112.         return super().flags(index)
  113.  
  114.     def columnCount(  # pylint: disable=invalid-name, no-self-use
  115.         self, _: QModelIndex = QModelIndex()
  116.     ) -> int:
  117.         """Return the column count."""
  118.         return len(self._header_labels)
  119.  
  120.     def rowCount(  # pylint: disable=invalid-name
  121.         self, parent: QModelIndex = QModelIndex()
  122.     ) -> int:
  123.         """Return the row count."""
  124.         if parent.isValid():
  125.             return cast(Node, parent.internalPointer()).child_count
  126.         count = self._root.child_count
  127.         return count
  128.  
  129.     def headerData(  # pylint: disable=invalid-name
  130.         self,
  131.         section: int,
  132.         orientation: Qt.Orientation,
  133.         role: int = Qt.ItemDataRole.DisplayRole,
  134.     ) -> Optional[str]:
  135.         """Return the header data for the given section."""
  136.         if (
  137.             orientation == Qt.Orientation.Horizontal
  138.             and role == Qt.ItemDataRole.DisplayRole
  139.         ):
  140.             return self._header_labels[Model.Header(section)]
  141.         return None
  142.  
  143.     def on_add_path(self, path: Tuple[str, ...]) -> None:
  144.         """Add a path to the model if it doesn't exist yet."""
  145.         if path not in self._path_cache:
  146.             self._add_path_to_node(path, QModelIndex())
  147.             self._path_cache.append(path)
  148.  
  149.     def _add_path_to_node(
  150.         self, path: Tuple[str, ...], index: QModelIndex
  151.     ) -> None:
  152.         """Add the path to the given node if it doesn't exist yet."""
  153.         if index.isValid():
  154.             node: Node = index.internalPointer()
  155.         else:
  156.             node = self._root
  157.         for child in node.children:
  158.             if child.name == path[0]:
  159.                 self._add_path_to_node(
  160.                     path[1:], self.index(child.row, 0, index)
  161.                 )
  162.                 return
  163.         new_node = Node(path[0])
  164.         self.beginInsertRows(index, node.child_count, node.child_count)
  165.         node.add_child(new_node)
  166.         self.endInsertRows()
  167.         if path[1:]:
  168.             self._add_path_to_node(
  169.                 path[1:], self.index(new_node.row, 0, index)
  170.             )
  171.  
  172.     def parent(self, child: QModelIndex) -> QModelIndex:  # type: ignore
  173.         """Return the parent index for a given index."""
  174.         if not child.isValid():
  175.             return QModelIndex()
  176.  
  177.         child_item = child.internalPointer()
  178.         parent_item = child_item.parent
  179.  
  180.         if parent_item is self._root:
  181.             return QModelIndex()
  182.  
  183.         return self.createIndex(parent_item.row, 0, parent_item)
  184.  
  185.     def hasChildren(  # pylint: disable=invalid-name
  186.         self, index: QModelIndex = QModelIndex()
  187.     ) -> bool:
  188.         """Evaluate if children exist for the given index."""
  189.         if index.isValid():
  190.             return bool(index.internalPointer().child_count)
  191.         return bool(self._root.child_count)
  192.  
  193.     def index(
  194.         self, row: int, col: int, parent: QModelIndex = QModelIndex()
  195.     ) -> QModelIndex:
  196.         """Return an index for the given row and column and parent."""
  197.         # a not existent index should never be requested
  198.         assert self.hasIndex(row, col, parent)
  199.  
  200.         if not parent.isValid():
  201.             parent_item = self._root
  202.         else:
  203.             parent_item = parent.internalPointer()
  204.  
  205.         return self.createIndex(row, col, parent_item.child(row))
  206.  
  207.     def data(  # pylint: disable=no-self-use
  208.         self, index: QModelIndex, role: int = Qt.ItemDataRole.DisplayRole
  209.     ) -> Union[str, Qt.CheckState, None]:
  210.         """Return data for the given index and role."""
  211.         if role == Qt.ItemDataRole.DisplayRole:
  212.             item = cast(Node, index.internalPointer())
  213.             return item.name
  214.         if role == Qt.ItemDataRole.CheckStateRole:
  215.             item = cast(Node, index.internalPointer())
  216.             return item.check_state
  217.         return None
  218.  
  219.     def setData(  # pylint: disable=invalid-name
  220.             self,
  221.             index: QModelIndex,
  222.             value: Qt.CheckState,
  223.             role: int = Qt.ItemDataRole.EditRole,
  224.     ) -> bool:
  225.         """Set the data for the given index and role."""
  226.         if role == Qt.ItemDataRole.CheckStateRole:
  227.             self.layoutAboutToBeChanged.emit()
  228.             item = cast(Node, index.internalPointer())
  229.             item.check_state = Qt.CheckState(value)
  230.             self.dataChanged.emit(
  231.                 index, index, [Qt.ItemDataRole.CheckStateRole]
  232.             )
  233.             self.layoutChanged.emit()
  234.             return True
  235.         return False
  236.  
  237.  
  238. if __name__ == "__main__":
  239.     APP = QApplication(sys.argv)
  240.     model = Model()
  241.     rand_words = [
  242.         "".join(choices(string.ascii_uppercase + string.digits, k=6))
  243.         for _ in range(20)
  244.     ]
  245.     for char in "ABCDEFG":
  246.         for number in range(100000, 100010):
  247.             for word in rand_words:
  248.                 model.on_add_path((char, str(number), word))
  249.     filter_model = QSortFilterProxyModel()
  250.     filter_model.setSourceModel(model)
  251.     view = QTreeView()
  252.     view.setModel(filter_model)
  253.     view.show()
  254.     sys.exit(APP.exec_())
  255.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement