Compare commits

...

3 Commits

Author SHA1 Message Date
3343428796 попытка перехода на python 3.12 и nutika для компиляции без зависимостей 2025-07-09 19:07:27 +03:00
f881132fa8 добавлена кнопка для открытия выходного файла
+ exe
2025-07-09 15:56:18 +03:00
4962276760 работа с таблицей перенесена в отдельный файл
сделано автозаполнение при поиске переменых
сделано правильное формирование структур, через . или ->

пофиксены мелкие фиксы
2025-07-09 15:40:16 +03:00
156 changed files with 311005 additions and 5611 deletions

Binary file not shown.

View File

@ -1,9 +1,10 @@
import re
from PySide6.QtWidgets import (
QDialog, QTreeWidget, QTreeWidgetItem, QVBoxLayout, QPushButton,
QLineEdit, QLabel, QHeaderView
QLineEdit, QLabel, QHeaderView, QCompleter, QCheckBox, QHBoxLayout
)
from PySide6.QtCore import Qt
from PySide6.QtGui import QKeySequence, QKeyEvent
from PySide6.QtCore import Qt, QStringListModel, QSettings
from setupVars import *
from scanVars import *
@ -25,9 +26,21 @@ class VariableSelectorDialog(QDialog):
self.xml_path = xml_path # сохраняем путь к xml
# --- Добавляем чекбокс для автодополнения ---
self.autocomplete_checkbox = QCheckBox("Включить автодополнение")
self.autocomplete_checkbox.setChecked(True)
# Инициализируем QSettings с именем организации и приложения
self.settings = QSettings("SET", "DebugVarEdit_VarsSelector")
# Восстанавливаем сохранённое состояние чекбокса, если есть
checked = self.settings.value("autocomplete_enabled", True, type=bool)
self.autocomplete_checkbox.setChecked(checked)
# При изменении состояния чекбокса сохраняем его
self.autocomplete_checkbox.stateChanged.connect(self.save_checkbox_state)
self.search_input = QLineEdit()
self.search_input.setPlaceholderText("Поиск по имени переменной...")
self.search_input.textChanged.connect(self.filter_tree)
self.search_input.textChanged.connect(self.on_search_text_changed)
self.tree = QTreeWidget()
self.tree.setHeaderLabels(["Имя переменной", "Тип"])
@ -51,17 +64,33 @@ class VariableSelectorDialog(QDialog):
self.btn_delete = QPushButton("Удалить выбранные")
self.btn_delete.clicked.connect(self.on_delete_clicked)
self.completer = QCompleter()
self.completer.setCompletionMode(QCompleter.PopupCompletion) # важно!
self.completer.setCaseSensitivity(Qt.CaseInsensitive)
self.completer.setFilterMode(Qt.MatchContains)
self.completer.setWidget(self.search_input)
self.search_input.installEventFilter(self)
# Создаем горизонтальный layout для "Поиск:" и чекбокса справа
search_layout = QHBoxLayout()
label_search = QLabel("Поиск:")
search_layout.addWidget(label_search, alignment=Qt.AlignLeft)
search_layout.addStretch() # чтобы чекбокс прижался вправо
search_layout.addWidget(self.autocomplete_checkbox, alignment=Qt.AlignRight)
self.completer.activated[str].connect(self.insert_completion)
layout = QVBoxLayout()
layout.addWidget(QLabel("Поиск:"))
layout.addLayout(search_layout) # заменили label и чекбокс
layout.addWidget(self.search_input)
layout.addWidget(self.tree)
layout.addWidget(self.btn_add)
layout.addWidget(self.btn_delete) # Кнопка удаления
layout.addWidget(self.btn_delete)
self.setLayout(layout)
self.populate_tree()
def add_tree_item_recursively(self, parent, var):
"""
Рекурсивно добавляет переменную и её дочерние поля в дерево.
@ -113,7 +142,7 @@ class VariableSelectorDialog(QDialog):
def filter_tree(self):
text = self.search_input.text().strip().lower()
path_parts = text.split('.') if text else []
path_parts = self.split_path(text) if text else []
def hide_all(item):
item.setHidden(True)
@ -121,7 +150,7 @@ class VariableSelectorDialog(QDialog):
hide_all(item.child(i))
def path_matches_search(name, search_parts):
name_parts = name.lower().split('.')
name_parts = self.split_path(name.lower())
if len(name_parts) < len(search_parts):
return False
for sp, np in zip(search_parts, name_parts):
@ -156,12 +185,190 @@ class VariableSelectorDialog(QDialog):
return matched or matched_any_child
for i in range(self.tree.topLevelItemCount()):
item = self.tree.topLevelItem(i)
hide_all(item)
show_matching_path(item, 0)
# Если в поиске нет точки — особая логика для первого уровня
if '.' 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)
item.setExpanded(False) # НЕ раскрываем потомков
else:
hide_all(item)
item.setHidden(True)
else:
# Обычная логика с поиском по пути
for i in range(self.tree.topLevelItemCount()):
item = self.tree.topLevelItem(i)
hide_all(item)
show_matching_path(item, 0)
def update_completions(self, text = None):
if text is None:
text = self.search_input.text().strip()
else:
text = text.strip()
parts = self.split_path(text)
path_parts = parts[:-1]
prefix = parts[-1].lower() if not text.endswith(('.', '>')) else ''
# Если путь есть (например: project.adc или project.adc.), ищем внутри него
search_deep = len(path_parts) > 0
def find_path_items(path_parts):
items = [self.tree.topLevelItem(i) for i in range(self.tree.topLevelItemCount())]
for part in path_parts:
part_lower = part.lower()
matched = []
for item in items:
# Берём последний фрагмент имени item, разделённого точками
item_name_part = self.split_path(item.text(0))[-1].lower()
if item_name_part == part_lower:
matched.append(item)
if not matched:
return []
items = []
# Собираем детей для следующего уровня поиска
for node in matched:
for i in range(node.childCount()):
items.append(node.child(i))
return matched
if not search_deep:
# Без точки — ищем только в топ-уровне, фильтруя по prefix
items = []
for i in range(self.tree.topLevelItemCount()):
item = self.tree.topLevelItem(i)
name_part = self.split_path(item.text(0))[-1].lower()
if name_part.startswith(prefix):
items.append(item)
completions = [item.text(0) for item in items]
else:
# С точкой — углубляемся по пути и показываем имена детей
if len(path_parts) == 0:
items = [self.tree.topLevelItem(i) for i in range(self.tree.topLevelItemCount())]
else:
items = find_path_items(path_parts)
completions = []
for item in items:
for i in range(item.childCount()):
child = item.child(i)
name_part = self.split_path(child.text(0))[-1].lower()
if prefix == '' or name_part.startswith(prefix):
completions.append(child.text(0))
self.completer.setModel(QStringListModel(completions))
return completions
def eventFilter(self, obj, event):
if obj == self.search_input and isinstance(event, QKeyEvent):
if event.key() == Qt.Key_Space and event.modifiers() & Qt.ControlModifier:
completions = self.update_completions()
self.completer.complete()
text = self.search_input.text().strip()
if len(completions) == 1 and completions[0].lower() == text.lower():
# Найдем узел с таким именем
def find_exact_item(name):
stack = [self.tree.topLevelItem(i) for i in range(self.tree.topLevelItemCount())]
while stack:
node = stack.pop()
if node.text(0).lower() == name.lower():
return node
for i in range(node.childCount()):
stack.append(node.child(i))
return None
node = find_exact_item(completions[0])
if node and node.childCount() > 0:
# Используем первую подсказку, чтобы определить нужный разделитель
completions = self.update_completions(text + '.')
suggestion = completions[0]
# Ищем, какой символ идёт после текущего текста
separator = '.'
if suggestion.startswith(text):
rest = suggestion[len(text):]
if rest.startswith('->'):
separator = '->'
elif rest.startswith('.'):
separator = '.'
self.search_input.setText(text + separator)
completions = self.update_completions()
self.completer.setModel(QStringListModel(completions))
self.completer.complete()
return True
# Иначе просто показываем подсказки
self.completer.setModel(QStringListModel(completions))
if completions:
self.completer.complete()
return True
return super().eventFilter(obj, event)
# Функция для поиска узла с полным именем
def find_node_by_fullname(self, name):
stack = [self.tree.topLevelItem(i) for i in range(self.tree.topLevelItemCount())]
while stack:
node = stack.pop()
if node.text(0).lower() == name.lower():
return node
for i in range(node.childCount()):
stack.append(node.child(i))
return None
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('->')):
# Определяем разделитель по имени первого ребёнка
child_name = node.child(0).text(0)
if child_name.startswith(text + '->'):
text += '->'
else:
text += '.'
self.search_input.setText(text)
self.search_input.setCursorPosition(len(text))
self.update_completions()
self.completer.complete()
else:
self.search_input.setText(text)
self.search_input.setCursorPosition(len(text))
def on_search_text_changed(self, text):
if self.autocomplete_checkbox.isChecked():
completions = self.update_completions(text)
node = self.find_node_by_fullname(text)
should_show = False
if completions:
if len(completions) > 1:
should_show = True
elif len(completions) == 1:
single_node = self.find_node_by_fullname(completions[0])
if single_node and single_node.childCount() > 0:
should_show = True
elif node and node.childCount() > 0 and not (text.endswith('.') or text.endswith('->')):
should_show = True
if should_show:
self.completer.setModel(QStringListModel(completions))
self.completer.complete()
self.filter_tree()
def on_add_clicked(self):
self.selected_names = []
@ -205,17 +412,64 @@ class VariableSelectorDialog(QDialog):
self.var_map[name] = new_var # Чтобы в будущем не добавлялось повторно
self.accept()
def on_delete_clicked(self):
# Деактивируем (удаляем из видимых) выбранные переменные
for item in self.tree.selectedItems():
name = item.text(0)
if not name:
continue
selected_names = self._get_selected_var_names()
if not selected_names:
return
# Обновляем var_map и all_vars
for name in selected_names:
if name in self.var_map:
var = self.var_map[name]
var['show_var'] = 'false'
var['enable'] = 'false'
self.var_map[name]['show_var'] = 'false'
self.var_map[name]['enable'] = 'false'
for v in self.all_vars:
if v['name'] == name:
v['show_var'] = 'false'
v['enable'] = 'false'
break
# Проверка пути к XML
if not hasattr(self, 'xml_path') or not self.xml_path:
from PySide6.QtWidgets import QMessageBox
QMessageBox.warning(self, "Ошибка", "Путь к XML не задан, невозможно обновить переменные.")
return
import xml.etree.ElementTree as ET
tree = ET.parse(self.xml_path)
root = tree.getroot()
if root is None:
return
vars_section = root.find('variables')
if vars_section is None:
return
for var_elem in vars_section.findall('var'):
name = var_elem.attrib.get('name')
if name in selected_names:
def set_text(tag, value):
el = var_elem.find(tag)
if el is None:
el = ET.SubElement(var_elem, tag)
el.text = value
set_text('show_var', 'false')
set_text('enable', 'false')
# Преобразуем дерево в строку
rough_string = ET.tostring(root, encoding="utf-8")
# Парсим и форматируем с отступами
reparsed = minidom.parseString(rough_string)
pretty_xml = reparsed.toprettyxml(indent=" ")
# Записываем в файл
with open(self.xml_path, "w", encoding="utf-8") as f:
f.write(pretty_xml)
self.populate_tree()
self.accept()
@ -231,16 +485,11 @@ class VariableSelectorDialog(QDialog):
super().keyPressEvent(event)
def delete_selected_vars(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'
selected_names = self._get_selected_var_names()
if not selected_names:
return
# Проверка пути к XML
if not hasattr(self, 'xml_path') or not self.xml_path:
from PySide6.QtWidgets import QMessageBox
QMessageBox.warning(self, "Ошибка", "Путь к XML не задан, невозможно удалить переменные.")
@ -249,31 +498,47 @@ class VariableSelectorDialog(QDialog):
import xml.etree.ElementTree as ET
tree = ET.parse(self.xml_path)
root = tree.getroot()
if root is None:
return
vars_section = root.find('variables')
if vars_section is None:
return # Нет секции variables — ничего удалять
selected_names = [item.text(0) for item in self.tree.selectedItems() if item.text(0)]
return
removed_any = False
for var_elem in vars_section.findall('var'):
for var_elem in list(vars_section.findall('var')):
name = var_elem.attrib.get('name')
if name in selected_names:
vars_section.remove(var_elem)
removed_any = True
if name in self.var_map:
del self.var_map[name]
# Удаляем элементы из списка на месте
self.all_vars[:] = [v for v in self.all_vars if v['name'] != name]
self.var_map.pop(name, None)
# Удаляем из all_vars (глобально)
self.all_vars[:] = [v for v in self.all_vars if v['name'] not in selected_names]
if removed_any:
ET.indent(tree, space=" ", level=0)
tree.write(self.xml_path, encoding='utf-8', xml_declaration=True)
# Преобразуем дерево в строку
rough_string = ET.tostring(root, encoding="utf-8")
# Парсим и форматируем с отступами
reparsed = minidom.parseString(rough_string)
pretty_xml = reparsed.toprettyxml(indent=" ")
# Записываем в файл
with open(self.xml_path, "w", encoding="utf-8") as f:
f.write(pretty_xml)
self.populate_tree()
self.filter_tree()
def _get_selected_var_names(self):
return [item.text(0) for item in self.tree.selectedItems() if item.text(0)]
self.populate_tree()
def save_checkbox_state(self):
self.settings.setValue("autocomplete_enabled", self.autocomplete_checkbox.isChecked())
def split_path(self, path):
# Разбиваем по точке или по -> (учитываем, что -> длиной 2 символа)
return re.split(r'\.|->', path)

184
Src/VariableTable.py Normal file
View File

@ -0,0 +1,184 @@
from PySide6.QtWidgets import (
QTableWidget, QTableWidgetItem, QCheckBox, QComboBox, QLineEdit, QCompleter,
QAbstractItemView, QHeaderView
)
from PySide6.QtCore import Qt
from enum import IntEnum
from generateVars import type_map
class rows(IntEnum):
No = 0
include = 1
name = 2
type = 3
pt_type = 4
iq_type = 5
ret_type = 6
short_name = 7
class VariableTableWidget(QTableWidget):
def __init__(self, parent=None):
super().__init__(0, 8, parent)
# Таблица переменных
self.setHorizontalHeaderLabels([
'', # новый столбец
'En',
'Name',
'Origin Type',
'Pointer Type',
'IQ Type',
'Return Type',
'Short Name'
])
self.setEditTriggers(QAbstractItemView.AllEditTriggers)
self.type_options = list(dict.fromkeys(type_map.values()))
self.display_type_options = [t.replace('pt_', '') for t in self.type_options]
self.iq_types = ['iq_none', 'iq'] + [f'iq{i}' for i in range(1, 31)]
header = self.horizontalHeader()
# Для остальных колонок — растяжение (Stretch), чтобы они заняли всю оставшуюся ширину
for col in range(self.columnCount()):
if col == self.columnCount() - 1:
header.setSectionResizeMode(col, QHeaderView.Stretch)
else:
header.setSectionResizeMode(col, QHeaderView.Interactive)
parent_widget = self.parentWidget()
# Сделаем колонки с номерами фиксированной ширины
self.setColumnWidth(rows.No, 30)
self.setColumnWidth(rows.include, 30)
self.setColumnWidth(rows.pt_type, 85)
self.setColumnWidth(rows.iq_type, 85)
self.setColumnWidth(rows.ret_type, 85)
self.setColumnWidth(rows.name, 300)
self.setColumnWidth(rows.type, 100)
self._resizing = False
self.horizontalHeader().sectionResized.connect(self.on_section_resized)
def populate(self, vars_list, structs, on_change_callback):
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)]
filtered_vars = [v for v in vars_list if v.get('show_var', 'false') == 'true']
self.setRowCount(len(filtered_vars))
self.verticalHeader().setVisible(False)
for row, var in enumerate(filtered_vars):
# №
no_item = QTableWidgetItem(str(row))
no_item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
self.setItem(row, rows.No, no_item)
# Enable
cb = QCheckBox()
cb.setChecked(var.get('enable', 'false') == 'true')
cb.stateChanged.connect(on_change_callback)
self.setCellWidget(row, rows.include, cb)
# Name
name_edit = QLineEdit(var['name'])
if var['type'] in structs:
completer = QCompleter(structs[var['type']].keys())
completer.setCaseSensitivity(Qt.CaseInsensitive)
name_edit.setCompleter(completer)
name_edit.textChanged.connect(on_change_callback)
self.setCellWidget(row, rows.name, name_edit)
# Origin Type (readonly)
origin_item = QTableWidgetItem(var.get('type', ''))
origin_item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
self.setItem(row, rows.type, origin_item)
# pt_type
pt_combo = QComboBox()
pt_combo.addItems(self.display_type_options)
value = var['pt_type'].replace('pt_', '')
if value not in self.display_type_options:
pt_combo.addItem(value)
pt_combo.setCurrentText(value)
pt_combo.currentTextChanged.connect(on_change_callback)
self.setCellWidget(row, rows.pt_type, pt_combo)
# iq_type
iq_combo = QComboBox()
iq_combo.addItems(self.iq_types)
value = var['iq_type'].replace('t_', '')
if value not in self.iq_types:
iq_combo.addItem(value)
iq_combo.setCurrentText(value)
iq_combo.currentTextChanged.connect(on_change_callback)
self.setCellWidget(row, rows.iq_type, iq_combo)
# return_type
ret_combo = QComboBox()
ret_combo.addItems(self.iq_types)
ret_combo.setCurrentText(var.get('return_type', ''))
ret_combo.currentTextChanged.connect(on_change_callback)
self.setCellWidget(row, rows.ret_type, ret_combo)
# short_name
short_name_edit = QLineEdit(var.get('shortname', var['name']))
short_name_edit.textChanged.connect(on_change_callback)
self.setCellWidget(row, rows.short_name, short_name_edit)
def read_data(self):
result = []
for row in range(self.rowCount()):
cb = self.cellWidget(row, rows.include)
name = self.cellWidget(row, rows.name).text()
pt = self.cellWidget(row, rows.pt_type).currentText()
iq = self.cellWidget(row, rows.iq_type).currentText()
ret = self.cellWidget(row, rows.ret_type).currentText()
shortname = self.cellWidget(row, rows.short_name).text()
origin_type = self.item(row, rows.type).text()
result.append({
'show_var': True,
'enable': cb.isChecked(),
'name': name,
'pt_type': f'pt_{pt}',
'iq_type': iq,
'return_type': ret,
'shortname': shortname,
'type': origin_type,
})
return result
def on_section_resized(self, logicalIndex, oldSize, newSize):
if self._resizing:
return # предотвращаем рекурсию
min_width = 50
delta = newSize - oldSize
right_index = logicalIndex + 1
if right_index >= self.columnCount():
# Если правая колока - нет соседа, ограничиваем минимальную ширину
if newSize < min_width:
self._resizing = True
self.setColumnWidth(logicalIndex, min_width)
self._resizing = False
return
self._resizing = True
try:
right_width = self.columnWidth(right_index)
new_right_width = right_width - delta
# Если соседняя колонка станет уже минимальной - подкорректируем левую
if new_right_width < min_width:
new_right_width = min_width
newSize = oldSize + (right_width - min_width)
self.setColumnWidth(logicalIndex, newSize)
self.setColumnWidth(right_index, new_right_width)
finally:
self._resizing = False

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.

View File

@ -8,6 +8,7 @@ import os
import re
import xml.etree.ElementTree as ET
from pathlib import Path
from xml.dom import minidom
import argparse
@ -214,8 +215,16 @@ def add_new_vars_to_xml(proj_path, xml_rel_path, output_path):
added_count += 1
if added_count > 0:
ET.indent(tree, space=" ", level=0)
tree.write(xml_full_path, encoding="utf-8", xml_declaration=True)
# Преобразуем дерево в строку
rough_string = ET.tostring(root, encoding="utf-8")
# Парсим и форматируем с отступами
reparsed = minidom.parseString(rough_string)
pretty_xml = reparsed.toprettyxml(indent=" ")
# Записываем в файл
with open(xml_full_path, "w", encoding="utf-8") as f:
f.write(pretty_xml)
print(f"[INFO] В XML добавлено новых переменных: {added_count}")
return True
else:
@ -330,12 +339,13 @@ def generate_vars_file(proj_path, xml_path, output_dir):
# Дополнительные поля, например комментарий
comment = info.get("comment", "")
short_name = info.get("shortname", f'"{vname}"')
if pt_type not in ('pt_struct', 'pt_union'):
formated_name = f'"{vname}"'
# Добавим комментарий после записи, если он есть
comment_str = f' // {comment}' if comment else ''
line = f'{{(char *)&{vname:<41} , {pt_type:<21} , {iq_type:<21} , {formated_name:<42}}}, \\{comment_str}'
line = f'{{(char *)&{vname:<41} , {pt_type:<21} , {iq_type:<21} , {short_name:<42}}}, \\{comment_str}'
new_debug_vars[vname] = line
else:

View File

@ -7,11 +7,14 @@ def strip_single_line_comments(code):
return re.sub(r'//.*?$', '', code, flags=re.MULTILINE)
def read_file_try_encodings(filepath):
if not os.path.isfile(filepath):
# Файл не существует — просто вернуть пустую строку или None
return "", None
for enc in ['utf-8', 'cp1251']:
try:
with open(filepath, 'r', encoding=enc) as f:
content = f.read()
content = strip_single_line_comments(content) # <=== ВАЖНО
content = strip_single_line_comments(content)
return content, enc
except UnicodeDecodeError:
continue
@ -38,6 +41,8 @@ def find_all_includes_recursive(c_files, include_dirs, processed_files=None):
processed_files.add(norm_path)
content, _ = read_file_try_encodings(cfile)
if content is None:
continue
includes = include_pattern.findall(content)
for inc in includes:
# Ищем полный путь к include-файлу в include_dirs
@ -61,9 +66,9 @@ def find_all_includes_recursive(c_files, include_dirs, processed_files=None):
return include_files
def parse_makefile(makefile_path):
def parse_makefile(makefile_path, proj_path):
makefile_dir = os.path.dirname(makefile_path)
project_root = os.path.dirname(makefile_dir) # поднялись из Debug
project_root = proj_path
with open(makefile_path, 'r', encoding='utf-8') as f:
lines = f.readlines()
@ -115,6 +120,8 @@ def parse_makefile(makefile_path):
continue
if "v120" in obj_path:
continue
if "v100" in obj_path:
continue
if obj_path.startswith("Debug\\") or obj_path.startswith("Debug/"):
rel_path = obj_path.replace("Debug\\", "Src\\").replace("Debug/", "Src/")
@ -129,6 +136,10 @@ def parse_makefile(makefile_path):
else:
c_path = abs_path
# Проверяем существование файла, если нет — пропускаем
if not os.path.isfile(c_path):
continue
# Сохраняем только .c файлы
if c_path.lower().endswith(".c"):
c_files.append(c_path)

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -812,7 +812,7 @@ Usage example:
print(f"Error: Makefile path '{makefile_path}' does not exist.")
sys.exit(1)
c_files, h_files, include_dirs = parse_makefile(makefile_path)
c_files, h_files, include_dirs = parse_makefile(makefile_path, proj_path)
vars, includes, externs = analyze_variables_across_files(c_files, h_files, include_dirs)
typedefs, structs = analyze_typedefs_and_structs_across_files(c_files, include_dirs)
@ -855,7 +855,7 @@ def run_scan(proj_path, makefile_path, output_xml, verbose=2):
if not os.path.isfile(makefile_path):
raise FileNotFoundError(f"Makefile path '{makefile_path}' does not exist.")
c_files, h_files, include_dirs = parse_makefile(makefile_path)
c_files, h_files, include_dirs = parse_makefile(makefile_path, proj_path)
vars, includes, externs = analyze_variables_across_files(c_files, h_files, include_dirs)
typedefs, structs = analyze_typedefs_and_structs_across_files(c_files, include_dirs)

View File

@ -63,6 +63,21 @@ def parse_vars(filename, typedef_map=None):
iq_type = var.findtext('iq_type')
if not iq_type:
iq_type = get_iq_define(var_type)
# Записываем iq_type в XML
iq_type_elem = var.find('iq_type')
if iq_type_elem is None:
iq_type_elem = ET.SubElement(var, 'iq_type')
iq_type_elem.text = iq_type
# Вычисляем pt_type и iq_type
pt_type = var.findtext('pt_type')
if not pt_type:
pt_type = map_type_to_pt(var_type, name, typedef_map)
# Записываем pt_type в XML
pt_type_elem = var.find('pt_type')
if pt_type_elem is None:
pt_type_elem = ET.SubElement(var, 'pt_type')
pt_type_elem.text = pt_type
vars_list.append({
'name': name,
@ -78,6 +93,17 @@ def parse_vars(filename, typedef_map=None):
'static': var.findtext('static', 'false') == 'true',
})
# Преобразуем дерево в строку
rough_string = ET.tostring(root, encoding="utf-8")
# Парсим и форматируем с отступами
reparsed = minidom.parseString(rough_string)
pretty_xml = reparsed.toprettyxml(indent=" ")
# Записываем в файл
with open(filename, "w", encoding="utf-8") as f:
f.write(pretty_xml)
return vars_list
@ -159,6 +185,9 @@ 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):
if depth > 10:
return []
@ -178,7 +207,15 @@ def expand_struct_recursively(prefix, type_str, structs, typedefs, var_attrs, de
if field_name == 'type':
continue
full_name = f"{prefix}.{field_name}"
# Определяем разделитель между prefix и полем
if prefix.endswith('*'):
separator = '->'
# Для красоты можно убрать пробелы у указателя
# например, если prefix="ptr*" -> "ptr->field"
full_name = f"{prefix[:-1]}{separator}{field_name}"
else:
separator = '.'
full_name = f"{prefix}{separator}{field_name}"
if isinstance(field_value, dict):
# Если вложенная структура — берем её имя типа из поля 'type' или пустую строку
@ -187,10 +224,14 @@ def expand_struct_recursively(prefix, type_str, structs, typedefs, var_attrs, de
'name': full_name,
'type': type_name,
'pt_type': '',
'iq_type': '',
'return_type': '',
'file': var_attrs.get('file'),
'extern': var_attrs.get('extern'),
'static': var_attrs.get('static'),
}
if '*' in type_name and not full_name.endswith('*'):
full_name += '*'
# Рекурсивно раскрываем вложенные поля
subchildren = expand_struct_recursively(full_name, field_value, structs, typedefs, var_attrs, depth + 1)
if subchildren:
@ -205,6 +246,8 @@ def expand_struct_recursively(prefix, type_str, structs, typedefs, var_attrs, de
'name': full_name,
'type': field_value,
'pt_type': '',
'iq_type': '',
'return_type': '',
'file': var_attrs.get('file'),
'extern': var_attrs.get('extern'),
'static': var_attrs.get('static'),
@ -215,6 +258,7 @@ def expand_struct_recursively(prefix, type_str, structs, typedefs, var_attrs, de
return children
def expand_vars(vars_list, structs, typedefs):
"""
Раскрывает структуры и массивы структур в деревья.
@ -228,6 +272,11 @@ def expand_vars(vars_list, structs, typedefs):
fields = structs.get(base_type)
if pt_type.startswith('pt_ptr_') 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)
if pt_type.startswith('pt_arr_') and isinstance(fields, dict):
new_var = var.copy()
new_var['children'] = expand_struct_recursively(var['name'], raw_type, structs, typedefs, var)

