Skip to content

ssh_tunnel_manager.gui.dialogs.tunnel_config

tunnel_config

SSH Tunnel Manager - Configuration Dialog

Classes

TunnelConfigDialog

Bases: QDialog

Dialog for creating/editing tunnel configurations.

Source code in src/ssh_tunnel_manager/gui/dialogs/tunnel_config.py
class TunnelConfigDialog(QDialog):
    """Dialog for creating/editing tunnel configurations."""

    def __init__(self, config: Optional[TunnelConfig] = None, parent=None):
        super().__init__(parent)
        self.config = config
        self.setWindowTitle("Tunnel Configuration")
        self.setFixedSize(600, 550)  # Increased size for RTSP section
        self.setup_ui()

        if config:
            self.load_config(config)

    def setup_ui(self):
        """Setup the dialog UI."""
        layout = QVBoxLayout(self)

        # Form layout
        form = QFormLayout()

        # Basic settings
        self.name_edit = QLineEdit()
        self.description_edit = QLineEdit()
        form.addRow("Name:", self.name_edit)
        form.addRow("Description:", self.description_edit)

        # SSH connection settings
        ssh_group = QGroupBox("SSH Connection")
        ssh_layout = QFormLayout(ssh_group)

        self.ssh_host_edit = QLineEdit()
        self.ssh_port_spin = QSpinBox()
        self.ssh_port_spin.setRange(1, 65535)
        self.ssh_port_spin.setValue(DEFAULT_SSH_PORT)
        self.ssh_user_edit = QLineEdit()
        self.ssh_key_edit = QLineEdit()

        ssh_layout.addRow("SSH Host:", self.ssh_host_edit)
        ssh_layout.addRow("SSH Port:", self.ssh_port_spin)
        ssh_layout.addRow("SSH User:", self.ssh_user_edit)
        ssh_layout.addRow("SSH Key Path (optional):", self.ssh_key_edit)

        # Tunnel settings
        tunnel_group = QGroupBox("Tunnel Configuration")
        tunnel_layout = QFormLayout(tunnel_group)

        self.tunnel_type_combo = QComboBox()
        self.tunnel_type_combo.addItems(['local', 'remote', 'dynamic'])
        self.tunnel_type_combo.currentTextChanged.connect(self.on_tunnel_type_changed)

        self.local_port_spin = QSpinBox()
        self.local_port_spin.setRange(1024, 65535)
        self.local_port_spin.setValue(DEFAULT_LOCAL_RTSP_PORT)

        self.remote_host_edit = QLineEdit()
        self.remote_host_edit.setText("localhost")

        self.remote_port_spin = QSpinBox()
        self.remote_port_spin.setRange(1, 65535)
        self.remote_port_spin.setValue(554)

        tunnel_layout.addRow("Tunnel Type:", self.tunnel_type_combo)
        tunnel_layout.addRow("Local Port:", self.local_port_spin)
        tunnel_layout.addRow("Remote Host:", self.remote_host_edit)
        tunnel_layout.addRow("Remote Port:", self.remote_port_spin)

        # RTSP Configuration
        rtsp_group = QGroupBox("RTSP Configuration (Optional)")
        rtsp_layout = QFormLayout(rtsp_group)

        self.rtsp_url_edit = QLineEdit()
        self.rtsp_url_edit.setPlaceholderText("rtsp://localhost:8554/live/0")

        rtsp_help = QLabel("Leave empty to use default: rtsp://localhost:[local_port]/live/0")
        rtsp_help.setWordWrap(True)
        rtsp_help.setStyleSheet("color: gray; font-size: 10px;")

        rtsp_layout.addRow("RTSP URL:", self.rtsp_url_edit)
        rtsp_layout.addRow("", rtsp_help)

        # Options
        self.auto_start_check = QCheckBox("Auto-start on application launch")

        # Layout assembly
        layout.addLayout(form)
        layout.addWidget(ssh_group)
        layout.addWidget(tunnel_group)
        layout.addWidget(rtsp_group)
        layout.addWidget(self.auto_start_check)

        # Buttons
        buttons = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
        buttons.accepted.connect(self.accept)
        buttons.rejected.connect(self.reject)
        layout.addWidget(buttons)

        # Set initial state
        self.on_tunnel_type_changed()

    def on_tunnel_type_changed(self):
        """Handle tunnel type changes - enable/disable relevant fields."""
        tunnel_type = self.tunnel_type_combo.currentText()

        if tunnel_type == 'dynamic':
            # Dynamic port forwarding (SOCKS proxy)
            self.remote_host_edit.setEnabled(False)
            self.remote_port_spin.setEnabled(False)
            self.local_port_spin.setEnabled(True)
        elif tunnel_type == 'local':
            # Local port forwarding
            self.remote_host_edit.setEnabled(True)
            self.remote_port_spin.setEnabled(True)
            self.local_port_spin.setEnabled(True)
        elif tunnel_type == 'remote':
            # Remote port forwarding
            self.remote_host_edit.setEnabled(True)
            self.remote_port_spin.setEnabled(True)
            self.local_port_spin.setEnabled(True)

    def load_config(self, config: TunnelConfig):
        """Load configuration into dialog fields."""
        self.name_edit.setText(config.name)
        self.description_edit.setText(config.description)
        self.ssh_host_edit.setText(config.ssh_host)
        self.ssh_port_spin.setValue(config.ssh_port)
        self.ssh_user_edit.setText(config.ssh_user)
        self.ssh_key_edit.setText(config.ssh_key_path or "")
        self.tunnel_type_combo.setCurrentText(config.tunnel_type)
        self.local_port_spin.setValue(config.local_port)
        self.remote_host_edit.setText(config.remote_host or "localhost")
        self.remote_port_spin.setValue(config.remote_port or 80)
        self.auto_start_check.setChecked(config.auto_start)

        # Load RTSP URL
        if hasattr(config, 'rtsp_url') and config.rtsp_url:
            self.rtsp_url_edit.setText(config.rtsp_url)

        # Update field states
        self.on_tunnel_type_changed()

    def get_config(self) -> TunnelConfig:
        """Get configuration from dialog fields."""
        return TunnelConfig(
            name=self.name_edit.text().strip(),
            description=self.description_edit.text().strip(),
            ssh_host=self.ssh_host_edit.text().strip(),
            ssh_port=self.ssh_port_spin.value(),
            ssh_user=self.ssh_user_edit.text().strip(),
            ssh_key_path=self.ssh_key_edit.text().strip() or "",
            tunnel_type=self.tunnel_type_combo.currentText(),
            local_port=self.local_port_spin.value(),
            remote_host=self.remote_host_edit.text().strip() if self.tunnel_type_combo.currentText() != 'dynamic' else "",
            remote_port=self.remote_port_spin.value() if self.tunnel_type_combo.currentText() != 'dynamic' else 0,
            auto_start=self.auto_start_check.isChecked(),
            rtsp_url=self.rtsp_url_edit.text().strip()
        )
