plate-config-app/app/widgets/dialogs/command_terminal_dialog.py
Danil Iarosh 9fd10a3acd v1.3.0: simple mode backlight and UI polish
- обновлён простой режим: тест цвета и проверка подсветки кнопок объединены в раздел «Тест подсветки»

- добавлена проверка подсветки кнопок через 8-й силовой выход с автоматическим коротким тестом

- упрощены описания пользовательских проверок и убраны лишние технические подробности из простого режима

- изменена раскладка теста цвета, увеличена длительность шага до 1 секунды и убраны описания из PRO-блока

- добавлена защита выпадающих списков от случайного изменения колесом мыши

- улучшены немодальные PRO-диалоги, настройки соединения и обработка событий COM-порта

- обновлены визуальные детали приложения, пользовательские ресурсы, инструкции AGENTS и Release Notes 1.3.0
2026-05-26 12:59:21 +03:00

175 lines
6.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Диалог «Ввод команд» — мини-терминал для ручного общения с платой.
Позволяет отправлять произвольные команды и видеть ответы.
"""
from __future__ import annotations
from PySide6.QtCore import Qt
from PySide6.QtGui import QKeyEvent
from PySide6.QtWidgets import (
QHBoxLayout,
QLineEdit,
QPushButton,
QTextEdit,
QVBoxLayout,
)
from ...services.com_service import ComConnection
from ...ui.icons import get_icon_muted, icon
from .styled_dialog import StyledDialog
class CommandTerminalDialog(StyledDialog):
def __init__(self, connection: ComConnection, parent=None) -> None:
super().__init__(parent)
self._conn = connection
self.setWindowTitle("Ввод команд")
self.setMinimumSize(540, 400)
self._setup_ui()
self._conn.line_received.connect(
self._on_line_received, Qt.ConnectionType.QueuedConnection
)
# История команд (макс. 50)
self._history: list[str] = []
self._history_index: int = -1
self._history_draft: str = ""
self._MAX_HISTORY = 50
# Перехватываем клавиши ↑↓ в поле ввода
self._input.installEventFilter(self)
# Фокус на поле ввода при открытии
self._input.setFocus()
def _setup_ui(self) -> None:
root = self.containerLayout()
root.setSpacing(8)
# ── Лог ──
self._log = QTextEdit()
self._log.setReadOnly(True)
self._log.setStyleSheet(
"QTextEdit { background: #1e293b; color: #e2e8f0; font-family: 'Consolas', 'Courier New', monospace; font-size: 12px; border-radius: 8px; padding: 8px; }"
)
root.addWidget(self._log, 1)
# ── Ввод ──
input_layout = QHBoxLayout()
self._input = QLineEdit()
self._input.setPlaceholderText("Введите команду (напр. 99, 05, 91, 8003)…")
self._input.returnPressed.connect(self._send_command)
input_layout.addWidget(self._input, 1)
send_btn = QPushButton("Отправить")
send_btn.setProperty("role", "primary")
send_btn.setIcon(icon("fa5s.paper-plane", color="#ecfdf5"))
send_btn.clicked.connect(self._send_command)
input_layout.addWidget(send_btn)
clear_btn = QPushButton("Очистить")
clear_btn.setIcon(icon("fa5s.eraser", color=get_icon_muted()))
clear_btn.clicked.connect(self._log.clear)
input_layout.addWidget(clear_btn)
root.addLayout(input_layout)
# ── Быстрые команды ──
quick_layout = QHBoxLayout()
quick_commands = [
("Статус (99)", "99"),
("Версия (05)", "05"),
("MAC (06)", "06"),
("Входы (91)", "91"),
("Сброс (00)", "00"),
]
for label, cmd in quick_commands:
btn = QPushButton(label)
btn.setMinimumHeight(30)
btn.setStyleSheet("padding: 4px 10px;")
btn.clicked.connect(lambda checked=False, c=cmd: self._send(c))
quick_layout.addWidget(btn)
quick_layout.addStretch()
root.addLayout(quick_layout)
# ── Закрыть ──
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)
def _send_command(self) -> None:
text = self._input.text().strip()
if not text:
return
# Сохранить в историю
if not self._history or self._history[-1] != text:
self._history.append(text)
if len(self._history) > self._MAX_HISTORY:
self._history.pop(0)
self._history_index = -1
self._history_draft = ""
self._send(text)
self._input.clear()
def eventFilter(self, watched, event): # type: ignore[override]
if watched is self._input and isinstance(event, QKeyEvent):
if event.type() == QKeyEvent.Type.KeyPress:
key = event.key()
if key == Qt.Key.Key_Up:
self._history_navigate(-1)
return True
if key == Qt.Key.Key_Down:
self._history_navigate(1)
return True
return super().eventFilter(watched, event)
def _history_navigate(self, direction: int) -> None:
"""Перемещение по истории: direction=-1 (назад), +1 (вперёд)."""
if not self._history:
return
if self._history_index == -1:
# Сохраняем текущий ввод как черновик
self._history_draft = self._input.text()
new_index = self._history_index - direction
if new_index < -1:
new_index = -1
elif new_index >= len(self._history):
new_index = len(self._history) - 1
self._history_index = new_index
if new_index == -1:
self._input.setText(self._history_draft)
else:
self._input.setText(self._history[len(self._history) - 1 - new_index])
def _send(self, cmd: str) -> None:
if not self._conn.is_connected:
self._log.append('<span style="color:#f87171;">✗ Порт не подключён</span>')
return
try:
self._conn.send_async(cmd)
self._log.append(f'<span style="color:#4ade80;">→ {cmd}</span>')
except Exception as exc:
self._log.append(f'<span style="color:#f87171;">✗ {exc}</span>')
def _on_line_received(self, line: str) -> None:
stripped = line.strip()
if stripped:
self._log.append(f'<span style="color:#93c5fd;">← {stripped}</span>')
def closeEvent(self, event) -> None:
try:
self._conn.line_received.disconnect(self._on_line_received)
except (RuntimeError, TypeError):
pass
super().closeEvent(event)