From 69c0bf15746ba822a522e3536e5ee64ff9ef316d Mon Sep 17 00:00:00 2001 From: Razvalyaev Date: Sat, 12 Jul 2025 18:13:51 +0300 Subject: [PATCH] =?UTF-8?q?=D0=BF=D0=BE=D1=87=D0=B5=D0=BC=D1=83-=D1=82?= =?UTF-8?q?=D0=BE=20=D0=B2=D0=BD=D1=83=D0=BA=D0=B8,=20=D0=BF=D1=80=D0=B0?= =?UTF-8?q?=D0=B2=D0=BD=D1=83=D0=BA=D0=B8=20=D0=B8=20=D1=82=D0=B0=D0=BA=20?= =?UTF-8?q?=D0=B4=D0=B0=D0=BB=D0=B5=D0=B5=20=D0=B2=20=D0=BF=D0=BE=D0=B8?= =?UTF-8?q?=D1=81=D0=BA=D0=B5=20=D0=BD=D0=B0=20=D0=BD=D0=B0=D1=85=D0=BE?= =?UTF-8?q?=D0=B4=D1=8F=D1=82=D1=81=D1=8F...?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Src/selectTable.py | 183 ++++++++++++++++++++++++++++++++------------- 1 file changed, 129 insertions(+), 54 deletions(-) diff --git a/Src/selectTable.py b/Src/selectTable.py index bd55df4..07c88fb 100644 --- a/Src/selectTable.py +++ b/Src/selectTable.py @@ -1,5 +1,3 @@ -# Поместите этот код перед классом VariableSelectorDialog - import re from PySide2.QtWidgets import ( QWidget, QTreeWidget, QTreeWidgetItem, QVBoxLayout, QLineEdit, @@ -54,38 +52,98 @@ def split_path(path): tokens.append(token) return tokens -def filter_vars(vars_list, path_parts): - """Рекурсивно фильтруем vars_list по path_parts по вхождению на любом уровне.""" - filtered = [] +def hide_all(item): + item.setHidden(True) + for i in range(item.childCount()): + hide_all(item.child(i)) - def matches_path(name, search_parts): - name_parts = split_path(name) - if len(name_parts) < len(search_parts): - return False +# Функция парсит имя с индексами в (базовое_имя, список_индексов) +def parse_name_with_indices(name): + # Регулярка для имени и индексов: + # имя - подряд букв/цифр/подчёркиваний, + # индексы в квадратных скобках + base_match = re.match(r'^([a-zA-Z_]\w*)', name) + if not base_match: + return name, [] # если не совпало, возвращаем как есть + + base_name = base_match.group(1) + indices = re.findall(r'\[(\d+)\]', name) + indices = list(map(int, indices)) + return base_name, indices - for sp, np in zip(search_parts, name_parts): - if sp not in np: - return False +def match_path_part(search_part, node_part): + # Если часть — индекс (вида [N]), требуем точное совпадение + if search_part.startswith('[') and search_part.endswith(']'): + return search_part == node_part + + # Если search_part содержит '[', значит поиск неполный индекс + if '[' in search_part: + return node_part.startswith(search_part) + + # Иначе — обычное имя: допускаем совпадение по префиксу + return node_part.startswith(search_part) + + + +def show_matching_path(item, path_parts, level=0): + node_name = item.text(0).lower() + node_parts = split_path(node_name) + + if level >= len(path_parts): + # Путь полностью пройден — показываем только этот узел (без раскрытия всех детей) + item.setHidden(False) + # Показываем детей, которые тоже соответствуют, но на этом уровне их нет, + # поэтому детей не раскрываем + for i in range(item.childCount()): + hide_all(item.child(i)) + item.setExpanded(False) return True - for var in vars_list: - fullname = var.get('fullname', var['name']) # желательно иметь полное имя - if not path_parts or matches_path(fullname, path_parts): - new_var = var.copy() - if 'children' in var: - new_var['children'] = filter_vars(var['children'], path_parts) - filtered.append(new_var) - else: - if 'children' in var: - child_filtered = filter_vars(var['children'], path_parts) - if child_filtered: - new_var = var.copy() - new_var['children'] = child_filtered - filtered.append(new_var) + if level >= len(node_parts): + # Уровень поиска больше длины пути узла — скрываем + item.setHidden(True) + return False - return filtered + search_part = path_parts[level] + node_part = node_parts[level] + + if search_part == node_part: + # Точное совпадение — показываем узел, идём вглубь только по совпадениям + item.setHidden(False) + matched_any = False + for i in range(item.childCount()): + child = item.child(i) + if show_matching_path(child, path_parts, level + 1): + matched_any = True + else: + hide_all(child) + item.setExpanded(matched_any) + return matched_any or item.childCount() == 0 + + elif node_part.startswith(search_part): + # Неполное совпадение — показываем только этот узел, детей скрываем, не раскрываем + item.setHidden(False) + for i in range(item.childCount()): + hide_all(item.child(i)) + item.setExpanded(False) + return True + + else: + # Несовпадение — скрываем + item.setHidden(True) + return False + + + + + + + + + + class VariableSelectWidget(QWidget): def __init__(self, parent=None): super().__init__(parent) @@ -177,7 +235,11 @@ class VariableSelectWidget(QWidget): while item: names.append(item.text(0)) item = item.parent() - return '.'.join(reversed(names)) + fullname = '.'.join(reversed(names)) + # Заменяем '->' на '.' + fullname = fullname.replace('->', '.') + return fullname + def add_tree_item_recursively(self, parent, var): """ @@ -213,31 +275,30 @@ class VariableSelectWidget(QWidget): for child in var.get('children', []): self.add_tree_item_recursively(item, child) - - + + def filter_tree(self): text = self.search_input.text().strip().lower() path_parts = split_path(text) if text else [] - filtered_vars = filter_vars(self.expanded_vars, path_parts) - # Сначала перерисовываем дерево - self.populate_tree(filtered_vars) + if '.' not in text and '->' not in text and '[' not in text and text != '': + for i in range(self.tree.topLevelItemCount()): + item = self.tree.topLevelItem(i) + name = item.text(0).lower() + if text in name: + item.setHidden(False) + # Не сбрасываем expanded, чтобы можно было раскрывать вручную + else: + hide_all(item) + item.setHidden(True) + else: + for i in range(self.tree.topLevelItemCount()): + item = self.tree.topLevelItem(i) + hide_all(item) + item = self.tree.topLevelItem(i) + show_matching_path(item, path_parts, 0) - # Теперь node_index уже пересоздан — можно работать - expand_level = len(path_parts) - 1 if path_parts else 0 - for i in range(self.tree.topLevelItemCount()): - item = self.tree.topLevelItem(i) - self.expand_to_level(item, expand_level) - # Раскрываем путь до точного совпадения - if path_parts: - fullname = '.'.join(path_parts) - node = self.node_index.get(fullname.lower()) - if node: - parent = node.parent() - while parent: - parent.setExpanded(True) - parent = parent.parent() @@ -255,12 +316,14 @@ class VariableSelectWidget(QWidget): current_level = node.get('children', []) return node + def update_completions(self, text=None): if text is None: text = self.search_input.text().strip() else: text = text.strip() + normalized_text = text.replace('->', '.') parts = split_path(text) path_parts = parts[:-1] if parts else [] prefix = parts[-1].lower() if parts else '' @@ -287,6 +350,8 @@ class VariableSelectWidget(QWidget): seen = set() for i in range(parent_node.childCount()): child = parent_node.child(i) + if child.isHidden(): + continue cname = child.text(0) m = re.match(rf'^{re.escape(base_text)}\[(\d+)\]$', cname) if m and cname not in seen: @@ -298,11 +363,17 @@ class VariableSelectWidget(QWidget): if ends_with_sep: node = self.find_node_by_fullname(text[:-1]) if node: - completions.extend(node.child(i).text(0) for i in range(node.childCount())) + for i in range(node.childCount()): + child = node.child(i) + if child.isHidden(): + continue + completions.append(child.text(0)) elif not path_parts: # Первый уровень — только если имя начинается с prefix for i in range(self.tree.topLevelItemCount()): item = self.tree.topLevelItem(i) + if item.isHidden(): + continue name = item.text(0) if name.lower().startswith(prefix): completions.append(name) @@ -311,6 +382,8 @@ class VariableSelectWidget(QWidget): if node: for i in range(node.childCount()): child = node.child(i) + if child.isHidden(): + continue name = child.text(0) name_parts = child.data(0, Qt.UserRole + 10) if name_parts is None: @@ -327,13 +400,13 @@ class VariableSelectWidget(QWidget): return completions - - - # Функция для поиска узла с полным именем def find_node_by_fullname(self, name): - return self.node_index.get(name.lower()) - + if name is None: + return None + normalized_name = name.replace('->', '.').lower() + return self.node_index.get(normalized_name) + def insert_completion(self, text): node = self.find_node_by_fullname(text) if node and node.childCount() > 0 and not (text.endswith('.') or text.endswith('->') or text.endswith('[')): @@ -398,7 +471,9 @@ class VariableSelectWidget(QWidget): node = find_exact_item(completions[0]) if node and node.childCount() > 0: # Используем первую подсказку, чтобы определить нужный разделитель - completions = self.update_completions(text + '.') + completions = self.update_completions(text + '.') + if not completions: + return suggestion = completions[0] # Ищем, какой символ идёт после текущего текста