Functions
get_config()

Get configuration from dialog fields.

Source code in src/ssh_tunnel_manager/gui/dialogs/tunnel_config.py
def get_config(self) -> TunnelConfig:
    """Get configuration from dialog fields."""
    return TunnelConfig(
        name=self.name_edit.text().strip(),
        description=self.description_edit.text().strip(),
        ssh_host=self.ssh_host_edit.text().strip(),
        ssh_port=self.ssh_port_spin.value(),
        ssh_user=self.ssh_user_edit.text().strip(),
        ssh_key_path=self.ssh_key_edit.text().strip() or "",
        tunnel_type=self.tunnel_type_combo.currentText(),
        local_port=self.local_port_spin.value(),
        remote_host=self.remote_host_edit.text().strip() if self.tunnel_type_combo.currentText() != 'dynamic' else "",
        remote_port=self.remote_port_spin.value() if self.tunnel_type_combo.currentText() != 'dynamic' else 0,
        auto_start=self.auto_start_check.isChecked(),
        rtsp_url=self.rtsp_url_edit.text().strip()
    )
load_config(config)

Load configuration into dialog fields.

Source code in src/ssh_tunnel_manager/gui/dialogs/tunnel_config.py
def load_config(self, config: TunnelConfig):
    """Load configuration into dialog fields."""
    self.name_edit.setText(config.name)
    self.description_edit.setText(config.description)
    self.ssh_host_edit.setText(config.ssh_host)
    self.ssh_port_spin.setValue(config.ssh_port)
    self.ssh_user_edit.setText(config.ssh_user)
    self.ssh_key_edit.setText(config.ssh_key_path or "")
    self.tunnel_type_combo.setCurrentText(config.tunnel_type)
    self.local_port_spin.setValue(config.local_port)
    self.remote_host_edit.setText(config.remote_host or "localhost")
    self.remote_port_spin.setValue(config.remote_port or 80)
    self.auto_start_check.setChecked(config.auto_start)

    # Load RTSP URL
    if hasattr(config, 'rtsp_url') and config.rtsp_url:
        self.rtsp_url_edit.setText(config.rtsp_url)

    # Update field states
    self.on_tunnel_type_changed()
on_tunnel_type_changed()

