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
Remove layer ndisplay event #5491
Remove layer ndisplay event #5491
Conversation
@jni : this is my attempt at removing @alisterburt : you might also be interested in this, as it hits a bit on |
Codecov Report
@@ Coverage Diff @@
## main #5491 +/- ##
=======================================
Coverage 89.30% 89.31%
=======================================
Files 600 600
Lines 51011 51011
=======================================
+ Hits 45556 45561 +5
+ Misses 5455 5450 -5
Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here. |
@@ -227,11 +227,11 @@ def changeRendering(self, text): | |||
This will make nearer objects appear more prominent. | |||
""" | |||
self.layer.rendering = text | |||
self._toggle_rendering_parameter_visbility() | |||
self._update_rendering_parameter_visbility() |
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.
Old typo in "visibility" :)
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 catch! Fixed!
viewer.dims.events.ndisplay.connect(self._on_ndisplay_change) | ||
|
||
# TODO: I think this is needed if viewer is initialized with ndisplay=3, | ||
# but maybe we should do this differently. | ||
self._on_ndisplay_change(Event('ndisplay', value=viewer.dims.ndisplay)) | ||
|
||
def _on_ndisplay_change(self, event): | ||
for widget in self.widgets.values(): | ||
if widget is not self.empty_widget: | ||
widget.ndisplay = event.value |
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.
This is kind ahow we handle VispyLayer
s in the backend (calling reset()
), so it makes sense to me. Not sure we need the event actually, since you can simply do:
self._on_display_change()
and then
def _on_ndisplay_change(self):
for widget in self.widgets.values():
if widget is not self.empty_widget:
widget.ndisplay = viewer.dims.ndisplay
Just like we handle all these things in VispyLayer
s.
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 to know. I actually realized that _on_ndisplay_change
will never do anything at this point since self.widgets
only contains self.empty_widget
, so I just deleted this.
|
||
def changeDepiction(self, text): | ||
self.layer.depiction = text | ||
self._toggle_plane_parameter_visibility() | ||
self._update_plane_parameter_visibility() |
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.
I made these changes to clarify that this is updating based on other state, rather than a true toggle of the plane control visibility.
self.attenuationLabel.hide() | ||
|
||
def _toggle_plane_parameter_visibility(self): | ||
iso_threshold_visible = rendering == ImageRendering.ISO |
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.
I made this changes to be a bit more concise and functional.
Thanks for the review @brisvag ! As you gave this a first pass and I addressed your feedback, I changed this from draft to ready to review. If you're happy to approve/reject this, please do! I think the main talking point of this PR is adding the |
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.
I think the changes make perfect sense, the state should be held here, imo. I like the small logic simplification as well. I'm not sure I like the kwarg
approach, but I don't consider this blocking!
def __init__(self, layer): | ||
super().__init__(layer) |
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.
One thing that I'm not sure about is having this as a parameter; is that necessary, or can we just initialize it like everything else and only update it via events?
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.
The main reason I did it this way is that if a layer is added to the viewer (i.e. without ndisplay
changing), we have to assign the correct value of from QtLayerControlsContainer
to the layer control panel somehow. Passing the value as an initialization parameter means that any other initialization that might be dependent on that value has access to it immediately. It's also more atomic - i.e. we can avoid initializing then setting either explicitly or through manually calling a callback/event.
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.
Does that explanation seem good enough? Just wondering before I change **kwargs
to *, ndisplay: int = 2
for the other layer types.
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.
I actually lean more towards explicitly calling the callback at the end of the init. This is what we do with all the VispyLayer
s (and soon Overlays
), and since I'm familiar with that part of the codebase, doing it that way feels more "consistent" to me.
However, since here the controls do not have references to the viewer, we can just do it in the container by adding this line to _add()
:
class QtLayerControlsContainer(QStackedWidget):
def _add(self, event):
...
controls.ndisplay = viewer.ndisplay
...
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.
I lean the other way, but don't feel too strongly. So in the interest of trying new things and being more consistent with other parts of the code base, I made the changes you suggested.
Yeah, I don't like that too much either. I did it out of laziness, especially since this was a draft PR. But now, I'm happy to go in and just explicitly expand the signature to include |
@@ -148,7 +165,6 @@ def _remove(self, event): | |||
layer = event.value | |||
controls = self.widgets[layer] | |||
self.removeWidget(controls) | |||
# controls.close() |
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.
I have a passion for deleting commented code that lacks any explanatory comments.
qtctrl = QtImageControls(layer) | ||
qtbot.addWidget(qtctrl) | ||
|
||
layer._slice_dims(ndisplay=3) |
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.
Since QtImageControls.ndisplay == 2
is the default on initialization now, I reordered the test here to make it a little shorter and understandable (i.e. one mutation instead of two).
Since this has approval and I've addressed some optional comments, I'll assume this is good to merge. But it's also Friday for me, so I'll probably wait until Monday comes around to push the green button. So feel free to object and suggest alternatives before then. |
This removes the private event `Layer.events._ndisplay`. This was needed to notify the layer control widgets, some of which should only be presented in 2D others in 3D. Now, each layer control widget stores `ndisplay` as a public mutable property and triggers similar updates when `ViewerModel.dims.ndisplay` changes. There are two motivations for this change. 1. It makes `Layer._slice_dims` simpler and more clearly similar to `Layer.refresh`. 2. We use and test against the public API of the widgets and model rather than rely on a private event. The main downside of this change is that the layer control widget has some extra state (`ndisplay`) that we need to keep in sync. Initially, I hoped we could pass through the new value of `ndisplay` through events (rather than store it as state), but things are complex enough in `QtImageControls` to make that difficult. This is mostly a refactor with no napari public API changes. Partly motivated by #4795 - [x] all existing tests pass with my change
This removes the private event `Layer.events._ndisplay`. This was needed to notify the layer control widgets, some of which should only be presented in 2D others in 3D. Now, each layer control widget stores `ndisplay` as a public mutable property and triggers similar updates when `ViewerModel.dims.ndisplay` changes. There are two motivations for this change. 1. It makes `Layer._slice_dims` simpler and more clearly similar to `Layer.refresh`. 2. We use and test against the public API of the widgets and model rather than rely on a private event. The main downside of this change is that the layer control widget has some extra state (`ndisplay`) that we need to keep in sync. Initially, I hoped we could pass through the new value of `ndisplay` through events (rather than store it as state), but things are complex enough in `QtImageControls` to make that difficult. This is mostly a refactor with no napari public API changes. Partly motivated by #4795 - [x] all existing tests pass with my change
This removes the private event `Layer.events._ndisplay`. This was needed to notify the layer control widgets, some of which should only be presented in 2D others in 3D. Now, each layer control widget stores `ndisplay` as a public mutable property and triggers similar updates when `ViewerModel.dims.ndisplay` changes. There are two motivations for this change. 1. It makes `Layer._slice_dims` simpler and more clearly similar to `Layer.refresh`. 2. We use and test against the public API of the widgets and model rather than rely on a private event. The main downside of this change is that the layer control widget has some extra state (`ndisplay`) that we need to keep in sync. Initially, I hoped we could pass through the new value of `ndisplay` through events (rather than store it as state), but things are complex enough in `QtImageControls` to make that difficult. This is mostly a refactor with no napari public API changes. Partly motivated by #4795 - [x] all existing tests pass with my change
This removes the private event `Layer.events._ndisplay`. This was needed to notify the layer control widgets, some of which should only be presented in 2D others in 3D. Now, each layer control widget stores `ndisplay` as a public mutable property and triggers similar updates when `ViewerModel.dims.ndisplay` changes. There are two motivations for this change. 1. It makes `Layer._slice_dims` simpler and more clearly similar to `Layer.refresh`. 2. We use and test against the public API of the widgets and model rather than rely on a private event. The main downside of this change is that the layer control widget has some extra state (`ndisplay`) that we need to keep in sync. Initially, I hoped we could pass through the new value of `ndisplay` through events (rather than store it as state), but things are complex enough in `QtImageControls` to make that difficult. This is mostly a refactor with no napari public API changes. Partly motivated by #4795 - [x] all existing tests pass with my change
This removes the private event `Layer.events._ndisplay`. This was needed to notify the layer control widgets, some of which should only be presented in 2D others in 3D. Now, each layer control widget stores `ndisplay` as a public mutable property and triggers similar updates when `ViewerModel.dims.ndisplay` changes. There are two motivations for this change. 1. It makes `Layer._slice_dims` simpler and more clearly similar to `Layer.refresh`. 2. We use and test against the public API of the widgets and model rather than rely on a private event. The main downside of this change is that the layer control widget has some extra state (`ndisplay`) that we need to keep in sync. Initially, I hoped we could pass through the new value of `ndisplay` through events (rather than store it as state), but things are complex enough in `QtImageControls` to make that difficult. This is mostly a refactor with no napari public API changes. Partly motivated by #4795 - [x] all existing tests pass with my change
Description
This removes the private event
Layer.events._ndisplay
. This was needed to notify the layer control widgets, some of which should only be presented in 2D others in 3D. Now, each layer control widget storesndisplay
as a public mutable property and triggers similar updates whenViewerModel.dims.ndisplay
changes.There are two motivations for this change.
Layer._slice_dims
simpler and more clearly similar toLayer.refresh
.The main downside of this change is that the layer control widget has some extra state (
ndisplay
) that we need to keep in sync. Initially, I hoped we could pass through the new value ofndisplay
through events (rather than store it as state), but things are complex enough inQtImageControls
to make that difficult.Type of change
This is mostly a refactor with no napari public API changes.
References
Partly motivated by #4795
How has this been tested?