def test_container_widget(): """Test basic container functionality.""" container = widgets.Container(labels=False) labela = widgets.Label(value="hi", name="labela") labelb = widgets.Label(value="hi", name="labelb") container.append(labela) container.extend([labelb]) # different ways to index assert container[0] == labela assert container["labelb"] == labelb assert container[:1] == [labela] assert container[-1] == labelb with pytest.raises(NotImplementedError): container[0] = "something" assert container.layout == "vertical" with pytest.raises(NotImplementedError): container.layout = "horizontal" assert all(x in dir(container) for x in ["labela", "labelb"]) assert container.margins container.margins = (8, 8, 8, 8) assert container.margins == (8, 8, 8, 8) del container[1:] del container[-1] assert not container
def test_containers_show_nested_containers(): """make sure showing a container shows a nested FunctionGui.""" @magicgui def func(x: int, y: str): pass assert not func.visible c2 = widgets.Container(widgets=[func]) assert not c2.visible c2.show() assert c2.visible and func.visible
def test_delete_widget(): """We can delete widgets from containers.""" a = widgets.Label(name="a") container = widgets.Container(widgets=[a]) # we can delete widgets del container.a with pytest.raises(AttributeError): getattr(container, "a") # they disappear from the layout with pytest.raises(ValueError): container.index(a)
def test_container_removal(): c = widgets.Container() s = widgets.Slider(label="label") assert len(c) == 0 assert c.native.layout().count() == 0 c.append(s) assert len(c) == 1 assert c.native.layout().count() == 1 c.pop() assert len(c) == 0 assert c.native.layout().count() == 0
def test_visible_in_container(): """Test that visibility depends on containers.""" w1 = widgets.Label(value="hi", name="w1") w2 = widgets.Label(value="hi", name="w2") w3 = widgets.Label(value="hi", name="w2", visible=False) container = widgets.Container(widgets=[w2, w3]) assert not w1.visible assert not w2.visible assert not w3.visible assert not container.visible container.show() assert container.visible assert w2.visible assert not w3.visible w1.show() assert w1.visible
def test_container_label_widths(): """Test basic container functionality.""" container = widgets.Container(layout="vertical") labela = widgets.Label(value="hi", name="labela") labelb = widgets.Label(value="hi", name="I have a very long label") def _label_width(): measure = use_app().get_obj("get_text_width") return max( measure(w.label) for w in container if not isinstance(w, widgets._bases.ButtonWidget)) container.append(labela) before = _label_width() container.append(labelb) assert _label_width() > before
def test_labeled_widget_container(): """Test that _LabeledWidgets follow their children.""" from magicgui.widgets._concrete import _LabeledWidget w1 = widgets.Label(value="hi", name="w1") w2 = widgets.Label(value="hi", name="w2") container = widgets.Container(widgets=[w1, w2], layout="vertical") assert w1._labeled_widget lw = w1._labeled_widget() assert isinstance(lw, _LabeledWidget) assert not lw.visible container.show() assert w1.visible assert lw.visible w1.hide() assert not w1.visible assert not lw.visible w1.label = "another label" assert lw._label_widget.value == "another label"
def test_container_indexing_with_native_mucking(): """Mostly make sure that the inner model isn't messed up. keeping indexes with a manipulated native model *may* be something to do in future. """ l1 = widgets.Label(name="l1") l2 = widgets.Label(name="l2") l3 = widgets.Label(name="l3") c = widgets.Container(widgets=[l1, l2, l3]) assert c[-1] == l3 # so far they should be in sync native = c.native.layout() assert native.count() == len(c) # much with native layout native.addStretch() # haven't changed the magicgui container assert len(c) == 3 assert c[-1] == l3 # though it has changed the native model assert native.count() == 4
def test_reset_choice_recursion(): """Test that reset_choices recursion works for multiple types of widgets.""" x = 0 def get_choices(widget): nonlocal x x += 1 return list(range(x)) @magicgui(c={"choices": get_choices}) def f(c): pass assert f.c.choices == (0, ) container = widgets.Container(widgets=[f]) container.reset_choices() assert f.c.choices == (0, 1) container.reset_choices() assert f.c.choices == (0, 1, 2)
def test_basic_widget_attributes(): """Basic test coverage for getting/setting attributes.""" widget = widgets.create_widget(value=1, name="my_name") container = widgets.Container(labels=False) assert widget.enabled widget.enabled = False assert not widget.enabled assert not widget.visible widget.show() assert widget.visible assert widget.parent is None container.append(widget) assert widget.parent is container.native widget.parent = None assert widget.parent is None assert widget.label == "my name" widget.label = "A different label" assert widget.label == "A different label" assert widget.width < 100 widget.width = 150 assert widget.width == 150 assert widget.param_kind == inspect.Parameter.POSITIONAL_OR_KEYWORD widget.param_kind = inspect.Parameter.KEYWORD_ONLY widget.param_kind = "positional_only" assert widget.param_kind == inspect.Parameter.POSITIONAL_ONLY with pytest.raises(KeyError): widget.param_kind = "not a proper param type" with pytest.raises(TypeError): widget.param_kind = 1 assert repr(widget) == "SpinBox(value=1, annotation=None, name='my_name')" assert widget.options == { "max": 1000, "min": 0, "step": 1, "enabled": False, "visible": False, }
# alternatively, you can the `widget.called` signal to connect a callback function # where the result of the function being called is at `event.value` def _on_func_a(event): func_b.input.value = event.value func_a.called.connect(_on_func_a) @magicgui( auto_call=True, input={"visible": False, "max": 100000}, result_widget=True, labels=False, ) def func_c(input: int, format: str = "({} + {}) * {} is {}") -> str: print("calling func_c\n") return format.format(func_a.x.value, func_a.y.value, func_b.mult.value, input) container = widgets.Container( widgets=[func_a, func_b, func_c], layout="vertical", labels=False ) container.native.setMinimumWidth(500) func_a() container.show(run=True) # notice which functions get called when you change each widget.
@magicgui(ri={"choices": ["Oil", "Water", "Air"]}, auto_call=True) def as_list(ri="Water"): print("refractive index is", Medium[ri].value) @magicgui(auto_call=True) def as_enum(ri: Medium = Medium.Water): print("refractive index is", ri.value) @magicgui(ri={"choices": [("Oil", 1.515), ("Water", 1.33), ("Air", 1.0)]}, auto_call=True) def as_2tuple(ri=1.33): print("refractive index is", ri) def get_choices(gui): return [("Oil", 1.515), ("Water", 1.33), ("Air", 1.0)] @magicgui(ri={"choices": get_choices}, auto_call=True) def as_function(ri: float): print("refractive index is", ri) container = widgets.Container( widgets=[as_list, as_enum, as_2tuple, as_function], layout="vertical") container.show(run=True)
def create_selector_widget(self, label_layer): # TODO: Generalize this. Instead of 0, 1, 2, 3, 4: Arbitrary class numbers. Ability to add classes & name them? choices = ['Deselect', 'Class 1', 'Class 2', 'Class 3', 'Class 4'] selector = widgets.RadioButtons(choices=choices, label='Selection Class:', value='Class 1') save_button = widgets.PushButton(value=True, text='Save Classifier') run_button = widgets.PushButton(value=True, text='Run Classifier') container = widgets.Container( widgets=[selector, save_button, run_button]) # TODO: Add text field & button to save classifier output to disk for a given site @label_layer.mouse_drag_callbacks.append def toggle_label(obj, event): # TODO: Add a warning when user clicks while the wrong layer is selected? self.selection_layer.visible = True # Need to scale position that event.position returns by the label_layer scale. # If scale is (1, 1, 1), nothing changes # If scale is anything else, this makes the click still match the correct label scaled_position = tuple( pos / scale for pos, scale in zip(event.position, label_layer.scale)) label = label_layer.get_value(scaled_position) #label = label_layer.get_value(event.position) if selector.value is None: napari_warn( 'No class is selected. Select a class in the classifier widget.' ) # Check if background or foreground was clicked. If background was clicked, do nothing (background can't be assigned a class) elif label == 0: pass else: # Check if the label exists in the current dataframe. Otherwise, do nothing if (self.DataFrame, label) in self.clf.train_data.index: # Assign name of class #self.clf.train_data.loc[(DataFrame, label)] = selector.value # Assign a numeric value to make it easier (colormap currently only supports this mode) self.clf.train_data.loc[(self.DataFrame, label)] = choices.index( selector.value) self.update_label_colormap(self.selection_layer, label, choices.index(selector.value)) else: napari_warn('The data that was provided to the classifier '\ 'does not contain an object with index {}. '\ 'Thus, this object cannot be included in the ' \ 'classifier'.format(label)) @selector.changed.connect def change_choice(choice): self.selection_layer.visible = True self.viewer.layers.selection.clear() self.viewer.layers.selection.add(self.label_layer) @label_layer.bind_key('s') @save_button.changed.connect def save_classifier(event): show_info('Saving classifier') self.clf.save() @label_layer.bind_key('t') @run_button.changed.connect def run_classifier(event): # TODO: Add Run mode? Fuzzy, Cross-validated, train/test split show_info('Running classifier') self.clf.train() self.create_label_colormap(self.prediction_layer, self.clf.predict_data, 'predict') self.clf.save() self.selection_layer.visible = False self.prediction_layer.visible = True # TODO: Report classifier performance to the user? => Get the print into the napari notification engine @label_layer.bind_key('o') def toggle_selection(layer): current = self.selection_layer.visible self.selection_layer.visible = not current @label_layer.bind_key('p') def toggle_selection(layer): current = self.prediction_layer.visible self.prediction_layer.visible = not current @label_layer.bind_key('v') def toggle_selection(event): # Toggling off the label layer would be inconvenient (can't click on it anymore) # => just toggle the opacity to 0 opacity = label_layer.opacity if opacity > 0: label_layer.opacity = 0.0 else: label_layer.opacity = 0.8 @label_layer.bind_key('0') def set_class_0(event): selector.value = choices[0] change_choice(event) @label_layer.bind_key('1') def set_class_1(event): selector.value = choices[1] change_choice(event) @label_layer.bind_key('2') def set_class_2(event): selector.value = choices[2] change_choice(event) @label_layer.bind_key('3') def set_class_3(event): selector.value = choices[3] change_choice(event) @label_layer.bind_key('4') def set_class_4(event): selector.value = choices[4] change_choice(event) return container