mirror of
https://github.com/bec-project/bec_widgets.git
synced 2026-07-25 20:53:00 +02:00
fix(ide-explorer): add tooltips for scripts and macros sections with help popups
This commit is contained in:
@@ -3,9 +3,18 @@ from __future__ import annotations
|
||||
from bec_qthemes import material_icon
|
||||
from qtpy.QtCore import QMimeData, Qt, Signal
|
||||
from qtpy.QtGui import QDrag
|
||||
from qtpy.QtWidgets import QHBoxLayout, QPushButton, QSizePolicy, QToolButton, QVBoxLayout, QWidget
|
||||
from qtpy.QtWidgets import (
|
||||
QHBoxLayout,
|
||||
QLabel,
|
||||
QPushButton,
|
||||
QSizePolicy,
|
||||
QToolButton,
|
||||
QVBoxLayout,
|
||||
QWidget,
|
||||
)
|
||||
|
||||
from bec_widgets.utils.error_popups import SafeProperty
|
||||
from bec_widgets.widgets.containers.main_window.addons.hover_widget import WidgetTooltip
|
||||
|
||||
|
||||
class CollapsibleSection(QWidget):
|
||||
@@ -34,6 +43,9 @@ class CollapsibleSection(QWidget):
|
||||
super().__init__(parent=parent)
|
||||
self.title = title
|
||||
self.content_widget = None
|
||||
self._help_text = tooltip
|
||||
self._help_tooltip: WidgetTooltip | None = None
|
||||
self.destroyed.connect(self._cleanup_help_tooltip)
|
||||
self.setAcceptDrops(True)
|
||||
self._expanded = True
|
||||
|
||||
@@ -58,13 +70,24 @@ class CollapsibleSection(QWidget):
|
||||
self.header_button.mouseMoveEvent = self._header_mouse_move_event
|
||||
self.header_button.dragEnterEvent = self._header_drag_enter_event
|
||||
self.header_button.dropEvent = self._header_drop_event
|
||||
if tooltip:
|
||||
self.header_button.setToolTip(tooltip)
|
||||
|
||||
self.drag_start_position = None
|
||||
|
||||
# Add header to layout
|
||||
header_layout.addWidget(self.header_button)
|
||||
self.header_info_button = QToolButton()
|
||||
self.header_info_button.setSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed)
|
||||
self.header_info_button.setFixedSize(20, 20)
|
||||
self.header_info_button.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonIconOnly)
|
||||
self.header_info_button.setAutoRaise(True)
|
||||
self.header_info_button.setEnabled(bool(self._help_text))
|
||||
self.header_info_button.setVisible(bool(self._help_text))
|
||||
self.header_info_button.setFocusPolicy(Qt.FocusPolicy.NoFocus)
|
||||
self.header_info_button.setIcon(material_icon("info", convert_to_pixmap=False))
|
||||
if self._help_text:
|
||||
self.header_info_button.setToolTip("Show help")
|
||||
self.header_info_button.clicked.connect(self._toggle_help_tooltip)
|
||||
header_layout.addWidget(self.header_info_button)
|
||||
header_layout.addStretch()
|
||||
|
||||
# Add button in header (icon-only)
|
||||
@@ -146,6 +169,37 @@ class CollapsibleSection(QWidget):
|
||||
"""
|
||||
self.header_add_button.clicked.connect(slot)
|
||||
|
||||
def closeEvent(self, event):
|
||||
self._cleanup_help_tooltip()
|
||||
super().closeEvent(event)
|
||||
|
||||
def _toggle_help_tooltip(self):
|
||||
"""Show or hide the section help popover near the info button."""
|
||||
if not self._help_text:
|
||||
return
|
||||
|
||||
if self._help_tooltip is not None and self._help_tooltip.isVisible():
|
||||
self._help_tooltip.hide()
|
||||
return
|
||||
|
||||
if self._help_tooltip is None:
|
||||
help_label = QLabel(self._help_text)
|
||||
help_label.setWordWrap(True)
|
||||
help_label.setMinimumWidth(220)
|
||||
help_label.setMaximumWidth(320)
|
||||
self._help_tooltip = WidgetTooltip(help_label)
|
||||
|
||||
anchor = self.header_info_button.mapToGlobal(self.header_info_button.rect().center())
|
||||
self._help_tooltip.show_near(anchor)
|
||||
|
||||
def _cleanup_help_tooltip(self, *_args):
|
||||
"""Close and release any help popup owned by this section."""
|
||||
if self._help_tooltip is None:
|
||||
return
|
||||
self._help_tooltip.close()
|
||||
self._help_tooltip.deleteLater()
|
||||
self._help_tooltip = None
|
||||
|
||||
def _header_mouse_press_event(self, event):
|
||||
"""Handle mouse press on header for drag start"""
|
||||
if event.button() == Qt.MouseButton.LeftButton:
|
||||
|
||||
@@ -78,7 +78,15 @@ class IDEExplorer(BECWidget, QWidget):
|
||||
self._remove_section(section)
|
||||
|
||||
def add_script_section(self):
|
||||
section = CollapsibleSection(parent=self, title="SCRIPTS", indentation=0)
|
||||
section = CollapsibleSection(
|
||||
parent=self,
|
||||
title="SCRIPTS",
|
||||
indentation=0,
|
||||
tooltip="Scripts are executable Python files for scan workflows, "
|
||||
"automation, and quick experiments.\n"
|
||||
"In contrast to macros, scripts can contain executable code outside of function definitions.\n"
|
||||
"Scripts can be run directly from the IDE by clicking the 'Run' button in the toolbar.",
|
||||
)
|
||||
|
||||
script_explorer = Explorer(parent=self)
|
||||
local_script_dir = os.path.expanduser(
|
||||
@@ -120,7 +128,10 @@ class IDEExplorer(BECWidget, QWidget):
|
||||
title="MACROS",
|
||||
indentation=0,
|
||||
show_add_button=True,
|
||||
tooltip="Macros are reusable functions that can be called from scripts or the console.",
|
||||
tooltip="Macros are reusable Python functions that can be called from scripts or the console.\n"
|
||||
"All macros are automatically loaded and available for use. As a result,\n"
|
||||
"macros must not have executable code outside of function definitions to\n"
|
||||
"avoid unintended execution when imported.",
|
||||
)
|
||||
section.header_add_button.setIcon(
|
||||
material_icon("refresh", size=(20, 20), convert_to_pixmap=False)
|
||||
|
||||
@@ -24,6 +24,41 @@ def test_ide_explorer_initialization(ide_explorer):
|
||||
assert ide_explorer.main_explorer.sections[0].title == "SCRIPTS"
|
||||
|
||||
|
||||
def test_script_section_has_info_tooltip(ide_explorer):
|
||||
"""Scripts section exposes click-to-open help on the info icon."""
|
||||
scripts_section = ide_explorer.main_explorer.get_section("SCRIPTS")
|
||||
|
||||
assert not scripts_section.header_button.toolTip()
|
||||
assert not scripts_section.header_info_button.isHidden()
|
||||
assert scripts_section.header_info_button.isEnabled()
|
||||
assert scripts_section.header_info_button.toolTip() == "Show help"
|
||||
|
||||
|
||||
def test_macro_section_has_info_tooltip(ide_explorer):
|
||||
"""Macros section exposes click-to-open help on the info icon."""
|
||||
macros_section = ide_explorer.main_explorer.get_section("MACROS")
|
||||
|
||||
assert not macros_section.header_button.toolTip()
|
||||
assert not macros_section.header_info_button.isHidden()
|
||||
assert macros_section.header_info_button.isEnabled()
|
||||
assert macros_section.header_info_button.toolTip() == "Show help"
|
||||
|
||||
|
||||
def test_script_section_info_button_opens_help_popup(ide_explorer, qtbot):
|
||||
"""Clicking the info button should show the styled help popup."""
|
||||
scripts_section = ide_explorer.main_explorer.get_section("SCRIPTS")
|
||||
|
||||
scripts_section.header_info_button.click()
|
||||
qtbot.waitUntil(
|
||||
lambda: scripts_section._help_tooltip is not None
|
||||
and scripts_section._help_tooltip.isVisible(),
|
||||
timeout=1000,
|
||||
)
|
||||
|
||||
assert scripts_section._help_tooltip.content.text().startswith("Scripts are executable")
|
||||
scripts_section._cleanup_help_tooltip()
|
||||
|
||||
|
||||
def test_ide_explorer_add_local_script(ide_explorer, qtbot, tmpdir):
|
||||
local_script_section = ide_explorer.main_explorer.get_section(
|
||||
"SCRIPTS"
|
||||
|
||||
Reference in New Issue
Block a user