# -*- coding: utf-8 -*- # gui/components/_tree_node_building.py """Сервис построения узлов дерева и ленивой загрузки (композиция).""" from __future__ import annotations from typing import TYPE_CHECKING, Dict, Any from PySide6.QtCore import Qt from PySide6.QtWidgets import QTreeWidgetItem if TYPE_CHECKING: from gui.components.topology_tree_widget import TopologyTreeWidget class TreeNodeBuilder: """Создание элементов QTreeWidget и ленивая загрузка дочерних узлов.""" def __init__(self, host: "TopologyTreeWidget") -> None: self._host = host def load_root_nodes(self) -> None: """Загрузка корневых узлов (сайтов).""" try: root_nodes = self._host.data_loader('site', None) for node_data in root_nodes: item = self._create_tree_item(node_data) self._host._tree.addTopLevelItem(item) if node_data.has_children and not node_data.children_loaded: stub = QTreeWidgetItem(["Загрузка..."]) stub.setData(0, Qt.UserRole, {"is_stub": True}) item.addChild(stub) except Exception as e: error_msg = f"Ошибка загрузки корневых узлов: {e}" print(error_msg) self._host.dataLoadError.emit(error_msg) def _create_tree_item(self, node_data) -> QTreeWidgetItem: """Создание элемента дерева на основе данных узла.""" item = QTreeWidgetItem() display_attrs = self._host.DISPLAY_ATTRIBUTES.get(node_data.node_type, []) col_values = self._get_column_values( node_data.display_data, display_attrs, node_data.node_type, ) for i, value in enumerate(col_values): if i < self._host._tree.columnCount(): item.setText(i, str(value)) item.setData(0, Qt.UserRole, { 'type': node_data.node_type, 'id': node_data.node_id, 'display_data': node_data.display_data, 'has_children': node_data.has_children, 'children_loaded': node_data.children_loaded, 'raw_data': node_data.raw_data, }) item.setChildIndicatorPolicy( QTreeWidgetItem.ShowIndicator if node_data.has_children else QTreeWidgetItem.DontShowIndicator ) return item @staticmethod def _get_column_values( data: Dict[str, Any], attrs: list[str], node_type: str, ) -> list[str]: """Значения для колонок (только значения атрибутов, без ключей).""" values = [] if attrs: for attr in attrs: if attr in data and data[attr]: values.append(str(data[attr])) break else: values.append("") else: values.append("") if len(attrs) > 1: other = [str(data[a]) for a in attrs[1:] if a in data and data[a]] sep = " " if node_type == "shelf" else ", " values.append(sep.join(other)) else: values.append("") return values def load_children(self, parent_item: QTreeWidgetItem) -> None: """Ленивая загрузка дочерних элементов для узла.""" parent_data = parent_item.data(0, Qt.UserRole) if not parent_data: return parent_type = parent_data['type'] parent_id = parent_data['id'] node_key = f"{parent_type}:{parent_id}" if node_key in self._host._loading_nodes: return self._host._loading_nodes.add(node_key) try: if parent_item.childCount() == 1: child = parent_item.child(0) child_data = child.data(0, Qt.UserRole) if child else {} if child_data and child_data.get('is_stub'): parent_item.removeChild(child) children_data = self._host.data_loader( self._get_child_type(parent_type), parent_id, ) for child_data in children_data: child_item = self._create_tree_item(child_data) parent_item.addChild(child_item) if child_data.has_children and not child_data.children_loaded: stub = QTreeWidgetItem(["Загрузка..."]) stub.setData(0, Qt.UserRole, {"is_stub": True}) child_item.addChild(stub) parent_data['children_loaded'] = True parent_item.setData(0, Qt.UserRole, parent_data) self._host._tree.resizeColumnToContents(0) except Exception as e: msg = f"Ошибка загрузки дочерних элементов для {parent_type} {parent_id}: {e}" print(msg) self._host.dataLoadError.emit(msg) err_item = QTreeWidgetItem(["Ошибка загрузки", str(e)[:50]]) err_item.setData(0, Qt.UserRole, {"is_error": True}) parent_item.addChild(err_item) finally: self._host._loading_nodes.remove(node_key) @staticmethod def _get_child_type(parent_type: str) -> str: """Тип дочерних элементов на основе типа родителя.""" hierarchy = { 'site': 'facility', 'facility': 'zone', 'zone': 'rack', 'rack': 'shelf', 'shelf': 'cell', 'cell': 'volume', } return hierarchy.get(parent_type, '')