/
Flipper
/
Marginal
Обзор
Документация
Войти
/
Flipper
/
Marginal
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
1
CI/CD
Аналитика
Безопасность
master
src/UI/CustomUI/LineDirSelector.py
260 строк
7 KB
Alex
Bug fixes.
12 янв 2026, 13:17
12 янв 2026, 13:17
e907759
Код
Авторство
О чём код?
import os from src.utils import openFolder from PySide6.QtWidgets import QFileDialog, QLineEdit, QMenu from PySide6.QtCore import Qt class FolderLineEdit(QLineEdit): """ Custom QLineEdit for folder path selection with drag-and-drop support. Validates paths, provides visual feedback, and allows folder browsing. """ def __init__(self, parent=None): """ Initialize FolderLineEdit with default properties. Args: parent (QWidget, optional): Parent widget. """ super().__init__(parent) self.setPlaceholderText("Drag and drop a folder here or type path manually") self.setAcceptDrops(True) # Enable drag-and-drop self.defaultDir = str() # Default directory for browsing self.validPath = True # Whether current text is a valid folder path self.translateFunc = None # Translation function for internationalization # Initialize last valid path from current text or default directory if os.path.exists(os.path.expanduser(self.text())): self.lastValidPath = os.path.expanduser(self.text()) else: self.lastValidPath = os.path.expanduser(self.defaultDir) def dragEnterEvent(self, event): """ Handle drag enter event - accept only folder paths. Args: event: Drag enter event. Returns: None """ if event.mimeData().hasUrls(): urls = event.mimeData().urls() if urls and self.testPath(urls[0].toLocalFile()): event.acceptProposedAction() # Accept if it's a folder else: event.ignore() # Ignore non-folder items else: event.ignore() # Ignore non-URL data def dropEvent(self, event): """ Handle drop event - set text to dropped folder path. Args: event: Drop event. Returns: None """ urls = event.mimeData().urls() if urls: folderPath = urls[0].toLocalFile() if self.testPath(folderPath): self.setAccepted(folderPath) else: self.setDeclined() def keyPressEvent(self, event): """ Handle key press events for Enter/Return key validation. Args: event: Key press event. Returns: None """ if event.key() == Qt.Key_Return or event.key() == Qt.Key_Enter: folderPath = self.text().strip() if not self.testPath(folderPath): self.setDeclined() else: self.setAccepted(folderPath) super().keyPressEvent(event) else: super().keyPressEvent(event) def focusOutEvent(self, event): """ Handle focus out event - validate path when focus is lost. Args: event: Focus out event. Returns: None """ self.clearFocus() folderPath = self.text().strip() if self.testPath(folderPath): self.setAccepted(folderPath) else: self.setDeclined() def onTextChanged(self): """ Slot for text changed signal - validate path on text change. Returns: None """ if self.testPath(): self.setAccepted(self.text().strip()) else: self.setDeclined() def mouseDoubleClickEvent(self, event): """ Handle double-click event - open folder selection dialog. Args: event: Mouse double-click event. Returns: None """ if self.validPath: # Use current text as starting directory folderPath = QFileDialog.getExistingDirectory( self, "Select Folder", self.text() ) else: # Use default directory if available, else system default if isinstance(self.defaultDir, str) and os.path.exists( os.path.expanduser(self.defaultDir) ): defaultDir = os.path.expanduser(self.defaultDir) folderPath = QFileDialog.getExistingDirectory( self, "Select Folder", defaultDir ) else: folderPath = QFileDialog.getExistingDirectory(self, "Select Folder") if folderPath: self.setAccepted(folderPath) def contextMenuEvent(self, event): """ Handle right-click context menu for folder operations. Args: event: Context menu event. Returns: None """ menu = QMenu(self) # Add translated or default "Open folder" action openFolderAction = ( menu.addAction(self.translateFunc("Open folder")) if self.translateFunc else menu.addAction("Open folder") ) # Disable open action if no valid path is available if not self.validPath and not os.path.exists(self.defaultDir): openFolderAction.setDisabled(True) selectedAction = menu.exec(event.globalPos()) if selectedAction == openFolderAction: if self.validPath: openFolder(self.text().strip()) elif os.path.exists(self.defaultDir): openFolder(self.defaultDir) def testPath(self, folderPath: str = None) -> bool: """ Test if a path exists and is a directory. Args: folderPath (str, optional): Path to test. If None, uses current text. Returns: bool -> True if path is a valid directory, False otherwise. """ if folderPath is None: folderPath = self.text().strip() return os.path.isdir(folderPath) def setAccepted(self, folderPath: str): """ Set folder path as accepted and update UI state. Args: folderPath (str): Valid folder path. Returns: None """ self.setText(folderPath) self.setProperty("invalid", False) self.refreshStyle() self.validPath = True self.lastValidPath = folderPath # Update last valid path def setDeclined(self): """ Set current input as invalid and update UI state. Returns: None """ self.setProperty("invalid", True) self.refreshStyle() self.validPath = False def clear(self): """ Clear the input field and reset validation state. Returns: None """ self.setProperty("invalid", False) self.refreshStyle() self.setText("") self.validPath = False def refreshStyle(self): """ Refresh widget style to apply property changes (invalid state). Returns: None """ self.style().unpolish(self) self.style().polish(self) def getFolder(self) -> str | None: """ Get the current valid folder path, with fallback to last valid path. Returns: str | None -> Valid folder path, or None if no valid path exists. """ if self.validPath: return self.text() elif self.lastValidPath is not None: return self.lastValidPath else: return None def setTranslatorFunc(self, transleFunc): self.translateFunc = transleFunc if self.translateFunc: self.setPlaceholderText(self.translateFunc("Drag and drop a folder here or type path manually"))