View File

@ -12,27 +12,18 @@ from scanVars import run_scan
from generateVars import run_generate
from setupVars import *
from VariableSelector import *
from VariableTable import VariableTableWidget, rows
from PySide6.QtWidgets import (
QApplication, QWidget, QTableWidget, QTableWidgetItem,
QCheckBox, QComboBox, QLineEdit, QVBoxLayout, QHBoxLayout, QPushButton,
QCompleter, QAbstractItemView, QLabel, QMessageBox, QFileDialog, QTextEdit,
QDialog, QTreeWidget, QTreeWidgetItem, QSizePolicy
QDialog, QTreeWidget, QTreeWidgetItem, QSizePolicy, QHeaderView
)
from PySide6.QtGui import QTextCursor, QKeyEvent
from PySide6.QtCore import Qt, QProcess, QObject, Signal, QTimer
from PySide6.QtCore import Qt, QProcess, QObject, Signal, QSettings
class rows(IntEnum):
No = 0
include = 1
name = 2
type = 3
pt_type = 4
iq_type = 5
ret_type = 6
short_name = 7
class EmittingStream(QObject):
text_written = Signal(str)
@ -54,11 +45,12 @@ class EmittingStream(QObject):
self._buffer = ""
class ProcessOutputWindowDummy(QWidget):
class ProcessOutputWindowDummy(QDialog):
def __init__(self, on_done_callback):
super().__init__()
self.setWindowTitle("Поиск переменных...")
self.resize(600, 400)
self.setModal(True) # сделаем окно модальным
self.layout = QVBoxLayout(self)
self.output_edit = QTextEdit()
@ -75,7 +67,7 @@ class ProcessOutputWindowDummy(QWidget):
def __handle_done(self):
if self._on_done_callback:
self._on_done_callback()
self.close()
self.accept() # закрыть диалог
def append_text(self, text):
cursor = self.output_edit.textCursor()
@ -161,20 +153,6 @@ class VarEditor(QWidget):
self.btn_update_vars = QPushButton("Обновить данные о переменных")
self.btn_update_vars.clicked.connect(self.update_vars_data)
# Таблица переменных
self.table = QTableWidget(len(self.vars_list), 8)
self.table.setHorizontalHeaderLabels([
'', # новый столбец
'En',
'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)
@ -183,7 +161,11 @@ class VarEditor(QWidget):
self.btn_add_vars = QPushButton("Add Variables")
self.btn_add_vars.clicked.connect(self.__open_variable_selector)
# Кнопка открыть output-файл с выбором программы
btn_open_output = QPushButton("Открыть Output File...")
btn_open_output.clicked.connect(self.__open_output_file_with_program)
# Таблица
self.table = VariableTableWidget()
# Основной layout
layout = QVBoxLayout()
layout.addLayout(xml_layout)
@ -194,69 +176,11 @@ class VarEditor(QWidget):
layout.addWidget(self.btn_add_vars)
layout.addLayout(source_output_layout)
layout.addWidget(btn_save)
header = self.table.horizontalHeader()
# Для остальных колонок — растяжение (Stretch), чтобы они заняли всю оставшуюся ширину
for col in range(self.table.columnCount()):
if col == self.table.columnCount() - 1:
header.setSectionResizeMode(col, QHeaderView.Stretch)
else:
header.setSectionResizeMode(col, QHeaderView.Interactive)
parent_widget = self.table.parentWidget()
if parent_widget:
w = parent_widget.width()
h = parent_widget.height()
viewport_width = self.table.viewport().width()
# Сделаем колонки с номерами фиксированной ширины
self.table.setColumnWidth(rows.No, 30)
self.table.setColumnWidth(rows.include, 30)
self.table.setColumnWidth(rows.pt_type, 85)
self.table.setColumnWidth(rows.iq_type, 85)
self.table.setColumnWidth(rows.ret_type, 85)
self.table.setColumnWidth(rows.name, 300)
self.table.setColumnWidth(rows.type, 100)
self.table.horizontalHeader().sectionResized.connect(self.on_section_resized)
layout.addWidget(btn_open_output)
self.setLayout(layout)
def on_section_resized(self, logicalIndex, oldSize, newSize):
if self._resizing:
return # предотвращаем рекурсию
min_width = 50
delta = newSize - oldSize
right_index = logicalIndex + 1
if right_index >= self.table.columnCount():
# Если правая колока - нет соседа, ограничиваем минимальную ширину
if newSize < min_width:
self._resizing = True
self.table.setColumnWidth(logicalIndex, min_width)
self._resizing = False
return
self._resizing = True
try:
right_width = self.table.columnWidth(right_index)
new_right_width = right_width - delta
# Если соседняя колонка станет уже минимальной - подкорректируем левую
if new_right_width < min_width:
new_right_width = min_width
newSize = oldSize + (right_width - min_width)
self.table.setColumnWidth(logicalIndex, newSize)
self.table.setColumnWidth(right_index, new_right_width)
finally:
self._resizing = False
def get_xml_path(self):
xml_path = self.xml_output_edit.text().strip()
@ -416,18 +340,17 @@ class VarEditor(QWidget):
else:
self.makefile_path = None
if not self.structs_path:
# --- structs_path из атрибута ---
structs_path = root.attrib.get('structs_path', '').strip()
structs_path_full = make_absolute_path(structs_path, self.proj_path)
if structs_path_full and os.path.isfile(structs_path_full):
self.structs_path = structs_path_full
self.structs, self.typedef_map = parse_structs(structs_path_full)
else:
self.structs_path = None
# --- structs_path из атрибута ---
structs_path = root.attrib.get('structs_path', '').strip()
structs_path_full = make_absolute_path(structs_path, self.proj_path)
if structs_path_full and os.path.isfile(structs_path_full):
self.structs_path = structs_path_full
self.structs, self.typedef_map = parse_structs(structs_path_full)
else:
self.structs_path = None
self.vars_list = parse_vars(self.xml_path, self.typedef_map)
self.update_table()
self.table.populate(self.vars_list, self.structs, self.write_to_xml)
except Exception as e:
QMessageBox.warning(self, "Ошибка", f"Ошибка при чтении XML:\n{e}")
@ -528,7 +451,7 @@ class VarEditor(QWidget):
v['show_var'] = 'false'
break
self.update_table()
self.table.populate(self.vars_list, self.structs, self.write_to_xml)
def __open_variable_selector(self):
@ -542,102 +465,6 @@ class VarEditor(QWidget):
self.update()
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)]
filtered_vars = [v for v in self.vars_list if v.get('show_var', 'false') == 'true']
self.table.setRowCount(len(filtered_vars))
self.table.verticalHeader().setVisible(False)
for row, var in enumerate(filtered_vars):
# Добавляем номер строки в колонку No (0)
no_item = QTableWidgetItem(str(row))
no_item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled) # readonly
self.table.setItem(row, rows.No, no_item)
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 = var['pt_type'].replace('pt_', '')
if internal_type in self.display_type_options:
pt_type_combo.setCurrentText(internal_type)
else:
pt_type_combo.addItem(internal_type)
pt_type_combo.setCurrentText(internal_type)
self.table.setCellWidget(row, rows.pt_type, pt_type_combo)
iq_combo = QComboBox()
iq_combo.addItems(iq_types)
iq_type = var['iq_type'].replace('t_', '')
if iq_type in iq_types:
iq_combo.setCurrentText(iq_type)
else:
iq_combo.addItem(iq_type)
iq_combo.setCurrentText(iq_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.write_to_xml)
name_edit.textChanged.connect(self.write_to_xml)
pt_type_combo.currentTextChanged.connect(self.write_to_xml)
iq_combo.currentTextChanged.connect(self.write_to_xml)
ret_combo.currentTextChanged.connect(self.write_to_xml)
short_name_edit.textChanged.connect(self.write_to_xml)
self.write_to_xml()
def read_table(self):
vars_data = []
for row in range(self.table.rowCount()):
cb = self.table.cellWidget(row, rows.include)
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)
origin_item = self.table.item(row, rows.type)
vars_data.append({
'show_var': True,
'enable': cb.isChecked() if cb else False,
'name': name_edit.text() if name_edit else '',
'pt_type': 'pt_' + pt_type_combo.currentText() if pt_type_combo else '',
'iq_type': iq_combo.currentText() if iq_combo else '',
'return_type': ret_combo.currentText() if ret_combo else '',
'shortname': short_name_edit.text() if short_name_edit else '',
'type': origin_item.text() if origin_item else '',
})
return vars_data
def write_to_xml(self):
self.update_all_paths()
@ -682,7 +509,7 @@ class VarEditor(QWidget):
}
# Читаем переменные из таблицы (активные/изменённые)
table_vars = {v['name']: v for v in self.read_table()}
table_vars = {v['name']: v for v in self.table.read_data()}
# Все переменные (в том числе новые, которых нет в таблице)
all_vars_by_name = {v['name']: v for v in self.vars_list}
@ -707,8 +534,11 @@ class VarEditor(QWidget):
el = ET.SubElement(parent, tag)
el.text = str(text)
set_sub_elem_text(var_elem, 'show_var', v.get('show_var', 'false'))
set_sub_elem_text(var_elem, 'enable', v.get('enable', 'false'))
show_var_val = str(v.get('show_var', 'false')).lower()
enable_val = str(v_table['enable'] if v_table and 'enable' in v_table else v.get('enable', 'false')).lower()
set_sub_elem_text(var_elem, 'show_var', show_var_val)
set_sub_elem_text(var_elem, 'enable', enable_val)
# Тут подтягиваем из таблицы, если есть, иначе из v
shortname_val = v_table['shortname'] if v_table and 'shortname' in v_table else v.get('shortname', '')
@ -731,13 +561,40 @@ class VarEditor(QWidget):
set_sub_elem_text(var_elem, 'extern', extern_val)
set_sub_elem_text(var_elem, 'static', static_val)
# Преобразуем дерево в строку
rough_string = ET.tostring(root, encoding="utf-8")
ET.indent(tree, space=" ", level=0)
tree.write(self.xml_path, encoding='utf-8', xml_declaration=True)
# Парсим и форматируем с отступами
reparsed = minidom.parseString(rough_string)
pretty_xml = reparsed.toprettyxml(indent=" ")
# Записываем в файл
with open(self.xml_path, "w", encoding="utf-8") as f:
f.write(pretty_xml)
except Exception as e:
print(f"Ошибка при сохранении XML: {e}")
def __open_output_file_with_program(self):
output_path = self.get_output_path()
if not output_path:
QMessageBox.warning(self, "Ошибка", "Путь к output-файлу не задан.")
return
output_file = os.path.join(output_path, "debug_vars.c")
if not os.path.isfile(output_file):
QMessageBox.warning(self, "Ошибка", f"Файл не найден:\n{output_file}")
return
try:
# Открыть стандартное окно Windows "Открыть с помощью..."
subprocess.run([
"rundll32.exe",
"shell32.dll,OpenAs_RunDLL",
output_file
], shell=False)
except Exception as e:
QMessageBox.critical(self, "Ошибка", f"Не удалось открыть диалог 'Открыть с помощью':\n{e}")

