import re from PySide6.QtWidgets import ( QDialog, QTreeWidget, QTreeWidgetItem, QVBoxLayout, QPushButton, QLineEdit, QLabel, QHeaderView ) from PySide6.QtCore import Qt from setupVars import * from scanVars import * array_re = re.compile(r'^(\w+)\[(\d+)\]$') class VariableSelectorDialog(QDialog): def __init__(self, all_vars, structs, typedefs, xml_path=None, parent=None): super().__init__(parent) self.setWindowTitle("Выбор переменных") self.resize(600, 500) self.selected_names = [] self.all_vars = all_vars self.structs = structs self.typedefs = typedefs self.expanded_vars = [] self.var_map = {v['name']: v for v in all_vars} self.xml_path = xml_path # сохраняем путь к xml self.search_input = QLineEdit() self.search_input.setPlaceholderText("Поиск по имени переменной...") self.search_input.textChanged.connect(self.filter_tree) self.tree = QTreeWidget() self.tree.setHeaderLabels(["Имя переменной", "Тип"]) self.tree.setSelectionMode(QTreeWidget.ExtendedSelection) self.tree.setRootIsDecorated(True) self.tree.setUniformRowHeights(True) self.tree.setStyleSheet(""" QTreeWidget::item:selected { background-color: #87CEFA; color: black; } QTreeWidget::item:hover { background-color: #D3D3D3; } """) self.btn_add = QPushButton("Добавить выбранные") self.btn_add.clicked.connect(self.on_add_clicked) self.btn_delete = QPushButton("Удалить выбранные") self.btn_delete.clicked.connect(self.on_delete_clicked) layout = QVBoxLayout() layout.addWidget(QLabel("Поиск:")) layout.addWidget(self.search_input) layout.addWidget(self.tree) layout.addWidget(self.btn_add) layout.addWidget(self.btn_delete) # Кнопка удаления self.setLayout(layout) self.populate_tree() def add_tree_item_recursively(self, parent, var): """ Рекурсивно добавляет переменную и её дочерние поля в дерево. Если parent == None, добавляет на верхний уровень. """ name = var['name'] type_str = var.get('type', '') show_var = var.get('show_var', 'false') == 'true' item = QTreeWidgetItem([name, type_str]) item.setData(0, Qt.UserRole, name) # Делаем bitfield-поля неактивными if "(bitfield:" in type_str: item.setDisabled(True) self.set_tool(item, "Битовые поля недоступны для выбора") for i, attr in enumerate(['file', 'extern', 'static']): item.setData(0, Qt.UserRole + 1 + i, var.get(attr)) if show_var: item.setForeground(0, Qt.gray) item.setForeground(1, Qt.gray) self.set_tool(item, "Уже добавлена") if parent is None: self.tree.addTopLevelItem(item) else: parent.addChild(item) for child in var.get('children', []): self.add_tree_item_recursively(item, child) def populate_tree(self): self.tree.clear() expanded_vars = expand_vars(self.all_vars, self.structs, self.typedefs) for var in expanded_vars: self.add_tree_item_recursively(None, var) header = self.tree.header() header.setSectionResizeMode(QHeaderView.Interactive) # вручную можно менять self.tree.setColumnWidth(0, 400) self.tree.resizeColumnToContents(1) """ header.setSectionResizeMode(0, QHeaderView.Stretch) header.setSectionResizeMode(1, QHeaderView.ResizeToContents) """ def filter_tree(self): text = self.search_input.text().strip().lower() path_parts = text.split('.') if text else [] def hide_all(item): item.setHidden(True) for i in range(item.childCount()): hide_all(item.child(i)) def path_matches_search(name, search_parts): name_parts = name.lower().split('.') if len(name_parts) < len(search_parts): return False for sp, np in zip(search_parts, name_parts): if not np.startswith(sp): return False return True def show_matching_path(item, level=0): name = item.text(0).lower() # Проверяем соответствие до длины path_parts if not path_parts: matched = True else: matched = False # Проверяем совпадение по пути if path_matches_search(name, path_parts[:level+1]): matched = True item.setHidden(not matched) # Раскрываем, если это не последний уровень поиска if matched and level < len(path_parts) - 1: item.setExpanded(True) else: item.setExpanded(False) matched_any_child = False for i in range(item.childCount()): child = item.child(i) if show_matching_path(child, level + 1): matched_any_child = True return matched or matched_any_child for i in range(self.tree.topLevelItemCount()): item = self.tree.topLevelItem(i) hide_all(item) show_matching_path(item, 0) def on_add_clicked(self): self.selected_names = [] for item in self.tree.selectedItems(): name = item.text(0) # имя переменной (в колонке 1) type_str = item.text(1) # тип переменной (в колонке 2) if not name: continue self.selected_names.append((name, type_str)) if name in self.var_map: # Если переменная уже есть, просто включаем её и показываем var = self.var_map[name] var['show_var'] = 'true' var['enable'] = 'true' else: # Создаём новый элемент переменной # Получаем родительские параметры file_val = item.data(0, Qt.UserRole + 1) extern_val = item.data(0, Qt.UserRole + 2) static_val = item.data(0, Qt.UserRole + 3) new_var = { 'name': name, 'type': type_str, 'show_var': 'true', 'enable': 'true', 'shortname': name, 'pt_type': '', 'iq_type': '', 'return_type': 'iq_none', 'file': file_val, 'extern': str(extern_val).lower() if extern_val else 'false', 'static': str(static_val).lower() if static_val else 'false', } # Добавляем в список переменных self.all_vars.append(new_var) self.var_map[name] = new_var # Чтобы в будущем не добавлялось повторно self.accept() def on_delete_clicked(self): # Деактивируем (удаляем из видимых) выбранные переменные for item in self.tree.selectedItems(): name = item.text(0) if not name: continue if name in self.var_map: var = self.var_map[name] var['show_var'] = 'false' var['enable'] = 'false' self.accept() def set_tool(self, item, text): item.setToolTip(0, text) item.setToolTip(1, text) def keyPressEvent(self, event): if event.key() == Qt.Key_Delete: self.delete_selected_vars() else: super().keyPressEvent(event) def delete_selected_vars(self): # Деактивируем (удаляем из видимых) выбранные переменные for item in self.tree.selectedItems(): name = item.text(0) if not name: continue if name in self.var_map: var = self.var_map[name] var['show_var'] = 'false' var['enable'] = 'false' if not hasattr(self, 'xml_path') or not self.xml_path: from PySide6.QtWidgets import QMessageBox QMessageBox.warning(self, "Ошибка", "Путь к XML не задан, невозможно удалить переменные.") return import xml.etree.ElementTree as ET tree = ET.parse(self.xml_path) root = tree.getroot() if root is None: return vars_section = root.find('variables') if vars_section is None: return # Нет секции variables — ничего удалять selected_names = [item.text(0) for item in self.tree.selectedItems() if item.text(0)] removed_any = False for var_elem in vars_section.findall('var'): name = var_elem.attrib.get('name') if name in selected_names: vars_section.remove(var_elem) removed_any = True if name in self.var_map: del self.var_map[name] # Удаляем элементы из списка на месте self.all_vars[:] = [v for v in self.all_vars if v['name'] != name] if removed_any: ET.indent(tree, space=" ", level=0) tree.write(self.xml_path, encoding='utf-8', xml_declaration=True) self.populate_tree()