-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
implement manager view and dialog (#124)
* implement manager view and dialog and delegate tooltips to views * add up-to-date check to manager view + tweak to avoid hard-coded column index * make get_tooltip public and more robust * improve test coverage found a tiny bug in the process! * use reload module to patch thread_worker so it's picked up by coverage * Improve docstring Co-authored-by: Igor Tatarnikov <61896994+IgorTatarnikov@users.noreply.github.com> --------- Co-authored-by: Igor Tatarnikov <61896994+IgorTatarnikov@users.noreply.github.com>
- Loading branch information
1 parent
a5497f8
commit 014f5c5
Showing
11 changed files
with
447 additions
and
85 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
from bg_atlasapi.list_atlases import get_all_atlases_lastversions | ||
|
||
|
||
def format_atlas_name(name: str) -> str: | ||
"""Format an atlas name nicely. | ||
Assumes input in the form of atlas_name_in_snake_case_RESOLUTIONum, | ||
e.g. allen_mouse_100um""" | ||
assert name in get_all_atlases_lastversions().keys(), "invalid atlas name!" | ||
formatted_name = name.split("_") | ||
formatted_name[0] = formatted_name[0].capitalize() | ||
formatted_name[-1] = f"({formatted_name[-1].split('um')[0]} \u03BCm)" | ||
return " ".join([formatted for formatted in formatted_name]) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
"""The purpose of this file is to provide interactive table view to download | ||
and update atlases. Users interacting with the table can request to | ||
* download an atlas (double-click on row of a not-yet downloaded atlas) | ||
* update an atlas (double-click on row of outdated local atlas) | ||
They can also hover over an up-to-date local atlas and see that | ||
it's up to date. | ||
It is designed to be agnostic from the viewer framework by emitting signals | ||
that any interested observers can connect to. | ||
""" | ||
|
||
from typing import Callable | ||
|
||
from bg_atlasapi.list_atlases import ( | ||
get_all_atlases_lastversions, | ||
get_atlases_lastversions, | ||
get_downloaded_atlases, | ||
) | ||
from bg_atlasapi.update_atlases import install_atlas, update_atlas | ||
from napari.qt import thread_worker | ||
from qtpy.QtCore import Signal | ||
from qtpy.QtWidgets import QTableView, QWidget | ||
|
||
from brainrender_napari.data_models.atlas_table_model import AtlasTableModel | ||
from brainrender_napari.utils.formatting import format_atlas_name | ||
from brainrender_napari.widgets.atlas_manager_dialog import AtlasManagerDialog | ||
|
||
|
||
class AtlasManagerView(QTableView): | ||
download_atlas_confirmed = Signal(str) | ||
update_atlas_confirmed = Signal(str) | ||
|
||
def __init__(self, parent: QWidget = None): | ||
"""Initialises an atlas table view with latest atlas versions. | ||
Also responsible for appearance, behaviour on selection, and | ||
setting up signal-slot connections. | ||
""" | ||
super().__init__(parent) | ||
|
||
self.setModel(AtlasTableModel(AtlasManagerView)) | ||
self.setEnabled(True) | ||
self.verticalHeader().hide() | ||
self.resizeColumnsToContents() | ||
|
||
self.setSelectionBehavior(QTableView.SelectionBehavior.SelectRows) | ||
self.setSelectionMode(QTableView.SelectionMode.SingleSelection) | ||
|
||
self.doubleClicked.connect(self._on_row_double_clicked) | ||
self.hideColumn( | ||
self.model().column_headers.index("Raw name") | ||
) # hide raw name | ||
|
||
def _on_row_double_clicked(self): | ||
atlas_name = self.selected_atlas_name() | ||
if atlas_name in get_downloaded_atlases(): | ||
up_to_date = get_atlases_lastversions()[atlas_name]["updated"] | ||
if not up_to_date: | ||
update_dialog = AtlasManagerDialog(atlas_name, "Update") | ||
update_dialog.ok_button.clicked.connect( | ||
self._on_update_atlas_confirmed | ||
) | ||
update_dialog.exec() | ||
else: | ||
download_dialog = AtlasManagerDialog(atlas_name, "Download") | ||
download_dialog.ok_button.clicked.connect( | ||
self._on_download_atlas_confirmed | ||
) | ||
download_dialog.exec() | ||
|
||
def _on_download_atlas_confirmed(self): | ||
"""Downloads the currently selected atlas and signals this.""" | ||
atlas_name = self.selected_atlas_name() | ||
worker = self._apply_in_thread(install_atlas, atlas_name) | ||
worker.returned.connect(self.download_atlas_confirmed.emit) | ||
worker.start() | ||
|
||
def _on_update_atlas_confirmed(self): | ||
"""Updates the currently selected atlas and signals this.""" | ||
atlas_name = self.selected_atlas_name() | ||
worker = self._apply_in_thread(update_atlas, atlas_name) | ||
worker.returned.connect(self.update_atlas_confirmed.emit) | ||
worker.start() | ||
|
||
def selected_atlas_name(self) -> str: | ||
"""A single place to get a valid selected atlas name.""" | ||
selected_index = self.selectionModel().currentIndex() | ||
assert selected_index.isValid() | ||
selected_atlas_name_index = selected_index.siblingAtColumn(0) | ||
selected_atlas_name = self.model().data(selected_atlas_name_index) | ||
return selected_atlas_name | ||
|
||
@thread_worker | ||
def _apply_in_thread(self, apply: Callable, atlas_name: str): | ||
"""Calls `apply` on the given atlas in a separate thread.""" | ||
apply(atlas_name) | ||
self.model().refresh_data() | ||
return atlas_name | ||
|
||
@classmethod | ||
def get_tooltip_text(cls, atlas_name: str): | ||
"""Returns the atlas name as a formatted string, | ||
as well as instructions on how to interact with the atlas.""" | ||
if atlas_name in get_downloaded_atlases(): | ||
is_up_to_date = get_atlases_lastversions()[atlas_name]["updated"] | ||
if is_up_to_date: | ||
tooltip_text = f"{format_atlas_name(atlas_name)} is up-to-date" | ||
else: # needs updating | ||
tooltip_text = ( | ||
f"{format_atlas_name(atlas_name)} (double-click to update)" | ||
) | ||
elif atlas_name in get_all_atlases_lastversions().keys(): | ||
tooltip_text = ( | ||
f"{format_atlas_name(atlas_name)} (double-click to download)" | ||
) | ||
else: | ||
raise ValueError("Tooltip text called with invalid atlas name.") | ||
return tooltip_text |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.