View File

@ -1,34 +1,119 @@
import subprocess
import shutil
import os
import PySide6
# Пути
dist_path = os.path.abspath("./") # текущая папка — exe будет тут
work_path = os.path.abspath("./build_temp")
spec_path = os.path.abspath("./build_temp")
USE_NUITKA = True # переключатель: True — сборка через Nuitka, False — через PyInstaller
pyside6_path = os.path.dirname(PySide6.__file__)
plugins_platforms_path = os.path.join(pyside6_path, "plugins", "platforms")
dist_path = os.path.abspath(".") # итоговая папка с exe
work_path = os.path.abspath("./build_temp") # временная папка для PyInstaller
spec_path = os.path.abspath("./build_temp") # папка exдля spec-файлов PyInstaller
script_dir = os.path.dirname(os.path.abspath(__file__))
libclang_path = os.path.join(script_dir, "libclang.dll")
libssl_orig = os.path.join(script_dir, "libssl-3-x64.dll")
libcrypto_orig = os.path.join(script_dir, "libcrypto-3-x64.dll")
# Запуск PyInstaller с нужными параметрами
cmd = [
"pyinstaller",
"--onefile",
"--windowed",
"--name", "DebugVarEdit",
"--add-binary", f"{libclang_path};.",
"--distpath", dist_path,
"--workpath", work_path,
"--specpath", spec_path,
"./Src/setupVars_GUI.py"
]
# Собираем бинарники и данные для PyInstaller
add_binary_list = [f"{libclang_path};."]
if os.path.exists(libssl_orig):
add_binary_list.append(f"{libssl_orig};libssl.dll")
else:
print("WARNING: libssl-3-x64.dll не найден, OpenSSL не будет включён")
if os.path.exists(libcrypto_orig):
add_binary_list.append(f"{libcrypto_orig};libcrypto.dll")
else:
print("WARNING: libcrypto-3-x64.dll не найден, OpenSSL не будет включён")
from PyInstaller.utils.hooks import collect_data_files
datas = []
datas += collect_data_files('PySide6', includes=['plugins/platforms/*'])
datas += collect_data_files('PySide6', includes=['plugins/styles/*'])
datas += collect_data_files('PySide6', includes=['plugins/imageformats/*'])
add_data_list = [f"{src};{dest}" for src, dest in datas]
script_path = "./Src/setupVars_GUI.py"
output_name = "DebugVarEdit"
env = os.environ.copy()
env["CC"] = r"C:\Users\I\AppData\Local\Nuitka\Nuitka\Cache\DOWNLOADS\gcc\x86_64\14.2.0posix-19.1.1-12.0.0-msvcrt-r2\bin\gcc.exe"
env["CXX"] = r"C:\Users\I\AppData\Local\Nuitka\Nuitka\Cache\DOWNLOADS\gcc\x86_64\14.2.0posix-19.1.1-12.0.0-msvcrt-r2\bin\g++.exe"
if not USE_NUITKA:
# PyInstaller команда
cmd = [
"pyinstaller",
"--onefile",
"--name", output_name,
*[item for add_bin in add_binary_list for item in ("--add-binary", add_bin)],
*[item for add_dat in add_data_list for item in ("--add-data", add_dat)],
"--distpath", dist_path,
"--workpath", work_path,
"--specpath", spec_path,
"--hidden-import", "PySide6.QtWidgets",
"--hidden-import", "PySide6.QtGui",
"--hidden-import", "PySide6.QtCore",
"--windowed",
script_path
]
else:
# Nuitka команда
# Формируем --include-data-dir для плагинов PySide6 (datas как в PyInstaller)
# Было — неправильный способ: передаёт файлы
plugin_dirs = set()
for src, dest in datas:
src_dir = os.path.dirname(src)
dest_root = dest.split("/", 1)[0] if "/" in dest else dest
plugin_dirs.add((src_dir, dest_root))
include_data_dirs = [
f"--include-data-dir={src}={dest}" for src, dest in plugin_dirs
]
include_data_files = []
for dll_path, dll_name in [(libclang_path, "libclang.dll"),
(libssl_orig, "libssl.dll"),
(libcrypto_orig, "libcrypto.dll")]:
if os.path.exists(dll_path):
include_data_files.append(f"--include-data-file={dll_path}={dll_name}")
cmd = [
"python", "-m", "nuitka",
"--standalone", # полностью независимый билд (аналог PyInstaller --onefile/--onedir)
"--onefile", # упаковать в один exe (экспериментально)
"--enable-plugin=pyside6",
"--windows-console-mode=disable",
f"--output-dir={dist_path}",
f"--output-filename={output_name}.exe",
] + include_data_dirs + [
script_path
]
# Удаляем временные папки, если они у тебя есть
temp_dirs = ["build_temp", "__pycache__"] # добавь свои папки с временными файлами
for d in temp_dirs:
if os.path.exists(d):
shutil.rmtree(d)
print("Выполняется сборка:")
print(" ".join(cmd))
result = subprocess.run(cmd, env=env)
result = subprocess.run(cmd)
if result.returncode == 0:
# Удаляем временные папки
for folder in ["build_temp", "__pycache__"]:
if os.path.exists(folder):
shutil.rmtree(folder)
# Чистим временные папки, только для PyInstaller (для Nuitka обычно не нужно)
if not USE_NUITKA:
for folder in ["build_temp", "__pycache__"]:
if os.path.exists(folder):
shutil.rmtree(folder)
print("Сборка успешно завершена!")
else:
print("Сборка завершилась с ошибкой.")

