Add board v2.x commands support to UI dialogs and sections
- Добавлена поддержка выбора произвольного цвета (QColorDialog) с отправкой команды 8008RRRGGGBBB. - Реализована панель управления GPIO25/GPIO26 (команда 50) в output_test_dialog.py. - Добавлена поддержка direct-объектов и эффектов Бродяги морей (команда 81) в led_test_dialog.py. - Реализованы тесты матриц и direct-шкалы (команда 85) в led_test_dialog.py. - Создан новый диалог prize_showcase_dialog.py для ячеек призовой витрины (команда 25). - Добавлена кнопка «Витрина призов» в PRO-кнопки connection_tab.py. - В payment_section.py добавлен показ накопительного счетчика монет global_summa (команда 93).
This commit is contained in:
parent
85a7b4db55
commit
448b9bb07a
@ -269,6 +269,7 @@ class ConnectionTab(QWidget):
|
||||
self._button_backlight_section = ButtonBacklightSection(self._connection)
|
||||
self._input_section = InputTestSection(self._connection)
|
||||
self._payment_section = PaymentSection(
|
||||
connection=self._connection,
|
||||
send_raw=lambda cmd: self._input_section._send_raw(cmd),
|
||||
)
|
||||
|
||||
@ -310,6 +311,7 @@ class ConnectionTab(QWidget):
|
||||
("Тест силовых выходов", "fa5s.bolt", self._open_output_test),
|
||||
("Обновление прошивки", "fa5s.upload", self._open_firmware),
|
||||
("Задание роли", "fa5s.user-tag", self._open_role),
|
||||
("Витрина призов", "fa5s.gift", self._open_prize_showcase),
|
||||
)
|
||||
self._pro_buttons: list[tuple[QPushButton, str]] = []
|
||||
for title, icon_name, handler in pro_items:
|
||||
@ -605,6 +607,13 @@ class ConnectionTab(QWidget):
|
||||
lambda: RoleDialog(self._connection, parent=self),
|
||||
)
|
||||
|
||||
def _open_prize_showcase(self) -> None:
|
||||
from .dialogs.prize_showcase_dialog import PrizeShowcaseDialog
|
||||
self._exec_dialog(
|
||||
"prize_showcase",
|
||||
lambda: PrizeShowcaseDialog(self._connection, parent=self),
|
||||
)
|
||||
|
||||
def _open_terminal(self) -> None:
|
||||
from .dialogs.command_terminal_dialog import CommandTerminalDialog
|
||||
self._exec_dialog(
|
||||
|
||||
@ -16,6 +16,7 @@ import sys
|
||||
from PySide6.QtCore import Qt, QTimer
|
||||
from PySide6.QtWidgets import (
|
||||
QCheckBox,
|
||||
QColorDialog,
|
||||
QFormLayout,
|
||||
QGroupBox,
|
||||
QHBoxLayout,
|
||||
@ -198,6 +199,12 @@ class LedTestDialog(StyledDialog):
|
||||
gamma_layout.addWidget(apply_gamma_btn)
|
||||
color_form.addRow("Гамма-коррекция:", gamma_layout)
|
||||
|
||||
# Пользовательский цвет
|
||||
self._custom_color_btn = QPushButton("Выбрать и отправить цвет")
|
||||
self._custom_color_btn.setIcon(icon("fa5s.palette", color=get_icon_color()))
|
||||
self._custom_color_btn.clicked.connect(self._send_custom_color)
|
||||
color_form.addRow("Свой цвет (08):", self._custom_color_btn)
|
||||
|
||||
# Тест цвета
|
||||
self._color_test_btn = QPushButton("Тест цвета")
|
||||
self._color_test_btn.setProperty("role", "primary")
|
||||
@ -212,6 +219,159 @@ class LedTestDialog(StyledDialog):
|
||||
|
||||
root.addWidget(color_group)
|
||||
|
||||
# ── Бродяга морей (cmd 81) ──
|
||||
wanderer_group = QGroupBox("Бродяга морей (cmd 81)")
|
||||
wanderer_form = QFormLayout(wanderer_group)
|
||||
wanderer_form.setSpacing(6)
|
||||
|
||||
# Объект 1
|
||||
self._w1_level = QSpinBox()
|
||||
self._w1_level.setRange(0, 21)
|
||||
self._w1_level.setValue(5)
|
||||
|
||||
w1_apply_level = QPushButton("Задать уровень")
|
||||
w1_apply_level.setIcon(icon("fa5s.angle-double-right", color=get_icon_color()))
|
||||
w1_apply_level.clicked.connect(self._send_w1_level)
|
||||
|
||||
w1_level_layout = QHBoxLayout()
|
||||
w1_level_layout.addWidget(self._w1_level, 1)
|
||||
w1_level_layout.addWidget(w1_apply_level)
|
||||
wanderer_form.addRow("Шкала 1 (уровень 0..21):", w1_level_layout)
|
||||
|
||||
self._w1_effect = NoWheelComboBox()
|
||||
self._w1_effect.addItem("Эффект 1 (синий/фиолетовый)", 1)
|
||||
self._w1_effect.addItem("Эффект 2 (фиолетовый/голубой)", 2)
|
||||
self._w1_effect.addItem("Эффект 3 (мигание белым)", 3)
|
||||
|
||||
w1_apply_eff = QPushButton("Запустить эффект")
|
||||
w1_apply_eff.setProperty("role", "primary")
|
||||
w1_apply_eff.setIcon(icon("fa5s.play", color="#ecfdf5"))
|
||||
w1_apply_eff.clicked.connect(self._send_w1_effect)
|
||||
|
||||
w1_eff_layout = QHBoxLayout()
|
||||
w1_eff_layout.addWidget(self._w1_effect, 1)
|
||||
w1_eff_layout.addWidget(w1_apply_eff)
|
||||
wanderer_form.addRow("Эффекты шкалы 1:", w1_eff_layout)
|
||||
|
||||
# Объект 2
|
||||
self._w2_level = QSpinBox()
|
||||
self._w2_level.setRange(0, 44)
|
||||
self._w2_level.setValue(5)
|
||||
|
||||
w2_apply_level = QPushButton("Задать уровень")
|
||||
w2_apply_level.setIcon(icon("fa5s.angle-double-right", color=get_icon_color()))
|
||||
w2_apply_level.clicked.connect(self._send_w2_level)
|
||||
|
||||
w2_level_layout = QHBoxLayout()
|
||||
w2_level_layout.addWidget(self._w2_level, 1)
|
||||
w2_level_layout.addWidget(w2_apply_level)
|
||||
wanderer_form.addRow("Шкала 2 (уровень 0..44):", w2_level_layout)
|
||||
|
||||
self._w2_effect = NoWheelComboBox()
|
||||
self._w2_effect.addItem("Эффект 1 (синий/фиолетовый)", 1)
|
||||
self._w2_effect.addItem("Эффект 2 (фиолетовый/голубой)", 2)
|
||||
self._w2_effect.addItem("Эффект 3 (мигание белым)", 3)
|
||||
|
||||
w2_apply_eff = QPushButton("Запустить эффект")
|
||||
w2_apply_eff.setProperty("role", "primary")
|
||||
w2_apply_eff.setIcon(icon("fa5s.play", color="#ecfdf5"))
|
||||
w2_apply_eff.clicked.connect(self._send_w2_effect)
|
||||
|
||||
w2_eff_layout = QHBoxLayout()
|
||||
w2_eff_layout.addWidget(self._w2_effect, 1)
|
||||
w2_eff_layout.addWidget(w2_apply_eff)
|
||||
wanderer_form.addRow("Эффекты шкалы 2:", w2_eff_layout)
|
||||
|
||||
# Сброс обоих
|
||||
w_clear_btn = QPushButton("Очистить обе шкалы (8130)")
|
||||
w_clear_btn.setIcon(icon("fa5s.eraser", color=get_icon_color()))
|
||||
w_clear_btn.clicked.connect(self._clear_wanderer)
|
||||
wanderer_form.addRow(w_clear_btn)
|
||||
|
||||
root.addWidget(wanderer_group)
|
||||
|
||||
# ── Управление виртуальной матрицей (cmd 85/86/87) ──
|
||||
matrix_group = QGroupBox("Виртуальная матрица (cmd 85/86/87)")
|
||||
matrix_form = QFormLayout(matrix_group)
|
||||
matrix_form.setSpacing(6)
|
||||
|
||||
# Автокарусель / Рендеринг
|
||||
carousel_layout = QHBoxLayout()
|
||||
|
||||
self._mc_on_btn = QPushButton("Карусель Вкл")
|
||||
self._mc_on_btn.setIcon(icon("fa5s.play-circle", color=get_icon_color()))
|
||||
self._mc_on_btn.clicked.connect(self._send_carousel_on)
|
||||
carousel_layout.addWidget(self._mc_on_btn)
|
||||
|
||||
self._mc_off_btn = QPushButton("Карусель Выкл")
|
||||
self._mc_off_btn.setIcon(icon("fa5s.stop-circle", color=get_icon_color()))
|
||||
self._mc_off_btn.clicked.connect(self._send_carousel_off)
|
||||
carousel_layout.addWidget(self._mc_off_btn)
|
||||
|
||||
self._mc_stop_btn = QPushButton("Rendering OFF")
|
||||
self._mc_stop_btn.setProperty("role", "primary")
|
||||
self._mc_stop_btn.setIcon(icon("fa5s.power-off", color="#ecfdf5"))
|
||||
self._mc_stop_btn.clicked.connect(self._send_rendering_off)
|
||||
carousel_layout.addWidget(self._mc_stop_btn)
|
||||
|
||||
matrix_form.addRow("Режимы рендера:", carousel_layout)
|
||||
|
||||
# Тесты матриц (8525 / 8625 / 8725)
|
||||
test_type_layout = QHBoxLayout()
|
||||
self._m_test_type = NoWheelComboBox()
|
||||
self._m_test_type.addItem("Выключить тест (0)", 0)
|
||||
self._m_test_type.addItem("Общий тест / Змейка (1)", 1)
|
||||
self._m_test_type.addItem("Тест строки (2)", 2)
|
||||
self._m_test_type.addItem("Тест столбца (3)", 3)
|
||||
self._m_test_type.addItem("Тест пикселя (4)", 4)
|
||||
self._m_test_type.currentIndexChanged.connect(self._on_matrix_test_type_changed)
|
||||
test_type_layout.addWidget(self._m_test_type, 1)
|
||||
|
||||
self._m_test_param = QSpinBox()
|
||||
self._m_test_param.setRange(0, 999)
|
||||
self._m_test_param.setValue(0)
|
||||
self._m_test_param.setVisible(False)
|
||||
test_type_layout.addWidget(self._m_test_param)
|
||||
|
||||
m_test_btn = QPushButton("Запустить")
|
||||
m_test_btn.setIcon(icon("fa5s.vial", color=get_icon_color()))
|
||||
m_test_btn.clicked.connect(self._send_matrix_test)
|
||||
test_type_layout.addWidget(m_test_btn)
|
||||
|
||||
matrix_form.addRow("Аппаратный тест:", test_type_layout)
|
||||
|
||||
# Direct-шкалы (только для s_b)
|
||||
self._scales_container = QWidget()
|
||||
scales_layout = QVBoxLayout(self._scales_container)
|
||||
scales_layout.setContentsMargins(0, 0, 0, 0)
|
||||
scales_layout.setSpacing(4)
|
||||
|
||||
scales_title = QLabel("<b>Direct-шкалы (только для Морского боя s_b)</b>")
|
||||
scales_layout.addWidget(scales_title)
|
||||
|
||||
self._m_scales: list[QSpinBox] = []
|
||||
for i in range(4):
|
||||
scale_row = QHBoxLayout()
|
||||
spin = QSpinBox()
|
||||
spin.setRange(0, 22)
|
||||
spin.setValue(0)
|
||||
self._m_scales.append(spin)
|
||||
scale_row.addWidget(spin, 1)
|
||||
|
||||
btn = QPushButton(f"Задать шкалу {i+1}")
|
||||
btn.setIcon(icon("fa5s.level-up-alt", color=get_icon_color()))
|
||||
btn.clicked.connect(lambda checked=False, idx=i: self._send_matrix_scale(idx))
|
||||
scale_row.addWidget(btn)
|
||||
scales_layout.addLayout(scale_row)
|
||||
|
||||
clear_scales_btn = QPushButton("Очистить direct-шкалы (8530)")
|
||||
clear_scales_btn.setIcon(icon("fa5s.trash-alt", color=get_icon_color()))
|
||||
clear_scales_btn.clicked.connect(self._clear_matrix_scales)
|
||||
scales_layout.addWidget(clear_scales_btn)
|
||||
|
||||
matrix_form.addRow(self._scales_container)
|
||||
root.addWidget(matrix_group)
|
||||
|
||||
# ── Лог ──
|
||||
self._log = QLabel("")
|
||||
self._log.setObjectName("LogLabel")
|
||||
@ -257,6 +417,10 @@ class LedTestDialog(StyledDialog):
|
||||
# Кнопка «выключить» — только для has_duration
|
||||
self._stop_btn.setVisible(mode.has_duration)
|
||||
|
||||
# Показывать direct-шкалы только для Морского боя s_b (где base_command == "85")
|
||||
is_sb = (mode.base_command == "85")
|
||||
self._scales_container.setVisible(is_sb)
|
||||
|
||||
def _reload_modes(self) -> None:
|
||||
"""Перечитать .lm файлы из папки."""
|
||||
current_name = self._project_combo.currentText()
|
||||
@ -338,6 +502,16 @@ class LedTestDialog(StyledDialog):
|
||||
cmd = f"89{ch}2{val}"
|
||||
self._send(cmd)
|
||||
|
||||
def _send_custom_color(self) -> None:
|
||||
color = QColorDialog.getColor(Qt.GlobalColor.white, self, "Выберите цвет")
|
||||
if color.isValid():
|
||||
ch = self._cmd89_channel_combo.currentData()
|
||||
r = color.red()
|
||||
g = color.green()
|
||||
b = color.blue()
|
||||
cmd = f"8{ch}08{r:03d}{g:03d}{b:03d}"
|
||||
self._send(cmd)
|
||||
|
||||
def _run_color_test(self) -> None:
|
||||
"""Последовательно отправить 8003→8004→8005→8002→8000 с паузой 300 мс."""
|
||||
self._color_test_cmds = ["8003", "8004", "8005", "8002", "8000"]
|
||||
@ -359,6 +533,71 @@ class LedTestDialog(StyledDialog):
|
||||
self._color_test_idx += 1
|
||||
self._send(cmd)
|
||||
|
||||
def _send_w1_level(self) -> None:
|
||||
val = self._w1_level.value()
|
||||
cmd = f"8131{val:02d}"
|
||||
self._send(cmd)
|
||||
|
||||
def _send_w1_effect(self) -> None:
|
||||
val = self._w1_effect.currentData()
|
||||
cmd = f"8131{val}"
|
||||
self._send(cmd)
|
||||
|
||||
def _send_w2_level(self) -> None:
|
||||
val = self._w2_level.value()
|
||||
cmd = f"8132{val:02d}"
|
||||
self._send(cmd)
|
||||
|
||||
def _send_w2_effect(self) -> None:
|
||||
val = self._w2_effect.currentData()
|
||||
cmd = f"8132{val}"
|
||||
self._send(cmd)
|
||||
|
||||
def _clear_wanderer(self) -> None:
|
||||
self._send("8130")
|
||||
|
||||
def _send_carousel_on(self) -> None:
|
||||
mode = self._current_mode()
|
||||
if mode:
|
||||
self._send(f"{mode.base_command}01")
|
||||
|
||||
def _send_carousel_off(self) -> None:
|
||||
mode = self._current_mode()
|
||||
if mode:
|
||||
self._send(f"{mode.base_command}00")
|
||||
|
||||
def _send_rendering_off(self) -> None:
|
||||
mode = self._current_mode()
|
||||
if mode:
|
||||
self._send(mode.base_command)
|
||||
|
||||
def _on_matrix_test_type_changed(self, index: int) -> None:
|
||||
test_type = self._m_test_type.itemData(index)
|
||||
self._m_test_param.setVisible(test_type >= 2)
|
||||
|
||||
def _send_matrix_test(self) -> None:
|
||||
mode = self._current_mode()
|
||||
if not mode:
|
||||
return
|
||||
test_type = self._m_test_type.currentData()
|
||||
if test_type == 0 or test_type == 1:
|
||||
cmd = f"{mode.base_command}25{test_type}"
|
||||
elif test_type == 2 or test_type == 3:
|
||||
val = self._m_test_param.value()
|
||||
cmd = f"{mode.base_command}25{test_type}{val:02d}"
|
||||
elif test_type == 4:
|
||||
val = self._m_test_param.value()
|
||||
cmd = f"{mode.base_command}25{test_type}{val:03d}"
|
||||
self._send(cmd)
|
||||
|
||||
def _send_matrix_scale(self, index: int) -> None:
|
||||
val = self._m_scales[index].value()
|
||||
cmd = f"853{index+1}{val:02d}"
|
||||
self._send(cmd)
|
||||
|
||||
def _clear_matrix_scales(self) -> None:
|
||||
self._send("8530")
|
||||
|
||||
def _send(self, cmd: str) -> None:
|
||||
if not cmd:
|
||||
return
|
||||
|
||||
@ -9,6 +9,7 @@ from __future__ import annotations
|
||||
|
||||
from PySide6.QtCore import Qt
|
||||
from PySide6.QtWidgets import (
|
||||
QFormLayout,
|
||||
QGridLayout,
|
||||
QGroupBox,
|
||||
QHBoxLayout,
|
||||
@ -22,6 +23,7 @@ from ...services.com_service import ComConnection
|
||||
from ...services import settings_service
|
||||
from ...ui.icons import get_icon_color, get_icon_muted, icon
|
||||
from ...ui.theme import get_palette
|
||||
from ..no_wheel_combo_box import NoWheelComboBox
|
||||
from .styled_dialog import StyledDialog
|
||||
|
||||
_NUM_OUTPUTS = 8
|
||||
@ -122,6 +124,67 @@ class OutputTestDialog(StyledDialog):
|
||||
|
||||
root.addLayout(bulk_layout)
|
||||
|
||||
# ── Дополнительные GPIO25/GPIO26 (cmd 50) ──
|
||||
gpio_group = QGroupBox("Дополнительные GPIO25/GPIO26 (cmd 50)")
|
||||
gpio_form = QFormLayout(gpio_group)
|
||||
gpio_form.setSpacing(6)
|
||||
|
||||
# Выбор режимов
|
||||
self._gpio25_mode = NoWheelComboBox()
|
||||
self._gpio25_mode.addItem("Вход (Input)", 0)
|
||||
self._gpio25_mode.addItem("Выход (Output)", 1)
|
||||
|
||||
self._gpio26_mode = NoWheelComboBox()
|
||||
self._gpio26_mode.addItem("Вход (Input)", 0)
|
||||
self._gpio26_mode.addItem("Выход (Output)", 1)
|
||||
|
||||
mode_layout = QHBoxLayout()
|
||||
mode_layout.addWidget(self._gpio25_mode, 1)
|
||||
mode_layout.addWidget(QLabel("GPIO26:"))
|
||||
mode_layout.addWidget(self._gpio26_mode, 1)
|
||||
|
||||
apply_mode_btn = QPushButton("Задать режим")
|
||||
apply_mode_btn.setIcon(icon("fa5s.cog", color=get_icon_color()))
|
||||
apply_mode_btn.clicked.connect(self._send_gpio_modes)
|
||||
mode_layout.addWidget(apply_mode_btn)
|
||||
gpio_form.addRow("GPIO25 Режим:", mode_layout)
|
||||
|
||||
# Установка уровней
|
||||
self._gpio25_level = NoWheelComboBox()
|
||||
self._gpio25_level.addItem("Низкий (Low)", 0)
|
||||
self._gpio25_level.addItem("Высокий (High)", 1)
|
||||
|
||||
self._gpio26_level = NoWheelComboBox()
|
||||
self._gpio26_level.addItem("Низкий (Low)", 0)
|
||||
self._gpio26_level.addItem("Высокий (High)", 1)
|
||||
|
||||
level_layout = QHBoxLayout()
|
||||
level_layout.addWidget(self._gpio25_level, 1)
|
||||
level_layout.addWidget(QLabel("GPIO26:"))
|
||||
level_layout.addWidget(self._gpio26_level, 1)
|
||||
|
||||
apply_level_btn = QPushButton("Задать уровни")
|
||||
apply_level_btn.setIcon(icon("fa5s.bolt", color=get_icon_color()))
|
||||
apply_level_btn.clicked.connect(self._send_gpio_levels)
|
||||
level_layout.addWidget(apply_level_btn)
|
||||
gpio_form.addRow("GPIO25 Уровень:", level_layout)
|
||||
|
||||
# Запрос состояния
|
||||
status_layout = QHBoxLayout()
|
||||
read_state_btn = QPushButton("Запросить состояние (cmd 50)")
|
||||
read_state_btn.setProperty("role", "primary")
|
||||
read_state_btn.setIcon(icon("fa5s.sync-alt", color="#ecfdf5"))
|
||||
read_state_btn.clicked.connect(self._read_gpio_state)
|
||||
status_layout.addWidget(read_state_btn)
|
||||
|
||||
read_short_btn = QPushButton("Краткое состояние (cmd 5005)")
|
||||
read_short_btn.setIcon(icon("fa5s.info-circle", color=get_icon_color()))
|
||||
read_short_btn.clicked.connect(self._read_gpio_short)
|
||||
status_layout.addWidget(read_short_btn)
|
||||
|
||||
gpio_form.addRow(status_layout)
|
||||
root.addWidget(gpio_group)
|
||||
|
||||
# ── Лог ──
|
||||
self._log = QLabel("")
|
||||
self._log.setObjectName("LogLabel")
|
||||
@ -156,6 +219,26 @@ class OutputTestDialog(StyledDialog):
|
||||
cmd = "200"
|
||||
self._send(cmd)
|
||||
|
||||
def _send_gpio_modes(self) -> None:
|
||||
g25 = self._gpio25_mode.currentData()
|
||||
g26 = self._gpio26_mode.currentData()
|
||||
p_val = g25 * 10 + g26
|
||||
cmd = f"5001{p_val:02d}"
|
||||
self._send(cmd)
|
||||
|
||||
def _send_gpio_levels(self) -> None:
|
||||
g25 = self._gpio25_level.currentData()
|
||||
g26 = self._gpio26_level.currentData()
|
||||
p_val = g25 * 10 + g26
|
||||
cmd = f"5010{p_val:02d}"
|
||||
self._send(cmd)
|
||||
|
||||
def _read_gpio_state(self) -> None:
|
||||
self._send("50")
|
||||
|
||||
def _read_gpio_short(self) -> None:
|
||||
self._send("50050")
|
||||
|
||||
def _send(self, cmd: str) -> None:
|
||||
if not cmd:
|
||||
return
|
||||
|
||||
195
app/widgets/dialogs/prize_showcase_dialog.py
Normal file
195
app/widgets/dialogs/prize_showcase_dialog.py
Normal file
@ -0,0 +1,195 @@
|
||||
"""Диалог «Управление призовой витриной» — ячейки призовой стойки (cmd 25).
|
||||
|
||||
Управляет количеством ячеек от 1 до 20 (по умолчанию 5).
|
||||
Команды:
|
||||
- 25: Показать состояние всех ячеек
|
||||
- 2501B: Выдать приз из ячейки B
|
||||
- 2511B: Открыть ячейку B без списания приза
|
||||
- 25991/25990: Включить/выключить автозапуск и перезагрузить
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from PySide6.QtCore import Qt
|
||||
from PySide6.QtWidgets import (
|
||||
QFormLayout,
|
||||
QGridLayout,
|
||||
QGroupBox,
|
||||
QHBoxLayout,
|
||||
QLabel,
|
||||
QMessageBox,
|
||||
QPushButton,
|
||||
QScrollArea,
|
||||
QSpinBox,
|
||||
QVBoxLayout,
|
||||
QWidget,
|
||||
)
|
||||
|
||||
from ...services.com_service import ComConnection
|
||||
from ...ui.icons import get_icon_color, get_icon_muted, icon
|
||||
from .styled_dialog import StyledDialog
|
||||
|
||||
|
||||
class PrizeShowcaseDialog(StyledDialog):
|
||||
"""Диалог управления призовой витриной (cmd 25)."""
|
||||
|
||||
def __init__(self, connection: ComConnection, *, parent=None) -> None:
|
||||
super().__init__(parent)
|
||||
self._conn = connection
|
||||
self.setWindowTitle("Призовая витрина")
|
||||
self.setMinimumWidth(550)
|
||||
self.setMinimumHeight(450)
|
||||
self._build_ui()
|
||||
|
||||
def _build_ui(self) -> None:
|
||||
root = self.containerLayout()
|
||||
root.setSpacing(12)
|
||||
|
||||
# ── Описание ──
|
||||
desc = QLabel(
|
||||
"Управление ячейками витрины призов (команда 25).\n"
|
||||
"Поддерживается выдача призов, разблокировка ячеек и управление авторежимом."
|
||||
)
|
||||
desc.setWordWrap(True)
|
||||
desc.setStyleSheet("color: #9ca3af; font-size: 12px; margin-bottom: 4px;")
|
||||
root.addWidget(desc)
|
||||
|
||||
# ── Общие настройки авторежима ──
|
||||
global_group = QGroupBox("Общие параметры витрины")
|
||||
global_layout = QHBoxLayout(global_group)
|
||||
|
||||
query_btn = QPushButton("Состояние ячеек (cmd 25)")
|
||||
query_btn.setProperty("role", "primary")
|
||||
query_btn.setIcon(icon("fa5s.sync-alt", color="#ecfdf5"))
|
||||
query_btn.clicked.connect(self._query_state)
|
||||
global_layout.addWidget(query_btn)
|
||||
|
||||
auto_on_btn = QPushButton("Авторежим Вкл")
|
||||
auto_on_btn.setIcon(icon("fa5s.play-circle", color=get_icon_color()))
|
||||
auto_on_btn.clicked.connect(self._send_auto_on)
|
||||
global_layout.addWidget(auto_on_btn)
|
||||
|
||||
auto_off_btn = QPushButton("Авторежим Выкл")
|
||||
auto_off_btn.setIcon(icon("fa5s.stop-circle", color=get_icon_color()))
|
||||
auto_off_btn.clicked.connect(self._send_auto_off)
|
||||
global_layout.addWidget(auto_off_btn)
|
||||
|
||||
root.addWidget(global_group)
|
||||
|
||||
# ── Выбор количества ячеек ──
|
||||
cfg_layout = QHBoxLayout()
|
||||
cfg_layout.addWidget(QLabel("Количество ячеек витрины (1-20):"))
|
||||
self._cells_count_spin = QSpinBox()
|
||||
self._cells_count_spin.setRange(1, 20)
|
||||
self._cells_count_spin.setValue(5)
|
||||
self._cells_count_spin.valueChanged.connect(self._rebuild_grid)
|
||||
cfg_layout.addWidget(self._cells_count_spin)
|
||||
cfg_layout.addStretch()
|
||||
root.addLayout(cfg_layout)
|
||||
|
||||
# ── Область прокрутки для ячеек ──
|
||||
scroll = QScrollArea()
|
||||
scroll.setWidgetResizable(True)
|
||||
scroll.setStyleSheet("QScrollArea { background: transparent; border: none; }")
|
||||
|
||||
self._grid_widget = QWidget()
|
||||
self._grid_layout = QGridLayout(self._grid_widget)
|
||||
self._grid_layout.setSpacing(8)
|
||||
self._grid_layout.setContentsMargins(0, 0, 0, 0)
|
||||
scroll.setWidget(self._grid_widget)
|
||||
root.addWidget(scroll, 1)
|
||||
|
||||
# ── Лог ──
|
||||
self._log = QLabel("")
|
||||
self._log.setObjectName("LogLabel")
|
||||
self._log.setWordWrap(True)
|
||||
root.addWidget(self._log)
|
||||
|
||||
# ── Закрыть ──
|
||||
btn_layout = QHBoxLayout()
|
||||
btn_layout.addStretch()
|
||||
close_btn = QPushButton("Закрыть")
|
||||
close_btn.setIcon(icon("fa5s.times", color=get_icon_muted()))
|
||||
close_btn.clicked.connect(self.close)
|
||||
btn_layout.addWidget(close_btn)
|
||||
root.addLayout(btn_layout)
|
||||
|
||||
# Создаем сетку ячеек при старте
|
||||
self._rebuild_grid()
|
||||
|
||||
def _rebuild_grid(self) -> None:
|
||||
"""Перестроить сетку ячеек на основе значения spinbox."""
|
||||
# Очищаем старые элементы из макета
|
||||
while self._grid_layout.count():
|
||||
item = self._grid_layout.takeAt(0)
|
||||
widget = item.widget()
|
||||
if widget:
|
||||
widget.deleteLater()
|
||||
|
||||
count = self._cells_count_spin.value()
|
||||
|
||||
# Заголовки столбцов
|
||||
headers = ["Ячейка", "Выдать приз (01)", "Открыть ячейку (11)"]
|
||||
for col, h in enumerate(headers):
|
||||
lbl = QLabel(f"<b>{h}</b>")
|
||||
lbl.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
self._grid_layout.addWidget(lbl, 0, col)
|
||||
|
||||
for i in range(count):
|
||||
row = i + 1
|
||||
cell_idx = i + 1
|
||||
|
||||
lbl_cell = QLabel(f"Ячейка {cell_idx}")
|
||||
lbl_cell.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
lbl_cell.setStyleSheet(
|
||||
"background: #1f2937; border: 1px solid #374151; "
|
||||
"border-radius: 4px; padding: 4px; color: #f3f4f6;"
|
||||
)
|
||||
self._grid_layout.addWidget(lbl_cell, row, 0)
|
||||
|
||||
# Выдать приз (2501B)
|
||||
btn_dispense = QPushButton("Выдать")
|
||||
btn_dispense.setProperty("role", "primary")
|
||||
btn_dispense.setIcon(icon("fa5s.gift", color="#ecfdf5"))
|
||||
btn_dispense.clicked.connect(lambda checked=False, idx=cell_idx: self._dispense_prize(idx))
|
||||
self._grid_layout.addWidget(btn_dispense, row, 1)
|
||||
|
||||
# Открыть ячейку (2511B)
|
||||
btn_open = QPushButton("Открыть")
|
||||
btn_open.setIcon(icon("fa5s.unlock", color=get_icon_color()))
|
||||
btn_open.clicked.connect(lambda checked=False, idx=cell_idx: self._open_cell(idx))
|
||||
self._grid_layout.addWidget(btn_open, row, 2)
|
||||
|
||||
# ── Команды витрины ──
|
||||
|
||||
def _query_state(self) -> None:
|
||||
self._send("25")
|
||||
|
||||
def _send_auto_on(self) -> None:
|
||||
self._send("25991")
|
||||
|
||||
def _send_auto_off(self) -> None:
|
||||
self._send("25990")
|
||||
|
||||
def _dispense_prize(self, cell: int) -> None:
|
||||
self._send(f"2501{cell}")
|
||||
|
||||
def _open_cell(self, cell: int) -> None:
|
||||
self._send(f"2511{cell}")
|
||||
|
||||
def _send(self, cmd: str) -> None:
|
||||
if not cmd:
|
||||
return
|
||||
if not self._conn.is_connected:
|
||||
QMessageBox.warning(self, "Ошибка", "Порт не подключён.")
|
||||
return
|
||||
try:
|
||||
self._conn.send_command(cmd)
|
||||
self._log.setText(f"→ {cmd}")
|
||||
resp = self._conn.read_response(timeout=0.4)
|
||||
self._conn.release()
|
||||
if resp:
|
||||
self._log.setText(f"→ {cmd}\n← {resp}")
|
||||
except Exception as exc:
|
||||
self._conn.release()
|
||||
self._log.setText(f"Ошибка: {exc}")
|
||||
@ -9,6 +9,7 @@ from __future__ import annotations
|
||||
from PySide6.QtCore import QTimer
|
||||
from PySide6.QtWidgets import (
|
||||
QCheckBox,
|
||||
QColorDialog,
|
||||
QFormLayout,
|
||||
QGroupBox,
|
||||
QHBoxLayout,
|
||||
@ -112,6 +113,12 @@ class LedTestSection(QWidget):
|
||||
gamma_layout.addWidget(apply_gamma_btn)
|
||||
color_form.addRow("Гамма-коррекция:", gamma_layout)
|
||||
|
||||
# Пользовательский цвет
|
||||
self._custom_color_btn = QPushButton("Выбрать и отправить цвет")
|
||||
self._custom_color_btn.setIcon(icon("fa5s.palette", color=get_icon_color()))
|
||||
self._custom_color_btn.clicked.connect(self._send_custom_color)
|
||||
color_form.addRow("Свой цвет (08):", self._custom_color_btn)
|
||||
|
||||
# Тест цвета
|
||||
self._color_test_btn = QPushButton("Тест цвета")
|
||||
self._color_test_btn.setProperty("role", "primary")
|
||||
@ -157,6 +164,16 @@ class LedTestSection(QWidget):
|
||||
cmd = f"89{ch}2{val}"
|
||||
self._send(cmd)
|
||||
|
||||
def _send_custom_color(self) -> None:
|
||||
color = QColorDialog.getColor(Qt.GlobalColor.white, self, "Выберите цвет")
|
||||
if color.isValid():
|
||||
ch = self._cmd89_channel_combo.currentData()
|
||||
r = color.red()
|
||||
g = color.green()
|
||||
b = color.blue()
|
||||
cmd = f"8{ch}08{r:03d}{g:03d}{b:03d}"
|
||||
self._send(cmd)
|
||||
|
||||
def _run_color_test(self) -> None:
|
||||
self._color_test_cmds = ["8003", "8004", "8005", "8002", "8000"]
|
||||
self._color_test_idx = 0
|
||||
|
||||
@ -19,6 +19,7 @@ from PySide6.QtWidgets import (
|
||||
QWidget,
|
||||
)
|
||||
|
||||
from ...services.com_service import ComConnection
|
||||
from ...ui.icons import get_icon_color, icon
|
||||
|
||||
|
||||
@ -27,10 +28,12 @@ class PaymentSection(QWidget):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
connection: ComConnection,
|
||||
send_raw: Callable[[str], None],
|
||||
parent: QWidget | None = None,
|
||||
) -> None:
|
||||
super().__init__(parent)
|
||||
self._conn = connection
|
||||
self._send_raw = send_raw
|
||||
self._auto_dispense_hopper: int = 0
|
||||
self._auto_dispense_timer: QTimer | None = None
|
||||
@ -56,16 +59,32 @@ class PaymentSection(QWidget):
|
||||
|
||||
# ── Монетоприёмник ──
|
||||
coin_group = QGroupBox("Монетоприёмник")
|
||||
coin_layout = QHBoxLayout(coin_group)
|
||||
coin_root_layout = QVBoxLayout(coin_group)
|
||||
|
||||
coin_row = QHBoxLayout()
|
||||
self._coin_label = QLabel("Монеты: —")
|
||||
self._coin_label.setMinimumWidth(120)
|
||||
coin_layout.addWidget(self._coin_label)
|
||||
coin_row.addWidget(self._coin_label)
|
||||
self._coin_confirm_btn = QPushButton("Подтвердить (10)")
|
||||
self._coin_confirm_btn.setIcon(icon("fa5s.check-circle", color=get_icon_color()))
|
||||
self._coin_confirm_btn.setToolTip("Отправить подтверждение COIN (команда 10)")
|
||||
self._coin_confirm_btn.clicked.connect(lambda: self._send_raw("10"))
|
||||
coin_layout.addWidget(self._coin_confirm_btn)
|
||||
coin_layout.addStretch()
|
||||
coin_row.addWidget(self._coin_confirm_btn)
|
||||
coin_row.addStretch()
|
||||
coin_root_layout.addLayout(coin_row)
|
||||
|
||||
global_row = QHBoxLayout()
|
||||
self._global_summa_label = QLabel("Накопительный счетчик (cmd 93): —")
|
||||
global_row.addWidget(self._global_summa_label)
|
||||
|
||||
self._global_summa_refresh_btn = QPushButton("Запросить")
|
||||
self._global_summa_refresh_btn.setIcon(icon("fa5s.sync-alt", color=get_icon_color()))
|
||||
self._global_summa_refresh_btn.setToolTip("Запросить global_summa")
|
||||
self._global_summa_refresh_btn.clicked.connect(self._refresh_global_summa)
|
||||
global_row.addWidget(self._global_summa_refresh_btn)
|
||||
global_row.addStretch()
|
||||
coin_root_layout.addLayout(global_row)
|
||||
|
||||
root.addWidget(coin_group)
|
||||
|
||||
# ── Хопперы ──
|
||||
@ -133,6 +152,7 @@ class PaymentSection(QWidget):
|
||||
def refresh_icons(self) -> None:
|
||||
clr = get_icon_color()
|
||||
self._coin_confirm_btn.setIcon(icon("fa5s.check-circle", color=clr))
|
||||
self._global_summa_refresh_btn.setIcon(icon("fa5s.sync-alt", color=clr))
|
||||
self._h1_on.setIcon(icon("fa5s.play", color=clr))
|
||||
self._h1_off.setIcon(icon("fa5s.stop", color=clr))
|
||||
self._h1_auto_btn.setIcon(icon("fa5s.cog", color=clr))
|
||||
@ -215,3 +235,20 @@ class PaymentSection(QWidget):
|
||||
f"Хоппер {hopper}",
|
||||
f"Ошибка выдачи: датчик хоппера {hopper} не сработал за 10 секунд.",
|
||||
)
|
||||
|
||||
def _refresh_global_summa(self) -> None:
|
||||
if not self._conn.is_connected:
|
||||
QMessageBox.warning(self, "Ошибка", "Порт не подключён.")
|
||||
return
|
||||
try:
|
||||
self._conn.send_command("93")
|
||||
resp = self._conn.read_response(timeout=0.4)
|
||||
self._conn.release()
|
||||
if resp:
|
||||
self._global_summa_label.setText(f"Накопительный счетчик (cmd 93): {resp}")
|
||||
self._event_log.setText(f"← {resp}")
|
||||
else:
|
||||
self._global_summa_label.setText("Накопительный счетчик (cmd 93): нет ответа")
|
||||
except Exception as exc:
|
||||
self._conn.release()
|
||||
QMessageBox.critical(self, "Ошибка", f"Не удалось прочитать счетчик: {exc}")
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user