Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -133,8 +133,4 @@ def get_ide_plotting():
exclusive=True,
)
_app.show()
# developer_view.show()
# developer_view.setWindowTitle("Developer View")
# developer_view.resize(1920, 1080)
# developer_view.set_stretch(horizontal=[1, 3, 2], vertical=[5, 5]) #can be set during runtime
sys.exit(app.exec_())
83 changes: 54 additions & 29 deletions bec_widgets/applications/views/developer_view/developer_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@

import markdown
from bec_lib.endpoints import MessageEndpoints
from bec_lib.messages import ProcedureRequestMessage
from bec_lib.script_executor import upload_script
from bec_qthemes import material_icon
from qtpy.QtGui import QKeySequence, QShortcut
from qtpy.QtGui import QKeySequence, QShortcut # type: ignore
from qtpy.QtWidgets import QTextEdit

from bec_widgets.utils.error_popups import SafeSlot
Expand All @@ -16,6 +17,7 @@
from bec_widgets.widgets.containers.dock_area.basic_dock_area import DockAreaWidget
from bec_widgets.widgets.containers.dock_area.dock_area import BECDockArea
from bec_widgets.widgets.containers.qt_ads import CDockWidget
from bec_widgets.widgets.control.procedure_control.procedure_panel import ProcedurePanel
from bec_widgets.widgets.editors.monaco.monaco_dock import MonacoDock
from bec_widgets.widgets.editors.monaco.monaco_widget import MonacoWidget
from bec_widgets.widgets.editors.web_console.web_console import BECShell, WebConsole
Expand Down Expand Up @@ -125,6 +127,9 @@ def __init__(self, parent=None, **kwargs):
self._current_script_id: str | None = None
self.script_editor_tab = None

self.procedures = ProcedurePanel(self)
self.procedures.setObjectName("Procedure Control")

self._initialize_layout()

# Connect editor signals
Expand Down Expand Up @@ -183,24 +188,16 @@ def _initialize_layout(self) -> None:
)

# Plotting area on the right with signature help tabbed alongside
self.plotting_ads_dock = self.new(
self.plotting_ads,
where="right",
closable=False,
floatable=False,
movable=False,
return_dock=True,
title_buttons={"float": True},
)
self.signature_dock = self.new(
self.signature_help,
closable=False,
floatable=False,
movable=False,
tab_with=self.plotting_ads_dock,
return_dock=True,
title_buttons={"float": False, "close": False},
)
_r_panel = {
"closable": False,
"floatable": False,
"movable": False,
"return_dock": True,
"title_buttons": {"float": True},
}
self.plotting_dock = self.new(self.plotting_ads, where="right", **_r_panel)
self.signature_dock = self.new(self.signature_help, **_r_panel, tab_with=self.plotting_dock)
self.procedure_dock = self.new(self.procedures, **_r_panel, tab_with=self.plotting_dock)

self.set_layout_ratios(horizontal=[2, 5, 3], vertical=[7, 3])

Expand Down Expand Up @@ -233,6 +230,16 @@ def init_developer_toolbar(self):
run_action.action.triggered.connect(self.on_execute)
self.toolbar.components.add_safe("run", run_action)

submit_action = MaterialIconAction(
icon_name="animated_images",
tooltip="Run current file as a BEC procedure",
label_text="Run on server",
filled=True,
parent=self,
)
submit_action.action.triggered.connect(self.on_submit_procedure)
self.toolbar.components.add_safe("run_proc", submit_action)