BIN
build/libcrypto-3-x64.dll Normal file

Binary file not shown.

BIN
build/libssl-3-x64.dll Normal file

Binary file not shown.

View File

@ -3,34 +3,34 @@
// Èíêëþäû äëÿ äîñòóïà ê ïåðåìåííûì
#include "vector.h"
#include "RS_Functions_modbus.h"
#include "adc_tools.h"
#include "errors.h"
#include "v_pwm24.h"
#include "f281xpwm.h"
#include "xp_project.h"
#include "rotation_speed.h"
#include "teta_calc.h"
#include "vector.h"
#include "dq_to_alphabeta_cos.h"
#include "xp_write_xpwm_time.h"
#include "log_can.h"
#include "teta_calc.h"
#include "v_pwm24.h"
#include "errors.h"
#include "pwm_vector_regul.h"
#include "xp_project.h"
#include "xp_write_xpwm_time.h"
#include "rotation_speed.h"
#include "f281xpwm.h"
#include "adc_tools.h"
#include "log_can.h"
#include "RS_Functions.h"
#include "detect_phase_break2.h"
#include "svgen_dq.h"
#include "x_parallel_bus.h"
#include "Spartan2E_Functions.h"
#include "detect_phase_break2.h"
#include "pid_reg3.h"
#include "xp_rotation_sensor.h"
#include "x_serial_bus.h"
#include "xp_controller.h"
#include "xp_rotation_sensor.h"
#include "Spartan2E_Functions.h"
#include "xPeriphSP6_loader.h"
#include "log_to_memory.h"
#include "global_time.h"
#include "CAN_Setup.h"
#include "log_params.h"
#include "x_parallel_bus.h"
#include "CRC_Functions.h"
#include "pid_reg3.h"
#include "log_params.h"
#include "CAN_Setup.h"
#include "global_time.h"
#include "log_to_memory.h"
#include "IQmathLib.h"
#include "doors_control.h"
#include "isolation.h"
@ -317,5 +317,5 @@ extern int zero_ADC[20];
int DebugVar_Qnt = 1;
#pragma DATA_SECTION(dbg_vars,".dbgvar_info")
DebugVar_t dbg_vars[] = {\
{(char *)&Bender.KOhms , pt_uint16 , iq_none , "Bender.KOhms" }, \
{(char *)&ADC0startAddr , pt_int64 , iq12 , ADCdr }, \
};

