гитингор + чистка

+ часть работы xml перенесена в отдельный файл
+ фикс бага с записыванием в xml полных путей, вместо относительных
+ фикс бага при поиске
This commit is contained in:
Razvalyaev 2025-07-11 06:47:30 +03:00
parent 0d59f88444
commit a3850c2c8a
154 changed files with 1678 additions and 3042 deletions

7
.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
/.out
/Src/__pycache__
/build/__pycache__
/build_temp
/DebugVarEdit_GUI.build
/DebugVarEdit_GUI.dist
/DebugVarEdit_GUI.onefile-build

Binary file not shown.

Binary file not shown.

View File

@ -1,732 +0,0 @@
# pyinstaller --onefile --distpath . --workpath ./build --specpath ./build scanVars.py
import sys
import os
import re
import argparse
from pathlib import Path
# === Словарь соответствия типов XML → DebugVarType_t ===
type_map = dict([
*[(k, 'pt_int8') for k in ('signed char', 'char')],
*[(k, 'pt_int16') for k in ('int', 'int16', 'short')],
*[(k, 'pt_int32') for k in ('long', 'int32', '_iqx')],
*[(k, 'pt_int64') for k in ('long long', 'int64')],
*[(k, 'pt_uint8') for k in ('unsigned char',)],
*[(k, 'pt_uint16') for k in ('unsigned int', 'unsigned short', 'Uint16')],
*[(k, 'pt_uint32') for k in ('unsigned long', 'Uint32')],
*[(k, 'pt_uint64') for k in ('unsigned long long', 'Uint64')],
*[(k, 'pt_ptr_int8') for k in ('signed char*',)],
*[(k, 'pt_ptr_int16') for k in ('int*', 'short*')],
*[(k, 'pt_ptr_int32') for k in ('long*',)],
*[(k, 'pt_ptr_uint8') for k in ('unsigned char*',)],
*[(k, 'pt_ptr_uint16') for k in ('unsigned int*', 'unsigned short*')],
*[(k, 'pt_ptr_uint32') for k in ('unsigned long*',)],
('unsigned long long*', 'pt_int64'),
*[(k, 'pt_arr_int8') for k in ('signed char[]',)],
*[(k, 'pt_arr_int16') for k in ('int[]', 'short[]')],
*[(k, 'pt_arr_int32') for k in ('long[]',)],
*[(k, 'pt_arr_uint8') for k in ('unsigned char[]',)],
*[(k, 'pt_arr_uint16') for k in ('unsigned int[]', 'unsigned short[]')],
*[(k, 'pt_arr_uint32') for k in ('unsigned long[]',)],
*[(k, 'pt_float') for k in ('float', 'float32')],
('struct', 'pt_struct'),
('union', 'pt_union'),
])
def parse_makefile(makefile_path):
makefile_dir = os.path.dirname(makefile_path)
project_root = os.path.dirname(makefile_dir) # поднялись из Debug
with open(makefile_path, 'r', encoding='utf-8') as f:
lines = f.readlines()
objs_lines = []
collecting = False
for line in lines:
stripped = line.strip()
if stripped.startswith("ORDERED_OBJS") and "+=" in stripped:
parts = stripped.split("\\")
first_part = parts[0]
idx = first_part.find("+=")
tail = first_part[idx+2:].strip()
if tail:
objs_lines.append(tail)
collecting = True
if len(parts) > 1:
for p in parts[1:]:
p = p.strip()
if p:
objs_lines.append(p)
continue
if collecting:
if stripped.endswith("\\"):
objs_lines.append(stripped[:-1].strip())
else:
objs_lines.append(stripped)
collecting = False
objs_str = ' '.join(objs_lines)
objs_str = re.sub(r"\$\([^)]+\)", "", objs_str)
objs = []
for part in objs_str.split():
part = part.strip()
if part.startswith('"') and part.endswith('"'):
part = part[1:-1]
if part:
objs.append(part)
c_files = []
include_dirs = set()
for obj_path in objs:
if "DebugTools" in obj_path:
continue
if "v120" in obj_path:
continue
if obj_path.startswith("Debug\\") or obj_path.startswith("Debug/"):
rel_path = obj_path.replace("Debug\\", "Src\\").replace("Debug/", "Src/")
else:
rel_path = obj_path
abs_path = os.path.normpath(os.path.join(project_root, rel_path))
root, ext = os.path.splitext(abs_path)
if ext.lower() == ".obj":
c_path = root + ".c"
else:
c_path = abs_path
# Сохраняем только .c файлы
if c_path.lower().endswith(".c"):
c_files.append(c_path)
dir_path = os.path.dirname(c_path)
if dir_path and "DebugTools" not in dir_path:
include_dirs.add(dir_path)
return c_files, sorted(include_dirs)
# Шаблон для поиска глобальных переменных
# Пример: int varname;
# Пропускаем строки с static и функции
VAR_PATTERN = re.compile(
r'^\s*(?!static)(\w[\w\s\*]+)\s+(\w+)\s*(=\s*[^;]+)?\s*;',
re.MULTILINE)
EXTERN_PATTERN = re.compile(
r'^\s*extern\s+[\w\s\*]+\s+(\w+)\s*;',
re.MULTILINE)
TYPEDEF_PATTERN = re.compile(
r'typedef\s+(struct|union)?\s*(\w+)?\s*{[^}]*}\s*(\w+)\s*;', re.DOTALL)
TYPEDEF_SIMPLE_PATTERN = re.compile(
r'typedef\s+(.+?)\s+(\w+)\s*;', re.DOTALL)
def map_type_to_pt(typename, varname):
typename = typename.strip()
# Убираем const и volatile, где бы они ни были (например, "const volatile int")
for qualifier in ('const', 'volatile'):
typename = typename.replace(qualifier, '')
typename = typename.strip() # снова убрать лишние пробелы после удаления
# Раскрутка через typedef
resolved_type = typedef_aliases.get(typename, typename)
# Прямая проверка
if resolved_type in type_map:
return type_map[resolved_type]
if resolved_type.startswith('struct'):
return type_map['struct']
if resolved_type.startswith('union'):
return type_map['union']
if '_iq' in resolved_type and '_iqx' in type_map:
return type_map['_iqx']
return 'pt_unknown'
def get_iq_define(vtype):
if '_iq' in vtype:
# Преобразуем _iqXX в t_iqXX
return 't' + vtype[vtype.index('_iq'):]
else:
return 't_iq_none'
def get_files_by_ext(roots, exts):
files = []
for root in roots:
for dirpath, _, filenames in os.walk(root):
for f in filenames:
if any(f.endswith(e) for e in exts):
files.append(os.path.join(dirpath, f))
return files
def read_file_try_encodings(filepath):
for enc in ['utf-8', 'cp1251']:
try:
with open(filepath, 'r', encoding=enc) as f:
content = f.read()
content = strip_single_line_comments(content) # <=== ВАЖНО
return content, enc
except UnicodeDecodeError:
continue
raise UnicodeDecodeError(f"Не удалось прочитать файл {filepath} с кодировками utf-8 и cp1251")
FUNC_PATTERN = re.compile(
r'\w[\w\s\*\(\),]*\([^;{)]*\)\s*\{(?:[^{}]*|\{[^}]*\})*?\}', re.DOTALL)
def strip_single_line_comments(code):
# Удалим // ... до конца строки
return re.sub(r'//.*?$', '', code, flags=re.MULTILINE)
def remove_function_bodies(code):
result = []
i = 0
length = len(code)
while i < length:
match = re.search(r'\b[\w\s\*\(\),]*\([^;{}]*\)\s*\{', code[i:])
if not match:
result.append(code[i:])
break
start = i + match.start()
brace_start = i + match.end() - 1
result.append(code[i:start]) # Добавляем всё до функции
# Ищем конец функции по уровню вложенности скобок
brace_level = 1
j = brace_start + 1
in_string = False
while j < length and brace_level > 0:
char = code[j]
if char == '"' or char == "'":
quote = char
j += 1
while j < length and code[j] != quote:
if code[j] == '\\':
j += 2
else:
j += 1
elif code[j] == '{':
brace_level += 1
elif code[j] == '}':
brace_level -= 1
j += 1
# Заменяем тело функции пробелами той же длины
result.append(' ' * (j - start))
i = j
return ''.join(result)
def extract_struct_definitions_from_file(filepath):
with open(filepath, 'r', encoding='utf-8', errors='ignore') as f:
content = f.read()
# Удаляем комментарии
content = re.sub(r'//.*', '', content)
content = re.sub(r'/\*.*?\*/', '', content, flags=re.DOTALL)
type_definitions = {}
anonymous_counter = [0]
def split_fields(body):
"""
Разбивает тело struct/union на поля с учётом вложенных { }.
Возвращает список строк - полей (без ;).
"""
fields = []
start = 0
brace_level = 0
for i, ch in enumerate(body):
if ch == '{':
brace_level += 1
elif ch == '}':
brace_level -= 1
elif ch == ';' and brace_level == 0:
fields.append(body[start:i].strip())
start = i + 1
# если что-то осталось после последнего ;
tail = body[start:].strip()
if tail:
fields.append(tail)
return fields
def parse_fields(body):
fields = {}
body = body.strip('{} \n\t')
field_strings = split_fields(body)
for field_str in field_strings:
if field_str.startswith(('struct ', 'union ')):
# Вложенный struct/union
# Пытаемся найти имя и тело
m = re.match(r'(struct|union)\s*(\w*)\s*({.*})\s*(\w+)?', field_str, re.DOTALL)
if m:
kind, tag, inner_body, varname = m.groups()
if not varname:
# Анонимная вложенная структура/объединение
varname = f"__anon_{anonymous_counter[0]}"
anonymous_counter[0] += 1
type_definitions[varname] = parse_fields(inner_body)
fields[varname] = varname
else:
# Если есть имя переменной
anon_type_name = f"__anon_{anonymous_counter[0]}"
anonymous_counter[0] += 1
type_definitions[anon_type_name] = parse_fields(inner_body)
fields[varname] = anon_type_name if tag == '' else f"{kind} {tag}"
else:
# Не смогли распарсить вложенную структуру - кладём как есть
fields[field_str] = None
else:
# Обычное поле
# Нужно выделить тип и имя поля с учётом указателей и массивов
m = re.match(r'(.+?)\s+([\w\*\[\]]+)$', field_str)
if m:
typename, varname = m.groups()
fields[varname.strip()] = typename.strip()
else:
# не распарсили поле — кладём "как есть"
fields[field_str] = None
return fields
# Парсим typedef struct/union {...} Alias;
typedef_struct_pattern = re.compile(
r'\btypedef\s+(struct|union)\s*({.*?})\s*(\w+)\s*;', re.DOTALL)
for match in typedef_struct_pattern.finditer(content):
alias = match.group(3)
body = match.group(2)
type_definitions[alias] = parse_fields(body)
# Парсим struct/union Name {...};
named_struct_pattern = re.compile(
r'\b(struct|union)\s+(\w+)\s*({.*?})\s*;', re.DOTALL)
for match in named_struct_pattern.finditer(content):
name = match.group(2)
body = match.group(3)
if name not in type_definitions:
type_definitions[name] = parse_fields(body)
return type_definitions
def parse_vars_from_file(filepath):
content, encoding = read_file_try_encodings(filepath)
content_clean = remove_function_bodies(content)
vars_found = []
for m in VAR_PATTERN.finditer(content_clean):
typename = m.group(1).strip()
varlist = m.group(2)
for var in varlist.split(','):
varname = var.strip()
# Убираем указатели и массивы
varname = varname.strip('*').split('[')[0]
# Фильтрация мусора
if not re.match(r'^[_a-zA-Z][_a-zA-Z0-9]*$', varname):
continue
vars_found.append((varname, typename))
return vars_found, encoding
def parse_typedefs_from_file(filepath):
"""
Парсит typedef из файла C:
- typedef struct/union { ... } Alias;
- typedef simple_type Alias;
Возвращает словарь alias -> базовый тип (например, 'MyType' -> 'struct' или 'unsigned int').
"""
with open(filepath, 'r', encoding='utf-8', errors='ignore') as f:
content = f.read()
# Убираем однострочные комментарии
content = re.sub(r'//.*?$', '', content, flags=re.MULTILINE)
# Убираем многострочные комментарии
content = re.sub(r'/\*.*?\*/', '', content, flags=re.DOTALL)
aliases = {}
# --- Парсим typedef struct/union {...} Alias;
# Используем стек для вложенных фигурных скобок
typedef_struct_union_pattern = re.compile(r'\btypedef\s+(struct|union)\b', re.IGNORECASE)
pos = 0
while True:
m = typedef_struct_union_pattern.search(content, pos)
if not m:
break
kind = m.group(1)
brace_open_pos = content.find('{', m.end())
if brace_open_pos == -1:
# Нет тела структуры, пропускаем
pos = m.end()
continue
# Ищем позицию закрывающей скобки с учётом вложенности
brace_level = 1
i = brace_open_pos + 1
while i < len(content) and brace_level > 0:
if content[i] == '{':
brace_level += 1
elif content[i] == '}':
brace_level -= 1
i += 1
if brace_level != 0:
# Некорректный синтаксис
pos = m.end()
continue
# Отрезок typedef структуры/объединения
typedef_block = content[m.start():i]
# После закрывающей скобки ожидаем имя алиаса и точку с запятой
rest = content[i:].lstrip()
alias_match = re.match(r'(\w+)\s*;', rest)
if alias_match:
alias_name = alias_match.group(1)
aliases[alias_name] = kind # например, "struct" или "union"
pos = i + alias_match.end()
else:
# Анонимный typedef? Просто пропускаем
pos = i
# --- Удаляем typedef struct/union {...} Alias; чтобы не мешали простым typedef
# Для этого удалим весь блок typedef struct/union {...} Alias;
def remove_typedef_struct_union_blocks(text):
result = []
last_pos = 0
for m in typedef_struct_union_pattern.finditer(text):
brace_open_pos = text.find('{', m.end())
if brace_open_pos == -1:
continue
brace_level = 1
i = brace_open_pos + 1
while i < len(text) and brace_level > 0:
if text[i] == '{':
brace_level += 1
elif text[i] == '}':
brace_level -= 1
i += 1
if brace_level != 0:
continue
# Ищем имя алиаса и точку с запятой после i
rest = text[i:].lstrip()
alias_match = re.match(r'\w+\s*;', rest)
if alias_match:
end_pos = i + alias_match.end()
result.append(text[last_pos:m.start()])
last_pos = end_pos
result.append(text[last_pos:])
return ''.join(result)
content_simple = remove_typedef_struct_union_blocks(content)
# --- Парсим простые typedef: typedef base_type alias;
simple_typedef_pattern = re.compile(
r'\btypedef\s+([^{};]+?)\s+(\w+)\s*;', re.MULTILINE)
for m in simple_typedef_pattern.finditer(content_simple):
base_type = m.group(1).strip()
alias = m.group(2).strip()
if alias not in aliases:
aliases[alias] = base_type
return aliases
def parse_externs_from_file(filepath):
content, encoding = read_file_try_encodings(filepath)
extern_vars = set(EXTERN_PATTERN.findall(content))
return extern_vars, encoding
def get_relpath_to_srcdirs(filepath, src_dirs):
# Ищем первый SRC_DIR, в котором лежит filepath, и возвращаем относительный путь
for d in src_dirs:
try:
rel = os.path.relpath(filepath, d)
# Проверим, что rel не уходит выше корня (например, не начинается с '..')
if not rel.startswith('..'):
return rel.replace('\\', '/') # Для единообразия в путях
except ValueError:
continue
# Если ни один SRC_DIR не подходит, вернуть basename
return os.path.basename(filepath)
def find_all_includes_recursive(c_files, include_dirs, processed_files=None):
"""
Рекурсивно ищет все include-файлы начиная с заданных c_files.
include_dirs список директорий, в которых ищем include-файлы.
processed_files множество уже обработанных файлов (для избежания циклов).
"""
if processed_files is None:
processed_files = set()
include_files = set()
include_pattern = re.compile(r'#include\s+"([^"]+)"')
for cfile in c_files:
norm_path = os.path.normpath(cfile)
if norm_path in processed_files:
continue
processed_files.add(norm_path)
content, _ = read_file_try_encodings(cfile)
includes = include_pattern.findall(content)
for inc in includes:
include_files.add(inc)
# Ищем полный путь к include-файлу в include_dirs
inc_full_path = None
for dir_ in include_dirs:
candidate = os.path.normpath(os.path.join(dir_, inc))
if os.path.isfile(candidate):
inc_full_path = candidate
break
# Если нашли include-файл и ещё не обработали — рекурсивно ищем include внутри него
if inc_full_path and inc_full_path not in processed_files:
nested_includes = find_all_includes_recursive(
[inc_full_path], include_dirs, processed_files
)
include_files.update(nested_includes)
return include_files
def file_uses_typedef_vars(filepath, missing_vars, typedefs):
"""
Проверяем, содержит ли файл typedef с одним из missing_vars.
typedefs словарь alias->базовый тип, полученный parse_typedefs_from_file.
"""
# Здесь проще проверить, есть ли в typedefs ключи из missing_vars,
# но в условии — typedef переменных из missing_in_h,
# значит, нужно проверить typedef переменных с этими именами
# Для упрощения — прочитаем содержимое и проверим наличие typedef с именами из missing_vars
content, _ = read_file_try_encodings(filepath)
for var in missing_vars:
# Ищем в content что-то типа typedef ... var ...;
# Для простоты регулярка: typedef ... var;
pattern = re.compile(r'\btypedef\b[^;]*\b' + re.escape(var) + r'\b[^;]*;', re.DOTALL)
if pattern.search(content):
return True
return False
def file_contains_extern_vars(filepath, extern_vars):
content, _ = read_file_try_encodings(filepath)
for var in extern_vars:
pattern = re.compile(r'\bextern\b[^;]*\b' + re.escape(var) + r'\b\s*;')
if pattern.search(content):
return True
return False
def add_struct_fields(new_debug_vars, var_prefix, struct_type, all_structs, existing_debug_vars):
"""
Рекурсивно добавляет поля структуры в new_debug_vars.
var_prefix: имя переменной или путь к полю (например "myVar" или "myVar.subfield")
struct_type: имя типа структуры (например "MyStruct")
all_structs: словарь всех структур
existing_debug_vars: множество уже существующих имен переменных
"""
if struct_type not in all_structs:
# Типа нет в структуре, значит не структура или неизвестный тип — выходим
return
fields = all_structs[struct_type]
for field_name, field_type in fields.items():
full_var_name = f"{var_prefix}.{field_name}"
if full_var_name in existing_debug_vars or full_var_name in new_debug_vars:
continue
if field_type is None:
continue
iq_type = get_iq_define(field_type)
pt_type = map_type_to_pt(field_type, full_var_name)
formated_name = f'"{full_var_name}"'
line = f'\t{{(char *)&{full_var_name:<40} , {pt_type:<20} , {iq_type:<20} , {formated_name:<40}}}, \\'
new_debug_vars[full_var_name] = line
# Если поле — тоже структура, рекурсивно раскрываем
# При этом убираем указатели и массивы из типа, если они есть
base_field_type = field_type.split()[0] # например "struct" или "MyStruct*"
# Удаляем указатели и массивы из имени типа для поиска в all_structs
base_field_type = re.sub(r'[\*\[\]0-9]+', '', base_field_type)
base_field_type = base_field_type.strip()
if base_field_type in all_structs:
add_struct_fields(new_debug_vars, full_var_name, base_field_type, all_structs, existing_debug_vars)
else:
a=1
def main(make_path):
c_files, include_dirs = parse_makefile(make_path)
all_dirs = c_files + include_dirs
h_files = get_files_by_ext(include_dirs, ['.h'])
vars_in_c = {}
encodings_c = set()
for cf in c_files:
vars_found, enc = parse_vars_from_file(cf)
encodings_c.add(enc)
for vname, vtype in vars_found:
vars_in_c[vname] = (vtype, cf)
externs_in_h = set()
for hf in h_files:
externs, _ = parse_externs_from_file(hf)
externs_in_h |= externs
missing_in_h = {v: vars_in_c[v] for v in vars_in_c if v not in externs_in_h}
all_structs = {}
for fl in c_files + h_files:
structs = extract_struct_definitions_from_file(fl)
all_structs.update(structs)
# Подготовка typedef-ов
global typedef_aliases
typedef_aliases = {}
for f in h_files + c_files:
aliases = parse_typedefs_from_file(f)
typedef_aliases.update(aliases)
included_headers = find_all_includes_recursive(c_files, include_dirs) # все подключенные .h
include_files = []
for header in included_headers:
# Полный путь к файлу нужно получить
full_path = None
for d in all_dirs:
candidate = os.path.join(d, header)
if os.path.isfile(candidate):
full_path = candidate
break
if not full_path:
continue # файл не найден в SRC_DIRS — игнорируем
# Проверяем, что это строго .h
if not full_path.endswith('.h'):
continue
# Парсим typedef из файла
typedefs = parse_typedefs_from_file(full_path)
# Проверяем, использует ли typedef переменные из missing_in_h по их vtype
uses_typedef = any(vtype in typedefs for (vtype, path) in missing_in_h.values())
# Проверяем наличие extern переменных
has_extern = file_contains_extern_vars(full_path, externs_in_h)
if not has_extern and not uses_typedef:
continue
# Если прошло оба условия — добавляем
include_files.append(full_path)
# Путь к debug_vars.h
common_prefix = os.path.commonpath(include_dirs)
output_dir = os.path.join(common_prefix, 'DebugTools')
os.makedirs(output_dir, exist_ok=True)
output_path = os.path.join(output_dir, 'debug_vars.c')
# Считываем существующие переменные
existing_debug_vars = {}
if os.path.isfile(output_path):
with open(output_path, 'r', encoding='utf-8', errors='ignore') as f:
old_lines = f.readlines()
for line in old_lines:
m = re.match(r'\s*{.*?,\s+.*?,\s+.*?,\s+"([_a-zA-Z][_a-zA-Z0-9]*)"\s*},', line)
if m:
varname = m.group(1)
existing_debug_vars[varname] = line.strip()
# Генерируем новые переменные
new_debug_vars = {}
for vname, (vtype, path) in vars_in_c.items():
if vname in existing_debug_vars:
continue
iq_type = get_iq_define(vtype)
pt_type = map_type_to_pt(vtype, vname)
if pt_type not in ('pt_struct', 'pt_union'):
formated_name = f'"{vname}"'
line = f'{{(char *)&{vname:<41} , {pt_type:<21} , {iq_type:<21} , {formated_name:<42}}}, \\'
new_debug_vars[vname] = line
else:
continue
# Если тип переменной — структура, добавляем поля
base_type = vtype.split()[0]
# Удаляем символы указателей '*' и всю квадратную скобку с содержимым (например [10])
base_type = re.sub(r'\*|\[[^\]]*\]', '', base_type).strip()
if base_type in all_structs:
add_struct_fields(new_debug_vars, vname, base_type, all_structs, existing_debug_vars)
# Сортируем новые переменные по алфавиту по имени
sorted_new_debug_vars = dict(sorted(new_debug_vars.items()))
# Объединяем все переменные
all_debug_lines = list(existing_debug_vars.values()) + list(sorted_new_debug_vars.values())
# DebugVar_Numb теперь по всем переменным
out_lines = []
out_lines.append("// Этот файл сгенерирован автоматически")
out_lines.append(f'#include "debug_tools.h"')
out_lines.append('\n\n// Инклюды для доступа к переменным')
out_lines.append(f'#include "IQmathLib.h"')
for incf in include_files:
out_lines.append(f'#include "{incf}"')
out_lines.append('\n\n// Экстерны для доступа к переменным')
for vname, (vtype, path) in missing_in_h.items():
out_lines.append(f'extern {vtype} {vname};')
out_lines.append(f'\n\n// Определение массива с указателями на переменные для отладки')
out_lines.append(f'int DebugVar_Numb = {len(all_debug_lines)};')
out_lines.append('#pragma DATA_SECTION(dbg_vars,".dbgvar_info")')
out_lines.append('DebugVar_t dbg_vars[] = {\\')
out_lines.extend(all_debug_lines)
out_lines.append('};')
out_lines.append('')
# Выберем кодировку для записи файла
# Если встречается несколько, возьмем первую из set
enc_to_write = 'cp1251'
#print("== GLOBAL VARS FOUND ==")
#for vname, (vtype, path) in vars_in_c.items():
#print(f"{vtype:<20} {vname:<40} // {path}")
with open(output_path, 'w', encoding=enc_to_write) as f:
f.write('\n'.join(out_lines))
print(f'Файл debug_vars.c сгенерирован в кодировке, переменных: {len(all_debug_lines)}')
if __name__ == '__main__':
if len(sys.argv) < 2:
main('F:/Work/Projects/TMS/TMS_new_bus/Debug/makefile')
print("Usage: parse_makefile.py path/to/Makefile")
sys.exit(1)
else:
main(sys.argv[1])

