8000 Enh: Add zoom options and keybindings to View menu by psobolewskiPhD · Pull Request #7200 · napari/napari · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Enh: Add zoom options and keybindings to View menu #7200

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

Merged
merged 12 commits into from
Sep 23, 2024
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
2 changes: 2 additions & 0 deletions napari/_app_model/constants/_menus.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ def contributables(cls) -> set['MenuId']:
class MenuGroup:
NAVIGATION = 'navigation' # always the first group in any menu
RENDER = '1_render'
# View menu
ZOOM = 'zoom'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI I think the order that groups are shown in the menu is lexigraphic (which is why we have '1' and '2' preceding group names). This is fine, just letting you know

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah ngl I have no idea what the structure of this MenuGroup class really is... should Plugins stuff actually be a subclass, like LAYERLIST_CONTEXT? And also File stuff? And then this would be the only group in the View subclass? Agreed we can leave as is for now but should probably decide and update docstring

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just noticed the subclasses! I mean maybe that is what Talley had in mind as I see it was you and I that added the plugins and file menu bar ones, while the others sit within subclasses. I have no idea with 'RENDER' means though?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have no idea with 'RENDER' means though?

lol neither. But it seems like maybe the top level groups are maybe supposed to be used across other menus? Right now we use them in View menu and Help menu but I don't think that answers any questions about its semantics lol

# Plugins menubar
PLUGINS = '1_plugins'
PLUGIN_MULTI_SUBMENU = '2_plugin_multi_submenu'
Expand Down
33 changes: 33 additions & 0 deletions napari/_qt/_qapp_model/_tests/test_view_menu.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,3 +189,36 @@ def test_toggle_layer_tooltips(make_napari_viewer, qtbot):
# Restore layer tooltip visibility
app.commands.execute_command(action_id)
assert not _get_current_tooltip_visibility()


def test_zoom_actions(make_napari_viewer):
"""Test zoom actions"""
viewer = make_napari_viewer()
app = get_app_model()

viewer.add_image(np.ones((10, 10, 10)))

# get initial zoom state
initial_zoom = viewer.camera.zoom

# Check zoom in action
app.commands.execute_command('napari.viewer.camera.zoom_in')
assert viewer.camera.zoom == pytest.approx(1.5 * initial_zoom)

# Check zoom out action
app.commands.execute_command('napari.viewer.camera.zoom_out')
assert viewer.camera.zoom == pytest.approx(initial_zoom)

viewer.camera.zoom = 2
# Check reset zoom action
app.commands.execute_command('napari.viewer.fit_to_view')
assert viewer.camera.zoom == pytest.approx(initial_zoom)

# Check that angle is preserved
viewer.dims.ndisplay = 3
viewer.camera.angles = (90, 0, 0)
viewer.camera.zoom = 2
app.commands.execute_command('napari.viewer.fit_to_view')
# Zoom should be reset, but angle unchanged
assert viewer.camera.zoom == pytest.approx(initial_zoom)
assert viewer.camera.angles == (90, 0, 0)
52 changes: 52 additions & 0 deletions napari/_qt/_qapp_model/qactions/_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from napari._qt.qt_viewer import QtViewer
from napari.settings import get_settings
from napari.utils.translations import trans
from napari.viewer import Viewer

