コード例 #1
0
    def __init__(self,
                 min: float = 1,
                 max: float = 100,
                 base: float = math.e,
                 **kwargs):
        for key in ("maximum", "minimum"):
            if key in kwargs:
                import warnings

                warnings.warn(
                    f"The {key!r} keyword arguments has been changed to {key[:3]!r}. "
                    "In the future this will raise an exception\n",
                    FutureWarning,
                )
                if key == "maximum":
                    max = kwargs.pop(key)
                else:
                    min = kwargs.pop(key)
        self._base = base
        app = use_app()
        assert app.native
        super().__init__(
            min=min,
            max=max,
            widget_type=app.get_obj("Slider"),
            **kwargs,
        )
コード例 #2
0
ファイル: tqdm.py プロジェクト: uschmidt83/magicgui
    def __init__(self, iterable: Iterable = None, *args, **kwargs) -> None:
        kwargs = kwargs.copy()
        pbar_kwargs = {k: kwargs.pop(k) for k in set(kwargs) - _tqdm_kwargs}
        self._mgui = _find_calling_function_gui()
        if self._in_visible_gui:
            kwargs["gui"] = True
            kwargs.setdefault("mininterval", 0.025)
        super().__init__(iterable, *args, **kwargs)
        if not self._in_visible_gui:
            return

        self.sp = lambda x: None  # no-op status printer, required for older tqdm compat
        if self.disable:
            return

        # check if we're being instantiated inside of a magicgui container
        self.progressbar = self._get_progressbar(**pbar_kwargs)
        self._app = use_app()

        if self.total is not None:
            # initialize progress bar range
            self.progressbar.range = (self.n, self.total)
            self.progressbar.value = self.n
        else:
            # show a busy indicator instead of a percentage of steps
            self.progressbar.range = (0, 0)
        self.progressbar.show()
コード例 #3
0
def _int_widget_to_float(name):
    app = use_app()
    assert app.native
    cls = app.get_obj(name)
    import builtins

    def update_precision(self, min=None, max=None, step=None):
        orig = self._precision

        if min is not None or max is not None:
            min = min or self._mgui_get_min()
            max = max or self._mgui_get_max()

            # make sure val * precision is within int32 overflow limit for Qt
            val = builtins.max([abs(min), abs(max)])
            while abs(self._precision * val) >= 2**32 // 2:
                self._precision *= 0.1
        elif step:
            while step < (1 / self._precision):
                self._precision *= 10

        ratio = self._precision / orig
        if ratio != 1:
            self._mgui_set_value(self._mgui_get_value() * ratio)
            if not step:
                self._mgui_set_max(self._mgui_get_max() * ratio)
                self._mgui_set_min(self._mgui_get_min() * ratio)
            # self._mgui_set_step(self._mgui_get_step() * ratio)

    new_cls = type(
        f"Float{cls.__name__}",
        (cls, ),
        {
            "__module__": __name__,
            "_precision": 1e6,
            "_update_precision": update_precision,
        },
    )

    # patch the backend widget to convert between float/int
    for attr in ["value", "max", "min", "step"]:
        get_meth_name = f"_mgui_get_{attr}"
        set_meth_name = f"_mgui_set_{attr}"

        def new_getter(self, o_getter=getattr(new_cls, get_meth_name)):
            return o_getter(self) / self._precision

        def new_setter(self,
                       val,
                       o_setter=getattr(new_cls, set_meth_name),
                       attr=attr):
            if attr in ("step", "max", "min"):
                self._update_precision(**{attr: val})
            o_setter(self, int(val * self._precision))

        setattr(new_cls, get_meth_name, new_getter)
        setattr(new_cls, set_meth_name, new_setter)

    return new_cls
コード例 #4
0
 def __init__(self, **kwargs):
     app = use_app()
     assert app.native
     widget = app.get_obj(widget_name or cls.__name__)
     if transform:
         widget = transform(widget)
     kwargs["widget_type"] = widget
     super(cls, self).__init__(**kwargs)
コード例 #5
0
    def _unify_label_widths(self, event=None):
        if not self._initialized:
            return

        need_labels = [w for w in self if not isinstance(w, ButtonWidget)]
        if self.layout == "vertical" and self.labels and need_labels:
            measure = use_app().get_obj("get_text_width")
            widest_label = max(measure(w.label) for w in need_labels)
            for w in self:
                labeled_widget = w._labeled_widget()
                if labeled_widget:
                    labeled_widget.label_width = widest_label
