Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement a simple atlas manager widget #153

Merged
merged 6 commits into from
Jul 25, 2024
Merged
Show file tree
Hide file tree
Changes from 5 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
2 changes: 1 addition & 1 deletion brainrender_napari/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
__version__ = "0.0.1"


__all__ = "BrainrenderWidget"
__all__ = "BrainrenderViewerWidget, BrainrenderManagerWidget"
54 changes: 54 additions & 0 deletions brainrender_napari/brainrender_manager_widget.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
"""
A napari widget to download and update atlases.

Available atlases are shown in a table view using the Qt model/view framework
[Qt Model/View framework](https://doc.qt.io/qt-6/model-view-programming.html)
"""

from brainglobe_utils.qtpy.logo import header_widget
from napari.viewer import Viewer
from qtpy.QtWidgets import (
QGroupBox,
QVBoxLayout,
QWidget,
)

from brainrender_napari.widgets.atlas_manager_view import AtlasManagerView


class BrainrenderManagerWidget(QWidget):
"""The purpose of this class is
* to hold atlas visualisation widgets for napari
* coordinate between these widgets and napari
"""

def __init__(self, napari_viewer: Viewer):
"""Instantiates the atlas viewer widget
and sets up coordinating connections"""
super().__init__()

self._viewer = napari_viewer
self.setLayout(QVBoxLayout())
self.layout().addWidget(
header_widget(
"brainrender",
"Atlas management",
tutorial_file_name="manage-atlas-napari.html",
citation_doi="https://doi.org/10.7554/eLife.65751",
alessandrofelder marked this conversation as resolved.
Show resolved Hide resolved
github_repo_name="brainrender-napari",
help_text="For help, hover the cursor over the "
"atlases/regions.",
)
)

# create widgets
self.atlas_manager_view = AtlasManagerView(parent=self)

# add widgets to the layout as group boxes
self.atlas_manager_group = QGroupBox("Atlas Manager")
self.atlas_manager_group.setToolTip(
"Double-click on row to download/update an atlas"
)
self.atlas_manager_group.setLayout(QVBoxLayout())
self.atlas_manager_group.layout().addWidget(self.atlas_manager_view)
self.layout().addWidget(self.atlas_manager_group)
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
from brainrender_napari.widgets.structure_view import StructureView


class BrainrenderWidget(QWidget):
class BrainrenderViewerWidget(QWidget):
"""The purpose of this class is
* to hold atlas visualisation widgets for napari
* coordinate between these widgets and napari
Expand Down
13 changes: 9 additions & 4 deletions brainrender_napari/napari.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,14 @@ name: brainrender-napari
display_name: brainrender
contributions:
commands:
- id: brainrender-napari.make_brainrender_widget
python_name: brainrender_napari.brainrender_widget:BrainrenderWidget
title: Make Brainrender Napari
- id: brainrender-napari.make_brainrender_viewer_widget
python_name: brainrender_napari.brainrender_viewer_widget:BrainrenderViewerWidget
title: Make Brainrender Napari Viewer
- id: brainrender-napari.make_brainrender_manager_widget
python_name: brainrender_napari.brainrender_manager_widget:BrainrenderManagerWidget
title: Make Brainrender Napari Manager
widgets:
- command: brainrender-napari.make_brainrender_widget
- command: brainrender-napari.make_brainrender_viewer_widget
display_name: Brainrender
- command: brainrender-napari.make_brainrender_manager_widget
display_name: Manage atlas versions
32 changes: 32 additions & 0 deletions tests/test_integration/test_brainrender_manager_widget.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
"""These tests should just check that the subwidget signal and napari
are connected as expected. Lower level tests should happen in the tests
for the widget themselves."""

import pytest

from brainrender_napari.brainrender_manager_widget import (
BrainrenderManagerWidget,
)


@pytest.fixture
def manager_widget(make_napari_viewer) -> BrainrenderManagerWidget:
"""Fixture to expose the atlas viewer widget to tests.

Simultaneously acts as a smoke test that the widget
can be instantiated without crashing."""
viewer = make_napari_viewer()
return BrainrenderManagerWidget(viewer)


def test_atlas_manager_view_tooltip(manager_widget):
for expected_keyword in [
"double-click",
"download/update",
"row",
"atlas",
]:
assert (
expected_keyword
in manager_widget.atlas_manager_group.toolTip().lower()
)
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,19 @@

import pytest

from brainrender_napari.brainrender_widget import BrainrenderWidget
from brainrender_napari.brainrender_viewer_widget import (
BrainrenderViewerWidget,
)


@pytest.fixture
def brainrender_widget(make_napari_viewer) -> BrainrenderWidget:
def viewer_widget(make_napari_viewer) -> BrainrenderViewerWidget:
"""Fixture to expose the atlas viewer widget to tests.

