WeldingSpotPerformance/src/gui/settings_window.py

220 lines
8.4 KiB
Python
Raw Normal View History

from typing import Callable, Optional, Any
from PyQt5.QtWidgets import (QWidget, QPushButton,
QLineEdit, QHBoxLayout,
QVBoxLayout, QLabel,
QTableWidget, QTableWidgetItem,
QStyledItemDelegate)
from PyQt5.QtGui import QIntValidator, QDoubleValidator
2024-12-05 13:18:53 +03:00
from utils.json_tools import read_json, write_json
from utils 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.dark_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])
self._param_table.setRowCount(len(self._data))
self._param_table.setColumnCount(column_count)
self._param_table.setVerticalHeaderLabels(self._data.keys())
int_delegate = ValidatorDelegate(data_type='int', parent=self._param_table)
float_delegate = ValidatorDelegate(data_type='float', parent=self._param_table)
str_delegate = ValidatorDelegate(data_type='str', parent=self._param_table)
for i, (_, items) in enumerate(self._data.items()):
for j, item in enumerate(items):
self._param_table.setItem(i, j, QTableWidgetItem(str(item)))
if type(item) == int:
self._param_table.setItemDelegateForRow(i, int_delegate)
elif type(item) == float:
self._param_table.setItemDelegateForRow(i, float_delegate)
else:
self._param_table.setItemDelegateForRow(i, str_delegate)
def _save(self) -> None:
"""Сохраняет текущие параметры из таблицы в self._data и вызывает _upd_func()."""
new_data = {}
col_count = self._param_table.columnCount()
for i, key in enumerate(self._data.keys()):
row_data = []
for j in range(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
if desired_columns <= 0:
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)
class SystemSettings(settingsWindow):
def __init__(self, path, name, upd_func):
super().__init__(path, name, upd_func)
self._num_points.setVisible(False)
def _expand(self):
pass
class OperatorSettings(settingsWindow):
pass
class ValidatorDelegate(QStyledItemDelegate):
def __init__(self, data_type='str', parent=None):
super().__init__(parent)
self.data_type = data_type
if self.data_type == 'int':
self.validator = QIntValidator()
elif self.data_type == 'float':
self.validator = QDoubleValidator()
self.validator.setNotation(QDoubleValidator.StandardNotation)
else:
self.validator = None
def createEditor(self, parent, option, index):
editor = QLineEdit(parent)
if self.validator:
editor.setValidator(self.validator)
return editor
if __name__ == '__main__':
import pyqtgraph as pg
app = pg.mkQApp("Parameter Tree Example")
window = settingsWindow('params\operator_params.json', 'operator')
app.exec()