コード例 #6
0
 def __init__(
     self, mode: FileDialogMode = FileDialogMode.EXISTING_FILE, filter=None, **kwargs
 ):
     self.line_edit = LineEdit(value=kwargs.pop("value", None))
     self.choose_btn = PushButton()
     self.mode = mode  # sets the button text too
     self.filter = filter
     kwargs["widgets"] = [self.line_edit, self.choose_btn]
     kwargs["labels"] = False
     kwargs["layout"] = "horizontal"
     super().__init__(**kwargs)
     self.margins = (0, 0, 0, 0)
     self._show_file_dialog = use_app().get_obj("show_file_dialog")
     self.choose_btn.changed.connect(self._on_choose_clicked)
コード例 #7
0
ファイル: widget.py プロジェクト: jni/magicgui
    def __init__(
            self,
            widget_type: Type[_protocols.WidgetProtocol],
            name: str = "",
            annotation: Any = None,
            label: str = None,
            tooltip: Optional[str] = None,
            visible: Optional[bool] = None,
            enabled: bool = True,
            gui_only=False,
            backend_kwargs=dict(),
            **extra,
    ):
        # for ipywidgets API compatibility
        label = label or extra.pop("description", None)
        if extra:
            warnings.warn(
                f"\n\n{self.__class__.__name__}.__init__() got unexpected "
                f"keyword arguments {set(extra)!r}.\n"
                "In the future this will raise an exception\n",
                FutureWarning,
            )

        _prot = self.__class__.__annotations__["_widget"]
        if not isinstance(_prot, str):
            _prot = _prot.__name__
        prot = getattr(_protocols, _prot.replace("_protocols.", ""))
        _protocols.assert_protocol(widget_type, prot)
        self.__magicgui_app__ = use_app()
        assert self.__magicgui_app__.native
        self._widget = widget_type(**backend_kwargs)
        self.name: str = name
        self.param_kind = inspect.Parameter.POSITIONAL_OR_KEYWORD
        self._label = label
        self.tooltip = tooltip
        self.enabled = enabled
        self.annotation: Any = annotation
        self.gui_only = gui_only
        self.parent_changed = EventEmitter(source=self, type="parent_changed")
        self.label_changed = EventEmitter(source=self, type="label_changed")
        self._widget._mgui_bind_parent_change_callback(self._emit_parent)

        # put the magicgui widget on the native object...may cause error on some backend
        self.native._magic_widget = self
        self._post_init()
        self._visible: bool = False
        self._explicitly_hidden: bool = False
        if visible is not None:
            self.visible = visible
コード例 #8
0
ファイル: _transforms.py プロジェクト: uschmidt83/magicgui
def transform_get_set(
    cls,
    get_transform: Optional[Callable[[Any], Any]] = None,
    set_transform: Optional[Callable[[Any], Any]] = None,
    prefix: str = "Transformed",
):
    """Overly complicated decorator to transform the get/set methods of a class."""
    if isinstance(cls, str):
        from magicgui.application import use_app

        app = use_app()
        assert app.native
        cls = app.get_obj(cls)
    if not issubclass(cls, ValueWidgetProtocol):
        raise TypeError(
            "Class must implement `BaseValueWidget` to transform getter and setter."
        )
    suffixes = ["value"]
    if issubclass(cls, RangedWidgetProtocol):
        suffixes.extend(["max", "min", "step"])

    new_cls = type(f"{prefix}{cls.__name__}", (cls, ),
                   {"__module__": __name__})
    for suffix in suffixes:
        if get_transform:
            meth_name = f"_mgui_get_{suffix}"
            old_getter = getattr(new_cls, meth_name, None)

            def new_getter(obj,
                           old_getter=old_getter,
                           transform=get_transform):
                return transform(old_getter(obj))

            setattr(new_cls, meth_name, new_getter)
        if set_transform:
            meth_name = f"_mgui_set_{suffix}"
            old_setter = getattr(new_cls, meth_name, None)

            def new_setter(obj,
                           val,
                           old_setter=old_setter,
                           transform=set_transform):
                old_setter(obj, transform(val))

            setattr(new_cls, meth_name, new_setter)
    return new_cls
コード例 #9
0
ファイル: _table.py プロジェクト: jni/magicgui
 def __init__(
     self,
     value: Optional[TableData] = None,
     *,
     index: Collection = None,
     columns: Collection = None,
     **kwargs,
 ) -> None:
     app = use_app()
     assert app.native
     kwargs["widget_type"] = app.get_obj("Table")
     super().__init__(**kwargs)
     self._data = DataView(self)
     data, _index, _columns = normalize_table_data(value)
     self.value = {
         "data": data,
         "index": index if index is not None else _index,
         "columns": columns if columns is not None else _columns,
     }
