почему-то внуки, правнуки и так далее в поиске на находятся...

This commit is contained in:
Razvalyaev 2025-07-12 18:13:51 +03:00
parent 4f949e9854
commit 69c0bf1574

View File

@ -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]
# Ищем, какой символ идёт после текущего текста