BIN
dist/DebugVarEdit.exe vendored Normal file

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.

Binary file not shown.

Binary file not shown.

BIN
dist/setupVars_GUI.dist/_bz2.pyd vendored Normal file

Binary file not shown.

BIN
dist/setupVars_GUI.dist/_ctypes.pyd vendored Normal file

Binary file not shown.

BIN
dist/setupVars_GUI.dist/_decimal.pyd vendored Normal file

Binary file not shown.

BIN
dist/setupVars_GUI.dist/_elementtree.pyd vendored Normal file

Binary file not shown.

BIN
dist/setupVars_GUI.dist/_hashlib.pyd vendored Normal file

Binary file not shown.

BIN
dist/setupVars_GUI.dist/_lzma.pyd vendored Normal file

Binary file not shown.

BIN
dist/setupVars_GUI.dist/_socket.pyd vendored Normal file

Binary file not shown.

BIN
dist/setupVars_GUI.dist/_ssl.pyd vendored Normal file

Binary file not shown.

BIN
dist/setupVars_GUI.dist/_wmi.pyd vendored Normal file

Binary file not shown.

BIN
dist/setupVars_GUI.dist/libcrypto-3.dll vendored Normal file

Binary file not shown.

BIN
dist/setupVars_GUI.dist/libffi-8.dll vendored Normal file

