улучшены коллбеки для считывания xml
улучшен парс структур и юнионов переменных улучшен поиск
This commit is contained in:
parent
0ba81a5147
commit
4517087194
@ -30,10 +30,9 @@ class VariableSelectorDialog(QDialog):
|
||||
self.tree = QTreeWidget()
|
||||
self.tree.setHeaderLabels(["Имя переменной", "Тип"])
|
||||
self.tree.setSelectionMode(QTreeWidget.ExtendedSelection)
|
||||
self.tree.setRootIsDecorated(False) # без иконки "вложенность"
|
||||
self.tree.setRootIsDecorated(True)
|
||||
self.tree.setUniformRowHeights(True)
|
||||
|
||||
# --- Автоматическая ширина колонок
|
||||
self.tree.setStyleSheet("""
|
||||
QTreeWidget::item:selected {
|
||||
background-color: #87CEFA;
|
||||
@ -47,11 +46,15 @@ class VariableSelectorDialog(QDialog):
|
||||
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()
|
||||
@ -68,14 +71,18 @@ class VariableSelectorDialog(QDialog):
|
||||
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)
|
||||
item.setToolTip(0, "Уже добавлена")
|
||||
item.setToolTip(1, "Уже добавлена")
|
||||
self.set_tool(item, "Уже добавлена")
|
||||
|
||||
if parent is None:
|
||||
self.tree.addTopLevelItem(item)
|
||||
@ -95,32 +102,61 @@ class VariableSelectorDialog(QDialog):
|
||||
self.add_tree_item_recursively(None, var)
|
||||
|
||||
header = self.tree.header()
|
||||
header.setSectionResizeMode(0, QHeaderView.Stretch)
|
||||
header.setSectionResizeMode(1, QHeaderView.ResizeToContents)
|
||||
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 match_recursive(item):
|
||||
matched = False
|
||||
def hide_all(item):
|
||||
item.setHidden(True)
|
||||
for i in range(item.childCount()):
|
||||
child = item.child(i)
|
||||
if match_recursive(child):
|
||||
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
|
||||
|
||||
# Проверяем текущий элемент
|
||||
name = item.text(0).lower()
|
||||
typ = item.text(1).lower()
|
||||
if text in name or text in typ:
|
||||
matched = True
|
||||
|
||||
item.setHidden(not matched)
|
||||
# Открываем только те элементы, у которых есть совпадение в потомках или в себе
|
||||
item.setExpanded(matched)
|
||||
return 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()):
|
||||
match_recursive(self.tree.topLevelItem(i))
|
||||
item = self.tree.topLevelItem(i)
|
||||
hide_all(item)
|
||||
show_matching_path(item, 0)
|
||||
|
||||
|
||||
def on_add_clicked(self):
|
||||
@ -165,4 +201,21 @@ class VariableSelectorDialog(QDialog):
|
||||
self.all_vars.append(new_var)
|
||||
self.var_map[name] = new_var # Чтобы в будущем не добавлялось повторно
|
||||
|
||||
self.accept()
|
||||
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)
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
10
debug_vars.c
10
debug_vars.c
@ -1,8 +1,8 @@
|
||||
// <EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
// Этот файл сгенерирован автоматически
|
||||
#include "debug_tools.h"
|
||||
|
||||
|
||||
// <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
// Инклюды для доступа к переменным
|
||||
#include "xp_project.h"
|
||||
#include "RS_Functions_modbus.h"
|
||||
#include "vector.h"
|
||||
@ -44,7 +44,7 @@
|
||||
#include "rmp_cntl_my1.h"
|
||||
|
||||
|
||||
// <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
// Экстерны для доступа к переменным
|
||||
extern int ADC0finishAddr;
|
||||
extern int ADC0startAddr;
|
||||
extern int ADC1finishAddr;
|
||||
@ -313,9 +313,9 @@ extern _iq zadan_Id_min;
|
||||
extern int zero_ADC[20];
|
||||
|
||||
|
||||
// <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
// Определение массива с указателями на переменные для отладки
|
||||
int DebugVar_Qnt = 1;
|
||||
#pragma DATA_SECTION(dbg_vars,".dbgvar_info")
|
||||
DebugVar_t dbg_vars[] = {\
|
||||
{(char *)&project.cds_tk.plane_address , pt_uint16 , t_iq_none , "project.cds_tk.plane_address" }, \
|
||||
{(char *)&ADC1finishAddr , pt_int16 , iq_none , "ADC1finishAddr" }, \
|
||||
};
|
||||
|
74
setupVars.py
74
setupVars.py
@ -89,7 +89,7 @@ def parse_structs(filename):
|
||||
|
||||
structs = {}
|
||||
typedef_map = {}
|
||||
|
||||
|
||||
def parse_struct_element(elem):
|
||||
fields = {}
|
||||
|
||||
@ -99,19 +99,22 @@ def parse_structs(filename):
|
||||
|
||||
# Проверка на вложенную структуру
|
||||
nested_struct_elem = field.find("struct")
|
||||
|
||||
if nested_struct_elem is not None:
|
||||
# Рекурсивно парсим вложенную структуру и вставляем её как подсловарь
|
||||
nested_fields = parse_struct_element(nested_struct_elem)
|
||||
fields[fname] = nested_fields
|
||||
|
||||
# Оборачиваем в dict с ключом 'type' для хранения типа из XML
|
||||
fields[fname] = {
|
||||
'type': ftype, # здесь тип, например "BENDER_ERROR"
|
||||
**nested_fields # развёрнутые поля вложенной структуры
|
||||
}
|
||||
else:
|
||||
# Обычное поле
|
||||
fields[fname] = ftype
|
||||
|
||||
return fields
|
||||
|
||||
|
||||
|
||||
|
||||
structs_elem = root.find("structs")
|
||||
if structs_elem is not None:
|
||||
for struct in structs_elem.findall("struct"):
|
||||
@ -156,16 +159,11 @@ def safe_parse_xml(xml_path):
|
||||
except Exception as e:
|
||||
print(f"Неожиданная ошибка при чтении XML файла '{xml_path}': {e}")
|
||||
return None, None
|
||||
|
||||
def expand_struct_recursively(prefix, type_str, structs, typedefs, var_attrs, depth=0):
|
||||
"""
|
||||
Рекурсивно разворачивает структуру.
|
||||
type_str может быть строкой (имя типа) или словарём (вложенная структура).
|
||||
"""
|
||||
if depth > 10:
|
||||
return []
|
||||
|
||||
# Если тип уже является вложенной структурой
|
||||
# Если type_str — словарь структуры
|
||||
if isinstance(type_str, dict):
|
||||
fields = type_str
|
||||
else:
|
||||
@ -175,21 +173,42 @@ def expand_struct_recursively(prefix, type_str, structs, typedefs, var_attrs, de
|
||||
return []
|
||||
|
||||
children = []
|
||||
for field_name, field_type in fields.items():
|
||||
full_name = f"{prefix}.{field_name}"
|
||||
child = {
|
||||
'name': full_name,
|
||||
'type': field_type,
|
||||
'pt_type': '',
|
||||
'file': var_attrs.get('file'),
|
||||
'extern': var_attrs.get('extern'),
|
||||
'static': var_attrs.get('static'),
|
||||
}
|
||||
for field_name, field_value in fields.items():
|
||||
# Пропускаем поле 'type', оно служит для хранения имени типа
|
||||
if field_name == 'type':
|
||||
continue
|
||||
|
||||
# Рекурсивно разворачиваем, если field_type — вложенная структура
|
||||
subchildren = expand_struct_recursively(full_name, field_type, structs, typedefs, var_attrs, depth + 1)
|
||||
if subchildren:
|
||||
child['children'] = subchildren
|
||||
full_name = f"{prefix}.{field_name}"
|
||||
|
||||
if isinstance(field_value, dict):
|
||||
# Если вложенная структура — берем её имя типа из поля 'type' или пустую строку
|
||||
type_name = field_value.get('type', '')
|
||||
child = {
|
||||
'name': full_name,
|
||||
'type': type_name,
|
||||
'pt_type': '',
|
||||
'file': var_attrs.get('file'),
|
||||
'extern': var_attrs.get('extern'),
|
||||
'static': var_attrs.get('static'),
|
||||
}
|
||||
# Рекурсивно раскрываем вложенные поля
|
||||
subchildren = expand_struct_recursively(full_name, field_value, structs, typedefs, var_attrs, depth + 1)
|
||||
if subchildren:
|
||||
child['children'] = subchildren
|
||||
else:
|
||||
# Простое поле — строка типа
|
||||
# Пропускаем указатели на функции
|
||||
if isinstance(field_value, str) and "(" in field_value and "*" in field_value and ")" in field_value:
|
||||
continue
|
||||
|
||||
child = {
|
||||
'name': full_name,
|
||||
'type': field_value,
|
||||
'pt_type': '',
|
||||
'file': var_attrs.get('file'),
|
||||
'extern': var_attrs.get('extern'),
|
||||
'static': var_attrs.get('static'),
|
||||
}
|
||||
|
||||
children.append(child)
|
||||
|
||||
@ -219,6 +238,11 @@ def expand_vars(vars_list, structs, typedefs):
|
||||
new_var['children'] = expand_struct_recursively(var['name'], raw_type, structs, typedefs, var)
|
||||
expanded.append(new_var)
|
||||
|
||||
elif pt_type == 'pt_union' and isinstance(fields, dict):
|
||||
new_var = var.copy()
|
||||
new_var['children'] = expand_struct_recursively(var['name'], raw_type, structs, typedefs, var)
|
||||
expanded.append(new_var)
|
||||
|
||||
else:
|
||||
expanded.append(var)
|
||||
|
||||
|
@ -118,6 +118,7 @@ class VarEditor(QWidget):
|
||||
proj_layout = QHBoxLayout()
|
||||
proj_layout.addWidget(QLabel("Project Path:"))
|
||||
self.proj_path_edit = QLineEdit()
|
||||
self.proj_path_edit.returnPressed.connect(self.read_xml_file)
|
||||
self.proj_path_edit.textChanged.connect(self.__on_proj_path_changed)
|
||||
proj_layout.addWidget(self.proj_path_edit)
|
||||
btn_proj_browse = QPushButton("...")
|
||||
@ -129,6 +130,7 @@ class VarEditor(QWidget):
|
||||
makefile_layout = QHBoxLayout()
|
||||
makefile_layout.addWidget(QLabel("Makefile Path (relative path):"))
|
||||
self.makefile_edit = QLineEdit()
|
||||
self.makefile_edit.returnPressed.connect(self.read_xml_file)
|
||||
self.makefile_edit.textChanged.connect(self.__on_makefile_path_changed)
|
||||
makefile_layout.addWidget(self.makefile_edit)
|
||||
btn_makefile_browse = QPushButton("...")
|
||||
@ -375,7 +377,6 @@ class VarEditor(QWidget):
|
||||
)
|
||||
self.xml_output_edit.setText(file_path)
|
||||
self.xml_path = file_path
|
||||
self.read_xml_file()
|
||||
|
||||
def keyPressEvent(self, event: QKeyEvent):
|
||||
if event.key() == Qt.Key_Delete:
|
||||
@ -405,9 +406,11 @@ class VarEditor(QWidget):
|
||||
|
||||
def __on_xml_path_changed(self):
|
||||
self.xml_path = self.get_xml_path()
|
||||
self.read_xml_file()
|
||||
|
||||
def __on_proj_path_changed(self):
|
||||
self.proj_path = self.get_proj_path()
|
||||
self.read_xml_file()
|
||||
|
||||
def __on_makefile_path_changed(self):
|
||||
self.makefile_path = self.get_makefile_path()
|
||||
@ -415,6 +418,7 @@ class VarEditor(QWidget):
|
||||
path = make_relative_path(self.makefile_path, self.proj_path)
|
||||
self.makefile_edit.setText(path)
|
||||
self.makefile_path = path
|
||||
self.read_xml_file()
|
||||
|
||||
|
||||
def __after_scanvars_finished(self):
|
||||
|
8
vars.xml
8
vars.xml
@ -1,5 +1,5 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<analysis proj_path="F:/Work/Projects/TMS/TMS_new_bus" makefile_path="Debug/makefile" structs_path="Src/DebugTools/structs.xml">
|
||||
<analysis proj_path="E:/.WORK/TMS/TMS_new_bus" makefile_path="Debug/makefile" structs_path="Src/DebugTools/structs.xml">
|
||||
<variables>
|
||||
<var name="ADC0finishAddr">
|
||||
<show_var>false</show_var>
|
||||
@ -27,7 +27,7 @@
|
||||
</var>
|
||||
<var name="ADC1finishAddr">
|
||||
<show_var>false</show_var>
|
||||
<enable>true</enable>
|
||||
<enable>false</enable>
|
||||
<shortname>ADC1finishAddr</shortname>
|
||||
<pt_type>pt_int16</pt_type>
|
||||
<iq_type>iq_none</iq_type>
|
||||
@ -3602,8 +3602,8 @@
|
||||
<static>false</static>
|
||||
</var>
|
||||
<var name="project.cds_tk.plane_address">
|
||||
<show_var>true</show_var>
|
||||
<enable>true</enable>
|
||||
<show_var>false</show_var>
|
||||
<enable>false</enable>
|
||||
<shortname>project.cds_tk.plane_address</shortname>
|
||||
<pt_type>pt_uint16</pt_type>
|
||||
<iq_type>t_iq_none</iq_type>
|
||||
|
Loading…
Reference in New Issue
Block a user