stop_action = MaterialIconAction(
icon_name="stop",
tooltip="Stop current execution",
Expand All @@ -246,6 +253,7 @@ def init_developer_toolbar(self):
execution_bundle = ToolbarBundle("execution", self.toolbar.components)
execution_bundle.add_action("run")
execution_bundle.add_action("stop")
execution_bundle.add_action("run_proc")
self.toolbar.add_bundle(execution_bundle)

vim_action = MaterialIconAction(
Expand Down Expand Up @@ -305,24 +313,41 @@ def _on_save_enabled_update(self, enabled: bool):
self.toolbar.components.get_action("save").action.setEnabled(enabled)
self.toolbar.components.get_action("save_as").action.setEnabled(enabled)

@SafeSlot()
def on_execute(self):
"""Upload and run the currently focused script in the Monaco editor."""
def _try_upload(self) -> str | None:
self.script_editor_tab = self.monaco.last_focused_editor
if not self.script_editor_tab:
return
widget = self.script_editor_tab.widget()
if not isinstance(widget, MonacoWidget):
return
return None
if not isinstance(widget := self.script_editor_tab.widget(), MonacoWidget):
return None
if widget.modified:
# Save the file before execution if there are unsaved changes
self.monaco.save_file()
if widget.modified:
# If still modified, user likely cancelled save dialog
return
self.current_script_id = upload_script(self.client.connector, widget.get_text())
self.console.write(f'bec._run_script("{self.current_script_id}")')
return None
return upload_script(self.client.connector, widget.get_text())

@SafeSlot()
def on_execute(self):
"""Upload and run the currently focused script in the Monaco editor."""
print(f"Uploaded script with ID: {self.current_script_id}")
if (script_id := self._try_upload()) is not None:
self.current_script_id = script_id
self.console.write(f'bec._run_script("{self.current_script_id}")')
print(f"Uploaded script with ID: {self.current_script_id}")

@SafeSlot()
def on_submit_procedure(self):
"""Upload and run the currently focused script in the Monaco editor as a procedure."""
if (script_id := self._try_upload()) is not None:
self.current_script_id = script_id
print(f"Uploaded script with ID: {self.current_script_id}")
self.client.connector.xadd(
MessageEndpoints.procedure_request(),
ProcedureRequestMessage(
identifier="run_script", args_kwargs=((self.current_script_id,), {})
).model_dump(),
)

@SafeSlot()
def on_stop(self):
Expand Down
182 changes: 182 additions & 0 deletions bec_widgets/cli/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -4787,6 +4787,188 @@ def screenshot(self, file_name: "str | None" = None):
"""


class ProcedurePanel(RPCBase):
@rpc_call
def new(
self,
widget: "QWidget | str",
*,
closable: "bool" = True,
floatable: "bool" = True,
movable: "bool" = True,
start_floating: "bool" = False,
floating_state: "Mapping[str, object] | None" = None,
where: "Literal['left', 'right', 'top', 'bottom'] | None" = None,
on_close: "Callable[[CDockWidget, QWidget], None] | None" = None,
tab_with: "CDockWidget | QWidget | str | None" = None,
relative_to: "CDockWidget | QWidget | str | None" = None,
return_dock: "bool" = False,
show_title_bar: "bool | None" = None,
title_buttons: "Mapping[str, bool] | Sequence[str] | str | None" = None,
show_settings_action: "bool | None" = False,
promote_central: "bool" = False,
dock_icon: "QIcon | None" = None,
apply_widget_icon: "bool" = True,
object_name: "str | None" = None,
**widget_kwargs,
) -> "QWidget | CDockWidget | BECWidget":
"""
Create a new widget (or reuse an instance) and add it as a dock.

Args:
widget(QWidget | str): Instance or registered widget type string.
closable(bool): Whether the dock is closable.
floatable(bool): Whether the dock is floatable.
movable(bool): Whether the dock is movable.
start_floating(bool): Whether to start the dock floating.
floating_state(Mapping | None): Optional floating geometry metadata to apply when floating.
where(Literal["left", "right", "top", "bottom"] | None): Dock placement hint relative to the dock area (ignored when
``relative_to`` is provided without an explicit value).
on_close(Callable[[CDockWidget, QWidget], None] | None): Optional custom close handler accepting (dock, widget).
tab_with(CDockWidget | QWidget | str | None): Existing dock (or widget/name) to tab the new dock alongside.
relative_to(CDockWidget | QWidget | str | None): Existing dock (or widget/name) used as the positional anchor.
When supplied and ``where`` is ``None``, the new dock inherits the
anchor's current dock area.
return_dock(bool): When True, return the created dock instead of the widget.
show_title_bar(bool | None): Explicitly show or hide the dock area's title bar.
title_buttons(Mapping[str, bool] | Sequence[str] | str | None): Mapping or iterable describing which title bar buttons should
remain visible. Provide a mapping of button names (``"float"``,
``"close"``, ``"menu"``, ``"auto_hide"``, ``"minimize"``) to booleans,
or a sequence of button names to hide.
show_settings_action(bool | None): Control whether a dock settings/property action should
be installed. Defaults to ``False`` for the basic dock area; subclasses
such as `AdvancedDockArea` override the default to ``True``.
promote_central(bool): When True, promote the created dock to be the dock manager's
central widget (useful for editor stacks or other root content).
dock_icon(QIcon | None): Optional icon applied to the dock via ``CDockWidget.setIcon``.
Provide a `QIcon` (e.g. from ``material_icon``). When ``None`` (default),
the widget's ``ICON_NAME`` attribute is used when available.
apply_widget_icon(bool): When False, skip automatically resolving the icon from
the widget's ``ICON_NAME`` (useful for callers who want no icon and do not pass one explicitly).
object_name(str | None): Optional object name to assign to the created widget.
**widget_kwargs: Additional keyword arguments passed to the widget constructor
when creating by type name.

Returns:
The widget instance by default, or the created `CDockWidget` when `return_dock` is True.
"""

@rpc_call
def dock_map(self) -> "dict[str, CDockWidget]":
"""
Return the dock widgets map as dictionary with names as keys.
"""

@rpc_call
def dock_list(self) -> "list[CDockWidget]":
"""
Return the list of dock widgets.
"""

@rpc_call
def widget_map(self, bec_widgets_only: "bool" = True) -> "dict[str, QWidget]":
"""
Return a dictionary mapping widget names to their corresponding widgets.

Args:
bec_widgets_only(bool): If True, only include widgets that are BECConnector instances.
"""

@rpc_call
def widget_list(self, bec_widgets_only: "bool" = True) -> "list[QWidget]":
"""
Return a list of widgets contained in the dock area.

Args:
bec_widgets_only(bool): If True, only include widgets that are BECConnector instances.
"""

@rpc_call
def attach_all(self):
"""
Re-attach floating docks back into the dock manager.
"""

@rpc_call
def delete_all(self):
"""
Delete all docks and their associated widgets.
"""

@rpc_call
def delete(self, object_name: "str") -> "bool":
"""
Remove a widget from the dock area by its object name.

Args:
object_name: The object name of the widget to remove.

Returns:
bool: True if the widget was found and removed, False otherwise.

Raises:
ValueError: If no widget with the given object name is found.

Example:
>>> dock_area.delete("my_widget")
True
"""

@rpc_call
def set_layout_ratios(
self,
*,
horizontal: "Sequence[float] | Mapping[int | str, float] | None" = None,
vertical: "Sequence[float] | Mapping[int | str, float] | None" = None,
splitter_overrides: "Mapping[int | str | Sequence[int], Sequence[float] | Mapping[int | str, float]] | None" = None,
) -> "None":
"""
Adjust splitter ratios in the dock layout.

Args:
horizontal: Weights applied to every horizontal splitter encountered.
vertical: Weights applied to every vertical splitter encountered.
splitter_overrides: Optional overrides targeting specific splitters identified
by their index path (e.g. ``{0: [1, 2], (1, 0): [3, 5]}``). Paths are zero-based
indices following the splitter hierarchy, starting from the root splitter.

Example:
To build three columns with custom per-column ratios::

area.set_layout_ratios(
horizontal=[1, 2, 1], # column widths
splitter_overrides={
0: [1, 2], # column 0 (two rows)
1: [3, 2, 1], # column 1 (three rows)
2: [1], # column 2 (single row)
},
)
"""

@rpc_call
def describe_layout(self) -> "list[dict[str, Any]]":
"""
Return metadata describing splitter paths, orientations, and contained docks.

Useful for determining the keys to use in `set_layout_ratios(splitter_overrides=...)`.
"""

@rpc_call
def print_layout_structure(self) -> "None":
"""
Pretty-print the current splitter paths to stdout.
"""

@rpc_call
def set_central_dock(self, dock: "CDockWidget | QWidget | str") -> "None":
"""
Promote an existing dock to be the dock manager's central widget.

Args:
dock(CDockWidget | QWidget | str): Dock reference to promote.
"""


class RectangularROI(RPCBase):
"""Defines a rectangular Region of Interest (ROI) with additional functionality."""

Expand Down
Empty file.
Loading
Loading