コード例 #10
0
 def __init__(self, choices=(), orientation="vertical", **kwargs):
     app = use_app()
     assert app.native
     kwargs["widget_type"] = app.get_obj("RadioButtons")
     super().__init__(choices=choices, **kwargs)
     self.orientation = orientation
コード例 #11
0
    def plugin(
        viewer: napari.Viewer,
        label_head,
        image: napari.layers.Image,
        axes,
        label_nn,
        model_type,
        model2d,
        model3d,
        model_folder,
        model_axes,
        norm_image,
        perc_low,
        perc_high,
        input_scale,
        label_nms,
        prob_thresh,
        nms_thresh,
        output_type,
        label_adv,
        n_tiles,
        norm_axes,
        timelapse_opts,
        cnn_output,
        set_thresholds,
        defaults_button,
        progress_bar: mw.ProgressBar,
    ) -> List[napari.types.LayerDataTuple]:

        model = get_model(*model_selected)
        if model._is_multiclass():
            warn(
                "multi-class mode not supported yet, ignoring classification output"
            )

        lkwargs = {}
        x = get_data(image)
        axes = axes_check_and_normalize(axes, length=x.ndim)

        if not (input_scale is None
                or isinstance(input_scale, numbers.Number)):
            input_scale = tuple(s for a, s in zip(axes, input_scale)
                                if a not in ("T", ))
            # print(f'scaling by {input_scale}')

        if not axes.replace("T", "").startswith(
                model._axes_out.replace("C", "")):
            warn(
                f"output images have different axes ({model._axes_out.replace('C','')}) than input image ({axes})"
            )
            # TODO: adjust image.scale according to shuffled axes

        if norm_image:
            axes_norm = axes_check_and_normalize(norm_axes)
            axes_norm = "".join(set(axes_norm).intersection(
                set(axes)))  # relevant axes present in input image
            assert len(axes_norm) > 0
            # always jointly normalize channels for RGB images
            if ("C" in axes and image.rgb == True) and ("C" not in axes_norm):
                axes_norm = axes_norm + "C"
                warn("jointly normalizing channels of RGB input image")
            ax = axes_dict(axes)
            _axis = tuple(sorted(ax[a] for a in axes_norm))
            # # TODO: address joint vs. channel/time-separate normalization properly (let user choose)
            # #       also needs to be documented somewhere
            # if 'T' in axes:
            #     if 'C' not in axes or image.rgb == True:
            #          # normalize channels jointly, frames independently
            #          _axis = tuple(i for i in range(x.ndim) if i not in (ax['T'],))
            #     else:
            #         # normalize channels independently, frames independently
            #         _axis = tuple(i for i in range(x.ndim) if i not in (ax['T'],ax['C']))
            # else:
            #     if 'C' not in axes or image.rgb == True:
            #          # normalize channels jointly
            #         _axis = None
            #     else:
            #         # normalize channels independently
            #         _axis = tuple(i for i in range(x.ndim) if i not in (ax['C'],))
            x = normalize(x, perc_low, perc_high, axis=_axis)

        # TODO: progress bar (labels) often don't show up. events not processed?
        if "T" in axes:
            app = use_app()
            t = axes_dict(axes)["T"]
            n_frames = x.shape[t]
            if n_tiles is not None:
                # remove tiling value for time axis
                n_tiles = tuple(v for i, v in enumerate(n_tiles) if i != t)

            def progress(it, **kwargs):
                progress_bar.label = "StarDist Prediction (frames)"
                progress_bar.range = (0, n_frames)
                progress_bar.value = 0
                progress_bar.show()
                app.process_events()
                for item in it:
                    yield item
                    progress_bar.increment()
                    app.process_events()
                app.process_events()

        elif n_tiles is not None and np.prod(n_tiles) > 1:
            n_tiles = tuple(n_tiles)
            app = use_app()

            def progress(it, **kwargs):
                progress_bar.label = "CNN Prediction (tiles)"
                progress_bar.range = (0, kwargs.get("total", 0))
                progress_bar.value = 0
                progress_bar.show()
                app.process_events()
                for item in it:
                    yield item
                    progress_bar.increment()
                    app.process_events()
                #
                progress_bar.label = "NMS Postprocessing"
                progress_bar.range = (0, 0)
                app.process_events()

        else:
            progress = False
            progress_bar.label = "StarDist Prediction"
            progress_bar.range = (0, 0)
            progress_bar.show()
            use_app().process_events()

        # semantic output axes of predictions
        assert model._axes_out[-1] == "C"
        axes_out = list(model._axes_out[:-1])

        if "T" in axes:
            x_reorder = np.moveaxis(x, t, 0)
            axes_reorder = axes.replace("T", "")
            axes_out.insert(t, "T")
            res = tuple(
                zip(*tuple(
                    model.predict_instances(
                        _x,
                        axes=axes_reorder,
                        prob_thresh=prob_thresh,
                        nms_thresh=nms_thresh,
                        n_tiles=n_tiles,
                        scale=input_scale,
                        sparse=(not cnn_output),
                        return_predict=cnn_output,
                    ) for _x in progress(x_reorder))))

            if cnn_output:
                labels, polys = tuple(zip(*res[0]))
                cnn_output = tuple(np.stack(c, t) for c in tuple(zip(*res[1])))
            else:
                labels, polys = res

            labels = np.asarray(labels)

            if len(polys) > 1:
                if timelapse_opts == TimelapseLabels.Match.value:
                    # match labels in consecutive frames (-> simple IoU tracking)
                    labels = group_matching_labels(labels)
                elif timelapse_opts == TimelapseLabels.Unique.value:
                    # make label ids unique (shift by offset)
                    offsets = np.cumsum([len(p["points"]) for p in polys])
                    for y, off in zip(labels[1:], offsets):
                        y[y > 0] += off
                elif timelapse_opts == TimelapseLabels.Separate.value:
                    # each frame processed separately (nothing to do)
                    pass
                else:
                    raise NotImplementedError(
                        f"unknown option '{timelapse_opts}' for time-lapse labels"
                    )

            labels = np.moveaxis(labels, 0, t)

            if isinstance(model, StarDist3D):
                # TODO poly output support for 3D timelapse
                polys = None
            else:
                polys = dict(
                    coord=np.concatenate(
                        tuple(
                            np.insert(p["coord"], t, _t, axis=-2)
                            for _t, p in enumerate(polys)),
                        axis=0,
                    ),
                    points=np.concatenate(
                        tuple(
                            np.insert(p["points"], t, _t, axis=-1)
                            for _t, p in enumerate(polys)),
                        axis=0,
                    ),
                )

            if cnn_output:
                pred = (labels, polys), cnn_output
            else:
                pred = labels, polys

        else:
            # TODO: possible to run this in a way that it can be canceled?
            pred = model.predict_instances(
                x,
                axes=axes,
                prob_thresh=prob_thresh,
                nms_thresh=nms_thresh,
                n_tiles=n_tiles,
                show_tile_progress=progress,
                scale=input_scale,
                sparse=(not cnn_output),
                return_predict=cnn_output,
            )
        progress_bar.hide()

        # determine scale for output axes
        scale_in_dict = dict(zip(axes, image.scale))
        scale_out = [scale_in_dict.get(a, 1.0) for a in axes_out]

        layers = []
        if cnn_output:
            (labels, polys), cnn_out = pred
            prob, dist = cnn_out[:2]
            dist = np.moveaxis(dist, -1, 0)

            assert len(model.config.grid) == len(model.config.axes) - 1
            grid_dict = dict(
                zip(model.config.axes.replace("C", ""), model.config.grid))
            # scale output axes to match input axes
            _scale = [
                s * grid_dict.get(a, 1) for a, s in zip(axes_out, scale_out)
            ]
            # small translation correction if grid > 1 (since napari centers objects)
            _translate = [0.5 * (grid_dict.get(a, 1) - 1) for a in axes_out]

            layers.append((
                dist,
                dict(
                    name="StarDist distances",
                    scale=[1] + _scale,
                    translate=[0] + _translate,
                    **lkwargs,
                ),
                "image",
            ))
            layers.append((
                prob,
                dict(
                    name="StarDist probability",
                    scale=_scale,
                    translate=_translate,
                    **lkwargs,
                ),
                "image",
            ))
        else:
            labels, polys = pred

        if output_type in (Output.Labels.value, Output.Both.value):
            layers.append((
                labels,
                dict(name="StarDist labels",
                     scale=scale_out,
                     opacity=0.5,
                     **lkwargs),
                "labels",
            ))
        if output_type in (Output.Polys.value, Output.Both.value):
            n_objects = len(polys["points"])
            if isinstance(model, StarDist3D):
                surface = surface_from_polys(polys)
                layers.append((
                    surface,
                    dict(
                        name="StarDist polyhedra",
                        contrast_limits=(0, surface[-1].max()),
                        scale=scale_out,
                        colormap=label_colormap(n_objects),
                        **lkwargs,
                    ),
                    "surface",
                ))
            else:
                # TODO: sometimes hangs for long time (indefinitely?) when returning many polygons (?)
                #       seems to be a known issue: https://github.com/napari/napari/issues/2015
                # TODO: coordinates correct or need offset (0.5 or so)?
                shapes = np.moveaxis(polys["coord"], -1, -2)
                layers.append((
                    shapes,
                    dict(
                        name="StarDist polygons",
                        shape_type="polygon",
                        scale=scale_out,
                        edge_width=0.75,
                        edge_color="yellow",
                        face_color=[0, 0, 0, 0],
                        **lkwargs,
                    ),
                    "shapes",
                ))
        return layers
