Skip to content
Merged
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
22 changes: 22 additions & 0 deletions datalab/gui/applications.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"ApplicationsDialog",
"RecipeCard",
"get_application_plugins",
"get_declared_metadata_keys",
"get_plugin_icon",
"record_application_use",
"set_application_hidden",
Expand Down Expand Up @@ -312,6 +313,27 @@ def get_application_plugins() -> tuple[PluginBase, ...]:
return tuple(sorted(plugins, key=lambda plugin: plugin.info.name.casefold()))


def get_declared_metadata_keys(object_type: str) -> list[tuple[str, str]]:
"""Return the metadata keys expected by the methods of application plugins

Args:
object_type: type of the objects carrying the metadata ("signal" or
"image")

Returns:
``(key, description)`` pairs, in declaration order, without duplicates
"""
keys: dict[str, str] = {}
for plugin in get_application_plugins():
for recipe in plugin.get_recipes():
for slot in recipe.inputs:
if slot.object_type.value != object_type:
continue
for requirement in slot.metadata:
keys.setdefault(requirement.key, requirement.description)
return list(keys.items())


def record_application_use(plugin_id: str) -> None:
"""Remember an application as the most recently used one

Expand Down
4 changes: 4 additions & 0 deletions datalab/gui/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -995,6 +995,10 @@ def show_applications(self, plugin_id: str | None = None) -> None:
panel.objview.SIG_SELECTION_CHANGED.connect(
self.__applications_dialog.schedule_readiness_update
)
# Metadata edits change readiness without changing the selection
panel.SIG_OBJECT_MODIFIED.connect(
self.__applications_dialog.schedule_readiness_update
)
self.tabwidget.currentChanged.connect(
self.__applications_dialog.schedule_readiness_update
)
Expand Down
13 changes: 11 additions & 2 deletions datalab/gui/panel/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
from datalab.config import APP_NAME, Conf, _
from datalab.env import execenv
from datalab.gui import actionhandler, objectview
from datalab.gui.applications import get_declared_metadata_keys
from datalab.gui.newobject import (
CREATION_PARAMETERS_OPTION,
LEGACY_CREATION_PARAMETERS_OPTION,
Expand Down Expand Up @@ -2493,9 +2494,17 @@ def get_known_metadata_keys(self, objs: Sequence[TypeObj]) -> list[tuple[str, st
objs: selected objects

Returns:
``(key, description)`` pairs
``(key, description)`` pairs: the keys found on the objects, then the
keys expected by the methods of application plugins
"""
return collect_metadata_keys(objs)
keys = collect_metadata_keys(objs)
found = {key for key, _description in keys}
keys.extend(
(key, description)
for key, description in get_declared_metadata_keys(self.PANEL_STR_ID)
if key not in found
)
return keys

