Compare commits
3 Commits
0b50c31aa8
...
3343428796
Author | SHA1 | Date | |
---|---|---|---|
3343428796 | |||
f881132fa8 | |||
4962276760 |
BIN
DebugVarEdit.exe
BIN
DebugVarEdit.exe
Binary file not shown.
@ -1,9 +1,10 @@
|
|||||||
import re
|
import re
|
||||||
from PySide6.QtWidgets import (
|
from PySide6.QtWidgets import (
|
||||||
QDialog, QTreeWidget, QTreeWidgetItem, QVBoxLayout, QPushButton,
|
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 setupVars import *
|
||||||
from scanVars import *
|
from scanVars import *
|
||||||
|
|
||||||
@ -25,9 +26,21 @@ class VariableSelectorDialog(QDialog):
|
|||||||
|
|
||||||
self.xml_path = xml_path # сохраняем путь к xml
|
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 = QLineEdit()
|
||||||
self.search_input.setPlaceholderText("Поиск по имени переменной...")
|
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 = QTreeWidget()
|
||||||
self.tree.setHeaderLabels(["Имя переменной", "Тип"])
|
self.tree.setHeaderLabels(["Имя переменной", "Тип"])
|
||||||
@ -51,17 +64,33 @@ class VariableSelectorDialog(QDialog):
|
|||||||
self.btn_delete = QPushButton("Удалить выбранные")
|
self.btn_delete = QPushButton("Удалить выбранные")
|
||||||
self.btn_delete.clicked.connect(self.on_delete_clicked)
|
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 = QVBoxLayout()
|
||||||
layout.addWidget(QLabel("Поиск:"))
|
layout.addLayout(search_layout) # заменили label и чекбокс
|
||||||
layout.addWidget(self.search_input)
|
layout.addWidget(self.search_input)
|
||||||
layout.addWidget(self.tree)
|
layout.addWidget(self.tree)
|
||||||
layout.addWidget(self.btn_add)
|
layout.addWidget(self.btn_add)
|
||||||
layout.addWidget(self.btn_delete) # Кнопка удаления
|
layout.addWidget(self.btn_delete)
|
||||||
self.setLayout(layout)
|
self.setLayout(layout)
|
||||||
|
|
||||||
self.populate_tree()
|
self.populate_tree()
|
||||||
|
|
||||||
|
|
||||||
def add_tree_item_recursively(self, parent, var):
|
def add_tree_item_recursively(self, parent, var):
|
||||||
"""
|
"""
|
||||||
Рекурсивно добавляет переменную и её дочерние поля в дерево.
|
Рекурсивно добавляет переменную и её дочерние поля в дерево.
|
||||||
@ -113,7 +142,7 @@ class VariableSelectorDialog(QDialog):
|
|||||||
|
|
||||||
def filter_tree(self):
|
def filter_tree(self):
|
||||||
text = self.search_input.text().strip().lower()
|
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):
|
def hide_all(item):
|
||||||
item.setHidden(True)
|
item.setHidden(True)
|
||||||
@ -121,7 +150,7 @@ class VariableSelectorDialog(QDialog):
|
|||||||
hide_all(item.child(i))
|
hide_all(item.child(i))
|
||||||
|
|
||||||
def path_matches_search(name, search_parts):
|
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):
|
if len(name_parts) < len(search_parts):
|
||||||
return False
|
return False
|
||||||
for sp, np in zip(search_parts, name_parts):
|
for sp, np in zip(search_parts, name_parts):
|
||||||
@ -156,12 +185,190 @@ class VariableSelectorDialog(QDialog):
|
|||||||
|
|
||||||
return matched or matched_any_child
|
return matched or matched_any_child
|
||||||
|
|
||||||
for i in range(self.tree.topLevelItemCount()):
|
# Если в поиске нет точки — особая логика для первого уровня
|
||||||
item = self.tree.topLevelItem(i)
|
if '.' not in text and '->' not in text and text != '':
|
||||||
hide_all(item)
|
for i in range(self.tree.topLevelItemCount()):
|
||||||
show_matching_path(item, 0)
|
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):
|
def on_add_clicked(self):
|
||||||
self.selected_names = []
|
self.selected_names = []
|
||||||
|
|
||||||
@ -205,17 +412,64 @@ class VariableSelectorDialog(QDialog):
|
|||||||
self.var_map[name] = new_var # Чтобы в будущем не добавлялось повторно
|
self.var_map[name] = new_var # Чтобы в будущем не добавлялось повторно
|
||||||
|
|
||||||
self.accept()
|
self.accept()
|
||||||
|
|
||||||
|
|
||||||
def on_delete_clicked(self):
|
def on_delete_clicked(self):
|
||||||
# Деактивируем (удаляем из видимых) выбранные переменные
|
selected_names = self._get_selected_var_names()
|
||||||
for item in self.tree.selectedItems():
|
if not selected_names:
|
||||||
name = item.text(0)
|
return
|
||||||
if not name:
|
|
||||||
continue
|
# Обновляем var_map и all_vars
|
||||||
|
for name in selected_names:
|
||||||
if name in self.var_map:
|
if name in self.var_map:
|
||||||
var = self.var_map[name]
|
self.var_map[name]['show_var'] = 'false'
|
||||||
var['show_var'] = 'false'
|
self.var_map[name]['enable'] = 'false'
|
||||||
var['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()
|
self.accept()
|
||||||
|
|
||||||
|
|
||||||
@ -231,16 +485,11 @@ class VariableSelectorDialog(QDialog):
|
|||||||
super().keyPressEvent(event)
|
super().keyPressEvent(event)
|
||||||
|
|
||||||
def delete_selected_vars(self):
|
def delete_selected_vars(self):
|
||||||
# Деактивируем (удаляем из видимых) выбранные переменные
|
selected_names = self._get_selected_var_names()
|
||||||
for item in self.tree.selectedItems():
|
if not selected_names:
|
||||||
name = item.text(0)
|
return
|
||||||
if not name:
|
|
||||||
continue
|
|
||||||
if name in self.var_map:
|
|
||||||
var = self.var_map[name]
|
|
||||||
var['show_var'] = 'false'
|
|
||||||
var['enable'] = 'false'
|
|
||||||
|
|
||||||
|
# Проверка пути к XML
|
||||||
if not hasattr(self, 'xml_path') or not self.xml_path:
|
if not hasattr(self, 'xml_path') or not self.xml_path:
|
||||||
from PySide6.QtWidgets import QMessageBox
|
from PySide6.QtWidgets import QMessageBox
|
||||||
QMessageBox.warning(self, "Ошибка", "Путь к XML не задан, невозможно удалить переменные.")
|
QMessageBox.warning(self, "Ошибка", "Путь к XML не задан, невозможно удалить переменные.")
|
||||||
@ -249,31 +498,47 @@ class VariableSelectorDialog(QDialog):
|
|||||||
import xml.etree.ElementTree as ET
|
import xml.etree.ElementTree as ET
|
||||||
tree = ET.parse(self.xml_path)
|
tree = ET.parse(self.xml_path)
|
||||||
root = tree.getroot()
|
root = tree.getroot()
|
||||||
|
|
||||||
if root is None:
|
if root is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
vars_section = root.find('variables')
|
vars_section = root.find('variables')
|
||||||
if vars_section is None:
|
if vars_section is None:
|
||||||
return # Нет секции variables — ничего удалять
|
return
|
||||||
|
|
||||||
selected_names = [item.text(0) for item in self.tree.selectedItems() if item.text(0)]
|
|
||||||
|
|
||||||
removed_any = False
|
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')
|
name = var_elem.attrib.get('name')
|
||||||
if name in selected_names:
|
if name in selected_names:
|
||||||
vars_section.remove(var_elem)
|
vars_section.remove(var_elem)
|
||||||
removed_any = True
|
removed_any = True
|
||||||
if name in self.var_map:
|
self.var_map.pop(name, None)
|
||||||
del self.var_map[name]
|
|
||||||
# Удаляем элементы из списка на месте
|
|
||||||
self.all_vars[:] = [v for v in self.all_vars if v['name'] != name]
|
|
||||||
|
|
||||||
|
# Удаляем из all_vars (глобально)
|
||||||
|
self.all_vars[:] = [v for v in self.all_vars if v['name'] not in selected_names]
|
||||||
|
|
||||||
if removed_any:
|
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
184
Src/VariableTable.py
Normal 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
|
BIN
Src/__pycache__/VariableSelector.cpython-312.pyc
Normal file
BIN
Src/__pycache__/VariableSelector.cpython-312.pyc
Normal file
Binary file not shown.
BIN
Src/__pycache__/VariableSelector.cpython-313.pyc
Normal file
BIN
Src/__pycache__/VariableSelector.cpython-313.pyc
Normal file
Binary file not shown.
BIN
Src/__pycache__/VariableSelector.cpython-37.pyc
Normal file
BIN
Src/__pycache__/VariableSelector.cpython-37.pyc
Normal file
Binary file not shown.
BIN
Src/__pycache__/VariableTable.cpython-312.pyc
Normal file
BIN
Src/__pycache__/VariableTable.cpython-312.pyc
Normal file
Binary file not shown.
BIN
Src/__pycache__/VariableTable.cpython-313.pyc
Normal file
BIN
Src/__pycache__/VariableTable.cpython-313.pyc
Normal file
Binary file not shown.
BIN
Src/__pycache__/VariableTable.cpython-37.pyc
Normal file
BIN
Src/__pycache__/VariableTable.cpython-37.pyc
Normal file
Binary file not shown.
BIN
Src/__pycache__/generateVars.cpython-312.pyc
Normal file
BIN
Src/__pycache__/generateVars.cpython-312.pyc
Normal file
Binary file not shown.
BIN
Src/__pycache__/generateVars.cpython-313.pyc
Normal file
BIN
Src/__pycache__/generateVars.cpython-313.pyc
Normal file
Binary file not shown.
BIN
Src/__pycache__/generateVars.cpython-37.pyc
Normal file
BIN
Src/__pycache__/generateVars.cpython-37.pyc
Normal file
Binary file not shown.
BIN
Src/__pycache__/parseMakefile.cpython-312.pyc
Normal file
BIN
Src/__pycache__/parseMakefile.cpython-312.pyc
Normal file
Binary file not shown.
BIN
Src/__pycache__/parseMakefile.cpython-313.pyc
Normal file
BIN
Src/__pycache__/parseMakefile.cpython-313.pyc
Normal file
Binary file not shown.
BIN
Src/__pycache__/parseMakefile.cpython-37.pyc
Normal file
BIN
Src/__pycache__/parseMakefile.cpython-37.pyc
Normal file
Binary file not shown.
BIN
Src/__pycache__/scanVars.cpython-312.pyc
Normal file
BIN
Src/__pycache__/scanVars.cpython-312.pyc
Normal file
Binary file not shown.
BIN
Src/__pycache__/scanVars.cpython-313.pyc
Normal file
BIN
Src/__pycache__/scanVars.cpython-313.pyc
Normal file
Binary file not shown.
BIN
Src/__pycache__/scanVars.cpython-37.pyc
Normal file
BIN
Src/__pycache__/scanVars.cpython-37.pyc
Normal file
Binary file not shown.
BIN
Src/__pycache__/setupVars.cpython-312.pyc
Normal file
BIN
Src/__pycache__/setupVars.cpython-312.pyc
Normal file
Binary file not shown.
BIN
Src/__pycache__/setupVars.cpython-313.pyc
Normal file
BIN
Src/__pycache__/setupVars.cpython-313.pyc
Normal file
Binary file not shown.
BIN
Src/__pycache__/setupVars.cpython-37.pyc
Normal file
BIN
Src/__pycache__/setupVars.cpython-37.pyc
Normal file
Binary file not shown.
BIN
Src/__pycache__/setupVars_GUI.cpython-37.pyc
Normal file
BIN
Src/__pycache__/setupVars_GUI.cpython-37.pyc
Normal file
Binary file not shown.
@ -8,6 +8,7 @@ import os
|
|||||||
import re
|
import re
|
||||||
import xml.etree.ElementTree as ET
|
import xml.etree.ElementTree as ET
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from xml.dom import minidom
|
||||||
import argparse
|
import argparse
|
||||||
|
|
||||||
|
|
||||||
@ -214,8 +215,16 @@ def add_new_vars_to_xml(proj_path, xml_rel_path, output_path):
|
|||||||
added_count += 1
|
added_count += 1
|
||||||
|
|
||||||
if added_count > 0:
|
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}")
|
print(f"[INFO] В XML добавлено новых переменных: {added_count}")
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
@ -330,12 +339,13 @@ def generate_vars_file(proj_path, xml_path, output_dir):
|
|||||||
|
|
||||||
# Дополнительные поля, например комментарий
|
# Дополнительные поля, например комментарий
|
||||||
comment = info.get("comment", "")
|
comment = info.get("comment", "")
|
||||||
|
short_name = info.get("shortname", f'"{vname}"')
|
||||||
|
|
||||||
if pt_type not in ('pt_struct', 'pt_union'):
|
if pt_type not in ('pt_struct', 'pt_union'):
|
||||||
formated_name = f'"{vname}"'
|
formated_name = f'"{vname}"'
|
||||||
# Добавим комментарий после записи, если он есть
|
# Добавим комментарий после записи, если он есть
|
||||||
comment_str = f' // {comment}' if comment else ''
|
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
|
new_debug_vars[vname] = line
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
@ -7,11 +7,14 @@ def strip_single_line_comments(code):
|
|||||||
return re.sub(r'//.*?$', '', code, flags=re.MULTILINE)
|
return re.sub(r'//.*?$', '', code, flags=re.MULTILINE)
|
||||||
|
|
||||||
def read_file_try_encodings(filepath):
|
def read_file_try_encodings(filepath):
|
||||||
|
if not os.path.isfile(filepath):
|
||||||
|
# Файл не существует — просто вернуть пустую строку или None
|
||||||
|
return "", None
|
||||||
for enc in ['utf-8', 'cp1251']:
|
for enc in ['utf-8', 'cp1251']:
|
||||||
try:
|
try:
|
||||||
with open(filepath, 'r', encoding=enc) as f:
|
with open(filepath, 'r', encoding=enc) as f:
|
||||||
content = f.read()
|
content = f.read()
|
||||||
content = strip_single_line_comments(content) # <=== ВАЖНО
|
content = strip_single_line_comments(content)
|
||||||
return content, enc
|
return content, enc
|
||||||
except UnicodeDecodeError:
|
except UnicodeDecodeError:
|
||||||
continue
|
continue
|
||||||
@ -38,6 +41,8 @@ def find_all_includes_recursive(c_files, include_dirs, processed_files=None):
|
|||||||
processed_files.add(norm_path)
|
processed_files.add(norm_path)
|
||||||
|
|
||||||
content, _ = read_file_try_encodings(cfile)
|
content, _ = read_file_try_encodings(cfile)
|
||||||
|
if content is None:
|
||||||
|
continue
|
||||||
includes = include_pattern.findall(content)
|
includes = include_pattern.findall(content)
|
||||||
for inc in includes:
|
for inc in includes:
|
||||||
# Ищем полный путь к include-файлу в include_dirs
|
# Ищем полный путь к include-файлу в include_dirs
|
||||||
@ -61,9 +66,9 @@ def find_all_includes_recursive(c_files, include_dirs, processed_files=None):
|
|||||||
return include_files
|
return include_files
|
||||||
|
|
||||||
|
|
||||||
def parse_makefile(makefile_path):
|
def parse_makefile(makefile_path, proj_path):
|
||||||
makefile_dir = os.path.dirname(makefile_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:
|
with open(makefile_path, 'r', encoding='utf-8') as f:
|
||||||
lines = f.readlines()
|
lines = f.readlines()
|
||||||
@ -115,6 +120,8 @@ def parse_makefile(makefile_path):
|
|||||||
continue
|
continue
|
||||||
if "v120" in obj_path:
|
if "v120" in obj_path:
|
||||||
continue
|
continue
|
||||||
|
if "v100" in obj_path:
|
||||||
|
continue
|
||||||
|
|
||||||
if obj_path.startswith("Debug\\") or obj_path.startswith("Debug/"):
|
if obj_path.startswith("Debug\\") or obj_path.startswith("Debug/"):
|
||||||
rel_path = obj_path.replace("Debug\\", "Src\\").replace("Debug/", "Src/")
|
rel_path = obj_path.replace("Debug\\", "Src\\").replace("Debug/", "Src/")
|
||||||
@ -129,6 +136,10 @@ def parse_makefile(makefile_path):
|
|||||||
else:
|
else:
|
||||||
c_path = abs_path
|
c_path = abs_path
|
||||||
|
|
||||||
|
# Проверяем существование файла, если нет — пропускаем
|
||||||
|
if not os.path.isfile(c_path):
|
||||||
|
continue
|
||||||
|
|
||||||
# Сохраняем только .c файлы
|
# Сохраняем только .c файлы
|
||||||
if c_path.lower().endswith(".c"):
|
if c_path.lower().endswith(".c"):
|
||||||
c_files.append(c_path)
|
c_files.append(c_path)
|
||||||
|
BIN
Src/pythonInstaller/python-3.12.0-amd64.exe
Normal file
BIN
Src/pythonInstaller/python-3.12.0-amd64.exe
Normal file
Binary file not shown.
BIN
Src/pythonInstaller/python-3.13.3-amd64.exe
Normal file
BIN
Src/pythonInstaller/python-3.13.3-amd64.exe
Normal file
Binary file not shown.
BIN
Src/pythonInstaller/python-3.7.9-amd64.exe
Normal file
BIN
Src/pythonInstaller/python-3.7.9-amd64.exe
Normal file
Binary file not shown.
@ -812,7 +812,7 @@ Usage example:
|
|||||||
print(f"Error: Makefile path '{makefile_path}' does not exist.")
|
print(f"Error: Makefile path '{makefile_path}' does not exist.")
|
||||||
sys.exit(1)
|
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)
|
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)
|
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):
|
if not os.path.isfile(makefile_path):
|
||||||
raise FileNotFoundError(f"Makefile path '{makefile_path}' does not exist.")
|
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)
|
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)
|
typedefs, structs = analyze_typedefs_and_structs_across_files(c_files, include_dirs)
|
||||||
|
@ -63,6 +63,21 @@ def parse_vars(filename, typedef_map=None):
|
|||||||
iq_type = var.findtext('iq_type')
|
iq_type = var.findtext('iq_type')
|
||||||
if not iq_type:
|
if not iq_type:
|
||||||
iq_type = get_iq_define(var_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({
|
vars_list.append({
|
||||||
'name': name,
|
'name': name,
|
||||||
@ -78,6 +93,17 @@ def parse_vars(filename, typedef_map=None):
|
|||||||
'static': var.findtext('static', 'false') == 'true',
|
'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
|
return vars_list
|
||||||
|
|
||||||
|
|
||||||
@ -159,6 +185,9 @@ def safe_parse_xml(xml_path):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Неожиданная ошибка при чтении XML файла '{xml_path}': {e}")
|
print(f"Неожиданная ошибка при чтении XML файла '{xml_path}': {e}")
|
||||||
return None, None
|
return None, None
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def expand_struct_recursively(prefix, type_str, structs, typedefs, var_attrs, depth=0):
|
def expand_struct_recursively(prefix, type_str, structs, typedefs, var_attrs, depth=0):
|
||||||
if depth > 10:
|
if depth > 10:
|
||||||
return []
|
return []
|
||||||
@ -178,7 +207,15 @@ def expand_struct_recursively(prefix, type_str, structs, typedefs, var_attrs, de
|
|||||||
if field_name == 'type':
|
if field_name == 'type':
|
||||||
continue
|
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):
|
if isinstance(field_value, dict):
|
||||||
# Если вложенная структура — берем её имя типа из поля 'type' или пустую строку
|
# Если вложенная структура — берем её имя типа из поля 'type' или пустую строку
|
||||||
@ -187,10 +224,14 @@ def expand_struct_recursively(prefix, type_str, structs, typedefs, var_attrs, de
|
|||||||
'name': full_name,
|
'name': full_name,
|
||||||
'type': type_name,
|
'type': type_name,
|
||||||
'pt_type': '',
|
'pt_type': '',
|
||||||
|
'iq_type': '',
|
||||||
|
'return_type': '',
|
||||||
'file': var_attrs.get('file'),
|
'file': var_attrs.get('file'),
|
||||||
'extern': var_attrs.get('extern'),
|
'extern': var_attrs.get('extern'),
|
||||||
'static': var_attrs.get('static'),
|
'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)
|
subchildren = expand_struct_recursively(full_name, field_value, structs, typedefs, var_attrs, depth + 1)
|
||||||
if subchildren:
|
if subchildren:
|
||||||
@ -205,6 +246,8 @@ def expand_struct_recursively(prefix, type_str, structs, typedefs, var_attrs, de
|
|||||||
'name': full_name,
|
'name': full_name,
|
||||||
'type': field_value,
|
'type': field_value,
|
||||||
'pt_type': '',
|
'pt_type': '',
|
||||||
|
'iq_type': '',
|
||||||
|
'return_type': '',
|
||||||
'file': var_attrs.get('file'),
|
'file': var_attrs.get('file'),
|
||||||
'extern': var_attrs.get('extern'),
|
'extern': var_attrs.get('extern'),
|
||||||
'static': var_attrs.get('static'),
|
'static': var_attrs.get('static'),
|
||||||
@ -215,6 +258,7 @@ def expand_struct_recursively(prefix, type_str, structs, typedefs, var_attrs, de
|
|||||||
return children
|
return children
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def expand_vars(vars_list, structs, typedefs):
|
def expand_vars(vars_list, structs, typedefs):
|
||||||
"""
|
"""
|
||||||
Раскрывает структуры и массивы структур в деревья.
|
Раскрывает структуры и массивы структур в деревья.
|
||||||
@ -228,6 +272,11 @@ def expand_vars(vars_list, structs, typedefs):
|
|||||||
|
|
||||||
fields = structs.get(base_type)
|
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):
|
if pt_type.startswith('pt_arr_') and isinstance(fields, dict):
|
||||||
new_var = var.copy()
|
new_var = var.copy()
|
||||||
new_var['children'] = expand_struct_recursively(var['name'], raw_type, structs, typedefs, var)
|
new_var['children'] = expand_struct_recursively(var['name'], raw_type, structs, typedefs, var)
|
||||||
|
@ -12,27 +12,18 @@ from scanVars import run_scan
|
|||||||
from generateVars import run_generate
|
from generateVars import run_generate
|
||||||
from setupVars import *
|
from setupVars import *
|
||||||
from VariableSelector import *
|
from VariableSelector import *
|
||||||
|
from VariableTable import VariableTableWidget, rows
|
||||||
|
|
||||||
from PySide6.QtWidgets import (
|
from PySide6.QtWidgets import (
|
||||||
QApplication, QWidget, QTableWidget, QTableWidgetItem,
|
QApplication, QWidget, QTableWidget, QTableWidgetItem,
|
||||||
QCheckBox, QComboBox, QLineEdit, QVBoxLayout, QHBoxLayout, QPushButton,
|
QCheckBox, QComboBox, QLineEdit, QVBoxLayout, QHBoxLayout, QPushButton,
|
||||||
QCompleter, QAbstractItemView, QLabel, QMessageBox, QFileDialog, QTextEdit,
|
QCompleter, QAbstractItemView, QLabel, QMessageBox, QFileDialog, QTextEdit,
|
||||||
QDialog, QTreeWidget, QTreeWidgetItem, QSizePolicy
|
QDialog, QTreeWidget, QTreeWidgetItem, QSizePolicy, QHeaderView
|
||||||
)
|
)
|
||||||
from PySide6.QtGui import QTextCursor, QKeyEvent
|
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):
|
class EmittingStream(QObject):
|
||||||
text_written = Signal(str)
|
text_written = Signal(str)
|
||||||
@ -54,11 +45,12 @@ class EmittingStream(QObject):
|
|||||||
self._buffer = ""
|
self._buffer = ""
|
||||||
|
|
||||||
|
|
||||||
class ProcessOutputWindowDummy(QWidget):
|
class ProcessOutputWindowDummy(QDialog):
|
||||||
def __init__(self, on_done_callback):
|
def __init__(self, on_done_callback):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.setWindowTitle("Поиск переменных...")
|
self.setWindowTitle("Поиск переменных...")
|
||||||
self.resize(600, 400)
|
self.resize(600, 400)
|
||||||
|
self.setModal(True) # сделаем окно модальным
|
||||||
|
|
||||||
self.layout = QVBoxLayout(self)
|
self.layout = QVBoxLayout(self)
|
||||||
self.output_edit = QTextEdit()
|
self.output_edit = QTextEdit()
|
||||||
@ -75,7 +67,7 @@ class ProcessOutputWindowDummy(QWidget):
|
|||||||
def __handle_done(self):
|
def __handle_done(self):
|
||||||
if self._on_done_callback:
|
if self._on_done_callback:
|
||||||
self._on_done_callback()
|
self._on_done_callback()
|
||||||
self.close()
|
self.accept() # закрыть диалог
|
||||||
|
|
||||||
def append_text(self, text):
|
def append_text(self, text):
|
||||||
cursor = self.output_edit.textCursor()
|
cursor = self.output_edit.textCursor()
|
||||||
@ -161,20 +153,6 @@ class VarEditor(QWidget):
|
|||||||
self.btn_update_vars = QPushButton("Обновить данные о переменных")
|
self.btn_update_vars = QPushButton("Обновить данные о переменных")
|
||||||
self.btn_update_vars.clicked.connect(self.update_vars_data)
|
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 = QPushButton("Build")
|
||||||
btn_save.clicked.connect(self.save_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 = QPushButton("Add Variables")
|
||||||
self.btn_add_vars.clicked.connect(self.__open_variable_selector)
|
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
|
||||||
layout = QVBoxLayout()
|
layout = QVBoxLayout()
|
||||||
layout.addLayout(xml_layout)
|
layout.addLayout(xml_layout)
|
||||||
@ -194,69 +176,11 @@ class VarEditor(QWidget):
|
|||||||
layout.addWidget(self.btn_add_vars)
|
layout.addWidget(self.btn_add_vars)
|
||||||
layout.addLayout(source_output_layout)
|
layout.addLayout(source_output_layout)
|
||||||
layout.addWidget(btn_save)
|
layout.addWidget(btn_save)
|
||||||
|
layout.addWidget(btn_open_output)
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
self.setLayout(layout)
|
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):
|
def get_xml_path(self):
|
||||||
xml_path = self.xml_output_edit.text().strip()
|
xml_path = self.xml_output_edit.text().strip()
|
||||||
@ -416,18 +340,17 @@ class VarEditor(QWidget):
|
|||||||
else:
|
else:
|
||||||
self.makefile_path = None
|
self.makefile_path = None
|
||||||
|
|
||||||
if not self.structs_path:
|
# --- structs_path из атрибута ---
|
||||||
# --- structs_path из атрибута ---
|
structs_path = root.attrib.get('structs_path', '').strip()
|
||||||
structs_path = root.attrib.get('structs_path', '').strip()
|
structs_path_full = make_absolute_path(structs_path, self.proj_path)
|
||||||
structs_path_full = make_absolute_path(structs_path, self.proj_path)
|
if structs_path_full and os.path.isfile(structs_path_full):
|
||||||
if structs_path_full and os.path.isfile(structs_path_full):
|
self.structs_path = structs_path_full
|
||||||
self.structs_path = structs_path_full
|
self.structs, self.typedef_map = parse_structs(structs_path_full)
|
||||||
self.structs, self.typedef_map = parse_structs(structs_path_full)
|
else:
|
||||||
else:
|
self.structs_path = None
|
||||||
self.structs_path = None
|
|
||||||
|
|
||||||
self.vars_list = parse_vars(self.xml_path, self.typedef_map)
|
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:
|
except Exception as e:
|
||||||
QMessageBox.warning(self, "Ошибка", f"Ошибка при чтении XML:\n{e}")
|
QMessageBox.warning(self, "Ошибка", f"Ошибка при чтении XML:\n{e}")
|
||||||
|
|
||||||
@ -528,7 +451,7 @@ class VarEditor(QWidget):
|
|||||||
v['show_var'] = 'false'
|
v['show_var'] = 'false'
|
||||||
break
|
break
|
||||||
|
|
||||||
self.update_table()
|
self.table.populate(self.vars_list, self.structs, self.write_to_xml)
|
||||||
|
|
||||||
|
|
||||||
def __open_variable_selector(self):
|
def __open_variable_selector(self):
|
||||||
@ -542,102 +465,6 @@ class VarEditor(QWidget):
|
|||||||
self.update()
|
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):
|
def write_to_xml(self):
|
||||||
self.update_all_paths()
|
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}
|
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 = ET.SubElement(parent, tag)
|
||||||
el.text = str(text)
|
el.text = str(text)
|
||||||
|
|
||||||
set_sub_elem_text(var_elem, 'show_var', v.get('show_var', 'false'))
|
show_var_val = str(v.get('show_var', 'false')).lower()
|
||||||
set_sub_elem_text(var_elem, 'enable', v.get('enable', 'false'))
|
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
|
# Тут подтягиваем из таблицы, если есть, иначе из v
|
||||||
shortname_val = v_table['shortname'] if v_table and 'shortname' in v_table else v.get('shortname', '')
|
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, 'extern', extern_val)
|
||||||
set_sub_elem_text(var_elem, 'static', static_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:
|
except Exception as e:
|
||||||
print(f"Ошибка при сохранении XML: {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}")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,34 +1,119 @@
|
|||||||
import subprocess
|
import subprocess
|
||||||
import shutil
|
import shutil
|
||||||
import os
|
import os
|
||||||
|
import PySide6
|
||||||
|
|
||||||
# Пути
|
USE_NUITKA = True # переключатель: True — сборка через Nuitka, False — через PyInstaller
|
||||||
dist_path = os.path.abspath("./") # текущая папка — exe будет тут
|
|
||||||
work_path = os.path.abspath("./build_temp")
|
pyside6_path = os.path.dirname(PySide6.__file__)
|
||||||
spec_path = os.path.abspath("./build_temp")
|
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__))
|
script_dir = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
|
||||||
libclang_path = os.path.join(script_dir, "libclang.dll")
|
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 с нужными параметрами
|
# Собираем бинарники и данные для PyInstaller
|
||||||
cmd = [
|
add_binary_list = [f"{libclang_path};."]
|
||||||
"pyinstaller",
|
|
||||||
"--onefile",
|
if os.path.exists(libssl_orig):
|
||||||
"--windowed",
|
add_binary_list.append(f"{libssl_orig};libssl.dll")
|
||||||
"--name", "DebugVarEdit",
|
else:
|
||||||
"--add-binary", f"{libclang_path};.",
|
print("WARNING: libssl-3-x64.dll не найден, OpenSSL не будет включён")
|
||||||
"--distpath", dist_path,
|
|
||||||
"--workpath", work_path,
|
if os.path.exists(libcrypto_orig):
|
||||||
"--specpath", spec_path,
|
add_binary_list.append(f"{libcrypto_orig};libcrypto.dll")
|
||||||
"./Src/setupVars_GUI.py"
|
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:
|
if result.returncode == 0:
|
||||||
# Удаляем временные папки
|
# Чистим временные папки, только для PyInstaller (для Nuitka обычно не нужно)
|
||||||
for folder in ["build_temp", "__pycache__"]:
|
if not USE_NUITKA:
|
||||||
if os.path.exists(folder):
|
for folder in ["build_temp", "__pycache__"]:
|
||||||
shutil.rmtree(folder)
|
if os.path.exists(folder):
|
||||||
|
shutil.rmtree(folder)
|
||||||
print("Сборка успешно завершена!")
|
print("Сборка успешно завершена!")
|
||||||
else:
|
else:
|
||||||
print("Сборка завершилась с ошибкой.")
|
print("Сборка завершилась с ошибкой.")
|
||||||
|
BIN
build/libcrypto-3-x64.dll
Normal file
BIN
build/libcrypto-3-x64.dll
Normal file
Binary file not shown.
BIN
build/libssl-3-x64.dll
Normal file
BIN
build/libssl-3-x64.dll
Normal file
Binary file not shown.
40
debug_vars.c
40
debug_vars.c
@ -3,34 +3,34 @@
|
|||||||
|
|
||||||
|
|
||||||
// Èíêëþäû äëÿ äîñòóïà ê ïåðåìåííûì
|
// Èíêëþäû äëÿ äîñòóïà ê ïåðåìåííûì
|
||||||
#include "vector.h"
|
|
||||||
#include "RS_Functions_modbus.h"
|
#include "RS_Functions_modbus.h"
|
||||||
#include "adc_tools.h"
|
#include "vector.h"
|
||||||
#include "errors.h"
|
|
||||||
#include "v_pwm24.h"
|
|
||||||
#include "f281xpwm.h"
|
|
||||||
#include "xp_project.h"
|
|
||||||
#include "rotation_speed.h"
|
|
||||||
#include "teta_calc.h"
|
|
||||||
#include "dq_to_alphabeta_cos.h"
|
#include "dq_to_alphabeta_cos.h"
|
||||||
#include "xp_write_xpwm_time.h"
|
#include "teta_calc.h"
|
||||||
#include "log_can.h"
|
#include "v_pwm24.h"
|
||||||
|
#include "errors.h"
|
||||||
#include "pwm_vector_regul.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 "RS_Functions.h"
|
||||||
#include "detect_phase_break2.h"
|
|
||||||
#include "svgen_dq.h"
|
#include "svgen_dq.h"
|
||||||
#include "x_parallel_bus.h"
|
#include "detect_phase_break2.h"
|
||||||
#include "Spartan2E_Functions.h"
|
#include "pid_reg3.h"
|
||||||
|
#include "xp_rotation_sensor.h"
|
||||||
#include "x_serial_bus.h"
|
#include "x_serial_bus.h"
|
||||||
#include "xp_controller.h"
|
#include "xp_controller.h"
|
||||||
#include "xp_rotation_sensor.h"
|
#include "Spartan2E_Functions.h"
|
||||||
#include "xPeriphSP6_loader.h"
|
#include "xPeriphSP6_loader.h"
|
||||||
#include "log_to_memory.h"
|
#include "x_parallel_bus.h"
|
||||||
#include "global_time.h"
|
|
||||||
#include "CAN_Setup.h"
|
|
||||||
#include "log_params.h"
|
|
||||||
#include "CRC_Functions.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 "IQmathLib.h"
|
||||||
#include "doors_control.h"
|
#include "doors_control.h"
|
||||||
#include "isolation.h"
|
#include "isolation.h"
|
||||||
@ -317,5 +317,5 @@ extern int zero_ADC[20];
|
|||||||
int DebugVar_Qnt = 1;
|
int DebugVar_Qnt = 1;
|
||||||
#pragma DATA_SECTION(dbg_vars,".dbgvar_info")
|
#pragma DATA_SECTION(dbg_vars,".dbgvar_info")
|
||||||
DebugVar_t dbg_vars[] = {\
|
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
BIN
dist/DebugVarEdit.exe
vendored
Normal file
Binary file not shown.
BIN
dist/setupVars_GUI.dist/PySide6/QtCore.pyd
vendored
Normal file
BIN
dist/setupVars_GUI.dist/PySide6/QtCore.pyd
vendored
Normal file
Binary file not shown.
BIN
dist/setupVars_GUI.dist/PySide6/QtGui.pyd
vendored
Normal file
BIN
dist/setupVars_GUI.dist/PySide6/QtGui.pyd
vendored
Normal file
Binary file not shown.
BIN
dist/setupVars_GUI.dist/PySide6/QtNetwork.pyd
vendored
Normal file
BIN
dist/setupVars_GUI.dist/PySide6/QtNetwork.pyd
vendored
Normal file
Binary file not shown.
BIN
dist/setupVars_GUI.dist/PySide6/QtWidgets.pyd
vendored
Normal file
BIN
dist/setupVars_GUI.dist/PySide6/QtWidgets.pyd
vendored
Normal file
Binary file not shown.
BIN
dist/setupVars_GUI.dist/PySide6/qt-plugins/iconengines/qsvgicon.dll
vendored
Normal file
BIN
dist/setupVars_GUI.dist/PySide6/qt-plugins/iconengines/qsvgicon.dll
vendored
Normal file
Binary file not shown.
BIN
dist/setupVars_GUI.dist/PySide6/qt-plugins/imageformats/qgif.dll
vendored
Normal file
BIN
dist/setupVars_GUI.dist/PySide6/qt-plugins/imageformats/qgif.dll
vendored
Normal file
Binary file not shown.
BIN
dist/setupVars_GUI.dist/PySide6/qt-plugins/imageformats/qicns.dll
vendored
Normal file
BIN
dist/setupVars_GUI.dist/PySide6/qt-plugins/imageformats/qicns.dll
vendored
Normal file
Binary file not shown.
BIN
dist/setupVars_GUI.dist/PySide6/qt-plugins/imageformats/qico.dll
vendored
Normal file
BIN
dist/setupVars_GUI.dist/PySide6/qt-plugins/imageformats/qico.dll
vendored
Normal file
Binary file not shown.
BIN
dist/setupVars_GUI.dist/PySide6/qt-plugins/imageformats/qjpeg.dll
vendored
Normal file
BIN
dist/setupVars_GUI.dist/PySide6/qt-plugins/imageformats/qjpeg.dll
vendored
Normal file
Binary file not shown.
BIN
dist/setupVars_GUI.dist/PySide6/qt-plugins/imageformats/qpdf.dll
vendored
Normal file
BIN
dist/setupVars_GUI.dist/PySide6/qt-plugins/imageformats/qpdf.dll
vendored
Normal file
Binary file not shown.
BIN
dist/setupVars_GUI.dist/PySide6/qt-plugins/imageformats/qsvg.dll
vendored
Normal file
BIN
dist/setupVars_GUI.dist/PySide6/qt-plugins/imageformats/qsvg.dll
vendored
Normal file
Binary file not shown.
BIN
dist/setupVars_GUI.dist/PySide6/qt-plugins/imageformats/qtga.dll
vendored
Normal file
BIN
dist/setupVars_GUI.dist/PySide6/qt-plugins/imageformats/qtga.dll
vendored
Normal file
Binary file not shown.
BIN
dist/setupVars_GUI.dist/PySide6/qt-plugins/imageformats/qtiff.dll
vendored
Normal file
BIN
dist/setupVars_GUI.dist/PySide6/qt-plugins/imageformats/qtiff.dll
vendored
Normal file
Binary file not shown.
BIN
dist/setupVars_GUI.dist/PySide6/qt-plugins/imageformats/qwbmp.dll
vendored
Normal file
BIN
dist/setupVars_GUI.dist/PySide6/qt-plugins/imageformats/qwbmp.dll
vendored
Normal file
Binary file not shown.
BIN
dist/setupVars_GUI.dist/PySide6/qt-plugins/imageformats/qwebp.dll
vendored
Normal file
BIN
dist/setupVars_GUI.dist/PySide6/qt-plugins/imageformats/qwebp.dll
vendored
Normal file
Binary file not shown.
BIN
dist/setupVars_GUI.dist/PySide6/qt-plugins/platforms/qdirect2d.dll
vendored
Normal file
BIN
dist/setupVars_GUI.dist/PySide6/qt-plugins/platforms/qdirect2d.dll
vendored
Normal file
Binary file not shown.
BIN
dist/setupVars_GUI.dist/PySide6/qt-plugins/platforms/qminimal.dll
vendored
Normal file
BIN
dist/setupVars_GUI.dist/PySide6/qt-plugins/platforms/qminimal.dll
vendored
Normal file
Binary file not shown.
BIN
dist/setupVars_GUI.dist/PySide6/qt-plugins/platforms/qoffscreen.dll
vendored
Normal file
BIN
dist/setupVars_GUI.dist/PySide6/qt-plugins/platforms/qoffscreen.dll
vendored
Normal file
Binary file not shown.
BIN
dist/setupVars_GUI.dist/PySide6/qt-plugins/platforms/qwindows.dll
vendored
Normal file
BIN
dist/setupVars_GUI.dist/PySide6/qt-plugins/platforms/qwindows.dll
vendored
Normal file
Binary file not shown.
BIN
dist/setupVars_GUI.dist/PySide6/qt-plugins/styles/qmodernwindowsstyle.dll
vendored
Normal file
BIN
dist/setupVars_GUI.dist/PySide6/qt-plugins/styles/qmodernwindowsstyle.dll
vendored
Normal file
Binary file not shown.
BIN
dist/setupVars_GUI.dist/PySide6/qt-plugins/tls/qcertonlybackend.dll
vendored
Normal file
BIN
dist/setupVars_GUI.dist/PySide6/qt-plugins/tls/qcertonlybackend.dll
vendored
Normal file
Binary file not shown.
BIN
dist/setupVars_GUI.dist/PySide6/qt-plugins/tls/qopensslbackend.dll
vendored
Normal file
BIN
dist/setupVars_GUI.dist/PySide6/qt-plugins/tls/qopensslbackend.dll
vendored
Normal file
Binary file not shown.
BIN
dist/setupVars_GUI.dist/PySide6/qt-plugins/tls/qschannelbackend.dll
vendored
Normal file
BIN
dist/setupVars_GUI.dist/PySide6/qt-plugins/tls/qschannelbackend.dll
vendored
Normal file
Binary file not shown.
BIN
dist/setupVars_GUI.dist/_bz2.pyd
vendored
Normal file
BIN
dist/setupVars_GUI.dist/_bz2.pyd
vendored
Normal file
Binary file not shown.
BIN
dist/setupVars_GUI.dist/_ctypes.pyd
vendored
Normal file
BIN
dist/setupVars_GUI.dist/_ctypes.pyd
vendored
Normal file
Binary file not shown.
BIN
dist/setupVars_GUI.dist/_decimal.pyd
vendored
Normal file
BIN
dist/setupVars_GUI.dist/_decimal.pyd
vendored
Normal file
Binary file not shown.
BIN
dist/setupVars_GUI.dist/_elementtree.pyd
vendored
Normal file
BIN
dist/setupVars_GUI.dist/_elementtree.pyd
vendored
Normal file
Binary file not shown.
BIN
dist/setupVars_GUI.dist/_hashlib.pyd
vendored
Normal file
BIN
dist/setupVars_GUI.dist/_hashlib.pyd
vendored
Normal file
Binary file not shown.
BIN
dist/setupVars_GUI.dist/_lzma.pyd
vendored
Normal file
BIN
dist/setupVars_GUI.dist/_lzma.pyd
vendored
Normal file
Binary file not shown.
BIN
dist/setupVars_GUI.dist/_socket.pyd
vendored
Normal file
BIN
dist/setupVars_GUI.dist/_socket.pyd
vendored
Normal file
Binary file not shown.
BIN
dist/setupVars_GUI.dist/_ssl.pyd
vendored
Normal file
BIN
dist/setupVars_GUI.dist/_ssl.pyd
vendored
Normal file
Binary file not shown.
BIN
dist/setupVars_GUI.dist/_wmi.pyd
vendored
Normal file
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
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
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
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
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
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
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
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
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
BIN
dist/setupVars_GUI.dist/python3.dll
vendored
Normal file
Binary file not shown.
BIN
dist/setupVars_GUI.dist/python312.dll
vendored
Normal file
BIN
dist/setupVars_GUI.dist/python312.dll
vendored
Normal file
Binary file not shown.
BIN
dist/setupVars_GUI.dist/qt6core.dll
vendored
Normal file
BIN
dist/setupVars_GUI.dist/qt6core.dll
vendored
Normal file
Binary file not shown.
BIN
dist/setupVars_GUI.dist/qt6gui.dll
vendored
Normal file
BIN
dist/setupVars_GUI.dist/qt6gui.dll
vendored
Normal file
Binary file not shown.
BIN
dist/setupVars_GUI.dist/qt6network.dll
vendored
Normal file
BIN
dist/setupVars_GUI.dist/qt6network.dll
vendored
Normal file
Binary file not shown.
BIN
dist/setupVars_GUI.dist/qt6pdf.dll
vendored
Normal file
BIN
dist/setupVars_GUI.dist/qt6pdf.dll
vendored
Normal file
Binary file not shown.
BIN
dist/setupVars_GUI.dist/qt6svg.dll
vendored
Normal file
BIN
dist/setupVars_GUI.dist/qt6svg.dll
vendored
Normal file
Binary file not shown.
BIN
dist/setupVars_GUI.dist/qt6widgets.dll
vendored
Normal file
BIN
dist/setupVars_GUI.dist/qt6widgets.dll
vendored
Normal file
Binary file not shown.
BIN
dist/setupVars_GUI.dist/select.pyd
vendored
Normal file
BIN
dist/setupVars_GUI.dist/select.pyd
vendored
Normal file
Binary file not shown.
BIN
dist/setupVars_GUI.dist/setupVars_GUI.dll
vendored
Normal file
BIN
dist/setupVars_GUI.dist/setupVars_GUI.dll
vendored
Normal file
Binary file not shown.
BIN
dist/setupVars_GUI.dist/shiboken6.abi3.dll
vendored
Normal file
BIN
dist/setupVars_GUI.dist/shiboken6.abi3.dll
vendored
Normal file
Binary file not shown.
BIN
dist/setupVars_GUI.dist/shiboken6/Shiboken.pyd
vendored
Normal file
BIN
dist/setupVars_GUI.dist/shiboken6/Shiboken.pyd
vendored
Normal file
Binary file not shown.
BIN
dist/setupVars_GUI.dist/shiboken6/msvcp140.dll
vendored
Normal file
BIN
dist/setupVars_GUI.dist/shiboken6/msvcp140.dll
vendored
Normal file
Binary file not shown.
BIN
dist/setupVars_GUI.dist/shiboken6/msvcp140_1.dll
vendored
Normal file
BIN
dist/setupVars_GUI.dist/shiboken6/msvcp140_1.dll
vendored
Normal file
Binary file not shown.
BIN
dist/setupVars_GUI.dist/shiboken6/msvcp140_2.dll
vendored
Normal file
BIN
dist/setupVars_GUI.dist/shiboken6/msvcp140_2.dll
vendored
Normal file
Binary file not shown.
BIN
dist/setupVars_GUI.dist/shiboken6/msvcp140_codecvt_ids.dll
vendored
Normal file
BIN
dist/setupVars_GUI.dist/shiboken6/msvcp140_codecvt_ids.dll
vendored
Normal file
Binary file not shown.
BIN
dist/setupVars_GUI.dist/unicodedata.pyd
vendored
Normal file
BIN
dist/setupVars_GUI.dist/unicodedata.pyd
vendored
Normal file
Binary file not shown.
BIN
dist/setupVars_GUI.dist/vcruntime140.dll
vendored
Normal file
BIN
dist/setupVars_GUI.dist/vcruntime140.dll
vendored
Normal file
Binary file not shown.
BIN
dist/setupVars_GUI.dist/vcruntime140_1.dll
vendored
Normal file
BIN
dist/setupVars_GUI.dist/vcruntime140_1.dll
vendored
Normal file
Binary file not shown.
5041
nuitka-crash-report.xml
Normal file
5041
nuitka-crash-report.xml
Normal file
File diff suppressed because it is too large
Load Diff
BIN
setupVars_GUI.dist/PySide6/QtCore.pyd
Normal file
BIN
setupVars_GUI.dist/PySide6/QtCore.pyd
Normal file
Binary file not shown.
BIN
setupVars_GUI.dist/PySide6/QtGui.pyd
Normal file
BIN
setupVars_GUI.dist/PySide6/QtGui.pyd
Normal file
Binary file not shown.
BIN
setupVars_GUI.dist/PySide6/QtNetwork.pyd
Normal file
BIN
setupVars_GUI.dist/PySide6/QtNetwork.pyd
Normal file
Binary file not shown.
BIN
setupVars_GUI.dist/PySide6/QtWidgets.pyd
Normal file
BIN
setupVars_GUI.dist/PySide6/QtWidgets.pyd
Normal file
Binary file not shown.
BIN
setupVars_GUI.dist/PySide6/qt-plugins/iconengines/qsvgicon.dll
Normal file
BIN
setupVars_GUI.dist/PySide6/qt-plugins/iconengines/qsvgicon.dll
Normal file
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user