from typing import Callable, Optional, Any from PyQt5.QtWidgets import ( QWidget, QPushButton, QLineEdit, QHBoxLayout, QVBoxLayout, QLabel, QTableWidget, QTableWidgetItem ) from PyQt5.QtGui import QIntValidator from utils.json_tools import read_json, write_json from gui import qt_settings as qts class settingsWindow(QWidget): def __init__(self, path: str, name: str, upd_func: Callable[[], None]): """ Окно настроек для редактирования параметров. :param path: Путь к файлу настроек (JSON). :param name: Название набора настроек. :param upd_func: Функция обновления (коллбэк). """ super().__init__() self._settingsPath = path self._name = name self._data: dict[str, list[Any]] = {} self._upd_func = upd_func self._num_points: Optional[QLineEdit] = None self._param_table: Optional[QTableWidget] = None self.load_settings() self._init_ui() def load_settings(self) -> None: """Загружает настройки из JSON-файла.""" data = read_json(self._settingsPath) if isinstance(data, dict): self._data = data else: self._data = {} def write_settings(self) -> None: """Записывает текущие настройки в JSON-файл.""" write_json(self._settingsPath, self._data) def getParams(self) -> dict: """Возвращает текущий словарь параметров.""" return self._data def _init_ui(self) -> None: """Инициализирует UI: кнопки, поля ввода, таблицу.""" save_button = QPushButton("Save") restore_button = QPushButton("Restore") self._num_points = QLineEdit() self._num_points.setPlaceholderText("Enter the number of welding points") self._num_points.setValidator(QIntValidator()) control_layout = QHBoxLayout() control_layout.addWidget(save_button) control_layout.addWidget(restore_button) control_layout.addWidget(self._num_points) save_button.pressed.connect(self._save) restore_button.pressed.connect(self._restore) self._num_points.editingFinished.connect(self._expand) self._param_table = QTableWidget() self._populate_table() layout = QVBoxLayout() header = QLabel(self._name) layout.addWidget(header) layout.addLayout(control_layout) layout.addWidget(self._param_table) self.setLayout(layout) self.setStyleSheet(qts.white_style) def _populate_table(self) -> None: """Заполняет таблицу значениями из self._data.""" # Если нет данных для заполнения if not self._data: self._param_table.setRowCount(0) self._param_table.setColumnCount(0) return # Предполагаем, что у всех ключей одинаковая длина списков параметров. first_key = next(iter(self._data), None) if first_key is None: self._param_table.setRowCount(0) self._param_table.setColumnCount(0) return column_count = len(self._data[first_key]) + 1 self._param_table.setRowCount(len(self._data)) self._param_table.setColumnCount(column_count) for i, (key, items) in enumerate(self._data.items()): self._param_table.setItem(i, 0, QTableWidgetItem(key)) for j, item in enumerate(items): self._param_table.setItem(i, j+1, QTableWidgetItem(str(item))) def _save(self) -> None: """Сохраняет текущие параметры из таблицы в self._data и вызывает _upd_func().""" new_data = {} row_count = self._param_table.rowCount() col_count = self._param_table.columnCount() for i in range(row_count): key_item = self._param_table.item(i, 0) if key_item is None: continue key = key_item.text() # Если ключ пустой, пропускаем if not key: continue row_data = [] for j in range(1, col_count): cell_item = self._param_table.item(i, j) if cell_item is None: continue param_str = cell_item.text() # Если ключ не trace_storage_path, конвертируем в float if key != "trace_storage_path": try: param = float(param_str) except ValueError: param = 0.0 else: param = param_str row_data.append(param) new_data[key] = row_data self._data = new_data self.write_settings() self._upd_func() def _restore(self) -> None: """Перезагружает данные из файла и обновляет таблицу.""" self.load_settings() self._populate_table() def _expand(self) -> None: """Расширяет количество столбцов таблицы в зависимости от введённого значения.""" if not self._num_points: return num_points_text = self._num_points.text() if not num_points_text.isdigit(): return num_points = int(num_points_text) if num_points < 0: return prev_columns = self._param_table.columnCount() desired_columns = num_points + 1 if desired_columns <= prev_columns: return self._param_table.setColumnCount(desired_columns) # Новые столбцы заполняем последним известным параметром для каждого ключа for i, (key, items) in enumerate(self._data.items()): # Если нет данных, пропускаем if not items: continue last_value = str(items[-1]) for col in range(prev_columns, desired_columns): self._param_table.setItem(i, col, QTableWidgetItem(last_value)) # Добавляем новый элемент также в self._data для консистентности # После этого можно будет сохранить при нажатии Save # Дополним также и в self._data additional_count = desired_columns - prev_columns self._data[key].extend([float(last_value) if key != "trace_storage_path" else last_value] * additional_count) if __name__ == '__main__': import pyqtgraph as pg app = pg.mkQApp("Parameter Tree Example") window = settingsWindow('params\operator_params.json', 'operator') app.exec()