Source code for mafw.steering_gui.views.path_editor

#  Copyright 2026 European Union
#  Author: Bulgheroni Antonio (antonio.bulgheroni@ec.europa.eu)
#  SPDX-License-Identifier: EUPL-1.2
"""Reusable line editor with a role-aware filesystem browse button."""

from __future__ import annotations

from collections.abc import Callable
from pathlib import Path
from typing import Any

from PySide6.QtCore import QEvent, Qt, Signal
from PySide6.QtWidgets import QFileDialog, QHBoxLayout, QLineEdit, QToolButton, QWidget

from mafw.processor.path_types import PathRole


[docs] class PathEditor(QWidget): """Inline path editor whose browse action depends on a :class:`~mafw.processor.PathRole`.""" value_changed = Signal(str) editing_finished = Signal() focused = Signal() def __init__( self, parent: QWidget | None = None, role: PathRole = PathRole.INPUT_FILE, on_selected: Callable[[str], None] | None = None, on_browse_started: Callable[[], None] | None = None, on_browse_cancelled: Callable[[], None] | None = None, ) -> None: """Build the editor for the given *role*. :param parent: Optional parent widget. :param role: The browse behaviour to use; defaults to input-file selection. :param on_selected: Optional callback for values chosen in a browse dialog. """ super().__init__(parent) self._role = role self._on_selected = on_selected self._on_browse_started = on_browse_started self._on_browse_cancelled = on_browse_cancelled self._browsing = False self._line_edit = QLineEdit() self._line_edit.textChanged.connect(self.value_changed) self._line_edit.editingFinished.connect(self._finish_editing) self._line_edit.installEventFilter(self) self._browse_button = QToolButton() self._browse_button.setText('Browse') self._browse_button.setFocusPolicy(Qt.FocusPolicy.NoFocus) self._browse_button.clicked.connect(self._browse) layout = QHBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) layout.addWidget(self._line_edit) layout.addWidget(self._browse_button) self.setFocusProxy(self._line_edit)
[docs] def value(self) -> str: """Return the currently selected path string.""" return self._line_edit.text()
[docs] def set_value(self, value: Any) -> None: """Update the displayed path string.""" self._line_edit.setText('' if value is None else str(value))
[docs] def _browse(self) -> None: """Open the file dialog matching the configured path role.""" current = self._line_edit.text() role = self._role line_edit = self._line_edit on_selected = self._on_selected on_browse_started = self._on_browse_started on_browse_cancelled = self._on_browse_cancelled self._browsing = True try: if on_browse_started is not None: on_browse_started() if role is PathRole.DIRECTORY: selected = QFileDialog.getExistingDirectory(None, 'Select folder', current) elif role is PathRole.OUTPUT_FILE: selected, _ = QFileDialog.getSaveFileName(None, 'Select new file', current) else: selected, _ = QFileDialog.getOpenFileName(None, 'Select file', current) if selected: path = str(Path(selected)) if on_selected is not None: on_selected(path) else: line_edit.setText(path) elif on_browse_cancelled is not None: on_browse_cancelled() finally: self._browsing = False
[docs] def _finish_editing(self) -> None: """Avoid committing a blank row while Browse temporarily owns focus.""" if not self._browsing: self.editing_finished.emit()
[docs] def eventFilter(self, watched: Any, event: QEvent) -> bool: # noqa: N802 """Notify a containing table when the inline path field receives focus.""" if watched is self._line_edit and event.type() is QEvent.Type.FocusIn: self.focused.emit() return super().eventFilter(watched, event)