Handle tunnel type changes - enable/disable relevant fields.

Source code in src/ssh_tunnel_manager/gui/dialogs/tunnel_config.py
def on_tunnel_type_changed(self):
    """Handle tunnel type changes - enable/disable relevant fields."""
    tunnel_type = self.tunnel_type_combo.currentText()

    if tunnel_type == 'dynamic':
        # Dynamic port forwarding (SOCKS proxy)
        self.remote_host_edit.setEnabled(False)
        self.remote_port_spin.setEnabled(False)
        self.local_port_spin.setEnabled(True)
    elif tunnel_type == 'local':
        # Local port forwarding
        self.remote_host_edit.setEnabled(True)
        self.remote_port_spin.setEnabled(True)
        self.local_port_spin.setEnabled(True)
    elif tunnel_type == 'remote':
        # Remote port forwarding
        self.remote_host_edit.setEnabled(True)
        self.remote_port_spin.setEnabled(True)
        self.local_port_spin.setEnabled(True)
setup_ui()

Setup the dialog UI.

Source code in src/ssh_tunnel_manager/gui/dialogs/tunnel_config.py
def setup_ui(self):
    """Setup the dialog UI."""
    layout = QVBoxLayout(self)

    # Form layout
    form = QFormLayout()

    # Basic settings
    self.name_edit = QLineEdit()
    self.description_edit = QLineEdit()
    form.addRow("Name:", self.name_edit)
    form.addRow("Description:", self.description_edit)

    # SSH connection settings
    ssh_group = QGroupBox("SSH Connection")
    ssh_layout = QFormLayout(ssh_group)

    self.ssh_host_edit = QLineEdit()
    self.ssh_port_spin = QSpinBox()
    self.ssh_port_spin.setRange(1, 65535)
    self.ssh_port_spin.setValue(DEFAULT_SSH_PORT)
    self.ssh_user_edit = QLineEdit()
    self.ssh_key_edit = QLineEdit()

    ssh_layout.addRow("SSH Host:", self.ssh_host_edit)
    ssh_layout.addRow("SSH Port:", self.ssh_port_spin)
    ssh_layout.addRow("SSH User:", self.ssh_user_edit)
    ssh_layout.addRow("SSH Key Path (optional):", self.ssh_key_edit)

    # Tunnel settings
    tunnel_group = QGroupBox("Tunnel Configuration")
    tunnel_layout = QFormLayout(tunnel_group)

    self.tunnel_type_combo = QComboBox()
    self.tunnel_type_combo.addItems(['local', 'remote', 'dynamic'])
    self.tunnel_type_combo.currentTextChanged.connect(self.on_tunnel_type_changed)

    self.local_port_spin = QSpinBox()
    self.local_port_spin.setRange(1024, 65535)
    self.local_port_spin.setValue(DEFAULT_LOCAL_RTSP_PORT)

    self.remote_host_edit = QLineEdit()
    self.remote_host_edit.setText("localhost")

    self.remote_port_spin = QSpinBox()
    self.remote_port_spin.setRange(1, 65535)
    self.remote_port_spin.setValue(554)

    tunnel_layout.addRow("Tunnel Type:", self.tunnel_type_combo)
    tunnel_layout.addRow("Local Port:", self.local_port_spin)
    tunnel_layout.addRow("Remote Host:", self.remote_host_edit)
    tunnel_layout.addRow("Remote Port:", self.remote_port_spin)

    # RTSP Configuration
    rtsp_group = QGroupBox("RTSP Configuration (Optional)")
    rtsp_layout = QFormLayout(rtsp_group)

    self.rtsp_url_edit = QLineEdit()
    self.rtsp_url_edit.setPlaceholderText("rtsp://localhost:8554/live/0")

    rtsp_help = QLabel("Leave empty to use default: rtsp://localhost:[local_port]/live/0")
    rtsp_help.setWordWrap(True)
    rtsp_help.setStyleSheet("color: gray; font-size: 10px;")

    rtsp_layout.addRow("RTSP URL:", self.rtsp_url_edit)
    rtsp_layout.addRow("", rtsp_help)

    # Options
    self.auto_start_check = QCheckBox("Auto-start on application launch")

    # Layout assembly
    layout.addLayout(form)
    layout.addWidget(ssh_group)
    layout.addWidget(tunnel_group)
    layout.addWidget(rtsp_group)
    layout.addWidget(self.auto_start_check)

    # Buttons
    buttons = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
    buttons.accepted.connect(self.accept)
    buttons.rejected.connect(self.reject)
    layout.addWidget(buttons)

    # Set initial state
    self.on_tunnel_type_changed()