1
0
mirror of https://github.com/bec-project/bec_widgets.git synced 2026-03-04 16:02:51 +01:00

wip - feat(file browser): add file browser widget

This commit is contained in:
2025-07-24 17:22:53 +02:00
parent 45fbf4015d
commit 161c1570bd
5 changed files with 379 additions and 0 deletions

View File

@@ -37,6 +37,7 @@ _Widgets = {
"DeviceBrowser": "DeviceBrowser",
"DeviceComboBox": "DeviceComboBox",
"DeviceLineEdit": "DeviceLineEdit",
"FileBrowser": "FileBrowser",
"Heatmap": "Heatmap",
"Image": "Image",
"LogPanel": "LogPanel",
@@ -1183,6 +1184,16 @@ class EllipticalROI(RPCBase):
"""
class FileBrowser(RPCBase):
"""A simple file browser widget."""
@rpc_call
def remove(self):
"""
Cleanup the BECConnector
"""
class Heatmap(RPCBase):
"""Heatmap widget for visualizing 2d grid data with color mapping for the z-axis."""

View File

@@ -0,0 +1,298 @@
from PySide6.QtWidgets import QVBoxLayout
from qtpy.QtCore import QDir
from qtpy.QtWidgets import QApplication, QFileSystemModel, QHeaderView, QTreeView, QWidget
from bec_widgets.utils.bec_widget import BECWidget
from bec_widgets.utils.error_popups import SafeProperty
from bec_widgets.utils.toolbars.actions import MaterialIconAction
from bec_widgets.utils.toolbars.bundles import ToolbarBundle
from bec_widgets.utils.toolbars.toolbar import ModularToolBar
class FileBrowser(BECWidget, QWidget):
"""
A simple file browser widget.
"""
PLUGIN = True
ICON_NAME = "folder_open"
def __init__(self, parent=None, config=None, client=None, gui_id=None, **kwargs):
super().__init__(parent=parent, client=client, gui_id=gui_id, config=config, **kwargs)
layout = QVBoxLayout(self)
layout.setContentsMargins(0, 0, 0, 0)
self.tree = QTreeView(self)
self.model = QFileSystemModel()
self.tree.setModel(self.model)
self.tree.setRootIndex(self.model.index(QDir.rootPath()))
self.model.setRootPath(QDir.rootPath())
self._allow_changing_root = True
self._original_root_path = QDir.rootPath()
self.toolbar = ModularToolBar(parent=self, orientation="horizontal")
self._go_back_action = MaterialIconAction("arrow_back", "Go Back", parent=self)
self.toolbar.components.add_safe("go_back", self._go_back_action)
self.toolbar.components.add_safe(
"refresh", MaterialIconAction("refresh", "Refresh", parent=self)
)
self.toolbar.components.add_safe(
"open", MaterialIconAction("folder_open", "Open File", parent=self)
)
bundle = ToolbarBundle("file_io", self.toolbar.components)
bundle.add_action("go_back")
bundle.add_action("refresh")
bundle.add_action("open")
self.toolbar.add_bundle(bundle)
self.toolbar.show_bundles(["file_io"])
layout.addWidget(self.toolbar)
layout.addWidget(self.tree)
self.setLayout(layout)
self._show_hidden_files = False
self._go_back_action.action.setEnabled(False)
self._go_back_action.action.triggered.connect(self._on_go_back)
self.tree.setSelectionMode(QTreeView.SelectionMode.SingleSelection)
self.tree.doubleClicked.connect(self._on_double_click)
def _on_double_click(self, index):
"""
Handle double-click events on the file browser.
Opens the selected file or directory.
"""
if not index.isValid():
return
path = self.model.filePath(index)
if self.model.isDir(index) and self._allow_changing_root:
self.tree.setRootIndex(index)
if path != self._original_root_path:
self._go_back_action.action.setEnabled(True)
else:
self._go_back_action.action.setEnabled(False)
return
print(f"Opening file: {path}")
def _on_go_back(self):
"""
Handle the go back action.
Navigates to the previous directory in the file browser.
"""
if self._allow_changing_root and self.tree.rootIndex().isValid():
parent_index = self.tree.rootIndex().parent()
if parent_index.isValid():
self.tree.setRootIndex(parent_index)
if parent_index != self.model.index(self._original_root_path):
self._go_back_action.action.setEnabled(True)
else:
self._go_back_action.action.setEnabled(False)
@SafeProperty(bool)
def show_toolbar(self):
"""
Get whether the toolbar is shown in the file browser.
"""
return not self.toolbar.isHidden()
@show_toolbar.setter
def show_toolbar(self, show: bool):
"""
Set whether the toolbar is shown in the file browser.
"""
self.toolbar.setVisible(show)
@SafeProperty(bool)
def show_hidden_files(self):
"""
Get whether hidden files are shown in the file browser.
"""
return self._show_hidden_files
@show_hidden_files.setter
def show_hidden_files(self, show: bool):
"""
Set whether hidden files are shown in the file browser.
"""
self._show_hidden_files = show
if show:
self.model.setFilter(
QDir.Filter.AllDirs
| QDir.Filter.Files
| QDir.Filter.NoDotAndDotDot
| QDir.Filter.Hidden
)
else:
self.model.setFilter(
QDir.Filter.AllDirs | QDir.Filter.Files | QDir.Filter.NoDotAndDotDot
)
self.tree.setRootIndex(self.model.index(self.model.rootPath()))
@SafeProperty(bool)
def allow_changing_root(self):
"""
Get whether changing the root path is allowed.
"""
return self._allow_changing_root
@allow_changing_root.setter
def allow_changing_root(self, allow: bool):
"""
Set whether changing the root path is allowed.
"""
self._allow_changing_root = allow
@SafeProperty(bool)
def show_file_size(self):
"""
Get whether the file size is shown in the file browser.
"""
index = self._section_index("Size")
return not self.tree.header().isSectionHidden(index)
@show_file_size.setter
def show_file_size(self, show: bool):
"""
Set whether the file size is shown in the file browser.
"""
index = self._section_index("Size")
self.tree.header().setSectionHidden(index, not show)
self.tree.header().repaint()
@SafeProperty(bool)
def show_file_kind(self):
"""
Get whether the file kind is shown in the file browser.
"""
index = self._section_index("Kind")
return not self.tree.header().isSectionHidden(index)
@show_file_kind.setter
def show_file_kind(self, show: bool):
"""
Set whether the file kind is shown in the file browser.
"""
index = self._section_index("Kind")
self.tree.header().setSectionHidden(index, not show)
self.tree.setRootIndex(self.model.index(self.model.rootPath()))
@SafeProperty(bool)
def show_file_timestamp(self):
"""
Get whether the file timestamp is shown in the file browser.
"""
index = self._section_index("Date Modified")
return not self.tree.header().isSectionHidden(index)
@show_file_timestamp.setter
def show_file_timestamp(self, show: bool):
"""
Set whether the file timestamp is shown in the file browser.
"""
index = self._section_index("Date Modified")
self.tree.header().setSectionHidden(index, not show)
self.tree.header().setSectionResizeMode(0, QHeaderView.ResizeMode.Stretch)
self.tree.setRootIndex(self.model.index(self.model.rootPath()))
@SafeProperty(str)
def root_path(self):
"""
Get the root path of the file browser.
"""
return self.model.rootPath()
@root_path.setter
def root_path(self, path: str):
"""
Set the root path of the file browser.
"""
self.model.setRootPath(path)
self.tree.setRootIndex(self.model.index(path))
self._original_root_path = path
@SafeProperty(bool)
def show_header(self):
"""
Get whether the header is shown in the file browser.
"""
return not self.tree.header().isHidden()
@show_header.setter
def show_header(self, show: bool):
"""
Set whether the header is shown in the file browser.
"""
self.tree.setHeaderHidden(not show)
self.tree.setRootIndex(self.model.index(self.model.rootPath()))
def _section_index(self, label: str) -> int:
header = self.tree.header()
model = self.tree.model()
for i in range(model.columnCount()):
if model.headerData(i, header.orientation()) == label:
return i
print(f"Section '{label}' not found in header.")
return -1
if __name__ == "__main__":
import sys
app = QApplication(sys.argv)
file_browser = FileBrowser()
file_browser.root_path = "/Users/wakonig_k/software/work/bec-widgets/bec_widgets"
file_browser.show_file_size = False
file_browser.show_file_kind = False
file_browser.show_file_timestamp = False
file_browser.show_hidden_files = True
file_browser.show_header = False
file_browser.show()
sys.exit(app.exec_())
# from qtpy.QtCore import Qt
# from qtpy.QtWidgets import QDockWidget, QFileSystemModel, QTreeView
# class ExplorerDock(QWidget):
# def __init__(self, cpath, themes):
# super().__init__()
# self._themes = themes
# self.setWindowTitle("Explorer")
# self.tree = QTreeView(self)
# self.model = QFileSystemModel()
# self.tree.setModel(self.model)
# self.tree.setRootIndex(self.model.index(cpath))
# self.model.setRootPath(cpath)
# layout = QVBoxLayout(self)
# layout.setContentsMargins(0, 0, 0, 0)
# layout.addWidget(self.tree)
# app = QApplication([])
# explorer = ExplorerDock(
# cpath="/Users/wakonig_k/software/work/csaxs_bec/csaxs_bec", themes={"sidebar_bg": "#2E2E2E"}
# )
# explorer.show()
# app.exec_()
# # self.dock = QDockWidget("Explorer", self)
# # self.dock.setMinimumWidth(200)
# # self.dock.visibilityChanged.connect(
# # lambda visible: self.onExplorerDockVisibilityChanged(visible)
# # )
# # self.dock.setAllowedAreas(Qt.DockWidgetArea.AllDockWidgetAreas)
# # tree_view = QTreeView()
# # self.model = QFileSystemModel()
# # bg = self._themes["sidebar_bg"]
# # tree_view.setStyleSheet(
# # f"QTreeView {{background-color: {bg}; color: white; border: none; }}"
# # )
# # tree_view.setModel(self.model)
# # tree_view.setRootIndex(self.model.index(cpath))
# # self.model.setRootPath(cpath)

View File

@@ -0,0 +1 @@
{'files': ['file_browser.py']}

View File

@@ -0,0 +1,54 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
from qtpy.QtDesigner import QDesignerCustomWidgetInterface
from bec_widgets.utils.bec_designer import designer_material_icon
from bec_widgets.widgets.utility.file_browser.file_browser import FileBrowser
DOM_XML = """
<ui language='c++'>
<widget class='FileBrowser' name='file_browser'>
</widget>
</ui>
"""
class FileBrowserPlugin(QDesignerCustomWidgetInterface): # pragma: no cover
def __init__(self):
super().__init__()
self._form_editor = None
def createWidget(self, parent):
t = FileBrowser(parent)
return t
def domXml(self):
return DOM_XML
def group(self):
return "BEC Utils"
def icon(self):
return designer_material_icon(FileBrowser.ICON_NAME)
def includeFile(self):
return "file_browser"
def initialize(self, form_editor):
self._form_editor = form_editor
def isContainer(self):
return False
def isInitialized(self):
return self._form_editor is not None
def name(self):
return "FileBrowser"
def toolTip(self):
return ""
def whatsThis(self):
return self.toolTip()

View File

@@ -0,0 +1,15 @@
def main(): # pragma: no cover
from qtpy import PYSIDE6
if not PYSIDE6:
print("PYSIDE6 is not available in the environment. Cannot patch designer.")
return
from PySide6.QtDesigner import QPyDesignerCustomWidgetCollection
from bec_widgets.widgets.utility.file_browser.file_browser_plugin import FileBrowserPlugin
QPyDesignerCustomWidgetCollection.addCustomWidget(FileBrowserPlugin())
if __name__ == "__main__": # pragma: no cover
main()