- Добавлена сохраняемая настройка размера текста индексов LED с мгновенным предпросмотром и сбросом. - Исправлено размещение быстрых кнопок в строке меню без перекрытия пунктов на узком окне. - Усилена проверка OTA-загрузок: права записи, конечный URL, авторизация, Content-Length и отдельная группа updater-процесса. - Добавлен единый загрузчик подложек с определением формата по содержимому, EXIF-ориентацией и преобразованием CMYK. - Расширен список форматов подложки и добавлена безопасная обработка крупных изображений. - Ошибки декодирования теперь показываются пользователю без замены текущей подложки. - Обновлены smoke-тесты, документация, PyInstaller-конфигурация и версия приложения до 3.4.0.
128 lines
5.0 KiB
Python
128 lines
5.0 KiB
Python
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
|
|
from PySide6.QtWidgets import (
|
|
QDialog,
|
|
QDialogButtonBox,
|
|
QCheckBox,
|
|
QDoubleSpinBox,
|
|
QFileDialog,
|
|
QFormLayout,
|
|
QHBoxLayout,
|
|
QLineEdit,
|
|
QPushButton,
|
|
QWidget,
|
|
)
|
|
|
|
try:
|
|
from .background_image_loader import supported_image_filter
|
|
from .localization import localize_dialog_buttons
|
|
from .models import BackgroundImage
|
|
except ImportError:
|
|
from background_image_loader import supported_image_filter
|
|
from localization import localize_dialog_buttons
|
|
from models import BackgroundImage
|
|
|
|
|
|
class BackgroundDialog(QDialog):
|
|
EMBEDDED_PATH_LABEL = "Встроенная"
|
|
|
|
def __init__(self, background: BackgroundImage, parent=None) -> None: # noqa: ANN001
|
|
super().__init__(parent)
|
|
self.setWindowTitle("Подложка")
|
|
self.setMinimumWidth(460)
|
|
|
|
self._source_background = background
|
|
self._keep_embedded_image = background.has_embedded_image()
|
|
self.path = QLineEdit(self.EMBEDDED_PATH_LABEL if self._keep_embedded_image else background.path)
|
|
self.path.setPlaceholderText("Файл изображения")
|
|
self.path.setReadOnly(self._keep_embedded_image)
|
|
if self._keep_embedded_image:
|
|
source_name = background.embedded_image_name or "изображение"
|
|
self.path.setToolTip(f"{source_name} встроено в JSON схемы")
|
|
browse = QPushButton("Выбрать")
|
|
browse.clicked.connect(self._browse)
|
|
path_row = QWidget()
|
|
path_layout = QHBoxLayout(path_row)
|
|
path_layout.setContentsMargins(0, 0, 0, 0)
|
|
path_layout.addWidget(self.path, 1)
|
|
path_layout.addWidget(browse)
|
|
|
|
self.offset_x = self._spin(-100000.0, 100000.0, background.offset_x, "")
|
|
self.offset_y = self._spin(-100000.0, 100000.0, background.offset_y, "")
|
|
self.scale_x = self._spin(1.0, 10000.0, background.scale_x_percent, " %")
|
|
self.scale_y = self._spin(1.0, 10000.0, background.scale_y_percent, " %")
|
|
self.opacity = self._spin(0.0, 100.0, background.opacity_percent, " %")
|
|
self.visible = QCheckBox("Показывать подложку")
|
|
self.visible.setChecked(background.visible)
|
|
reset_scale = QPushButton("100%")
|
|
reset_scale.clicked.connect(self._reset_scale)
|
|
scale_row = QWidget()
|
|
scale_layout = QHBoxLayout(scale_row)
|
|
scale_layout.setContentsMargins(0, 0, 0, 0)
|
|
scale_layout.addWidget(self.scale_x, 1)
|
|
scale_layout.addWidget(self.scale_y, 1)
|
|
scale_layout.addWidget(reset_scale)
|
|
|
|
form = QFormLayout(self)
|
|
form.addRow("Изображение", path_row)
|
|
form.addRow("Смещение центра X", self.offset_x)
|
|
form.addRow("Смещение центра Y", self.offset_y)
|
|
form.addRow("Масштаб X / Y", scale_row)
|
|
form.addRow("Прозрачность", self.opacity)
|
|
form.addRow("", self.visible)
|
|
buttons = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
|
|
localize_dialog_buttons(buttons)
|
|
buttons.accepted.connect(self.accept)
|
|
buttons.rejected.connect(self.reject)
|
|
form.addRow(buttons)
|
|
|
|
def background(self) -> BackgroundImage:
|
|
embedded = self._source_background if self._keep_embedded_image else BackgroundImage()
|
|
return BackgroundImage(
|
|
path="" if self._keep_embedded_image else self.path.text().strip(),
|
|
offset_x=self.offset_x.value(),
|
|
offset_y=self.offset_y.value(),
|
|
scale_x_percent=self.scale_x.value(),
|
|
scale_y_percent=self.scale_y.value(),
|
|
opacity_percent=self.opacity.value(),
|
|
visible=self.visible.isChecked(),
|
|
embedded_image_base64=embedded.embedded_image_base64,
|
|
embedded_image_mime_type=embedded.embedded_image_mime_type,
|
|
embedded_image_name=embedded.embedded_image_name,
|
|
embedded_image_sha256=embedded.embedded_image_sha256,
|
|
)
|
|
|
|
def _reset_scale(self) -> None:
|
|
self.scale_x.setValue(100.0)
|
|
self.scale_y.setValue(100.0)
|
|
|
|
def _browse(self) -> None:
|
|
start = "" if self._keep_embedded_image else self.path.text().strip()
|
|
start_path = Path(start).parent if start else Path.home()
|
|
selected, _ = QFileDialog.getOpenFileName(
|
|
self,
|
|
"Выбрать подложку",
|
|
str(start_path),
|
|
supported_image_filter(),
|
|
)
|
|
if selected:
|
|
self._set_external_path(selected)
|
|
|
|
def _set_external_path(self, path: str) -> None:
|
|
self._keep_embedded_image = False
|
|
self.path.setReadOnly(False)
|
|
self.path.setToolTip("")
|
|
self.path.setText(path)
|
|
|
|
@staticmethod
|
|
def _spin(minimum: float, maximum: float, value: float, suffix: str) -> QDoubleSpinBox:
|
|
spin = QDoubleSpinBox()
|
|
spin.setRange(minimum, maximum)
|
|
spin.setDecimals(2)
|
|
spin.setSingleStep(1.0)
|
|
spin.setValue(value)
|
|
spin.setSuffix(suffix)
|
|
return spin
|