Repository: dipy/dipy
Pull request #3520: ENH: Opacity slider turned off on hide
Author: login: maharshi-gor
Labels: name: state: ready-to-be-merged
name: area:visualization
description: Issues affecting the visualization module
Created: 2025-03-21T19:37:28Z | Merged: 2025-04-22T14:14:46Z

### ENH: Opacity slider turned off on hide

**PR Content**
- Opacity slider will be hidden when checkbox for opacity is clicked
- Added the implementation of the _toggle_actors function in the base in the inherited in other tabs having opacity.
- Visualization actors were removed as usual but the custom build opacity sliders are taken care in the different tab implementations.
- Also added a check for rois will not be considered for 4D data


Diff:
diff --git a/dipy/viz/horizon/app.py b/dipy/viz/horizon/app.py
index 6bc0c30b00..1faaa34618 100644
--- a/dipy/viz/horizon/app.py
+++ b/dipy/viz/horizon/app.py
@@ -559,7 +559,7 @@ def build_show(self, scene):
                 title = f"Image {img_count + 1}"
                 data, affine, fname = unpack_data(img, return_size=3)
                 self.vox2ras = affine
-                if self.__roi_images:
+                if self.__roi_images and data.ndim == 3:
                     if "rois" in self.random_colors:
                         roi_color = next(self.color_gen)
                     roi_actor = actor.contour_from_roi(
diff --git a/dipy/viz/horizon/tab/base.py b/dipy/viz/horizon/tab/base.py
index 0833c6c1be..24ae4521c4 100644
--- a/dipy/viz/horizon/tab/base.py
+++ b/dipy/viz/horizon/tab/base.py
@@ -33,8 +33,6 @@ class HorizonTab(ABC):
 
     def __init__(self):
         self._elements = []
-        self.hide = lambda *args: None
-        self.show = lambda *args: None
 
     @abstractmethod
     def build(self, tab_id):
@@ -81,6 +79,26 @@ def on_slice_change(self, _x_value, _y_value, _z_value):  # noqa: B027
         changes."""
         pass
 
+    def show(*args):
+        """Show elements in the scene.
+
+        Parameters
+        ----------
+        *args : HorizonUIElement or FURY actors
+            Elements to be hidden.
+        """
+        raise NotImplementedError("This method should be implemented in TabManager.")
+
+    def hide(*args):
+        """Hide elements from the scene.
+
+        Parameters
+        ----------
+        *args : HorizonUIElement or FURY actors
+            Elements to be hidden.
+        """
+        raise NotImplementedError("This method should be implemented in TabManager.")
+
     @property
     @abstractmethod
     def name(self):
diff --git a/dipy/viz/horizon/tab/peak.py b/dipy/viz/horizon/tab/peak.py
index b19f90b287..e295d99672 100644
--- a/dipy/viz/horizon/tab/peak.py
+++ b/dipy/viz/horizon/tab/peak.py
@@ -358,6 +358,21 @@ def update_slices(self, x_slice, y_slice, z_slice):
         if not self._slice_z.obj.value == z_slice:
             self._slice_z.obj.value = z_slice
 
+    def _toggle_actors(self, checkbox):
+        """Toggle the opacity of the peaks actor and slider.
+
+        Parameters
+        ----------
+        checkbox : CheckBox2D
+            FURY checkbox UI element.
+        """
+        if "" in checkbox.checked_labels:
+            self.show(self._opacity.obj)
+        else:
+            self.hide(self._opacity.obj)
+
+        super()._toggle_actors(checkbox)
+
     def build(self, tab_id):
         """Build all the elements under the tab.
 
diff --git a/dipy/viz/horizon/tab/roi.py b/dipy/viz/horizon/tab/roi.py
index 9186933d7c..cd2a5a058d 100644
--- a/dipy/viz/horizon/tab/roi.py
+++ b/dipy/viz/horizon/tab/roi.py
@@ -46,6 +46,21 @@ def _change_opacity(self, slider):
         for contour in self._actors:
             contour.GetProperty().SetOpacity(opacity)
 
+    def _toggle_actors(self, checkbox):
+        """Toggle the opacity of the ROIs actor and slider.
+
+        Parameters
+        ----------
+        checkbox : CheckBox2D
+            FURY checkbox UI element.
+        """
+        if "" in checkbox.checked_labels:
+            self.show(self._opacity_slider.obj)
+        else:
+            self.hide(self._opacity_slider.obj)
+
+        super()._toggle_actors(checkbox)
+
     def build(self, tab_id):
         """Position the elements in the tab.
 
diff --git a/dipy/viz/horizon/tab/slice.py b/dipy/viz/horizon/tab/slice.py
index 95ee69d959..778c4b255d 100644
--- a/dipy/viz/horizon/tab/slice.py
+++ b/dipy/viz/horizon/tab/slice.py
@@ -416,6 +416,21 @@ def update_volume(self, volume):
         if not self._volume.obj.value == volume:
             self._volume.obj.value = volume
 
+    def _toggle_actors(self, checkbox):
+        """Toggle the opacity of the slice actor and slider.
+
+        Parameters
+        ----------
+        checkbox : CheckBox2D
+            FURY checkbox UI element.
+        """
+        if "" in checkbox.checked_labels:
+            self.show(self._slice_opacity.obj)
+        else:
+            self.hide(self._slice_opacity.obj)
+
+        super()._toggle_actors(checkbox)
+
     def build(self, tab_id):
         """Build all the elements under the tab.
 
diff --git a/dipy/viz/horizon/tab/surface.py b/dipy/viz/horizon/tab/surface.py
index 4139f9bd49..b35b6953e2 100644
--- a/dipy/viz/horizon/tab/surface.py
+++ b/dipy/viz/horizon/tab/surface.py
@@ -59,6 +59,21 @@ def _update_opacities(self):
         for actor in self.actors:
             actor.GetProperty().SetOpacity(self._surface_opacity.selected_value)
 
+    def _toggle_actors(self, checkbox):
+        """Toggle the opacity of the surface actor and slider.
+
+        Parameters
+        ----------
+        checkbox : CheckBox2D
+            FURY checkbox UI element.
+        """
+        if "" in checkbox.checked_labels:
+            self.show(self._surface_opacity.obj)
+        else:
+            self.hide(self._surface_opacity.obj)
+
+        super()._toggle_actors(checkbox)
+
     def build(self, tab_id):
         """Build all the elements under the tab.
 
diff --git a/dipy/viz/horizon/tab/tests/meson.build b/dipy/viz/horizon/tab/tests/meson.build
index 76796a7947..dc2f7c1306 100644
--- a/dipy/viz/horizon/tab/tests/meson.build
+++ b/dipy/viz/horizon/tab/tests/meson.build
@@ -2,6 +2,9 @@ python_sources = [
   '__init__.py',
   'test_base.py',
   'test_peak.py',
+  'test_slices.py',
+  'test_roi.py',
+  'test_surface.py',
   ]
 
 py3.install_sources(
diff --git a/dipy/viz/horizon/tab/tests/test_peak.py b/dipy/viz/horizon/tab/tests/test_peak.py
index a08e5f9a09..e86296b27e 100644
--- a/dipy/viz/horizon/tab/tests/test_peak.py
+++ b/dipy/viz/horizon/tab/tests/test_peak.py
@@ -152,8 +152,10 @@ def test_update_slices(peak_actor):
     assert tab._slice_z.obj.value == 2
 
 
+@patch.object(HorizonTab, "show")
+@patch.object(HorizonTab, "hide")
 @pytest.mark.skipif(skip_it, reason="Needs xvfb")
-def test_on_tab_selected(peak_actor):
+def test_on_tab_selected(_m_show, _m_hide, peak_actor):
     """Test on_tab_selected method in PeaksTab."""
     tab = PeaksTab(peak_actor, "Peaks 1", "mock_peaks")
     tab.build(0)
@@ -228,3 +230,24 @@ def test_update_slice_visibility(peak_actor, slider):
     tab._update_slice_visibility(tab._slice_x_toggle.obj, selected_slice)
     assert selected_slice.visibility is True
     assert selected_slice.obj.visibility is True
+
+
+@pytest.mark.skipif(skip_it, reason="Needs xvfb")
+def test_toggle_actors(peak_actor):
+    """Test toggle_actors method in PeaksTab."""
+    tab = PeaksTab(peak_actor, "Peaks 1", "mock_peaks")
+    tab.build(0)
+
+    tab._actor_toggle.obj.checked_labels = [""]
+
+    with patch.object(HorizonTab, "show") as mock_show:
+        tab._toggle_actors(tab._actor_toggle.obj)
+        mock_show.assert_called_with(*tab.actors)
+        assert mock_show.call_count == 2
+
+    tab._actor_toggle.obj.checked_labels = []
+
+    with patch.object(HorizonTab, "hide") as mock_hide:
+        tab._toggle_actors(tab._actor_toggle.obj)
+        mock_hide.assert_called_with(*tab.actors)
+        assert mock_hide.call_count == 2
diff --git a/dipy/viz/horizon/tab/tests/test_roi.py b/dipy/viz/horizon/tab/tests/test_roi.py
new file mode 100644
index 0000000000..26f4e020a6
--- /dev/null
+++ b/dipy/viz/horizon/tab/tests/test_roi.py
@@ -0,0 +1,54 @@
+from unittest.mock import patch
+
+import numpy as np
+import pytest
+
+from dipy.testing.decorators import use_xvfb
+from dipy.utils.optpkg import optional_package
+from dipy.viz.horizon.tab.base import HorizonTab
+from dipy.viz.horizon.tab.roi import ROIsTab
+
+fury, has_fury, setup_module = optional_package("fury", min_version="0.10.0")
+skip_it = use_xvfb == "skip" or not has_fury
+
+if has_fury:
+    from fury.actor import contour_from_roi
+
+
+@pytest.fixture
+def roi_actors():
+    """Fixture to create a ROI Actors."""
+    affine = np.array(
+        [
+            [1.0, 0.0, 0.0, -98.0],
+            [0.0, 1.0, 0.0, -134.0],
+            [0.0, 0.0, 1.0, -72.0],
+            [0.0, 0.0, 0.0, 1.0],
+        ]
+    )
+    img = np.zeros((197, 233, 189))
+    img[0:25, :, :] = 1
+
+    roi_actor = contour_from_roi(img, affine=affine)
+    return [roi_actor]
+
+
+@pytest.mark.skipif(skip_it, reason="Needs xvfb")
+def test_toggle_actors(roi_actors):
+    """Test toggle_actors method in ROIsTab."""
+    tab = ROIsTab(roi_actors)
+    tab.build(0)
+
+    tab._actor_toggle.obj.checked_labels = [""]
+
+    with patch.object(HorizonTab, "show") as mock_show:
+        tab._toggle_actors(tab._actor_toggle.obj)
+        mock_show.assert_called_with(*tab.actors)
+        assert mock_show.call_count == 2
+
+    tab._actor_toggle.obj.checked_labels = []
+
+    with patch.object(HorizonTab, "hide") as mock_hide:
+        tab._toggle_actors(tab._actor_toggle.obj)
+        mock_hide.assert_called_with(*tab.actors)
+        assert mock_hide.call_count == 2
diff --git a/dipy/viz/horizon/tab/tests/test_slices.py b/dipy/viz/horizon/tab/tests/test_slices.py
new file mode 100644
index 0000000000..7f9c3770bf
--- /dev/null
+++ b/dipy/viz/horizon/tab/tests/test_slices.py
@@ -0,0 +1,58 @@
+from unittest.mock import patch
+
+import numpy as np
+import pytest
+
+from dipy.testing.decorators import set_random_number_generator, use_xvfb
+from dipy.utils.optpkg import optional_package
+from dipy.viz.horizon.tab.base import HorizonTab
+from dipy.viz.horizon.tab.slice import SlicesTab
+from dipy.viz.horizon.visualizer.slice import SlicesVisualizer
+
+fury, has_fury, setup_module = optional_package("fury", min_version="0.10.0")
+skip_it = use_xvfb == "skip" or not has_fury
+
+
+@pytest.fixture
+@set_random_number_generator()
+def slice_viz(rng):
+    """Fixture to create a Slice Actors."""
+    affine = np.array(
+        [
+            [1.0, 0.0, 0.0, -98.0],
+            [0.0, 1.0, 0.0, -134.0],
+            [0.0, 0.0, 1.0, -72.0],
+            [0.0, 0.0, 0.0, 1.0],
+        ]
+    )
+    data = 255 * rng.random((197, 233, 189))
+
+    class MockScene:
+        def add(self, *actor):
+            pass
+
+    slice_viz = SlicesVisualizer(
+        None, MockScene(), data, affine=affine, fname="mock_slices"
+    )
+    return slice_viz
+
+
+@pytest.mark.skipif(skip_it, reason="Needs xvfb")
+def test_toggle_actors(slice_viz):
+    """Test toggle_actors method in SlicesTab."""
+    tab = SlicesTab(slice_viz, "Slices 1", "mock_slices")
+    tab.build(0)
+
+    tab._actor_toggle.obj.checked_labels = [""]
+
+    with patch.object(HorizonTab, "show") as mock_show:
+        tab._toggle_actors(tab._actor_toggle.obj)
+        mock_show.assert_called_with(*tab.actors)
+        assert mock_show.call_count == 2
+
+    tab._actor_toggle.obj.checked_labels = []
+
+    with patch.object(HorizonTab, "hide") as mock_hide:
+        tab._toggle_actors(tab._actor_toggle.obj)
+        mock_hide.assert_called_with(*tab.actors)
+        assert mock_hide.call_count == 2
diff --git a/dipy/viz/horizon/tab/tests/test_surface.py b/dipy/viz/horizon/tab/tests/test_surface.py
new file mode 100644
index 0000000000..dd246e8294
--- /dev/null
+++ b/dipy/viz/horizon/tab/tests/test_surface.py
@@ -0,0 +1,49 @@
+from unittest.mock import patch
+
+import pytest
+
+from dipy.testing.decorators import set_random_number_generator, use_xvfb
+from dipy.utils.optpkg import optional_package
+from dipy.viz.horizon.tab.base import HorizonTab
+from dipy.viz.horizon.tab.surface import SurfaceTab
+from dipy.viz.horizon.visualizer.surface import SurfaceVisualizer
+
+fury, has_fury, setup_module = optional_package("fury", min_version="0.10.0")
+skip_it = use_xvfb == "skip" or not has_fury
+
+
+@pytest.fixture
+@set_random_number_generator()
+def surface_viz(rng):
+    """Fixture to create a Surface Visualizer."""
+    vertices = rng.random((100, 3))
+    faces = rng.integers(0, 100, size=(100, 3))
+
+    class MockScene:
+        def add(self, *actor):
+            pass
+
+    surface_viz = SurfaceVisualizer((vertices, faces), MockScene(), (0, 0, 0))
+
+    return surface_viz
+
+
+@pytest.mark.skipif(skip_it, reason="Needs xvfb")
+def test_toggle_actors(surface_viz):
+    """Test toggle_actors method in Surface Tab."""
+    tab = SurfaceTab(surface_viz, "Surface 1", "mock_surface")
+    tab.build(0)
+
+    tab._actor_toggle.obj.checked_labels = [""]
+
+    with patch.object(HorizonTab, "show") as mock_show:
+        tab._toggle_actors(tab._actor_toggle.obj)
+        mock_show.assert_called_with(*tab.actors)
+        assert mock_show.call_count == 2
+
+    tab._actor_toggle.obj.checked_labels = []
+
+    with patch.object(HorizonTab, "hide") as mock_hide:
+        tab._toggle_actors(tab._actor_toggle.obj)
+        mock_hide.assert_called_with(*tab.actors)
+        assert mock_hide.call_count == 2


Reviews:
user: login: Atharva-Shah-2298
body: Works as intended. LGTM
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-24T17:02:09Z
user: login: skoudoro
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-17T15:51:25Z
user: login: maharshi-gor
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-17T16:16:48Z
user: login: skoudoro
body: ok, all good, work as expected, merging.

thanks @maharshi-gor. 
state: APPROVED
author_association: MEMBER
submitted_at: 2025-04-22T14:14:36Z

Review comments:
diff_hunk: @@ -81,6 +79,26 @@ def on_slice_change(self, _x_value, _y_value, _z_value):  # noqa: B027
         changes."""
         pass
 
+    def show(*args):  # noqa: B027
+        """Show elements in the scene. This method will be implemented in TabManager.
+
+        Parameters
+        ----------
+        *args : HorizonUIElement or FURY actors
+            Elements to be hidden.
+        """
+        pass
+
+    def hide(*args):  # noqa: B027
+        """Hide elements from the scene. This method will be implemented in TabManager.
+
+        Parameters
+        ----------
+        *args : HorizonUIElement or FURY actors
+            Elements to be hidden.
+        """
+        pass
path: dipy/viz/horizon/tab/base.py
user: login: skoudoro
body: not sure to understand this change? What was wrong with the previous definition ?

before we were returning `None` and now it is just a `pass`. Why not `raise NotImplementedError` ?

Can you clarify ? Thanks
created_at: 2025-04-17T15:51:25Z
author_association: MEMBER
diff_hunk: @@ -81,6 +79,26 @@ def on_slice_change(self, _x_value, _y_value, _z_value):  # noqa: B027
         changes."""
         pass
 
+    def show(*args):  # noqa: B027
+        """Show elements in the scene. This method will be implemented in TabManager.
+
+        Parameters
+        ----------
+        *args : HorizonUIElement or FURY actors
+            Elements to be hidden.
+        """
+        pass
+
+    def hide(*args):  # noqa: B027
+        """Hide elements from the scene. This method will be implemented in TabManager.
+
+        Parameters
+        ----------
+        *args : HorizonUIElement or FURY actors
+            Elements to be hidden.
+        """
+        pass
path: dipy/viz/horizon/tab/base.py
user: login: maharshi-gor
body: The test cases can not mock this method and they need a proper definition. This method gets updated in Tab Manager anyways for all the tabs.

We can raise the `NotImplementedError`. 
created_at: 2025-04-17T16:16:48Z
author_association: MEMBER

PR comments:
user: login: codecov[bot]
created_at: 2025-03-21T20:41:42Z
body: ## [Codecov](https://app.codecov.io/gh/dipy/dipy/pull/3520?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dipy) Report
Attention: Patch coverage is `80.95238%` with `4 lines` in your changes missing coverage. Please review.
> Project coverage is 83.70%. Comparing base [(`863d0cd`)](https://app.codecov.io/gh/dipy/dipy/commit/863d0cd8a28a7739f9efd80f3e9b8962185e4d7d?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dipy) to head [(`df52e89`)](https://app.codecov.io/gh/dipy/dipy/commit/df52e8915f7f3b5aeb3f85456e7b83ce34d0fe9a?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dipy).

| [Files with missing lines](https://app.codecov.io/gh/dipy/dipy/pull/3520?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dipy) | Patch % | Lines |
|---|---|---|
| [dipy/viz/horizon/tab/slice.py](https://app.codecov.io/gh/dipy/dipy/pull/3520?src=pr&el=tree&filepath=dipy%2Fviz%2Fhorizon%2Ftab%2Fslice.py&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dipy#diff-ZGlweS92aXovaG9yaXpvbi90YWIvc2xpY2UucHk=) | 60.00% | [1 Missing and 1 partial :warning: ](https://app.codecov.io/gh/dipy/dipy/pull/3520?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dipy) |
| [dipy/viz/horizon/tab/surface.py](https://app.codecov.io/gh/dipy/dipy/pull/3520?src=pr&el=tree&filepath=dipy%2Fviz%2Fhorizon%2Ftab%2Fsurface.py&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dipy#diff-ZGlweS92aXovaG9yaXpvbi90YWIvc3VyZmFjZS5weQ==) | 60.00% | [1 Missing and 1 partial :warning: ](https://app.codecov.io/gh/dipy/dipy/pull/3520?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dipy) |

<details><summary>Additional details and impacted files</summary>


[![Impacted file tree graph](https://app.codecov.io/gh/dipy/dipy/pull/3520/graphs/tree.svg?width=650&height=150&src=pr&token=T6X4M9BSJd&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dipy)](https://app.codecov.io/gh/dipy/dipy/pull/3520?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dipy)

```diff
@@           Coverage Diff           @@
##           master    #3520   +/-   ##
=======================================
  Coverage   83.69%   83.70%           
=======================================
  Files         161      161           
  Lines       23978    23998   +20     
  Branches     3624     3628    +4     
=======================================
+ Hits        20068    20087   +19     
+ Misses       2950     2949    -1     
- Partials      960      962    +2     
```

| [Files with missing lines](https://app.codecov.io/gh/dipy/dipy/pull/3520?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dipy) | Coverage Δ | |
|---|---|---|
| [dipy/viz/horizon/app.py](https://app.codecov.io/gh/dipy/dipy/pull/3520?src=pr&el=tree&filepath=dipy%2Fviz%2Fhorizon%2Fapp.py&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dipy#diff-ZGlweS92aXovaG9yaXpvbi9hcHAucHk=) | `51.74% <100.00%> (ø)` | |
| [dipy/viz/horizon/tab/peak.py](https://app.codecov.io/gh/dipy/dipy/pull/3520?src=pr&el=tree&filepath=dipy%2Fviz%2Fhorizon%2Ftab%2Fpeak.py&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dipy#diff-ZGlweS92aXovaG9yaXpvbi90YWIvcGVhay5weQ==) | `85.83% <100.00%> (+0.61%)` | :arrow_up: |
| [dipy/viz/horizon/tab/roi.py](https://app.codecov.io/gh/dipy/dipy/pull/3520?src=pr&el=tree&filepath=dipy%2Fviz%2Fhorizon%2Ftab%2Froi.py&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dipy#diff-ZGlweS92aXovaG9yaXpvbi90YWIvcm9pLnB5) | `87.50% <100.00%> (+2.31%)` | :arrow_up: |
| [dipy/viz/horizon/tab/slice.py](https://app.codecov.io/gh/dipy/dipy/pull/3520?src=pr&el=tree&filepath=dipy%2Fviz%2Fhorizon%2Ftab%2Fslice.py&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dipy#diff-ZGlweS92aXovaG9yaXpvbi90YWIvc2xpY2UucHk=) | `55.45% <60.00%> (+0.11%)` | :arrow_up: |
| [dipy/viz/horizon/tab/surface.py](https://app.codecov.io/gh/dipy/dipy/pull/3520?src=pr&el=tree&filepath=dipy%2Fviz%2Fhorizon%2Ftab%2Fsurface.py&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dipy#diff-ZGlweS92aXovaG9yaXpvbi90YWIvc3VyZmFjZS5weQ==) | `85.00% <60.00%> (-3.58%)` | :arrow_down: |

... and [1 file with indirect coverage changes](https://app.codecov.io/gh/dipy/dipy/pull/3520/indirect-changes?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dipy)

</details>
<details><summary> :rocket: New features to boost your workflow: </summary>

- :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
</details>
author_association: NONE
user: login: skoudoro
created_at: 2025-04-07T16:41:37Z
body: I already communicated to @maharshi-gor that tests need to be added before merging. 

he is working on it.
author_association: MEMBER
user: login: maharshi-gor
created_at: 2025-04-17T13:37:14Z
body: Hi @skoudoro this PR is ready to go!
author_association: MEMBER