- обновлён простой режим: тест цвета и проверка подсветки кнопок объединены в раздел «Тест подсветки» - добавлена проверка подсветки кнопок через 8-й силовой выход с автоматическим коротким тестом - упрощены описания пользовательских проверок и убраны лишние технические подробности из простого режима - изменена раскладка теста цвета, увеличена длительность шага до 1 секунды и убраны описания из PRO-блока - добавлена защита выпадающих списков от случайного изменения колесом мыши - улучшены немодальные PRO-диалоги, настройки соединения и обработка событий COM-порта - обновлены визуальные детали приложения, пользовательские ресурсы, инструкции AGENTS и Release Notes 1.3.0
169 lines
6.5 KiB
Python
169 lines
6.5 KiB
Python
"""Диалог «Команды платы» — справочник команд из CSV-файлов."""
|
||
|
||
from __future__ import annotations
|
||
|
||
from PySide6.QtCore import Qt
|
||
from PySide6.QtWidgets import (
|
||
QFrame,
|
||
QHBoxLayout,
|
||
QLabel,
|
||
QLineEdit,
|
||
QScrollArea,
|
||
QVBoxLayout,
|
||
QWidget,
|
||
)
|
||
|
||
from ...services.commands_service import CommandSet, load_all_command_sets
|
||
from ...ui.icons import get_icon_color, icon
|
||
from ..no_wheel_combo_box import NoWheelComboBox
|
||
from .styled_dialog import StyledDialog
|
||
|
||
|
||
class _CommandCard(QFrame):
|
||
"""Карточка одной команды."""
|
||
|
||
def __init__(self, code: str, name: str, description: str,
|
||
parent: QWidget | None = None) -> None:
|
||
super().__init__(parent)
|
||
self.setObjectName("PanelCard")
|
||
self._code = code.lower()
|
||
self._name = name.lower()
|
||
self._desc = description.lower()
|
||
|
||
lay = QVBoxLayout(self)
|
||
lay.setContentsMargins(12, 10, 12, 10)
|
||
lay.setSpacing(4)
|
||
|
||
# Верхняя строка: код + имя
|
||
top = QHBoxLayout()
|
||
top.setSpacing(8)
|
||
|
||
code_label = QLabel(code)
|
||
code_label.setObjectName("HeroBadge")
|
||
code_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||
top.addWidget(code_label)
|
||
|
||
name_label = QLabel(name)
|
||
name_label.setObjectName("SectionTitle")
|
||
name_label.setWordWrap(True)
|
||
top.addWidget(name_label, 1)
|
||
lay.addLayout(top)
|
||
|
||
# Описание
|
||
desc_label = QLabel(description)
|
||
desc_label.setObjectName("HeroHint")
|
||
desc_label.setWordWrap(True)
|
||
lay.addWidget(desc_label)
|
||
|
||
def matches(self, query: str) -> bool:
|
||
q = query.lower()
|
||
return q in self._code or q in self._name or q in self._desc
|
||
|
||
|
||
class CommandsDialog(StyledDialog):
|
||
"""Справочник команд платы: поиск + прокручиваемый список карточек."""
|
||
|
||
def __init__(self, parent: QWidget | None = None) -> None:
|
||
super().__init__(parent)
|
||
self.setWindowTitle("Команды платы")
|
||
self.setMinimumWidth(420)
|
||
self.setMinimumHeight(460)
|
||
self.resize(520, 560)
|
||
|
||
lay = self.containerLayout()
|
||
lay.setSpacing(10)
|
||
|
||
# ── Строка фильтра ────────────────────────────────────────
|
||
filter_row = QHBoxLayout()
|
||
filter_row.setSpacing(8)
|
||
|
||
self._search = QLineEdit()
|
||
self._search.setPlaceholderText("Поиск по коду, названию или описанию…")
|
||
search_ico = icon("fa5s.search", color=get_icon_color())
|
||
if not search_ico.isNull():
|
||
self._search.addAction(search_ico, QLineEdit.ActionPosition.LeadingPosition)
|
||
self._search.textChanged.connect(self._apply_filter)
|
||
filter_row.addWidget(self._search, 1)
|
||
|
||
self._set_combo = NoWheelComboBox()
|
||
self._set_combo.setMinimumWidth(140)
|
||
self._set_combo.currentIndexChanged.connect(self._on_set_changed)
|
||
filter_row.addWidget(self._set_combo)
|
||
|
||
lay.addLayout(filter_row)
|
||
|
||
# ── Прокручиваемая область ────────────────────────────────
|
||
self._scroll = QScrollArea()
|
||
self._scroll.setWidgetResizable(True)
|
||
self._scroll.setFrameShape(QFrame.Shape.NoFrame)
|
||
self._scroll.setHorizontalScrollBarPolicy(
|
||
Qt.ScrollBarPolicy.ScrollBarAlwaysOff
|
||
)
|
||
|
||
self._cards_widget = QWidget()
|
||
self._cards_widget.setObjectName("CardsContainer")
|
||
self._cards_layout = QVBoxLayout(self._cards_widget)
|
||
self._cards_layout.setContentsMargins(2, 4, 6, 4)
|
||
self._cards_layout.setSpacing(6)
|
||
self._cards_layout.setAlignment(Qt.AlignmentFlag.AlignTop)
|
||
|
||
self._scroll.setWidget(self._cards_widget)
|
||
lay.addWidget(self._scroll, 1)
|
||
|
||
# ── Счётчик ───────────────────────────────────────────────
|
||
self._count_label = QLabel()
|
||
self._count_label.setObjectName("HeroHint")
|
||
self._count_label.setAlignment(Qt.AlignmentFlag.AlignRight)
|
||
lay.addWidget(self._count_label)
|
||
|
||
# ── Загрузка данных ───────────────────────────────────────
|
||
self._command_sets: list[CommandSet] = load_all_command_sets()
|
||
self._cards: list[_CommandCard] = []
|
||
|
||
if self._command_sets:
|
||
self._set_combo.blockSignals(True)
|
||
for cs in self._command_sets:
|
||
self._set_combo.addItem(cs.set_name)
|
||
self._set_combo.blockSignals(False)
|
||
self._build_cards(self._command_sets[0])
|
||
else:
|
||
self._set_combo.addItem("Нет файлов")
|
||
self._set_combo.setEnabled(False)
|
||
|
||
# ── Построение карточек ────────────────────────────────────────
|
||
|
||
def _clear_cards(self) -> None:
|
||
for card in self._cards:
|
||
self._cards_layout.removeWidget(card)
|
||
card.setParent(None) # type: ignore[call-overload]
|
||
card.deleteLater()
|
||
self._cards.clear()
|
||
|
||
def _build_cards(self, cs: CommandSet) -> None:
|
||
self._clear_cards()
|
||
for cmd in cs.commands:
|
||
card = _CommandCard(cmd.code, cmd.name, cmd.description)
|
||
self._cards_layout.addWidget(card)
|
||
self._cards.append(card)
|
||
self._apply_filter(self._search.text())
|
||
|
||
# ── Фильтрация ────────────────────────────────────────────────
|
||
|
||
def _apply_filter(self, text: str) -> None:
|
||
query = text.strip()
|
||
visible = 0
|
||
for card in self._cards:
|
||
show = card.matches(query) if query else True
|
||
card.setVisible(show)
|
||
if show:
|
||
visible += 1
|
||
total = len(self._cards)
|
||
if query:
|
||
self._count_label.setText(f"Показано {visible} из {total}")
|
||
else:
|
||
self._count_label.setText(f"Всего команд: {total}")
|
||
|
||
def _on_set_changed(self, index: int) -> None:
|
||
if 0 <= index < len(self._command_sets):
|
||
self._build_cards(self._command_sets[index])
|