You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

290 lines
11 KiB

# components/uart/uart_ui.py
# One-file UART page: dedicated handler + UI (no base_handler, no protocol_ui).
from PyQt6.QtWidgets import (
QWidget,
QVBoxLayout,
QHBoxLayout,
QPushButton,
QComboBox,
QLineEdit,
QSplitter,
)
from PyQt6.QtCore import Qt
# project-local deps (unchanged)
from components.console.console_ui import console_widget
from components.console.console_registry import log_main_console
import components.items.elements as elements
from components.commands.command_table_ui import command_table_widget
import config.config as config
# UART logic + dialogs + db (unchanged)
from components.uart.uart_logic import UART_logic
from components.uart.uart_command_editor import UartCommandEditorDialog
from components.data import db
# -----------------------------
# Internal, dedicated UART handler
# -----------------------------
# -----------------------------
# Thin handler that calls back into the UI widget
# -----------------------------
class UartHandler:
"""
Used by command_table_widget. It doesn't own UART logic.
It just forwards actions to the parent UartWidget.
"""
def __init__(self, widget: "UartWidget"):
self.w = widget # back-reference to the UI
# table may request the list via the handler
def get_command_list(self):
return self.w.uart_logic.get_predefined_commands()
# ----- actions triggered from the command table -----
def send_command(self, command: dict):
# command has "hex_string"
self.w.send_command(command)
# command editor ops (invoked from command table)
def add_command(self):
dialog = UartCommandEditorDialog()
if dialog.exec():
command = dialog.get_data()
db.add_uart_command(command)
def modify_command(self, command):
dialog = UartCommandEditorDialog(command=command)
if dialog.exec():
updated_command = dialog.get_data()
updated_command["id"] = command["id"]
db.modify_uart_command(updated_command)
def delete_command(self, command):
db.delete_uart_command(command)
# -----------------------------
# UART UI (layout preserved)
# -----------------------------
class UartWidget(QWidget):
"""
Drops in where your old ProtocolUIWidget(UART) was used.
Same controls, same enable/disable behavior, same splitter/table/console layout.
"""
def __init__(self, parent=None):
super().__init__(parent)
self.uart_logic = UART_logic() # all comms live here
self.handler = UartHandler(self) # thin handler for table actions
self.commands = self.uart_logic.get_predefined_commands()
self.comboboxes = {}
self.connection_status = False
self.init_ui()
def init_ui(self):
# === Top Control Row ===
top_controls = QWidget()
top_controls_layout = QHBoxLayout(top_controls)
top_controls_layout.setContentsMargins(0, 0, 0, 0)
top_controls_layout.setSpacing(12)
top_controls_layout.setAlignment(Qt.AlignmentFlag.AlignTop)
# Dynamically create comboboxes based on handler configuration
# Static definition instead of get_combobox_config()
self.comboboxes["port"] = QComboBox()
self.comboboxes["port"].addItems(self.uart_logic.get_channels())
top_controls_layout.addWidget(
elements.label_and_widget("Port", self.comboboxes["port"])
)
# Refresh button
self.button_refresh = elements.create_icon_button(
config.REFRESH_BUTTON_ICON_LINK, icon_size=30, border_size=4
)
top_controls_layout.addWidget(self.button_refresh)
self.comboboxes["baudrate"] = QComboBox()
self.comboboxes["baudrate"].addItems(self.uart_logic.get_baud_rates())
top_controls_layout.addWidget(
elements.label_and_widget("Baudrate", self.comboboxes["baudrate"])
)
self.comboboxes["data_bits"] = QComboBox()
self.comboboxes["data_bits"].addItems(self.uart_logic.get_data_bits())
top_controls_layout.addWidget(
elements.label_and_widget("Data Bits", self.comboboxes["data_bits"])
)
self.comboboxes["stop_bits"] = QComboBox()
self.comboboxes["stop_bits"].addItems(self.uart_logic.get_stop_bits())
top_controls_layout.addWidget(
elements.label_and_widget("Stop Bits", self.comboboxes["stop_bits"])
)
self.comboboxes["parity"] = QComboBox()
self.comboboxes["parity"].addItems(self.uart_logic.get_parity())
top_controls_layout.addWidget(
elements.label_and_widget("Parity", self.comboboxes["parity"])
)
# Connect / Disconnect
self.button_connect = QPushButton("Connect")
top_controls_layout.addWidget(
elements.label_and_widget("", self.button_connect)
)
self.button_disconnect = QPushButton("Disconnect")
top_controls_layout.addWidget(
elements.label_and_widget("", self.button_disconnect)
)
# === Command Table ===
self.command_table = command_table_widget(
commands=self.commands,
handler=self.handler, # table uses handler methods for send/CRUD
)
col1_widget = QWidget()
col1_layout = QVBoxLayout(col1_widget)
col1_layout.setContentsMargins(0, 0, 0, 0)
col1_layout.setSpacing(4)
col1_layout.addWidget(self.command_table)
# === Input Field + Send Button ===
self.input_hex = QLineEdit()
self.button_send_raw = QPushButton("Send Raw")
input_line_layout = QHBoxLayout()
input_line_layout.setContentsMargins(0, 0, 0, 0)
input_line_layout.setSpacing(4)
input_line_layout.addWidget(self.input_hex)
input_line_layout.addWidget(self.button_send_raw)
# === Console ===
self.console = console_widget()
self.uart_logic.set_logger(self.console.log)
console_stack_widget = QWidget()
console_stack_layout = QVBoxLayout(console_stack_widget)
console_stack_layout.setContentsMargins(0, 0, 0, 0)
console_stack_layout.setSpacing(4)
console_stack_layout.addLayout(input_line_layout)
console_stack_layout.addWidget(self.console)
# === Horizontal Splitter: Table | Console (sizes preserved) ===
splitter = QSplitter(Qt.Orientation.Horizontal)
splitter.addWidget(col1_widget)
splitter.addWidget(console_stack_widget)
splitter.setSizes([740, 1200])
splitter.setStretchFactor(0, 0)
splitter.setStretchFactor(1, 1)
# === Main Layout ===
main_layout = QVBoxLayout()
main_layout.setContentsMargins(4, 4, 4, 4)
main_layout.setSpacing(6)
main_layout.addWidget(top_controls)
main_layout.addWidget(splitter, stretch=1)
self.setLayout(main_layout)
# === Signals ===
self.button_refresh.clicked.connect(self.refresh)
self.button_connect.clicked.connect(self.connect)
self.button_disconnect.clicked.connect(self.disconnect)
self.button_send_raw.clicked.connect(self.send_command_raw)
self.disconnected_enable_status()
# ---- UI state toggles (unchanged) ----
def disconnected_enable_status(self):
for combo in self.comboboxes.values():
elements.set_enabled_state(True, combo, grayOut=False)
elements.set_enabled_state(True, self.command_table, grayOut=False)
elements.set_enabled_state(False, self.input_hex, grayOut=True)
elements.set_enabled_state(False, self.button_send_raw, grayOut=True)
elements.set_enabled_state(False, self.button_disconnect, grayOut=True)
elements.set_enabled_state(True, self.button_connect, grayOut=False)
def connected_enable_status(self):
for combo in self.comboboxes.values():
elements.set_enabled_state(False, combo, grayOut=True)
elements.set_enabled_state(True, self.command_table, grayOut=False)
elements.set_enabled_state(True, self.input_hex, grayOut=False)
elements.set_enabled_state(True, self.button_send_raw, grayOut=False)
elements.set_enabled_state(True, self.button_disconnect, grayOut=False)
elements.set_enabled_state(False, self.button_connect, grayOut=True)
# ---- Button handlers (unchanged behavior) ----
def connect(self):
log_main_console("info", "🔗 Connecting...")
port = self.comboboxes["port"].currentText()
baudrate = int(self.comboboxes["baudrate"].currentText())
data_bits = int(self.comboboxes["data_bits"].currentText())
stop_bits = float(self.comboboxes["stop_bits"].currentText())
parity = self.comboboxes["parity"].currentText()[0].upper()
success = self.uart_logic.connect(
port=port,
baudrate=baudrate,
data_bits=data_bits,
stop_bits=stop_bits,
parity=parity,
)
if success:
self.connected_enable_status()
self.command_table.set_connected_state()
self.connection_status = True
else:
elements.flash_button(
self.button_connect, flash_style="background-color: red;"
)
def disconnect(self):
log_main_console("info", "🔌 Disconnecting...")
success = self.uart_logic.disconnect()
if success:
log_main_console("info", "🔌 Disconnecting...")
self.disconnected_enable_status()
self.command_table.set_disconnected_state()
self.connection_status = False
self.refresh(silent=True)
else:
elements.flash_button(
self.button_disconnect, flash_style="background-color: red;"
)
def refresh(self, silent: bool = False):
log_main_console("info", "🔄 Refreshing...")
self.comboboxes["port"].clear()
port = self.uart_logic.get_channels()
if port:
self.comboboxes["port"].addItems(port)
if not silent:
elements.flash_button(self.button_refresh)
log_main_console("success", "🔄 Refresehd")
else:
elements.flash_button(
self.button_refresh, flash_style="background-color: red;"
)
log_main_console("error", "🔄 Refresehd")
def get_current_config(self):
return {key: cb.currentText() for key, cb in self.comboboxes.items()}
def send_command(self, command):
# self.uart_logic.send_command(command["hex_string"])
self.uart_logic.send_pgkomm2(command["hex_string"])
config.CURRENT_COMMAND = command["name"]
def send_command_raw(self):
hex_str = self.input_hex.text().strip()
if not hex_str:
self.console.log("warning", "⚠️ No command entered.")
else:
self.uart_logic.send_command(hex_str)
config.CURRENT_COMMAND = hex_str