def add_metadata(self, param: AddMetadataParam | None = None) -> None:
"""Add metadata item to selected object(s)
Expand Down
5 changes: 5 additions & 0 deletions datalab/gui/recipe_inputs.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,11 @@ def format_readiness(
RecipeReadinessStatus.NO_INPUT: _("Select the input data in the workspace"),
}
reasons = [format_input_issue(issue, descriptor) for issue in readiness.issues]
if any(
issue.code is RecipeInputIssueCode.MISSING_METADATA
for issue in readiness.issues
):
reasons.append(_("To set it, use Edit > Metadata > Add metadata..."))
if readiness.status is not RecipeReadinessStatus.NO_INPUT:
reasons.extend(
format_diagnostic(diagnostic) for diagnostic in readiness.diagnostics
Expand Down
3 changes: 3 additions & 0 deletions datalab/locale/fr/LC_MESSAGES/datalab.po
Original file line number Diff line number Diff line change
Expand Up @@ -4593,3 +4593,6 @@ msgstr "Multiplie les valeurs numériques, par ex. 0.001 pour convertir des ms e

msgid "unchanged"
msgstr "inchangé"

msgid "To set it, use Edit > Metadata > Add metadata..."
msgstr "Pour la définir, utilisez Édition > Métadonnées > Ajouter des métadonnées..."
44 changes: 44 additions & 0 deletions datalab/tests/features/plugins/applications_dialog_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

from types import SimpleNamespace

import numpy as np
import sigima.objects
from guidata.configtools import get_icon
from qtpy import QtCore as QC
from qtpy import QtGui as QG
Expand All @@ -18,6 +20,7 @@
from datalab.gui.applications import (
ApplicationsDialog,
get_application_plugins,
get_declared_metadata_keys,
record_application_use,
set_application_hidden,
set_application_pinned,
Expand Down Expand Up @@ -314,6 +317,7 @@ def test_application_page_shows_readiness_of_each_method() -> None:
text = card.readiness_label.text()
assert "cannot be analyzed" in text
assert "Flat frames: metadata 'exposure_s' missing on 2" in text
assert "Edit > Metadata > Add metadata..." in text
# The method item shows the readiness without being opened
toolbox = dialog.application_pages[0].toolbox
assert "cannot be analyzed" in toolbox.itemToolTip(0)
Expand Down Expand Up @@ -515,6 +519,12 @@ def test_main_window_exposes_applications_catalog(monkeypatch) -> None:
"exec",
lambda dialog: executed.append(dialog),
)
updates: list[tuple] = []
monkeypatch.setattr(
main.ApplicationsDialog,
"schedule_readiness_update",
lambda _dialog, *args: updates.append(args),
)

with datalab_test_app_context(console=False, exec_loop=False) as window:
menu_actions = window.menuBar().actions()
Expand All @@ -541,6 +551,40 @@ def test_main_window_exposes_applications_catalog(monkeypatch) -> None:

assert window.findChildren(ApplicationsDialog) == [dialog]

# Editing objects (e.g. their metadata) re-assesses the methods
updates.clear()
window.imagepanel.SIG_OBJECT_MODIFIED.emit()
assert updates


def test_add_metadata_suggests_keys_declared_by_applications() -> None:
"""Add metadata offers the keys expected by application methods"""
application = _plugin(
"org.example.camera",
"Camera Characterization",
frozenset({PluginCapability.APPLICATION}),
recipes=(RECIPE,),
)
with datalab_test_app_context(console=False, exec_loop=False) as window:
# DataLab unregisters the plugins on close: restore the registry first
registry = PluginRegistry.get_plugins()
previous_plugins = list(registry)
registry[:] = [application]
try:
assert get_declared_metadata_keys("image") == [
("exposure_s", "Exposure time")
]
assert get_declared_metadata_keys("signal") == []
image = sigima.objects.create_image("Flat", np.zeros((2, 2)))
image.metadata["gain"] = 2
assert window.imagepanel.get_known_metadata_keys([image]) == [
("gain", "e.g. 2"),
("exposure_s", "Exposure time"),
]
assert window.signalpanel.get_known_metadata_keys([]) == []
finally:
registry[:] = previous_plugins


def test_application_commands_delegate_to_plugin_contracts(monkeypatch) -> None:
"""Each catalog command calls its plugin entry point and keeps the dialog."""
Expand Down
2 changes: 1 addition & 1 deletion doc/features/common/overview.rst
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ A **Welcome** page is shown next to the signal and image views when DataLab star

When application plugins are installed, an **Applications** section at the top of the page shows their tiles: clicking the main tile of an application opens its page in the **Applications** catalog, or starts the main action provided by the plugin. An application may provide other tiles, for example to open an example directly: they are shown next to its main tile when the section has room for them, and otherwise move to the menu of the main tile. This menu, opened from the "…" button of the main tile or by right-clicking it, also lets you pin the application to the top of the section or hide it from the welcome page. Pinned applications come first, then recently used ones, then the others by name. The section is limited to two rows of tiles by default (this number may be changed in the :ref:`settings`): when more applications are installed, the last tile gives the number of applications left out and opens the catalog. The "Browse all applications..." button also opens the catalog, where a search field filters applications by name or description, and where the "Show on welcome page" and "Pin to the welcome page" check boxes of each application change the same preferences (see :ref:`about_plugins`).

On the page of an application, the methods are listed one below the other, followed by the tools and datasets of the application, if any: clicking a title opens its section and closes the one previously open. The colored dot before each method tells whether the current selection can be analyzed, and its tooltip gives the reason. The open method presents the inputs it expects and this status in detail. "Run on selection..." starts the method, and asks you to assign the selected objects to its inputs only when needed. The examples designed for a method are listed below it: "Try with this example" opens the example and runs the method with suitable parameters. The vertical strip between the application list and the page hides the list, or shows it again: the window shrinks or grows accordingly, and DataLab remembers this choice.
On the page of an application, the methods are listed one below the other, followed by the tools and datasets of the application, if any: clicking a title opens its section and closes the one previously open. The colored dot before each method tells whether the current selection can be analyzed, and its tooltip gives the reason. The open method presents the inputs it expects and this status in detail. When metadata are missing, the status points to "Edit > Metadata > Add metadata...", and it is updated as soon as objects are modified. "Run on selection..." starts the method, and asks you to assign the selected objects to its inputs only when needed. The examples designed for a method are listed below it: "Try with this example" opens the example and runs the method with suitable parameters. The vertical strip between the application list and the page hides the list, or shows it again: the window shrinks or grows accordingly, and DataLab remembers this choice.

The Welcome page is a regular dockable panel: as soon as a signal or an image is created or opened, the corresponding view is brought to the front, and the Welcome page remains available as a tab. It may be closed like any other panel, and reopened at any time from the "Welcome page" entry of the "?" (Help) menu or from the "View" menu; it is also reopened automatically when the current panel becomes empty. The "Show welcome page when the current panel is empty" check box, at the bottom of the page, controls this automatic display, including at startup (this option is also available in the :ref:`settings`).

Expand Down
2 changes: 1 addition & 1 deletion doc/features/image/menu_edit.rst
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ sample names, processing steps, or any other custom information.
When you select "Add metadata..." from the Edit menu, a dialog appears where you can:

- **Metadata key**: Enter the name of the metadata field to add (letters, digits, ``_``, ``.`` and ``-``, e.g. a plugin key such as ``plugin.org.example.my-plugin.gain``)
- **Known keys**: Pick a key already present on the selected images to copy it into the metadata key
- **Known keys**: Pick a key already present on the selected images, or a key expected by the methods of the installed application plugins, to copy it into the metadata key
- **Value pattern**: Define a pattern for the metadata value using Python format strings
- **Extraction pattern**: Optionally, extract the value from the formatted text with a Python regular expression: its first group, or the whole match if it has no group, becomes the value
- **If no match**: Leave images without a match unchanged (default), or report an error
Expand Down
2 changes: 1 addition & 1 deletion doc/features/signal/menu_edit.rst
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ sample names, processing steps, or any other custom information.
When you select "Add metadata..." from the Edit menu, a dialog appears where you can:

- **Metadata key**: Enter the name of the metadata field to add (letters, digits, ``_``, ``.`` and ``-``, e.g. a plugin key such as ``plugin.org.example.my-plugin.gain``)
- **Known keys**: Pick a key already present on the selected signals to copy it into the metadata key
- **Known keys**: Pick a key already present on the selected signals, or a key expected by the methods of the installed application plugins, to copy it into the metadata key
- **Value pattern**: Define a pattern for the metadata value using Python format strings
- **Extraction pattern**: Optionally, extract the value from the formatted text with a Python regular expression: its first group, or the whole match if it has no group, becomes the value
- **If no match**: Leave signals without a match unchanged (default), or report an error
Expand Down
Loading