# --------------------------------------------------------------------------- # Verbindungs-Panel # --------------------------------------------------------------------------- import serial from serial.tools.list_ports import comports from PyQt5.QtCore import pyqtSignal from PyQt5.QtWidgets import QGroupBox, QVBoxLayout, QTabWidget, QWidget, QFormLayout, QComboBox, QPushButton, \ QHBoxLayout, QLineEdit, QSpinBox from lru_store import LruStore from transport_network import TcpTransport from transport_serial import SerialTransport # --------------------------------------------------------------------------- # Stil-Konstante # --------------------------------------------------------------------------- FIELD_STYLE = """ QLineEdit, QComboBox, QSpinBox { background-color: #0d1b2a; color: #cccccc; border: 1px solid #2a4a6a; padding: 3px 6px; border-radius: 3px; } QComboBox QAbstractItemView { background-color: #0d1b2a; color: #cccccc; selection-background-color: #1a3a5c; } """ class ConnectionPanel(QGroupBox): connect_requested = pyqtSignal(object, str) disconnect_requested = pyqtSignal() def __init__(self, parent=None): super().__init__("Verbindung", parent) self.setStyleSheet(""" QGroupBox { color: #7ab7d4; border: 1px solid #2a4a6a; border-radius: 4px; margin-top: 8px; padding: 6px; } QGroupBox::title { subcontrol-origin: margin; left: 8px; } """ + FIELD_STYLE) self._lru = LruStore(max_size=5) self._build_ui() def _build_ui(self): root = QVBoxLayout(self) self._tabs = QTabWidget() self._tabs.setStyleSheet(""" QTabBar::tab { background: #1a3a5c; color: #cccccc; padding: 4px 12px; border-radius: 3px; } QTabBar::tab:selected { background: #2a5a8c; color: #ffffff; } """) # ── Tab: Seriell ───────────────────────────────────────────────────── serial_widget = QWidget() serial_form = QFormLayout(serial_widget) serial_form.setContentsMargins(6, 6, 6, 6) self._port_combo = QComboBox() self._port_combo.setMinimumWidth(200) refresh_btn = QPushButton("⟳") refresh_btn.setFixedWidth(28) refresh_btn.clicked.connect(self._refresh_ports) port_row = QHBoxLayout() port_row.addWidget(self._port_combo) port_row.addWidget(refresh_btn) serial_form.addRow("Port:", port_row) self._baud_combo = QComboBox() for b in ("9600", "19200", "38400", "57600", "115200", "230400", "460800"): self._baud_combo.addItem(b) self._baud_combo.setCurrentText("9600") serial_form.addRow("Baudrate:", self._baud_combo) self._tabs.addTab(serial_widget, "🔌 Seriell") # ── Tab: TCP/IP ─────────────────────────────────────────────────────── tcp_widget = QWidget() tcp_layout = QVBoxLayout(tcp_widget) tcp_layout.setContentsMargins(6, 6, 6, 6) tcp_layout.setSpacing(6) # LRU-Gruppe lru_group = QGroupBox("Letzte Verbindungen") lru_group.setStyleSheet(""" QGroupBox { color: #7ab7d4; border: 1px solid #2a4a6a; border-radius: 3px; margin-top: 6px; padding: 4px; } QGroupBox::title { subcontrol-origin: margin; left: 6px; } """ + FIELD_STYLE) lru_layout = QHBoxLayout(lru_group) lru_layout.setContentsMargins(4, 10, 4, 4) self._lru_combo = QComboBox() self._lru_combo.setMinimumWidth(220) self._lru_combo.setPlaceholderText("– Frühere Verbindung wählen –") self._lru_combo.currentIndexChanged.connect(self._on_lru_selected) lru_layout.addWidget(self._lru_combo, stretch=1) clear_btn = QPushButton("🗑") clear_btn.setFixedWidth(28) clear_btn.setToolTip("Liste leeren") clear_btn.clicked.connect(self._clear_lru) lru_layout.addWidget(clear_btn) tcp_layout.addWidget(lru_group) # Manuelle Eingabe tcp_form = QFormLayout() tcp_form.setSpacing(6) self._tcp_host = QLineEdit() self._tcp_host.setPlaceholderText("IP-Adresse oder Hostname") tcp_form.addRow("Host:", self._tcp_host) self._tcp_port = QSpinBox() self._tcp_port.setRange(1, 65535) self._tcp_port.setValue(4001) tcp_form.addRow("Port:", self._tcp_port) self._tcp_timeout = QSpinBox() self._tcp_timeout.setRange(1, 30) self._tcp_timeout.setValue(5) self._tcp_timeout.setSuffix(" s") tcp_form.addRow("Timeout:", self._tcp_timeout) tcp_layout.addLayout(tcp_form) tcp_layout.addStretch() self._tabs.addTab(tcp_widget, "🌐 TCP/IP") root.addWidget(self._tabs) # Verbinden-Knopf btn_row = QHBoxLayout() self._connect_btn = QPushButton("▶ Verbinden") self._connect_btn.clicked.connect(self._on_connect_clicked) btn_row.addStretch() btn_row.addWidget(self._connect_btn) root.addLayout(btn_row) self._refresh_ports() self._refresh_lru_combo() # ── LRU ────────────────────────────────────────────────────────────────── def _refresh_lru_combo(self): self._lru_combo.blockSignals(True) self._lru_combo.clear() for entry in self._lru.entries(): self._lru_combo.addItem(entry) self._lru_combo.setCurrentIndex(-1) self._lru_combo.blockSignals(False) def _on_lru_selected(self, index: int): if index < 0: return entry = self._lru_combo.currentText() if not entry: return try: host, port = self._lru.parse(entry) self._tcp_host.setText(host) self._tcp_port.setValue(port) except (ValueError, IndexError): pass def _clear_lru(self): self._lru.clear() self._refresh_lru_combo() # ── Seriell ─────────────────────────────────────────────────────────────── def _refresh_ports(self): self._port_combo.clear() for p in comports(): self._port_combo.addItem(f"{p.device} – {p.description}", p.device) if self._port_combo.count() == 0: self._port_combo.addItem("Kein Port gefunden", None) # ── Verbinden ───────────────────────────────────────────────────────────── def _on_connect_clicked(self): if self._connect_btn.text().startswith("■"): self.disconnect_requested.emit() return if self._tabs.currentIndex() == 0: port = self._port_combo.currentData() baud = int(self._baud_combo.currentText()) if not port: return factory = lambda p=port, b=baud: SerialTransport(p, b) label = f"{port} @ {baud} Baud" else: host = self._tcp_host.text().strip() port = self._tcp_port.value() timeout = self._tcp_timeout.value() if not host: return self._lru.add(host, port) self._refresh_lru_combo() factory = lambda h=host, p=port, t=timeout: TcpTransport(h, p, t) label = f"{host}:{port}" self.connect_requested.emit(factory, label) def set_connected(self, connected: bool): self._connect_btn.setText("■ Trennen" if connected else "▶ Verbinden") self._tabs.setEnabled(not connected)