Simultaneously acts as a smoke test that the widget
can be instantiated without crashing."""
viewer = make_napari_viewer()
return BrainrenderWidget(viewer)
return BrainrenderViewerWidget(viewer)


@pytest.mark.parametrize(
Expand All @@ -25,12 +27,12 @@ def brainrender_widget(make_napari_viewer) -> BrainrenderWidget:
],
)
def test_checkbox_visibility(
brainrender_widget, mocker, expected_visibility, atlas
viewer_widget, mocker, expected_visibility, atlas
):
checkbox_visibility_mock = mocker.patch(
"brainrender_napari.brainrender_widget.QCheckBox.setVisible"
"brainrender_napari.brainrender_viewer_widget.QCheckBox.setVisible"
)
brainrender_widget._on_atlas_selection_changed(atlas)
viewer_widget._on_atlas_selection_changed(atlas)
checkbox_visibility_mock.assert_called_once_with(expected_visibility)


Expand All @@ -43,82 +45,78 @@ def test_checkbox_visibility(
],
)
def test_double_click_on_locally_available_atlas_row(
brainrender_widget, mocker, qtbot, expected_atlas_name
viewer_widget, mocker, qtbot, expected_atlas_name
):
"""Check for a few local low-res atlases that double-clicking them
on the atlas viewer view calls the expected atlas representation function.
"""
add_atlas_to_viewer_mock = mocker.patch(
"brainrender_napari.brainrender_widget"
"brainrender_napari.brainrender_viewer_widget"
".NapariAtlasRepresentation.add_to_viewer"
)
with qtbot.waitSignal(
brainrender_widget.atlas_viewer_view.add_atlas_requested
):
brainrender_widget.atlas_viewer_view.add_atlas_requested.emit(
with qtbot.waitSignal(viewer_widget.atlas_viewer_view.add_atlas_requested):
viewer_widget.atlas_viewer_view.add_atlas_requested.emit(
expected_atlas_name
)
add_atlas_to_viewer_mock.assert_called_once()


def test_structure_row_double_clicked(brainrender_widget, mocker):
def test_structure_row_double_clicked(viewer_widget, mocker):
"""Checks that when the structure view widgets emit "VS" and
the allen_mouse_100um atlas is selected, the NapariAtlasRepresentation
function is called in the expected way.
"""
add_structure_to_viewer_mock = mocker.patch(
"brainrender_napari.brainrender_widget"
"brainrender_napari.brainrender_viewer_widget"
".NapariAtlasRepresentation.add_structure_to_viewer"
)
brainrender_widget.atlas_viewer_view.selectRow(
viewer_widget.atlas_viewer_view.selectRow(
4
) # allen_mouse_100um is in row 4

brainrender_widget.structure_view.add_structure_requested.emit("VS")
viewer_widget.structure_view.add_structure_requested.emit("VS")
add_structure_to_viewer_mock.assert_called_once_with("VS")


def test_add_additional_reference_selected(brainrender_widget, mocker):
def test_add_additional_reference_selected(viewer_widget, mocker):
"""Checks that when the atlas viewer view requests an additional
reference, the NapariAtlasRepresentation function is called in
the expected way."""
add_additional_reference_mock = mocker.patch(
"brainrender_napari.brainrender_widget"
"brainrender_napari.brainrender_viewer_widget"
".NapariAtlasRepresentation.add_additional_reference"
)
brainrender_widget.atlas_viewer_view.selectRow(
5
) # mpin_zfish_1um is in row 5
viewer_widget.atlas_viewer_view.selectRow(5) # mpin_zfish_1um is in row 5
assert (
brainrender_widget.atlas_viewer_view.selected_atlas_name()
viewer_widget.atlas_viewer_view.selected_atlas_name()
== "mpin_zfish_1um"
)
additional_reference_name = "GAD1b"
brainrender_widget.atlas_viewer_view.additional_reference_requested.emit(
viewer_widget.atlas_viewer_view.additional_reference_requested.emit(
additional_reference_name
)
add_additional_reference_mock.assert_called_once_with(
additional_reference_name
)


def test_show_structures_checkbox(brainrender_widget, mocker):
def test_show_structures_checkbox(viewer_widget, mocker):
structure_view_refresh_mock = mocker.patch(
"brainrender_napari.brainrender_widget.StructureView.refresh"
"brainrender_napari.brainrender_viewer_widget.StructureView.refresh"
)
brainrender_widget.atlas_viewer_view.selectRow(
viewer_widget.atlas_viewer_view.selectRow(
0
) # example_mouse_100um is in row 0
structure_view_refresh_mock.assert_called_with(
"example_mouse_100um", False
)

brainrender_widget.show_structure_names.click()
viewer_widget.show_structure_names.click()
assert structure_view_refresh_mock.call_count == 2
structure_view_refresh_mock.assert_called_with("example_mouse_100um", True)


def test_structure_view_tooltip(brainrender_widget):
def test_structure_view_tooltip(viewer_widget):
for expected_keyword in [
"double-click",
"atlas region",
Expand All @@ -131,11 +129,11 @@ def test_structure_view_tooltip(brainrender_widget):
]:
assert (
expected_keyword
in brainrender_widget.structure_tree_group.toolTip().lower()
in viewer_widget.structure_tree_group.toolTip().lower()
)


def test_atlas_viewer_view_tooltip(brainrender_widget):
def test_atlas_viewer_view_tooltip(viewer_widget):
for expected_keyword in [
"double-click",
"add",
Expand All @@ -146,5 +144,5 @@ def test_atlas_viewer_view_tooltip(brainrender_widget):
]:
assert (
expected_keyword
in brainrender_widget.atlas_viewer_group.toolTip().lower()
in viewer_widget.atlas_viewer_group.toolTip().lower()
)
Loading