Binary file not shown.

BIN
dist/setupVars_GUI.dist/libssl-3.dll vendored Normal file

Binary file not shown.

BIN
dist/setupVars_GUI.dist/msvcp140.dll vendored Normal file

Binary file not shown.

BIN
dist/setupVars_GUI.dist/msvcp140_1.dll vendored Normal file

Binary file not shown.

BIN
dist/setupVars_GUI.dist/msvcp140_2.dll vendored Normal file

Binary file not shown.

BIN
dist/setupVars_GUI.dist/pyexpat.pyd vendored Normal file

Binary file not shown.

BIN
dist/setupVars_GUI.dist/pyside6.abi3.dll vendored Normal file

Binary file not shown.

BIN
dist/setupVars_GUI.dist/python3.dll vendored Normal file

Binary file not shown.

BIN
dist/setupVars_GUI.dist/python312.dll vendored Normal file

Binary file not shown.

BIN
dist/setupVars_GUI.dist/qt6core.dll vendored Normal file

Binary file not shown.

BIN
dist/setupVars_GUI.dist/qt6gui.dll vendored Normal file

Binary file not shown.

BIN
dist/setupVars_GUI.dist/qt6network.dll vendored Normal file

Binary file not shown.

BIN
dist/setupVars_GUI.dist/qt6pdf.dll vendored Normal file

Binary file not shown.

BIN
dist/setupVars_GUI.dist/qt6svg.dll vendored Normal file

Binary file not shown.

BIN
dist/setupVars_GUI.dist/qt6widgets.dll vendored Normal file

Binary file not shown.

BIN
dist/setupVars_GUI.dist/select.pyd vendored Normal file

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.

BIN
dist/setupVars_GUI.dist/unicodedata.pyd vendored Normal file

Binary file not shown.

BIN
dist/setupVars_GUI.dist/vcruntime140.dll vendored Normal file

Binary file not shown.

Binary file not shown.

5041
nuitka-crash-report.xml Normal file

File diff suppressed because it is too large Load Diff

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