def make_checkboxes(pt): c = Container(labels=False) dt = [ ("cameras", DeviceType.CameraDevice), ("shutters", DeviceType.ShutterDevice), ("stages", DeviceType.StageDevice), ("wheels, turrets, etc.", DeviceType.StateDevice), ("other devices", OTHER_DEVICES), ] for label, dtype in dt: @magicgui(auto_call=True, dt={"bind": dtype}, vis={"label": label}) def toggle(vis: bool = True, dt=None): pt.set_dtype_visibility(dt, visible=vis) toggle.name = label[:2] c.append(toggle) @magicgui(auto_call=True, show={"label": "Show read-only"}) def show_ro(show: bool = True): pt.show_read_only = show c.append(show_ro) return c
def to_container(self, **kwargs) -> Container: """Return a ``magicgui.widgets.Container`` for this MagicSignature.""" from magicgui.widgets import Container return Container( widgets=list(self.widgets(kwargs.get("app")).values()), **kwargs, )
def create_gui_nms_slider(self, shape_layer): from magicgui.widgets import FloatSlider, Container, Label from torchvision.ops import nms slider = FloatSlider(min=0.0, max=1.0, step=0.01, name='NMS') slider_label = Label(name='IoU_threshold') container = Container(widgets=[slider, slider_label]) def change_boxes(event, shape_layer=shape_layer): # remove all shapes from layer self.select_all_shapes(shape_layer) shape_layer.remove_selected() # create mask and new information boxes = torch.tensor([ make_bbox_napari(box, reverse=True) for box in shape_layer.metadata['boxes'] ]) scores = torch.tensor(shape_layer.metadata['scores']) if boxes.size()[0] > 0: mask = nms(boxes, scores, slider.value) # torch.tensor mask = (np.array(mask), ) new_boxes = np.asarray(shape_layer.metadata['boxes'])[mask] new_labels = shape_layer.metadata['labels'][mask] new_scores = shape_layer.metadata['scores'][mask] # set the current properties as workaround shape_layer.current_properties['labels'] = new_labels shape_layer.current_properties['scores'] = new_scores # add shapes to layer if new_boxes.size > 0: shape_layer.add(list(new_boxes)) # set the properties shape_layer.properties['labels'] = new_labels shape_layer.properties['scores'] = new_scores # set temporary information shape_layer.metadata['boxes_nms'] = list(new_boxes) shape_layer.metadata['labels_nms'] = new_labels shape_layer.metadata['scores_nms'] = new_scores # change label slider_label.value = str(slider.value) slider.changed.connect(change_boxes) # invoke nms container.NMS.value = 1.0 # event triggered when the name of the shape layer changes self.shape_layer.events.name.connect(change_boxes) return container
def __init__(self, mmcore=None, parent=None): super().__init__(parent) self.pt = PropTable(mmcore) self.le = LineEdit(label="Filter:") self.le.native.setPlaceholderText("Filter...") self.le.changed.connect(self._on_le_change) right = Container(widgets=[self.le, self.pt], labels=False) self.cb = make_checkboxes(self.pt) self.cb.native.layout().addStretch() self.cb.native.layout().setSpacing(0) self.pt.show() self.cb.show() self._container = Container( layout="horizontal", widgets=[self.cb, right], labels=False ) self._container.margins = 0, 0, 0, 0 self.setLayout(QHBoxLayout()) self.setContentsMargins(0, 0, 0, 0) self.layout().addWidget(self._container.native)
def create_label_menu(shapes_layer, label_feature, labels): """Create a label menu widget that can be added to the napari viewer dock Parameters ---------- shapes_layer : napari.layers.Shapes a napari shapes layer label_feature : str the name of the shapes feature to use the displayed text labels : List[str] list of the possible text labels values. Returns ------- label_widget : magicgui.widgets.Container the container widget with the label combobox """ # Create the label selection menu label_menu = ComboBox(label='text label', choices=labels) label_widget = Container(widgets=[label_menu]) def update_label_menu(): """This is a callback function that updates the label menu when the default features of the Shapes layer change """ new_label = str(shapes_layer.feature_defaults[label_feature][0]) if new_label != label_menu.value: label_menu.value = new_label shapes_layer.events.feature_defaults.connect(update_label_menu) def set_selected_features_to_default(): """This is a callback that updates the feature values of the currently selected shapes. This is a side-effect of the deprecated current_properties setter, but does not occur when modifying feature_defaults.""" indices = list(shapes_layer.selected_data) default_value = shapes_layer.feature_defaults[label_feature][0] shapes_layer.features[label_feature][indices] = default_value shapes_layer.events.features() shapes_layer.events.feature_defaults.connect( set_selected_features_to_default) shapes_layer.events.features.connect(shapes_layer.refresh_text) def label_changed(value: str): """This is a callback that update the default features on the Shapes layer when the label menu selection changes """ shapes_layer.feature_defaults[label_feature] = value shapes_layer.events.feature_defaults() label_menu.changed.connect(label_changed) return label_widget
def __init__(self, mmcore=None, parent=None): super().__init__(parent) self.setLayout(QVBoxLayout()) self.setContentsMargins(0, 0, 0, 0) self._container = Container( widgets=[ get_editor_widget(prop, mmcore) for prop in iter_dev_props(mmcore) if LIGHT_LIST.search(prop.name) and prop.has_range ], labels=True, ) self.layout().addWidget(self._container.native)
def create_gui_score_slider(self, shape_layer): from magicgui.widgets import FloatSlider, Container, Label slider = FloatSlider(min=0.0, max=1.0, step=0.05, name='Score', value=0.0) slider_label = Label(name='Score_threshold', value=0.0) container = Container(widgets=[slider, slider_label]) def change_boxes(event, shape_layer=shape_layer): # remove all shapes from layer self.select_all_shapes(shape_layer) shape_layer.remove_selected() # create mask and new information mask = np.where(shape_layer.metadata['scores'] > slider.value) new_boxes = np.asarray(shape_layer.metadata['boxes'])[mask] new_labels = shape_layer.metadata['labels'][mask] new_scores = shape_layer.metadata['scores'][mask] # set the current properties as workaround shape_layer.current_properties['labels'] = new_labels shape_layer.current_properties['scores'] = new_scores # add shapes to layer if new_boxes.size > 0: shape_layer.add(list(new_boxes)) # set the properties shape_layer.properties['labels'] = new_labels shape_layer.properties['scores'] = new_scores # change label slider_label.value = str(slider.value) slider.changed.connect(change_boxes) # invoke scoring container.Score.value = 0.0 # event triggered when the name of the shape layer changes self.shape_layer.events.name.connect(change_boxes) return container
def create_label_menu(shapes_layer, label_property, labels): """Create a label menu widget that can be added to the napari viewer dock Parameters ---------- shapes_layer : napari.layers.Shapes a napari shapes layer label_property : str the name of the shapes property to use the displayed text labels : List[str] list of the possible text labels values. Returns ------- label_widget : magicgui.widgets.Container the container widget with the label combobox """ # Create the label selection menu label_menu = ComboBox(label='text label', choices=labels) label_widget = Container(widgets=[label_menu]) def update_label_menu(event): """This is a callback function that updates the label menu when the current properties of the Shapes layer change """ new_label = str(shapes_layer.current_properties[label_property][0]) if new_label != label_menu.value: label_menu.value = new_label shapes_layer.events.current_properties.connect(update_label_menu) def label_changed(event): """This is a callback that update the current properties on the Shapes layer when the label menu selection changes """ selected_label = event.value current_properties = shapes_layer.current_properties current_properties[label_property] = np.asarray([selected_label]) shapes_layer.current_properties = current_properties label_menu.changed.connect(label_changed) return label_widget
def create_gui_text_properties(self, shape_layer): TextColor = Combobox( choices=shape_layer._colors, name="text color", value="white" ) TextSize = Slider(min=1, max=50, name="text size", value=1) container = Container(widgets=[TextColor, TextSize]) def change_text_color(event): # This changes the text color shape_layer.text.color = str(TextColor.value) def change_text_size(event): # This changes the text size shape_layer.text.size = int(TextSize.value) TextColor.changed.connect(change_text_color) TextSize.changed.connect(change_text_size) return container
def create_label_menu(points_layer, labels): """Create a label menu widget that can be added to the napari viewer dock Parameters: ----------- points_layer : napari.layers.Points a napari points layer labels : List[str] list of the labels for each keypoint to be annotated (e.g., the body parts to be labeled). Returns: -------- label_menu : QComboBox the label menu qt widget """ # Create the label selection menu label_menu = ComboBox(label='feature_label', choices=labels) label_widget = Container(widgets=[label_menu]) def update_label_menu(event): """Update the label menu when the point selection changes""" new_label = str(points_layer.current_properties['label'][0]) if new_label != label_menu.value: label_menu.value = new_label points_layer.events.current_properties.connect(update_label_menu) def label_changed(event): """Update the Points layer when the label menu selection changes""" selected_label = event.value current_properties = points_layer.current_properties current_properties['label'] = np.asarray([selected_label]) points_layer.current_properties = current_properties label_menu.changed.connect(label_changed) return label_widget
"""Demonstrates decorating a method. Once the class is instantiated, `instance.method_name` will return a FunctionGui in which the instance will always be provided as the first argument (i.e. "self") when the FunctionGui or method is called. """ from magicgui import event_loop, magicgui from magicgui.widgets import Container class MyObject: def __init__(self, name): self.name = name self.counter = 0.0 @magicgui(auto_call=True) def method(self, sigma: float = 0): print( f"instance: {self.name}, counter: {self.counter}, sigma: {sigma}") self.counter = self.counter + sigma return self.name with event_loop(): a = MyObject("a") b = MyObject("b") container = Container(widgets=[a.method, b.method]) container.show() assert a.method() == "a" assert b.method() == "b"
def track_explorer(R, keep_only=False): """ Track explorer app. Written using magicgui (Thanks @tlambert03!) Allows one to easily browse through tracks, plot the data and see the corresponding movies. Can also be used for curation and quality control. Parameters: keep_only : [False] Bool - If true, only tracks that are in PosLbl.track_to_use will be loaded in a given position. This can be used to filter unwanted tracks before examining for quality with the explorer. """ from typing import List import matplotlib import matplotlib.pyplot as plt import numpy as np from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg from magicgui import magicgui from magicgui.widgets import Checkbox, PushButton, Container from oyLabImaging.Processing.imvisutils import get_or_create_viewer from scipy import stats from napari import run from natsort import natsorted cmaps = ['cyan', 'magenta', 'yellow', 'red', 'green', 'blue'] viewer = get_or_create_viewer() matplotlib.use('Agg') mpl_fig = plt.figure() ax = mpl_fig.add_subplot(111) fc = FigureCanvasQTAgg(mpl_fig) #attr_list = ['area', 'convex_area','centroid','perimeter','eccentricity','solidity','inertia_tensor_eigvals', 'orientation'] #todo: derive list from F regioprops position = list(natsorted(R.PosLbls.keys()))[0] PosLbl0 = R.PosLbls[position] attr_list = [ f for f in list(PosLbl0.framelabels[0].regionprops) if not f.startswith(('mean', 'median', 'max', 'min', '90th', 'slice')) ] attr_cmap = plt.cm.get_cmap('tab20b', len(attr_list)).colors @magicgui( auto_call=True, position={ "choices": natsorted([str(a) for a in R.PosLbls.keys()]) }, track_id={ "choices": range(R.PosLbls[sorted([str(a) for a in R.PosLbls.keys() ])[0]].get_track(0).numtracks) }, channels={ "widget_type": "Select", "choices": list(R.channels) }, features={ "widget_type": "Select", "choices": attr_list }, ) def widget(position: List[str], track_id: int, channels: List[str], features: List[str]): # preserving these parameters for things that the graphing function # needs... so that anytime this is called we have to graph. ... # do your graphing here PosLbl = R.PosLbls[position] t0 = PosLbl.get_track(track_id) ax.cla() ax.set_xlabel('Timepoint') ax.set_ylabel('kAU') ch_choices = widget.channels.choices for ch in channels: ax.plot(t0.T, stats.zscore(t0.mean(ch)), color=cmaps[ch_choices.index(ch)]) f_choices = widget.features.choices for ch in features: feat_to_plot = eval("t0.prop('" + ch + "')") if np.ndim(feat_to_plot) == 1: ax.plot(t0.T, stats.zscore(feat_to_plot, nan_policy='omit'), '--', color=attr_cmap[f_choices.index(ch)], alpha=0.33) else: mini_cmap = plt.cm.get_cmap('jet', np.shape(feat_to_plot)[1]) for dim in np.arange(np.shape(feat_to_plot)[1]): ax.plot(t0.T, stats.zscore(feat_to_plot[:, dim], nan_policy='omit'), '--', color=mini_cmap(dim), alpha=0.33) #ax.plot(t0.T, feat_to_plot[:,dim],'--', color=mini_cmap(dim), alpha=0.25) ax.legend(channels + features) fc.draw() @widget.position.changed.connect def _on_position_changed(): PosLbl = R.PosLbls[widget.position.value] try: PosLbl.track_to_use except: PosLbl.track_to_use = [] viewer.layers.clear() #update track_id choices - bug in choices: if keep_only: J = PosLbl.track_to_use else: J = range(PosLbl.get_track(0).numtracks) widget.track_id.choices = [] widget.track_id.choices = J #update keep_btn value #keep_btn.value= widget.track_id.value in PosLbl.track_to_use @widget.track_id.changed.connect def _on_track_changed(new_track: int): PosLbl = R.PosLbls[widget.position.value] viewer.layers.clear() keep_btn.value = widget.track_id.value in PosLbl.track_to_use #print("you cahnged to ", new_track) movie_btn = PushButton(text="Movie") widget.insert(1, movie_btn) @movie_btn.clicked.connect def _on_movie_clicked(): PosLbl = R.PosLbls[widget.position.value] channels = widget.channels.get_value() track_id = widget.track_id.get_value() t0 = PosLbl.get_track(track_id) viewer.layers.clear() ch_choices = widget.channels.choices t0.show_movie( Channel=channels, cmaps=[cmaps[ch_choices.index(ch)] for ch in channels]) btn = PushButton(text="NEXT") widget.insert(-1, btn) @btn.clicked.connect def _on_next_clicked(): choices = widget.track_id.choices current_index = choices.index(widget.track_id.value) widget.track_id.value = choices[(current_index + 1) % (len(choices))] PosLbl = R.PosLbls[widget.position.value] try: PosLbl.track_to_use except: PosLbl.track_to_use = [] keep_btn = Checkbox(text="Keep") keep_btn.value = widget.track_id.value in PosLbl.track_to_use widget.append(keep_btn) @keep_btn.clicked.connect def _on_keep_btn_clicked(value: bool): #print("keep is now", value) PosLbl = R.PosLbls[widget.position.value] if value == True: if widget.track_id.value not in PosLbl.track_to_use: PosLbl.track_to_use.append(widget.track_id.value) if value == False: if widget.track_id.value in PosLbl.track_to_use: PosLbl.track_to_use.remove(widget.track_id.value) R.PosLbls[widget.position.value] = PosLbl # widget.native # ... points to the underlying backend widget container = Container(layout='horizontal') # magicgui container expect magicgui objects # but we can access and modify the underlying QLayout # https://doc.qt.io/qt-5/qlayout.html#addWidget layout = container.native.layout() layout.addWidget(fc) layout.addWidget(widget.native) # adding native, because we're in Qt #container.show(run=True) # OR viewer.window.add_dock_widget(container) #run() matplotlib.use('Qt5Agg')