コード例 #12
0
ファイル: create_widget.py プロジェクト: uschmidt83/magicgui
def create_widget(
        value: Any = None,
        annotation: Any = None,
        name: str = "",
        param_kind: str | inspect._ParameterKind = "POSITIONAL_OR_KEYWORD",
        label=None,
        gui_only=False,
        app=None,
        widget_type: str | type[_protocols.WidgetProtocol] | None = None,
        options: WidgetOptions = dict(),
):
    """Create and return appropriate widget subclass.

    This factory function can be used to create a widget appropriate for the
    provided ``value`` and/or ``annotation`` provided.

    Parameters
    ----------
    value : Any, optional
        The starting value for the widget, by default ``None``
    annotation : Any, optional
        The type annotation for the parameter represented by the widget, by default
        ``None``
    name : str, optional
        The name of the parameter represented by this widget. by default ``""``
    param_kind : str, optional
        The :attr:`inspect.Parameter.kind` represented by this widget.  Used in building
        signatures from multiple widgets, by default "``POSITIONAL_OR_KEYWORD``"
    label : str
        A string to use for an associated Label widget (if this widget is being
        shown in a :class:`~magicgui.widgets.Container` widget, and labels are on).
        By default, ``name`` will be used. Note: ``name`` refers the name of the
        parameter, as might be used in a signature, whereas label is just the label
        for that widget in the GUI.
    gui_only : bool, optional
        Whether the widget should be considered "only for the gui", or if it should
        be included in any widget container signatures, by default False
    app : str, optional
        The backend to use, by default ``None``
    widget_type : str or Type[WidgetProtocol] or None
        A class implementing a widget protocol or a string with the name of a
        magicgui widget type (e.g. "Label", "PushButton", etc...).
        If provided, this widget type will be used instead of the type
        autodetermined from ``value`` and/or ``annotation`` above.
    options : WidgetOptions, optional
        Dict of options to pass to the Widget constructor, by default dict()

    Returns
    -------
    Widget
        An instantiated widget subclass

    Raises
    ------
    TypeError
        If the provided or autodetected ``widget_type`` does not implement any known
        widget protocols from widgets._protocols.
    """
    kwargs = locals()
    _kind = kwargs.pop("param_kind", None)
    _app = use_app(kwargs.pop("app"))
    assert _app.native
    if isinstance(widget_type, _protocols.WidgetProtocol):
        wdg_class = kwargs.pop("widget_type")
    else:
        from magicgui.type_map import get_widget_class

        if widget_type:
            options["widget_type"] = widget_type
        wdg_class, opts = get_widget_class(value, annotation, options)

        if issubclass(wdg_class, Widget):
            opts.update(kwargs.pop("options"))
            kwargs.update(opts)
            kwargs.pop("widget_type", None)
            widget = wdg_class(**kwargs)
            if _kind:
                widget.param_kind = _kind
            return widget

    # pick the appropriate subclass for the given protocol
    # order matters
    for p in ("Categorical", "Ranged", "Button", "Value", ""):
        prot = getattr(_protocols, f"{p}WidgetProtocol")
        if isinstance(wdg_class, prot):

            options = kwargs.pop("options", {})
            cls = getattr(_bases, f"{p}Widget")
            widget = cls(widget_type=wdg_class, **kwargs, **options)
            if _kind:
                widget.param_kind = _kind
            return widget

    raise TypeError(
        f"{wdg_class!r} does not implement any known widget protocols")