581 lines
24 KiB
Python
581 lines
24 KiB
Python
import sys
|
||
import os
|
||
import subprocess
|
||
import xml.etree.ElementTree as ET
|
||
from generateVars import map_type_to_pt, get_iq_define, type_map
|
||
from enum import IntEnum
|
||
from scanVars import run_scan
|
||
from generateVars import run_generate
|
||
|
||
from PySide2.QtWidgets import (
|
||
QApplication, QWidget, QTableWidget, QTableWidgetItem,
|
||
QCheckBox, QComboBox, QLineEdit, QVBoxLayout, QHBoxLayout, QPushButton,
|
||
QCompleter, QAbstractItemView, QLabel, QMessageBox, QFileDialog, QTextEdit
|
||
)
|
||
from PySide2.QtGui import QTextCursor
|
||
from PySide2.QtCore import Qt, QProcess
|
||
|
||
class rows(IntEnum):
|
||
include = 0
|
||
name = 1
|
||
type = 2
|
||
pt_type = 3
|
||
iq_type = 4
|
||
ret_type = 5
|
||
short_name = 6
|
||
|
||
# 1. Парсим vars.xml
|
||
def make_absolute_path(path, base_path):
|
||
if not os.path.isabs(path):
|
||
return os.path.abspath(os.path.join(base_path, path))
|
||
return os.path.abspath(path)
|
||
|
||
def parse_vars(filename):
|
||
tree = ET.parse(filename)
|
||
root = tree.getroot()
|
||
|
||
vars_list = []
|
||
variables_elem = root.find('variables')
|
||
if variables_elem is not None:
|
||
for var in variables_elem.findall('var'):
|
||
name = var.attrib.get('name', '')
|
||
vars_list.append({
|
||
'name': name,
|
||
'enable': var.findtext('enable', 'false'),
|
||
'shortname': var.findtext('shortname', name),
|
||
'pt_type': var.findtext('pt_type', 'pt_unknown'),
|
||
'iq_type': var.findtext('iq_type', 'iq_none'),
|
||
'return_type': var.findtext('return_type', 'int'),
|
||
'type': var.findtext('type', 'unknown'),
|
||
'file': var.findtext('file', ''),
|
||
'extern': var.findtext('extern', 'false') == 'true',
|
||
'static': var.findtext('static', 'false') == 'true',
|
||
})
|
||
|
||
return vars_list
|
||
|
||
# 2. Парсим structSup.xml
|
||
def parse_structs(filename):
|
||
tree = ET.parse(filename)
|
||
root = tree.getroot()
|
||
|
||
structs = {}
|
||
typedef_map = {}
|
||
|
||
# --- Считываем структуры ---
|
||
structs_elem = root.find('structs')
|
||
if structs_elem is not None:
|
||
for struct in structs_elem.findall('struct'):
|
||
name = struct.attrib['name']
|
||
fields = {}
|
||
for field in struct.findall('field'):
|
||
fname = field.attrib.get('name')
|
||
ftype = field.attrib.get('type')
|
||
if fname and ftype:
|
||
fields[fname] = ftype
|
||
structs[name] = fields
|
||
|
||
# --- Считываем typedef-ы ---
|
||
typedefs_elem = root.find('typedefs')
|
||
if typedefs_elem is not None:
|
||
for typedef in typedefs_elem.findall('typedef'):
|
||
name = typedef.attrib.get('name')
|
||
target_type = typedef.attrib.get('type')
|
||
if name and target_type:
|
||
typedef_map[name.strip()] = target_type.strip()
|
||
|
||
return structs, typedef_map
|
||
|
||
|
||
|
||
class ProcessOutputWindow(QWidget):
|
||
def __init__(self, command, args):
|
||
super().__init__()
|
||
self.setWindowTitle("Поиск переменных...")
|
||
self.resize(600, 400)
|
||
|
||
self.layout = QVBoxLayout(self)
|
||
self.output_edit = QTextEdit()
|
||
self.output_edit.setReadOnly(True)
|
||
self.layout.addWidget(self.output_edit)
|
||
|
||
self.btn_close = QPushButton("Закрыть")
|
||
self.btn_close.setEnabled(False)
|
||
self.btn_close.clicked.connect(self.close)
|
||
self.layout.addWidget(self.btn_close)
|
||
|
||
self.process = QProcess(self)
|
||
self.process.setProcessChannelMode(QProcess.MergedChannels)
|
||
self.process.readyReadStandardOutput.connect(self.handle_stdout)
|
||
self.process.finished.connect(self.process_finished)
|
||
|
||
self.process.start(command, args)
|
||
|
||
def handle_stdout(self):
|
||
data = self.process.readAllStandardOutput()
|
||
text = data.data().decode('utf-8')
|
||
self.output_edit.append(text)
|
||
|
||
def process_finished(self):
|
||
self.output_edit.append("\n--- Процесс завершён ---")
|
||
self.btn_close.setEnabled(True)
|
||
|
||
# 3. UI: таблица с переменными
|
||
class VarEditor(QWidget):
|
||
def __init__(self):
|
||
super().__init__()
|
||
self.vars_list = []
|
||
self.structs = {}
|
||
self.typedef_map = {}
|
||
self.structs_xml_path = None # сюда запишем путь из <structs_xml>
|
||
self.initUI()
|
||
|
||
def initUI(self):
|
||
self.setWindowTitle("Variable Editor")
|
||
|
||
# --- Поля ввода пути проекта и XML ---
|
||
|
||
# XML Output
|
||
xml_layout = QHBoxLayout()
|
||
xml_layout.addWidget(QLabel("XML Output:"))
|
||
self.xml_output_edit = QLineEdit()
|
||
xml_layout.addWidget(self.xml_output_edit)
|
||
self.xml_output_edit.returnPressed.connect(self.read_xml_file)
|
||
btn_xml_browse = QPushButton("...")
|
||
btn_xml_browse.setFixedWidth(30)
|
||
xml_layout.addWidget(btn_xml_browse)
|
||
btn_xml_browse.clicked.connect(self.browse_xml_output)
|
||
|
||
# Project Path
|
||
proj_layout = QHBoxLayout()
|
||
proj_layout.addWidget(QLabel("Project Path:"))
|
||
self.proj_path_edit = QLineEdit()
|
||
proj_layout.addWidget(self.proj_path_edit)
|
||
btn_proj_browse = QPushButton("...")
|
||
btn_proj_browse.setFixedWidth(30)
|
||
proj_layout.addWidget(btn_proj_browse)
|
||
btn_proj_browse.clicked.connect(self.browse_proj_path)
|
||
|
||
# Makefile Path
|
||
makefile_layout = QHBoxLayout()
|
||
makefile_layout.addWidget(QLabel("Makefile Path (relative path):"))
|
||
self.makefile_edit = QLineEdit()
|
||
makefile_layout.addWidget(self.makefile_edit)
|
||
btn_makefile_browse = QPushButton("...")
|
||
btn_makefile_browse.setFixedWidth(30)
|
||
makefile_layout.addWidget(btn_makefile_browse)
|
||
btn_makefile_browse.clicked.connect(self.browse_makefile)
|
||
|
||
|
||
|
||
# Source Output File/Directory
|
||
source_output_layout = QHBoxLayout()
|
||
source_output_layout.addWidget(QLabel("Source Output File:"))
|
||
self.source_output_edit = QLineEdit()
|
||
source_output_layout.addWidget(self.source_output_edit)
|
||
btn_source_output_browse = QPushButton("...")
|
||
btn_source_output_browse.setFixedWidth(30)
|
||
source_output_layout.addWidget(btn_source_output_browse)
|
||
btn_source_output_browse.clicked.connect(self.browse_source_output)
|
||
|
||
|
||
self.btn_update_vars = QPushButton("Обновить данные о переменных")
|
||
self.btn_update_vars.clicked.connect(self.update_vars_data)
|
||
|
||
# Таблица переменных
|
||
self.table = QTableWidget(len(self.vars_list), 7)
|
||
self.table.setHorizontalHeaderLabels([
|
||
'Include',
|
||
'Name',
|
||
'Origin Type',
|
||
'Pointer Type',
|
||
'IQ Type',
|
||
'Return Type',
|
||
'Short Name'
|
||
])
|
||
self.table.setEditTriggers(QAbstractItemView.AllEditTriggers)
|
||
|
||
# Кнопка сохранения
|
||
btn_save = QPushButton("Build")
|
||
btn_save.clicked.connect(self.save_build)
|
||
|
||
# Основной layout
|
||
layout = QVBoxLayout()
|
||
layout.addLayout(xml_layout)
|
||
layout.addLayout(proj_layout)
|
||
layout.addLayout(makefile_layout)
|
||
layout.addWidget(self.btn_update_vars)
|
||
layout.addWidget(self.table)
|
||
layout.addWidget(btn_save)
|
||
layout.addLayout(source_output_layout)
|
||
|
||
self.setLayout(layout)
|
||
|
||
def update_vars_data(self):
|
||
proj_path = os.path.abspath(self.proj_path_edit.text().strip())
|
||
xml_path = os.path.abspath(self.xml_output_edit.text().strip())
|
||
makefile_path = make_absolute_path(self.makefile_edit.text().strip(), proj_path)
|
||
|
||
if not proj_path or not xml_path:
|
||
QMessageBox.warning(self, "Ошибка", "Пожалуйста, укажите пути проекта и XML.")
|
||
return
|
||
|
||
if not os.path.isfile(makefile_path):
|
||
QMessageBox.warning(self, "Ошибка", f"Makefile не найден по пути:\n{makefile_path}")
|
||
return
|
||
|
||
scanvars_exe = "scanVars.exe"
|
||
args = [proj_path, makefile_path, xml_path]
|
||
|
||
# Создаем и показываем окно с выводом процесса
|
||
self.proc_win = ProcessOutputWindow("python", [])
|
||
self.proc_win.show()
|
||
self.proc_win.output_edit.append("Запуск анализа переменных...")
|
||
|
||
# Запускаем в отдельном процессе через QProcess, если нужен live-вывод:
|
||
from threading import Thread
|
||
|
||
def run_scan_wrapper():
|
||
try:
|
||
run_scan(proj_path, makefile_path, xml_path)
|
||
self.proc_win.output_edit.append("\n--- Анализ завершён ---")
|
||
except Exception as e:
|
||
self.proc_win.output_edit.append(f"\n[ОШИБКА] {e}")
|
||
self.proc_win.btn_close.setEnabled(True)
|
||
self.after_scanvars_finished(0, 0)
|
||
|
||
Thread(target=run_scan_wrapper).start()
|
||
# Можно подписаться на сигнал завершения процесса, если хочешь обновить UI после
|
||
#self.proc_win.process.finished.connect(lambda exitCode, exitStatus: self.after_scanvars_finished(exitCode, exitStatus))
|
||
|
||
|
||
def save_build(self):
|
||
vars_out = []
|
||
for row in range(self.table.rowCount()):
|
||
include_cb = self.table.cellWidget(row, rows.include)
|
||
if not include_cb.isChecked():
|
||
continue
|
||
name_edit = self.table.cellWidget(row, rows.name)
|
||
pt_type_combo = self.table.cellWidget(row, rows.pt_type)
|
||
iq_combo = self.table.cellWidget(row, rows.iq_type)
|
||
ret_combo = self.table.cellWidget(row, rows.ret_type)
|
||
short_name_edit = self.table.cellWidget(row, rows.short_name)
|
||
|
||
var_data = {
|
||
'name': name_edit.text(),
|
||
'type': 'pt_' + pt_type_combo.currentText(),
|
||
'iq_type': iq_combo.currentText(),
|
||
'return_type': ret_combo.currentText() if ret_combo.currentText() else 'int',
|
||
'short_name': short_name_edit.text(),
|
||
}
|
||
vars_out.append(var_data)
|
||
|
||
# Здесь нужно указать абсолютные пути к проекту, xml и output (замени на свои)
|
||
proj_path = os.path.abspath(self.proj_path_edit.text().strip())
|
||
xml_path = os.path.abspath(self.xml_output_edit.text().strip())
|
||
output_dir_c_file = os.path.abspath(self.source_output_edit.text().strip())
|
||
|
||
if not proj_path or not xml_path or not output_dir_c_file:
|
||
QMessageBox.warning(self, "Ошибка", "Заполните все пути: проект, XML и output.")
|
||
return
|
||
|
||
try:
|
||
run_generate(proj_path, xml_path, output_dir_c_file)
|
||
QMessageBox.information(self, "Готово", "Файл debug_vars.c успешно сгенерирован.")
|
||
except Exception as e:
|
||
QMessageBox.critical(self, "Ошибка при генерации", str(e))
|
||
|
||
def browse_proj_path(self):
|
||
dir_path = QFileDialog.getExistingDirectory(self, "Выберите папку проекта")
|
||
if dir_path:
|
||
self.proj_path_edit.setText(dir_path)
|
||
|
||
def read_xml_file(self):
|
||
file_path = self.xml_output_edit.text().strip()
|
||
if file_path and not os.path.isfile(file_path):
|
||
return
|
||
|
||
self.vars_list = parse_vars(file_path)
|
||
try:
|
||
tree = ET.parse(file_path)
|
||
root = tree.getroot()
|
||
|
||
proj_path = self.proj_path_edit.text().strip()
|
||
|
||
if not proj_path:
|
||
# Если в поле ничего нет, пробуем взять из XML
|
||
proj_path_from_xml = root.attrib.get('proj_path', '').strip()
|
||
if proj_path_from_xml and os.path.isdir(proj_path_from_xml):
|
||
proj_path = proj_path_from_xml
|
||
self.proj_path_edit.setText(proj_path_from_xml)
|
||
else:
|
||
QMessageBox.warning(
|
||
self,
|
||
"Внимание",
|
||
"Путь к проекту (proj_path) не найден или не существует.\n"
|
||
"Пожалуйста, укажите его вручную в поле 'Project Path'."
|
||
)
|
||
else:
|
||
if not os.path.isdir(proj_path):
|
||
QMessageBox.warning(
|
||
self,
|
||
"Внимание",
|
||
f"Указанный путь к проекту не существует:\n{proj_path}\n"
|
||
"Пожалуйста, исправьте путь в поле 'Project Path'."
|
||
)
|
||
|
||
|
||
# --- makefile_path из атрибута ---
|
||
makefile_path = root.attrib.get('makefile_path', '').strip()
|
||
makefile_path_full = make_absolute_path(makefile_path, proj_path)
|
||
if makefile_path_full and os.path.isfile(makefile_path_full):
|
||
self.makefile_edit.setText(makefile_path)
|
||
|
||
# --- structs_path из атрибута ---
|
||
structs_path = root.attrib.get('structs_path', '').strip()
|
||
structs_path_full = make_absolute_path(structs_path, proj_path)
|
||
if structs_path_full and os.path.isfile(structs_path_full):
|
||
self.structs_xml_path = structs_path_full
|
||
self.structs, self.typedef_map = parse_structs(structs_path_full)
|
||
else:
|
||
self.structs_xml_path = None
|
||
|
||
self.update_table()
|
||
except Exception as e:
|
||
QMessageBox.warning(self, "Ошибка", f"Ошибка при чтении XML:\n{e}")
|
||
|
||
def browse_xml_output(self):
|
||
file_path, _ = QFileDialog.getSaveFileName(
|
||
self,
|
||
"Выберите XML файл",
|
||
filter="XML files (*.xml);;All Files (*)"
|
||
)
|
||
self.xml_output_edit.setText(file_path)
|
||
self.read_xml_file()
|
||
|
||
|
||
def browse_xml_struct(self):
|
||
file_path, _ = QFileDialog.getSaveFileName(self, "Выберите XML файл", filter="XML files (*.xml);;All Files (*)")
|
||
if file_path:
|
||
self.xml_output_edit.setText(file_path)
|
||
if os.path.isfile(file_path):
|
||
self.structs, self.typedef_map = parse_structs(file_path)
|
||
|
||
def browse_makefile(self):
|
||
file_path, _ = QFileDialog.getOpenFileName(
|
||
self, "Выберите Makefile", filter="Makefile (makefile);;All Files (*)"
|
||
)
|
||
if file_path:
|
||
self.makefile_edit.setText(file_path)
|
||
|
||
def browse_source_output(self):
|
||
dir_path = QFileDialog.getExistingDirectory(self, "Выберите папку для debug_vars.c")
|
||
if dir_path:
|
||
self.source_output_edit.setText(dir_path)
|
||
|
||
def after_scanvars_finished(self, exitCode, exitStatus):
|
||
xml_path = self.xml_output_edit.text().strip()
|
||
if not os.path.isfile(xml_path):
|
||
QMessageBox.critical(self, "Ошибка", f"Файл не найден: {xml_path}")
|
||
return
|
||
|
||
try:
|
||
# Читаем структуры, если задан путь
|
||
if self.structs_xml_path and os.path.isfile(self.structs_xml_path):
|
||
try:
|
||
self.structs, self.typedef_map = parse_structs(self.structs_xml_path)
|
||
# При необходимости обновите UI или сделайте что-то с self.structs
|
||
except Exception as e:
|
||
QMessageBox.warning(self, "Внимание", f"Не удалось загрузить структуры из {self.structs_xml_path}:\n{e}")
|
||
|
||
self.vars_list = parse_vars(xml_path)
|
||
self.update_table()
|
||
|
||
|
||
except Exception as e:
|
||
QMessageBox.critical(self, "Ошибка", f"Не удалось загрузить переменные:\n{e}")
|
||
|
||
def update_table(self):
|
||
self.type_options = list(dict.fromkeys(type_map.values()))
|
||
self.display_type_options = [t.replace('pt_', '') for t in self.type_options]
|
||
iq_types = ['iq_none', 'iq'] + [f'iq{i}' for i in range(1, 31)]
|
||
self.table.setRowCount(len(self.vars_list))
|
||
|
||
for row, var in enumerate(self.vars_list):
|
||
cb = QCheckBox()
|
||
enable_str = var.get('enable', 'false')
|
||
cb.setChecked(enable_str.lower() == 'true')
|
||
self.table.setCellWidget(row, rows.include, cb)
|
||
|
||
name_edit = QLineEdit(var['name'])
|
||
if var['type'] in self.structs:
|
||
completer = QCompleter(self.structs[var['type']].keys())
|
||
completer.setCaseSensitivity(Qt.CaseInsensitive)
|
||
name_edit.setCompleter(completer)
|
||
self.table.setCellWidget(row, rows.name, name_edit)
|
||
|
||
# Type (origin)
|
||
origin_type = var.get('type', '').strip()
|
||
origin_item = QTableWidgetItem(origin_type)
|
||
origin_item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled) # read-only
|
||
self.table.setItem(row, rows.type, origin_item)
|
||
|
||
pt_type_combo = QComboBox()
|
||
pt_type_combo.addItems(self.display_type_options)
|
||
internal_type = map_type_to_pt(var['type'], var['name'], self.typedef_map)
|
||
display_type = internal_type.replace('pt_', '')
|
||
if display_type in self.display_type_options:
|
||
pt_type_combo.setCurrentText(display_type)
|
||
else:
|
||
pt_type_combo.addItem(display_type)
|
||
pt_type_combo.setCurrentText(display_type)
|
||
self.table.setCellWidget(row, rows.pt_type, pt_type_combo)
|
||
|
||
iq_combo = QComboBox()
|
||
iq_combo.addItems(iq_types)
|
||
iq_type = get_iq_define(var['type']) # Получаем IQ-тип, например 'iq24'
|
||
display_type = iq_type.replace('t_', '')
|
||
if iq_type in iq_types:
|
||
iq_combo.setCurrentText(display_type)
|
||
else:
|
||
iq_combo.addItem(display_type)
|
||
iq_combo.setCurrentText(display_type)
|
||
self.table.setCellWidget(row, rows.iq_type, iq_combo)
|
||
|
||
ret_combo = QComboBox()
|
||
ret_combo.addItems(iq_types)
|
||
self.table.setCellWidget(row, rows.ret_type, ret_combo)
|
||
|
||
short_name_edit = QLineEdit(var['name'])
|
||
self.table.setCellWidget(row, rows.short_name, short_name_edit)
|
||
|
||
cb.stateChanged.connect(self.save_table_to_xml)
|
||
name_edit.textChanged.connect(self.save_table_to_xml)
|
||
pt_type_combo.currentTextChanged.connect(self.save_table_to_xml)
|
||
iq_combo.currentTextChanged.connect(self.save_table_to_xml)
|
||
ret_combo.currentTextChanged.connect(self.save_table_to_xml)
|
||
short_name_edit.textChanged.connect(self.save_table_to_xml)
|
||
|
||
|
||
def save_table_to_xml(self):
|
||
def make_relative_path(abs_path, base_path):
|
||
try:
|
||
return os.path.relpath(abs_path, base_path).replace("\\", "/")
|
||
except ValueError:
|
||
return abs_path.replace("\\", "/")
|
||
|
||
xml_path = self.xml_output_edit.text().strip()
|
||
proj_path = self.proj_path_edit.text().strip()
|
||
makefile_path = self.makefile_edit.text().strip()
|
||
|
||
if not xml_path or not os.path.isfile(xml_path):
|
||
print("XML файл не найден или путь пустой")
|
||
return
|
||
|
||
try:
|
||
tree = ET.parse(xml_path)
|
||
root = tree.getroot()
|
||
|
||
# Обновим атрибуты с относительными путями
|
||
if os.path.isdir(proj_path):
|
||
root.set("proj_path", proj_path.replace("\\", "/"))
|
||
|
||
if os.path.isfile(makefile_path):
|
||
rel_makefile = make_relative_path(makefile_path, proj_path)
|
||
root.set("makefile_path", rel_makefile)
|
||
|
||
if self.structs_xml_path and os.path.isfile(self.structs_xml_path):
|
||
rel_struct_path = make_relative_path(self.structs_xml_path, proj_path)
|
||
root.set("structs_path", rel_struct_path)
|
||
|
||
|
||
vars_elem = root.find('variables')
|
||
if vars_elem is None:
|
||
# Если блока нет, создаём
|
||
vars_elem = ET.SubElement(root, 'variables')
|
||
|
||
original_info = {}
|
||
for var_elem in vars_elem.findall('var'):
|
||
name = var_elem.attrib.get('name')
|
||
if name:
|
||
original_info[name] = {
|
||
'type': var_elem.findtext('type', ''),
|
||
'file': var_elem.findtext('file', ''),
|
||
'extern': var_elem.findtext('extern', ''),
|
||
'static': var_elem.findtext('static', '')
|
||
}
|
||
# Собираем данные из таблицы
|
||
updated_vars = []
|
||
for row in range(self.table.rowCount()):
|
||
cb = self.table.cellWidget(row, 0)
|
||
name_edit = self.table.cellWidget(row, 1)
|
||
pt_type_combo = self.table.cellWidget(row, 3)
|
||
iq_combo = self.table.cellWidget(row, 4)
|
||
ret_combo = self.table.cellWidget(row, 5)
|
||
short_name_edit = self.table.cellWidget(row, 6)
|
||
|
||
var_name = name_edit.text()
|
||
|
||
# Берём оригинальные type и file из словаря, если есть
|
||
orig_type = original_info.get(var_name, {}).get('type', '')
|
||
orig_file = original_info.get(var_name, {}).get('file', '')
|
||
orig_extern = original_info.get(var_name, {}).get('extern', '')
|
||
orig_static = original_info.get(var_name, {}).get('static', '')
|
||
|
||
updated_vars.append({
|
||
'name': var_name,
|
||
'enable': cb.isChecked(),
|
||
'shortname': short_name_edit.text(),
|
||
'pt_type': 'pt_' + pt_type_combo.currentText(),
|
||
'iq_type': iq_combo.currentText(),
|
||
'return_type': ret_combo.currentText() or 'int',
|
||
'type': orig_type,
|
||
'file': orig_file,
|
||
'extern': orig_extern,
|
||
'static': orig_static,
|
||
})
|
||
|
||
# Обновляем или добавляем по одному var в XML
|
||
for v in updated_vars:
|
||
var_elem = None
|
||
for ve in vars_elem.findall('var'):
|
||
if ve.attrib.get('name') == v['name']:
|
||
var_elem = ve
|
||
break
|
||
if var_elem is None:
|
||
var_elem = ET.SubElement(vars_elem, 'var', {'name': v['name']})
|
||
|
||
def set_sub_elem_text(parent, tag, text):
|
||
el = parent.find(tag)
|
||
if el is None:
|
||
el = ET.SubElement(parent, tag)
|
||
el.text = str(text)
|
||
|
||
set_sub_elem_text(var_elem, 'enable', 'true' if v['enable'] else 'false')
|
||
set_sub_elem_text(var_elem, 'shortname', v['shortname'])
|
||
set_sub_elem_text(var_elem, 'pt_type', v['pt_type'])
|
||
set_sub_elem_text(var_elem, 'iq_type', v['iq_type'])
|
||
set_sub_elem_text(var_elem, 'return_type', v['return_type'])
|
||
set_sub_elem_text(var_elem, 'type', v['type'])
|
||
set_sub_elem_text(var_elem, 'file', v['file'])
|
||
set_sub_elem_text(var_elem, 'extern', v['extern'])
|
||
set_sub_elem_text(var_elem, 'static', v['static'])
|
||
|
||
# Сохраняем изменения
|
||
tree.write(xml_path, encoding='utf-8', xml_declaration=True)
|
||
|
||
except Exception as e:
|
||
print(f"Ошибка при сохранении XML: {e}")
|
||
|
||
|
||
|
||
if __name__ == "__main__":
|
||
app = QApplication(sys.argv)
|
||
|
||
editor = VarEditor()
|
||
editor.resize(900, 600)
|
||
editor.show()
|
||
|
||
sys.exit(app.exec_())
|
||
|
||
|