-
Notifications
You must be signed in to change notification settings - Fork 1
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
Add right click menu and styling #29
Merged
Merged
Changes from 10 commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
ebbd784
add group layer delegate
K-Meech c186b6d
rename group layer delegate
K-Meech ac0448c
add context for right click menu
K-Meech 60b16e5
working minimal right click menu
K-Meech c482acb
fix syncing of selection
K-Meech 7046b43
update docstrings and remove unused functions
K-Meech f1a7b2c
fix double click edit
K-Meech e6d2955
simplify right click actions and context to fix tests
K-Meech bdce75c
update docstrings
K-Meech ae0705d
Force widget in tests to have a parent (#35)
willGraham01 7801954
merge upstream changes
K-Meech 4db5c2a
Allows toggling visibility of group layers (#30)
K-Meech 6a2c425
remove logger and thumbnail role
K-Meech dfc0eb5
fix view controls when switching from group to layer
K-Meech File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
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,85 @@ | ||
from __future__ import annotations | ||
|
||
from typing import TYPE_CHECKING, List | ||
|
||
from app_model.types import Action | ||
from qtpy.QtCore import QPoint | ||
from qtpy.QtWidgets import QAction, QMenu | ||
|
||
from napari_experimental.group_layer import GroupLayer | ||
|
||
if TYPE_CHECKING: | ||
from qtpy.QtWidgets import QWidget | ||
|
||
|
||
class GroupLayerActions: | ||
"""Class holding all GroupLayerActions to be shown in the right click | ||
context menu. Based on structure in napari/layers/_layer_actions and | ||
napari/app_model/actions/_layerlist_context_actions | ||
|
||
Parameters | ||
---------- | ||
group_layers: GroupLayer | ||
Group layers to apply actions to | ||
""" | ||
|
||
def __init__(self, group_layers: GroupLayer) -> None: | ||
self.group_layers = group_layers | ||
|
||
self.actions: List[Action] = [ | ||
Action( | ||
id="napari:grouplayer:toggle_visibility", | ||
title="toggle_visibility", | ||
callback=self._toggle_visibility, | ||
) | ||
] | ||
|
||
def _toggle_visibility(self): | ||
"""Toggle the visibility of all selected layers inside the | ||
group layers""" | ||
for item in self.group_layers.selection: | ||
if not item.is_group(): | ||
visibility = item.layer.visible | ||
item.layer.visible = not visibility | ||
|
||
|
||
class ContextMenu(QMenu): | ||
"""Simplified context menu for the right click options. All actions are | ||
populated from GroupLayerActions. | ||
|
||
Parameters | ||
---------- | ||
group_layer_actions: GroupLayerActions | ||
Group layer actions used to populate actions in this menu | ||
title: str, optional | ||
Optional title for the menu | ||
parent: QWidget, optional | ||
Optional parent widget | ||
""" | ||
|
||
def __init__( | ||
self, | ||
group_layer_actions: GroupLayerActions, | ||
title: str | None = None, | ||
parent: QWidget | None = None, | ||
): | ||
QMenu.__init__(self, parent) | ||
self.group_layer_actions = group_layer_actions | ||
if title is not None: | ||
self.setTitle(title) | ||
self._populate_actions() | ||
|
||
def _populate_actions(self): | ||
"""Populate menu actions from GroupLayerActions""" | ||
for gl_action in self.group_layer_actions.actions: | ||
action = QAction(gl_action.title, parent=self) | ||
action.triggered.connect(gl_action.callback) | ||
self.addAction(action) | ||
|
||
def exec_(self, pos: QPoint): | ||
"""For now, rebuild actions every time the menu is shown. Otherwise, | ||
it doesn't react properly when items have been added/removed from | ||
the group_layer root""" | ||
self.clear() | ||
self._populate_actions() | ||
super().exec_(pos) |
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,122 @@ | ||
from __future__ import annotations | ||
|
||
from pathlib import Path | ||
from typing import TYPE_CHECKING | ||
|
||
from napari._qt.containers._base_item_model import ItemRole | ||
from napari._qt.containers._layer_delegate import LayerDelegate | ||
from napari._qt.containers.qt_layer_model import ThumbnailRole | ||
from napari._qt.qt_resources import QColoredSVGIcon | ||
from qtpy.QtCore import QPoint, QSize, Qt | ||
from qtpy.QtGui import QMouseEvent, QPainter, QPixmap | ||
|
||
from napari_experimental.group_layer_actions import ( | ||
ContextMenu, | ||
GroupLayerActions, | ||
) | ||
|
||
if TYPE_CHECKING: | ||
from qtpy import QtCore | ||
from qtpy.QtWidgets import QStyleOptionViewItem | ||
|
||
from napari_experimental.group_layer_qt import ( | ||
QtGroupLayerModel, | ||
QtGroupLayerView, | ||
) | ||
|
||
|
||
class GroupLayerDelegate(LayerDelegate): | ||
"""A QItemDelegate specialized for painting group layer objects.""" | ||
|
||
def get_layer_icon( | ||
self, option: QStyleOptionViewItem, index: QtCore.QModelIndex | ||
): | ||
"""Add the appropriate QIcon to the item based on the layer type. | ||
Same as LayerDelegate, but pulls folder icons from inside this plugin. | ||
""" | ||
item = index.data(ItemRole) | ||
if item is None: | ||
return | ||
if item.is_group(): | ||
expanded = option.widget.isExpanded(index) | ||
icon_name = "folder-open" if expanded else "folder" | ||
icon_path = ( | ||
Path(__file__).parent / "resources" / f"{icon_name}.svg" | ||
) | ||
icon = QColoredSVGIcon(str(icon_path)) | ||
else: | ||
icon_name = f"new_{item.layer._type_string}" | ||
try: | ||
icon = QColoredSVGIcon.from_resources(icon_name) | ||
except ValueError: | ||
return | ||
# guessing theme rather than passing it through. | ||
bg = option.palette.color(option.palette.ColorRole.Window).red() | ||
option.icon = icon.colored(theme="dark" if bg < 128 else "light") | ||
option.decorationSize = QSize(18, 18) | ||
option.decorationPosition = ( | ||
option.Position.Right | ||
) # put icon on the right | ||
option.features |= option.ViewItemFeature.HasDecoration | ||
|
||
def _paint_thumbnail( | ||
self, | ||
painter: QPainter, | ||
option: QStyleOptionViewItem, | ||
index: QtCore.QModelIndex, | ||
): | ||
"""paint the layer thumbnail - same as in LayerDelegate, but allows | ||
there to be no thumbnail for group layers""" | ||
thumb_rect = option.rect.translated(-2, 2) | ||
h = index.data(Qt.ItemDataRole.SizeHintRole).height() - 4 | ||
thumb_rect.setWidth(h) | ||
thumb_rect.setHeight(h) | ||
image = index.data(ThumbnailRole) | ||
if image is not None: | ||
painter.drawPixmap(thumb_rect, QPixmap.fromImage(image)) | ||
|
||
def editorEvent( | ||
self, | ||
event: QtCore.QEvent, | ||
model: QtCore.QAbstractItemModel, | ||
option: QStyleOptionViewItem, | ||
index: QtCore.QModelIndex, | ||
) -> bool: | ||
"""Called when an event has occurred in the editor""" | ||
# if the user clicks quickly on the visibility checkbox, we *don't* | ||
# want it to be interpreted as a double-click. Ignore this event. | ||
if event.type() == QMouseEvent.MouseButtonDblClick: | ||
self.initStyleOption(option, index) | ||
style = option.widget.style() | ||
check_rect = style.subElementRect( | ||
style.SubElement.SE_ItemViewItemCheckIndicator, | ||
option, | ||
option.widget, | ||
) | ||
if check_rect.contains(event.pos()): | ||
return True | ||
|
||
# refer all other events to LayerDelegate | ||
return super().editorEvent(event, model, option, index) | ||
|
||
def show_context_menu( | ||
self, | ||
index: QtCore.QModelIndex, | ||
model: QtGroupLayerModel, | ||
pos: QPoint, | ||
parent: QtGroupLayerView, | ||
): | ||
"""Show the group layer context menu. | ||
To add a new item to the menu, update the GroupLayerActions. | ||
""" | ||
item = index.data(ItemRole) | ||
|
||
# For now, don't show the right click context menu for groups. | ||
if not item.is_group(): | ||
if not hasattr(self, "_context_menu"): | ||
self._group_layer_actions = GroupLayerActions(model._root) | ||
self._context_menu = ContextMenu( | ||
self._group_layer_actions, parent=parent | ||
) | ||
|
||
self._context_menu.exec_(pos) |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Personally I've been going with this when I only want to iterate though either nodes or groups - "item" is generic and can refer to either nodes or groups but here we really are just using the nodes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point! Although now I've merged the
km/group-vis-toggle
branch, it needs to iterate through both groups and nodes to set visibility on both