1
0

tree.py 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. from typing import Any, Callable, Dict, Iterator, List, Literal, Optional, Set
  2. from typing_extensions import Self
  3. from .. import globals # pylint: disable=redefined-builtin
  4. from ..element import Element
  5. from ..events import GenericEventArguments, ValueChangeEventArguments, handle_event
  6. class Tree(Element):
  7. def __init__(self,
  8. nodes: List[Dict], *,
  9. node_key: str = 'id',
  10. label_key: str = 'label',
  11. children_key: str = 'children',
  12. on_select: Optional[Callable[..., Any]] = None,
  13. on_expand: Optional[Callable[..., Any]] = None,
  14. on_tick: Optional[Callable[..., Any]] = None,
  15. tick_strategy: Optional[Literal['leaf', 'leaf-filtered', 'strict']] = None,
  16. ) -> None:
  17. """Tree
  18. Display hierarchical data using Quasar's `QTree <https://quasar.dev/vue-components/tree>`_ component.
  19. If using IDs, make sure they are unique within the whole tree.
  20. To use checkboxes and ``on_tick``, set the ``tick_strategy`` parameter to "leaf", "leaf-filtered" or "strict".
  21. :param nodes: hierarchical list of node objects
  22. :param node_key: property name of each node object that holds its unique id (default: "id")
  23. :param label_key: property name of each node object that holds its label (default: "label")
  24. :param children_key: property name of each node object that holds its list of children (default: "children")
  25. :param on_select: callback which is invoked when the node selection changes
  26. :param on_expand: callback which is invoked when the node expansion changes
  27. :param on_tick: callback which is invoked when a node is ticked or unticked
  28. :param tick_strategy: whether and how to use checkboxes ("leaf", "leaf-filtered" or "strict"; default: ``None``)
  29. :param default_expand_all: whether to expand all nodes by default (default: ``False``)
  30. """
  31. super().__init__('q-tree')
  32. self._props['nodes'] = nodes
  33. self._props['node-key'] = node_key
  34. self._props['label-key'] = label_key
  35. self._props['children-key'] = children_key
  36. self._props['selected'] = []
  37. self._props['expanded'] = []
  38. self._props['ticked'] = []
  39. if tick_strategy is not None:
  40. self._props['tick-strategy'] = tick_strategy
  41. def update_prop(name: str, value: Any) -> None:
  42. if self._props[name] != value:
  43. self._props[name] = value
  44. self.update()
  45. def handle_selected(e: GenericEventArguments) -> None:
  46. update_prop('selected', e.args)
  47. handle_event(on_select, ValueChangeEventArguments(sender=self, client=self.client, value=e.args))
  48. self.on('update:selected', handle_selected)
  49. def handle_expanded(e: GenericEventArguments) -> None:
  50. update_prop('expanded', e.args)
  51. handle_event(on_expand, ValueChangeEventArguments(sender=self, client=self.client, value=e.args))
  52. self.on('update:expanded', handle_expanded)
  53. def handle_ticked(e: GenericEventArguments) -> None:
  54. update_prop('ticked', e.args)
  55. handle_event(on_tick, ValueChangeEventArguments(sender=self, client=self.client, value=e.args))
  56. self.on('update:ticked', handle_ticked)
  57. def expand(self, node_keys: Optional[List[str]] = None) -> Self:
  58. """Expand the given nodes.
  59. :param node_keys: list of node keys to expand (default: all nodes)
  60. """
  61. self._props['expanded'][:] = self._find_node_keys(node_keys).union(self._props['expanded'])
  62. self.update()
  63. return self
  64. def collapse(self, node_keys: Optional[List[str]] = None) -> Self:
  65. """Collapse the given nodes.
  66. :param node_keys: list of node keys to collapse (default: all nodes)
  67. """
  68. self._props['expanded'][:] = set(self._props['expanded']).difference(self._find_node_keys(node_keys))
  69. self.update()
  70. return self
  71. def _find_node_keys(self, node_keys: Optional[List[str]] = None) -> Set[str]:
  72. if node_keys is not None:
  73. return set(node_keys)
  74. CHILDREN_KEY = self._props['children-key']
  75. NODE_KEY = self._props['node-key']
  76. def iterate_nodes(nodes: List[Dict]) -> Iterator[Dict]:
  77. for node in nodes:
  78. yield node
  79. yield from iterate_nodes(node.get(CHILDREN_KEY, []))
  80. return {node[NODE_KEY] for node in iterate_nodes(self._props['nodes'])}
  81. def props(self, add: Optional[str] = None, *, remove: Optional[str] = None) -> Self:
  82. super().props(add, remove=remove)
  83. if 'default-expand-all' in self._props:
  84. # https://github.com/zauberzeug/nicegui/issues/1385
  85. del self._props['default-expand-all']
  86. globals.log.warning('The prop "default_expand_all" is not supported by `ui.tree`.\n'
  87. 'Use ".expand()" instead.')
  88. return self