from PySide2.QtWidgets import ( QTableWidget, QTableWidgetItem, QCheckBox, QComboBox, QLineEdit, QCompleter, QAbstractItemView, QHeaderView, QLabel ) from PySide2.QtGui import QColor, QBrush, QPalette from PySide2.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.var_list = [] 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.var_list = vars_list 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)] # --- ДО: удаляем отображение структур и union-переменных for var in vars_list: pt_type = var.get('pt_type', '') if 'struct' in pt_type or 'union' in pt_type: var['show_var'] = 'false' var['enable'] = 'false' filtered_vars = [v for v in vars_list if v.get('show_var', 'false') == 'true'] self.setRowCount(len(filtered_vars)) self.verticalHeader().setVisible(False) style_with_padding = "padding-left: 5px; padding-right: 5px; font-size: 14pt; font-family: 'Segoe UI';" 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) cb.setStyleSheet(style_with_padding) 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) name_edit.setStyleSheet(style_with_padding) self.setCellWidget(row, rows.name, name_edit) # Origin Type (readonly) origin_item = QTableWidgetItem(var.get('type', '')) origin_item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled) origin_item.setToolTip(var.get('type', '')) # Всплывающая подсказка origin_item.setForeground(QBrush(Qt.black)) 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) pt_combo.setStyleSheet(style_with_padding) pt_combo.wheelEvent = lambda e: e.ignore() 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) iq_combo.setStyleSheet(style_with_padding) 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) ret_combo.setStyleSheet(style_with_padding) self.setCellWidget(row, rows.ret_type, ret_combo) # short_name short_name_val = var.get('shortname', var['name']) short_name_edit = QLineEdit(short_name_val) short_name_edit.textChanged.connect(on_change_callback) short_name_edit.setStyleSheet(style_with_padding) self.setCellWidget(row, rows.short_name, short_name_edit) self.check() def check(self): warning_color = (QColor("#FFFACD")) # Жёлтый для длинных shortname error_color = (QColor("#FFB6C1")) # Светло-красный для отсутствующих переменных tooltip_shortname = "Short Name длиннее 10 символов — будет обрезано при генерации" tooltip_missing = f'Имя переменной не найдено среди переменных. Добавьте её через кнопку "Добавить переменные"' for row in range(self.rowCount()): # Получаем имя переменной (столбец `name`) name_widget = self.cellWidget(row, rows.name) name = name_widget.text() if name_widget else "" # Получаем shortname short_name_edit = self.cellWidget(row, rows.short_name) shortname = short_name_edit.text() if short_name_edit else "" # Флаги ошибок long_shortname = len(shortname) > 10 found = any(v.get('name') == name for v in self.var_list) # Выбираем цвет и подсказку color = None tooltip = "" if not found: color = error_color tooltip = tooltip_missing elif long_shortname: color = warning_color tooltip = tooltip_shortname self.highlight_row(row, color, tooltip) 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': f't_{iq}', 'return_type': f't_{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 def highlight_row(self, row: int, color: QColor = None, tooltip: str = ""): """ Подсвечивает строку таблицы цветом `color`, не меняя шрифт. Работает с QLineEdit, QComboBox, QCheckBox (включая обёртки). Если `color=None`, сбрасывает подсветку. """ css_reset = "background-color: none; font: inherit;" css_color = f"background-color: {color.name()};" if color else css_reset for col in range(self.columnCount()): item = self.item(row, col) widget = self.cellWidget(row, col) # Подсветка обычной item-ячейки (например, тип переменной) if item is not None: if color: item.setBackground(QBrush(color)) item.setToolTip(tooltip) else: item.setBackground(QBrush(Qt.NoBrush)) item.setToolTip("") # Подсветка виджетов — здесь главная доработка elif widget is not None: # Надёжная подсветка: через styleSheet widget.setStyleSheet(css_color) widget.setToolTip(tooltip if color else "") def get_selected_var_names(self): selected_indexes = self.selectedIndexes() selected_rows = set(index.row() for index in selected_indexes) names = [] for row in selected_rows: name_widget = self.cellWidget(row, rows.name) if name_widget: name = name_widget.text() if name: names.append(name) return names