Closed pmcesky closed 2 years ago
This isn't a difference between tracks or layers, it has more to do with using magicgui widgets inside of QWidgets and expecting all the magic to work the same :)
The way magicgui can update those comboboxes to show the current layers is by keeping an eye on any reparenting events, so that if you, for example, create a widget not in a viewer, and then add it to a viewer with add_dock_widget
, it knows to go look for layers.
If you want to combine Qt and magicgui like this, you'll need to recreate what magicgui was doing for you. Take this example, and note the commented out parts. Without it, neither tracks nor points work. With it, they both work.
import numpy as np
from magicgui.widgets import create_widget
from qtpy.QtWidgets import QFormLayout, QWidget
import napari
import napari.layers
class W(QWidget):
def __init__(self):
super().__init__()
self.setLayout(QFormLayout())
self._pnts = create_widget(annotation=napari.layers.Points)
self._trx = create_widget(annotation=napari.layers.Tracks)
self.layout().addRow('points', self._pnts.native)
self.layout().addRow('tracks', self._trx.native)
v = napari.Viewer()
v.add_points(None)
v.add_tracks(np.arange(20).reshape(5, 4))
wdg = W()
v.window.add_dock_widget(wdg)
# # Notify that the parent has changed.
# # NOTE: without these two lines, *neither* layer type works.
# wdg._trx._emit_parent()
# wdg._pnts._emit_parent()
napari.run()
The trick is knowing when the parent changes. You can see some advice for doing that in a response I gave here: https://github.com/napari/napari/issues/3659
Hi @tlambert03, thank you very much! This is my first time encountering and learning about this, your explanation and code example are really helpful. Thanks a lot!
One question, like your code above, the last two lines let the QWidget instance know they have been docked to the napari window. This works when you explicitly add the QWidget to the napari window by napari.viewer.window.add_dock_widget(). In another case, when building a plugin by:
@napari_hook_implementation
def napari_experimental_provide_dock_widget():
return [W]
What should we do to let it happen like in your code above?
As mentioned above You can see some advice for doing that in a response I gave here: napari/napari#3659
Let me know, after reading that issue, if it's not clear
Hi Talley, I checked the advice you give on napari/napari#3659. Though I'm not clear about the code and method you use, but it seems it uses event filter to catch the reparent event and let the QWidget know it happens. And I borrowed the codes about the eventfilter part there and now it works! I'm surprised when the QWidget knows the event, it starts to look for the layers. That's absolutely magic.
Thanks very much!
One question, in your code example above:
# # Notify that the parent has changed.
# # NOTE: without these two lines, *neither* layer type works.
wdg._trx._emit_parent()
wdg._pnts._emit_parent()
What is the ._emit_parent(), where is it from? I did not find it in magicgui and pyqt5.
What is the ._emit_parent(), where is it from? I did not find it in magicgui and pyqt5.
(actually, you might be better off using the non-private method: self.parent_changed.emit(self.parent)
)
Thanks so much!
This issue has been mentioned on Image.sc Forum. There might be relevant details there:
https://forum.image.sc/t/composing-workflows-in-napari/61222/2
This issue has been mentioned on Image.sc Forum. There might be relevant details there:
https://forum.image.sc/t/access-viewer-object-within-magicgui-widget-wrapper/61366/6
Describe the bug I was trying to load the tracks layer to a widget, using the code like below:
from napari.layers import Image, Labels, Tracks from magicgui.widgets import create_widget from qtpy.QtWidgets import QWidget, QPushButton, QLabel, QHBoxLayout, QVBoxLayout, QLineEdit
self.setLayout(QVBoxLayout()) self.tracks_select = create_widget(annotation=Tracks, label="tracks_layer") tracks_layer_selection_container = QWidget() tracks_layer_selection_container.setLayout(QHBoxLayout()) tracks_layer_selection_container.layout().addWidget(QLabel("Tracks layer")) tracks_layer_selection_container.layout().addWidget(self.tracks_select.native) self.layout().addWidget(tracks_layer_selection_container)
The code works for image and labels layer, as in https://github.com/BiAPoL/napari-clusters-plotter/blob/main/napari_clusters_plotter/_measure.py but fails for tracks layer. The running result is like this: If use "self.tracks_select = create_widget(label="tracks_layer")" instead, the widget would be like:
As you can see, the image and labels layer could be selected, but the tracks layer could not be selected in both cases. What's wrong? Is tracks layer supported by create_widget? What is the right way to do it?
Environment (please complete the following information):