- Добавлена подложка с видимостью, прозрачностью, масштабом, перемещением и редактированием через отдельное меню. - Добавлен инструмент создания свободных светодиодов и редактирование X/Y для вручную созданных точек в таблице. - Упрощена таблица выбранных LED: единый ID, координаты X/Y и UID в конце. - Добавлены настраиваемые горячие клавиши для всех действий и переключатель отображения id светодиодов. - Улучшена отрисовка линий: стрелки направления, изгибы при полном и частичном визуальном наложении сегментов. - Оптимизировано переключение активных линий, выбор точек и поиск наложений соединений для снижения подвисаний. - Исправлена логика ручных LED: row/col рассчитываются по сетке 34 мм, а ручные точки не сдвигают якорь сетки. - Обновлена сборка EXE: имя использует тег, короткий хэш или дату/время для dirty-репозитория. - Расширен smoke-набор до 113 проверок для новых функций и регрессий.
107 lines
3.8 KiB
Python
107 lines
3.8 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 .localization import localize_dialog_buttons
|
||
from .models import BackgroundImage
|
||
except ImportError:
|
||
from localization import localize_dialog_buttons
|
||
from models import BackgroundImage
|
||
|
||
|
||
class BackgroundDialog(QDialog):
|
||
def __init__(self, background: BackgroundImage, parent=None) -> None: # noqa: ANN001
|
||
super().__init__(parent)
|
||
self.setWindowTitle("Подложка")
|
||
self.setMinimumWidth(460)
|
||
|
||
self.path = QLineEdit(background.path)
|
||
self.path.setPlaceholderText("PNG или JPEG")
|
||
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:
|
||
return BackgroundImage(
|
||
path=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(),
|
||
)
|
||
|
||
def _reset_scale(self) -> None:
|
||
self.scale_x.setValue(100.0)
|
||
self.scale_y.setValue(100.0)
|
||
|
||
def _browse(self) -> None:
|
||
start = self.path.text().strip()
|
||
start_path = Path(start).parent if start else Path.home()
|
||
selected, _ = QFileDialog.getOpenFileName(
|
||
self,
|
||
"Выбрать подложку",
|
||
str(start_path),
|
||
"Изображения (*.png *.jpg *.jpeg);;Все файлы (*.*)",
|
||
)
|
||
if selected:
|
||
self.path.setText(selected)
|
||
|
||
@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
|