View File

@ -1,143 +0,0 @@
import xml.etree.ElementTree as ET
# === Словарь соответствия типов XML → DebugVarType_t ===
type_map = dict([
*[(k, 'pt_int8') for k in ('signed char', 'char')],
*[(k, 'pt_int16') for k in ('int', 'int16', 'short')],
*[(k, 'pt_int32') for k in ('long', 'int32', '_iqx')],
*[(k, 'pt_int64') for k in ('long long', 'int64')],
*[(k, 'pt_uint8') for k in ('unsigned char',)],
*[(k, 'pt_uint16') for k in ('unsigned int', 'unsigned short', 'Uint16')],
*[(k, 'pt_uint32') for k in ('unsigned long', 'Uint32')],
*[(k, 'pt_uint64') for k in ('unsigned long long', 'Uint64')],
*[(k, 'pt_ptr_int8') for k in ('signed char*',)],
*[(k, 'pt_ptr_int16') for k in ('int*', 'short*')],
*[(k, 'pt_ptr_int32') for k in ('long*',)],
*[(k, 'pt_ptr_uint8') for k in ('unsigned char*',)],
*[(k, 'pt_ptr_uint16') for k in ('unsigned int*', 'unsigned short*')],
*[(k, 'pt_ptr_uint32') for k in ('unsigned long*',)],
('unsigned long long*', 'pt_int64'),
*[(k, 'pt_arr_int8') for k in ('signed char[]',)],
*[(k, 'pt_arr_int16') for k in ('int[]', 'short[]')],
*[(k, 'pt_arr_int32') for k in ('long[]',)],
*[(k, 'pt_arr_uint8') for k in ('unsigned char[]',)],
*[(k, 'pt_arr_uint16') for k in ('unsigned int[]', 'unsigned short[]')],
*[(k, 'pt_arr_uint32') for k in ('unsigned long[]',)],
*[(k, 'pt_float') for k in ('float', 'float32')],
('struct', 'pt_struct'),
('union', 'pt_union'),
])
def is_anonymous(elem):
typ = elem.attrib.get('type', '')
return 'anonymous' in typ
def get_array_sizes(elem):
sizes = []
i = 0
while True:
attr = 'size' if i == 0 else f'size{i}'
if attr in elem.attrib:
sizes.append(elem.attrib[attr])
i += 1
else:
break
return ''.join(f'[{size}]' for size in sizes)
def collect_externs_and_vars(element, prefix, extern_vars, variables):
kind = element.attrib.get('kind', '')
name = element.attrib.get('name', prefix)
vtype = element.attrib.get('type', 'unknown')
# Пропускаем анонимные типы полностью
if is_anonymous(element):
return
# Для массивов учитываем все размеры size, size1, size2...
if kind == 'array':
array_dims = get_array_sizes(element)
extern_type = vtype # тип без размеров
extern_vars[name] = (extern_type, array_dims)
variables.append((name, vtype)) # В массив макросов кладём сам массив (без элементов)
return
# Для обычных структур (не анонимных) и переменных
if kind in ('struct', 'union'):
extern_vars[name] = (vtype, '')
variables.append((name, vtype))
return
# Простые переменные
extern_vars[name] = (vtype, '')
variables.append((name, vtype))
def parse_variables(xml_path):
tree = ET.parse(xml_path)
root = tree.getroot()
extern_vars = dict()
variables = []
for var in root.findall('variable'):
collect_externs_and_vars(var, var.attrib['name'], extern_vars, variables)
return variables, extern_vars
def format_extern_declaration(varname, vartype, dims):
base_type = vartype.replace('[]', '') # убираем [] из типа
return f"extern {base_type} {varname}{dims};"
def to_macro_entry(varname, vartype):
# pt_ и t_iq_none — пример, замените на вашу логику или словарь
ptr_type = type_map.get(vartype)
if ptr_type is None:
ptr_type = 'char *'
iq_type = "t_iq_none"
return f"\t{{(char *)&{varname:<40}, {ptr_type:<20}, {iq_type:<20}, \"{varname}\"}},"
def generate_code(variables, extern_vars):
# extern-блок
extern_lines = []
for var, (vtype, dims) in sorted(extern_vars.items()):
# фильтр анонимных структур по имени (пример)
if "anonymous" in var:
continue
extern_lines.append(format_extern_declaration(var, vtype, dims))
# define DebugVar_Numb
debugvar_numb = len(variables)
defines = [f"#define DebugVar_Numb\t{debugvar_numb}"]
# define DebugVar_Init
defines.append("#define DebugVar_Init\t\\")
defines.append("{\\")
for varname, vartype in variables:
defines.append(to_macro_entry(varname, vartype))
defines.append("}")
return "\n".join(extern_lines) + "\n\n" + "\n".join(defines)
if __name__ == "__main__":
xml_file = "F:/Work/Projects/TMS/TMS_new_bus/bin/New_bus_allVars.xml"
variables, extern_vars = parse_variables(xml_file)
code = generate_code(variables, extern_vars)
with open("debug_vars.h", "w") as f:
f.write(code)
print("Сгенерировано в debug_vars.h")

View File

@ -1,581 +0,0 @@
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_())

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,2 +0,0 @@
[Paths]
Prefix = .

View File

@ -1 +0,0 @@
<クd<>箆!ソ`。スン

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More