# View submenus
VIEW_SUBMENUS = [
Expand Down Expand Up @@ -61,6 +62,18 @@ def _get_current_tooltip_visibility() -> bool:
return get_settings().appearance.layer_tooltip_visibility


def _fit_to_view(viewer: Viewer):
viewer.reset_view(reset_camera_angle=False)


def _zoom_in(viewer: Viewer):
viewer.camera.zoom *= 1.5


def _zoom_out(viewer: Viewer):
viewer.camera.zoom /= 1.5


Q_VIEW_ACTIONS: list[Action] = [
Action(
id='napari.window.view.toggle_fullscreen',
Expand Down Expand Up @@ -113,6 +126,45 @@ def _get_current_tooltip_visibility() -> bool:
keybindings=[{'primary': KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KeyP}],
toggled=ToggleRule(get_current=_get_current_play_status),
),
Action(
id='napari.viewer.fit_to_view',
title=trans._('Fit to View'),
menus=[
{
'id': MenuId.MENUBAR_VIEW,
'group': MenuGroup.ZOOM,
'order': 1,
}
],
callback=_fit_to_view,
keybindings=[StandardKeyBinding.OriginalSize],
),
Action(
id='napari.viewer.camera.zoom_in',
title=trans._('Zoom In'),
menus=[
{
'id': MenuId.MENUBAR_VIEW,
'group': MenuGroup.ZOOM,
'order': 1,
}
],
callback=_zoom_in,
keybindings=[StandardKeyBinding.ZoomIn],
),
Action(
id='napari.viewer.camera.zoom_out',
title=trans._('Zoom Out'),
menus=[
{
'id': MenuId.MENUBAR_VIEW,
'group': MenuGroup.ZOOM,
'order': 1,
}
],
callback=_zoom_out,
keybindings=[StandardKeyBinding.ZoomOut],
),
Action(
id='napari.window.view.toggle_activity_dock',
title=trans._('Toggle Activity Dock'),
Expand Down
7 changes: 5 additions & 2 deletions napari/components/viewer_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,9 @@ def _sliced_extent_world_augmented(self) -> np.ndarray:
)
return self.layers._extent_world_augmented[:, self.dims.displayed]

def reset_view(self, *, margin: float = 0.05) -> None:
def reset_view(
self, *, margin: float = 0.05, reset_camera_angle: bool = True
) -> None:
"""Reset the camera view.

Parameters
Expand Down Expand Up @@ -426,7 +428,8 @@ def reset_view(self, *, margin: float = 0.05) -> None:
self.camera.zoom = scale_factor * np.min(
np.array(self._canvas_size) / scale
)
self.camera.angles = (0, 0, 90)
if reset_camera_angle:
self.camera.angles = (0, 0, 90)

# Emit a reset view event, which is no longer used internally, but
# which maybe useful for building on napari.
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ classifiers = [
requires-python = ">=3.9"
dependencies = [
"appdirs>=1.4.4",
"app-model>=0.2.8,<0.3.0",
"app-model>=0.3.0,<0.4.0",
"cachey>=0.2.1",
"certifi>=2018.1.18",
"dask[array]>=2021.10.0",
Expand Down
2 changes: 1 addition & 1 deletion resources/constraints/constraints_py3.10.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ alabaster==1.0.0
# via sphinx
annotated-types==0.7.0
# via pydantic
app-model==0.2.8
app-model==0.3.0
# via napari (napari_repo/pyproject.toml)
appdirs==1.4.4
# via
Expand Down
2 changes: 1 addition & 1 deletion resources/constraints/constraints_py3.10_docs.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ anyio==4.4.0
# via
# starlette
# watchfiles
app-model==0.2.8
app-model==0.3.0
# via napari (napari_repo/pyproject.toml)
appdirs==1.4.4
# via
Expand Down
2 changes: 1 addition & 1 deletion resources/constraints/constraints_py3.10_pydantic_1.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# uv pip compile --python-version 3.10 --output-file napari_repo/resources/constraints/constraints_py3.10_pydantic_1.txt napari_repo/pyproject.toml napari_repo/resources/constraints/version_denylist.txt napari_repo/resources/constraints/pydantic_le_2.txt --extra pyqt5 --extra pyqt6 --extra pyside2 --extra pyside6_experimental --extra testing --extra testing_extra --extra optional
alabaster==1.0.0
# via sphinx
app-model==0.2.8
app-model==0.3.0
# via napari (napari_repo/pyproject.toml)
appdirs==1.4.4
# via
Expand Down
2 changes: 1 addition & 1 deletion resources/constraints/constraints_py3.10_windows.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ alabaster==1.0.0
# via sphinx
annotated-types==0.7.0
# via pydantic
app-model==0.2.8
app-model==0.3.0
# via napari (napari_repo/pyproject.toml)
appdirs==1.4.4
# via
Expand Down
2 changes: 1 addition & 1 deletion resources/constraints/constraints_py3.11.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ alabaster==1.0.0
# via sphinx
annotated-types==0.7.0
# via pydantic
app-model==0.2.8
app-model==0.3.0
# via napari (napari_repo/pyproject.toml)
appdirs==1.4.4
# via
Expand Down
2 changes: 1 addition & 1 deletion resources/constraints/constraints_py3.11_docs.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ anyio==4.4.0
# via
# starlette
# watchfiles
app-model==0.2.7
app-model==0.3.0
# via napari (pyproject.toml)
appdirs==1.4.4
# via
Expand Down
2 changes: 1 addition & 1 deletion resources/constraints/constraints_py3.11_pydantic_1.txt
Original file line number Diff line number Diff line change
C94A Expand Up @@ -2,7 +2,7 @@
# uv pip compile --python-version 3.11 --output-file napari_repo/resources/constraints/constraints_py3.11_pydantic_1.txt napari_repo/pyproject.toml napari_repo/resources/constraints/version_denylist.txt napari_repo/resources/constraints/pydantic_le_2.txt --extra pyqt5 --extra pyqt6 --extra pyside2 --extra pyside6_experimental --extra testing --extra testing_extra --extra optional
alabaster==1.0.0
# via sphinx
app-model==0.2.8
app-model==0.3.0
# via napari (napari_repo/pyproject.toml)
appdirs==1.4.4
# via
Expand Down
2 changes: 1 addition & 1 deletion resources/constraints/constraints_py3.11_windows.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ alabaster==1.0.0
# via sphinx
annotated-types==0.7.0
# via pydantic
app-model==0.2.8
app-model==0.3.0
# via napari (napari_repo/pyproject.toml)
appdirs==1.4.4
# via
Expand Down
2 changes: 1 addition & 1 deletion resources/constraints/constraints_py3.12.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ alabaster==1.0.0
# via sphinx
annotated-types==0.7.0
# via pydantic
app-model==0.2.8
app-model==0.3.0
# via napari (napari_repo/pyproject.toml)
appdirs==1.4.4
# via
Expand Down
2 changes: 1 addition & 1 deletion resources/constraints/constraints_py3.12_pydantic_1.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# uv pip compile --python-version 3.12 --output-file napari_repo/resources/constraints/constraints_py3.12_pydantic_1.txt napari_repo/pyproject.toml napari_repo/resources/constraints/version_denylist.txt napari_repo/resources/constraints/pydantic_le_2.txt --extra pyqt5 --extra pyqt6 --extra pyside2 --extra pyside6_experimental --extra testing --extra testing_extra --extra optional
alabaster==1.0.0
# via sphinx
app-model==0.2.8
app-model==0.3.0
# via napari (napari_repo/pyproject.toml)
appdirs==1.4.4
# via
Expand Down
2 changes: 1 addition & 1 deletion resources/constraints/constraints_py3.12_windows.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ alabaster==1.0.0
# via sphinx
annotated-types==0.7.0
# via pydantic
app-model==0.2.8
app-model==0.3.0
# via napari (napari_repo/pyproject.toml)
appdirs==1.4.4
# via
Expand Down
2 changes: 1 addition & 1 deletion resources/constraints/constraints_py3.9.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ alabaster==0.7.16
# via sphinx
annotated-types==0.7.0
# via pydantic
app-model==0.2.8
app-model==0.3.0
# via napari (napari_repo/pyproject.toml)
appdirs==1.4.4
# via
Expand Down
2 changes: 1 addition & 1 deletion resources/constraints/constraints_py3.9_examples.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ alabaster==0.7.16
# via sphinx
annotated-types==0.7.0
# via pydantic
app-model==0.2.8
app-model==0.3.0
# via napari (napari_repo/pyproject.toml)
appdirs==1.4.4
# via
Expand Down
2 changes: 1 addition & 1 deletion resources/constraints/constraints_py3.9_pydantic_1.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# uv pip compile --python-version 3.9 --output-file napari_repo/resources/constraints/constraints_py3.9_pydantic_1.txt napari_repo/pyproject.toml napari_repo/resources/constraints/version_denylist.txt napari_repo/resources/constraints/pydantic_le_2.txt --extra pyqt5 --extra pyqt6 --extra pyside2 --extra pyside6_experimental --extra testing --extra testing_extra --extra optional
alabaster==0.7.16
# via sphinx
app-model==0.2.8
app-model==0.3.0
# via napari (napari_repo/pyproject.toml)
appdirs==1.4.4
# via
Expand Down
2 changes: 1 addition & 1 deletion resources/constraints/constraints_py3.9_windows.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ alabaster==0.7.16
# via sphinx
annotated-types==0.7.0
# via pydantic
app-model==0.2.8
app-model==0.3.0
# via napari (napari_repo/pyproject.toml)
appdirs==1.4.4
# via
Expand Down
Loading
0