Skip to content

Commit

Permalink
Initial porting of magicgui to QtWidgets
Browse files Browse the repository at this point in the history
  • Loading branch information
paddyroddy committed Jul 26, 2023
1 parent e750129 commit 82702ac
Show file tree
Hide file tree
Showing 6 changed files with 184 additions and 159 deletions.
11 changes: 4 additions & 7 deletions btrack/napari/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,8 @@

import logging

import qtpy.QtWidgets
from qtpy import QtWidgets

import magicgui.widgets
import napari

import btrack
Expand Down Expand Up @@ -49,11 +48,9 @@ def create_btrack_widget() -> Container:

# First create our UI along with some default configs for the widgets
all_configs = btrack.napari.config.create_default_configs()
widgets = btrack.napari.widgets.create_widgets()
btrack_widget = magicgui.widgets.Container(
widgets=widgets, scrollable=True
btrack_widget = btrack.napari.BtrackWidget(
viewer=napari.current_viewer(),
)
btrack_widget.viewer = napari.current_viewer()

# Set the cell_config defaults in the gui
btrack.napari.sync.update_widgets_from_config(
Expand Down Expand Up @@ -83,7 +80,7 @@ def create_btrack_widget() -> Container:
)

# there are lots of widgets so make the container scrollable
scroll = qtpy.QtWidgets.QScrollArea()
scroll = QtWidgets.QScrollArea()
scroll.setWidget(btrack_widget._widget._qwidget)
btrack_widget._widget._qwidget = scroll

Expand Down
2 changes: 1 addition & 1 deletion btrack/napari/widgets/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from btrack.napari.widgets.create_ui import create_widgets
from btrack.napari.widgets.create_ui import BtrackWidget
from btrack.napari.widgets.io import (
load_path_dialogue_box,
save_path_dialogue_box,
Expand Down
60 changes: 24 additions & 36 deletions btrack/napari/widgets/_general.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
from __future__ import annotations

from typing import TYPE_CHECKING

if TYPE_CHECKING:
from magicgui.widgets import Widget
from qtpy import QtWidgets

import magicgui
import napari


def create_input_widgets() -> list[Widget]:
def create_input_widgets() -> dict[str, QtWidgets.QWidget]:
"""Create widgets for selecting labels layer and TrackerConfig"""

segmentation_tooltip = (
Expand Down Expand Up @@ -42,43 +39,38 @@ def create_input_widgets() -> list[Widget]:
return [segmentation, config]


def create_update_method_widgets() -> list[Widget]:
def create_update_method_widgets() -> dict[str, QtWidgets.QWidget]:
"""Create widgets for selecting the update method"""

update_method_tooltip = (
update_method = QtWidgets.QComboBox()
update_method.addItems(
[
"EXACT",
"APPROXIMATE",
]
)
update_method.setTooltip(
"Select the update method.\n"
"EXACT: exact calculation of Bayesian belief matrix.\n"
"APPROXIMATE: approximate the Bayesian belief matrix. Useful for datasets with "
"more than 1000 particles per frame."
)
update_method = magicgui.widgets.create_widget(
value="EXACT",
name="update_method",
label="update method",
widget_type="ComboBox",
options={
"choices": ["EXACT", "APPROXIMATE"],
"tooltip": update_method_tooltip,
},
)
update_method_widgets = {"update method": update_method}

# TODO: this widget should be hidden when the update method is set to EXACT
max_search_radius_tooltip = (
max_search_radius = QtWidgets.QSPinBox()
max_search_radius.setRange(0, 1000)
max_search_radius.setSingleStep(1)
max_search_radius.setWrapping(True) # noqa: FBT003
max_search_radius.setTooltip = (
"The local spatial search radius (isotropic, pixels) used when the update "
"method is 'APPROXIMATE'"
)
max_search_radius = magicgui.widgets.create_widget(
value=100,
name="max_search_radius",
label="search radius",
widget_type="SpinBox",
options={"tooltip": max_search_radius_tooltip},
)
update_method_widgets["search radius"] = max_search_radius

return [update_method, max_search_radius]
return create_update_method_widgets()


def create_control_widgets() -> list[Widget]:
def create_control_widgets() -> dict[str, QtWidgets.QWidget]:
"""Create widgets for running the analysis or handling I/O.
This includes widgets for running the tracking, saving and loading
Expand All @@ -104,14 +96,10 @@ def create_control_widgets() -> list[Widget]:
"Run the tracking analysis with the current configuration.",
]

control_buttons = []
control_buttons = {}
for name, label, tooltip in zip(names, labels, tooltips):
widget = magicgui.widgets.create_widget(
name=name,
label=label,
widget_type="PushButton",
options={"tooltip": tooltip},
)
control_buttons.append(widget)
control_buttons[name] = QtWidgets.QPushButton()
control_buttons[name].setText(label)
control_buttons[name].setToolTip(tooltip)

return control_buttons
154 changes: 56 additions & 98 deletions btrack/napari/widgets/_hypothesis.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,11 @@
from __future__ import annotations

from typing import TYPE_CHECKING

if TYPE_CHECKING:
from magicgui.widgets import Widget

import magicgui
from qtpy import QtWidgets

import btrack.napari.constants


def _create_hypotheses_widgets() -> list[Widget]:
def _create_hypotheses_widgets() -> dict[str, QtWidgets.QWidget]:
"""Create widgets for selecting which hypotheses to generate."""

hypotheses = btrack.napari.constants.HYPOTHESES
Expand All @@ -24,16 +19,13 @@ def _create_hypotheses_widgets() -> list[Widget]:
"Hypothesis that two tracklets merge into one tracklet.",
]

hypotheses_widgets = []
hypotheses_widgets = {}
for hypothesis, tooltip in zip(hypotheses, tooltips):
widget = magicgui.widgets.create_widget(
value=True,
name=hypothesis,
label=hypothesis,
widget_type="CheckBox",
options={"tooltip": tooltip},
)
hypotheses_widgets.append(widget)
widget = QtWidgets.QCheckBox()
widget.setName(hypothesis)
widget.setToolTip(tooltip)
widget.setCheckState(True) # noqa: FBT003
hypotheses_widgets[hypothesis] = widget

# P_FP is always required
P_FP_hypothesis = hypotheses_widgets[0]
Expand All @@ -46,7 +38,7 @@ def _create_hypotheses_widgets() -> list[Widget]:
return hypotheses_widgets


def _create_scaling_factor_widgets() -> list[Widget]:
def _create_scaling_factor_widgets() -> dict[str, QtWidgets.QWidget]:
"""Create widgets for setting the scaling factors of the HypothesisModel"""

widget_values = [5.0, 3.0, 10.0, 50.0]
Expand All @@ -73,135 +65,101 @@ def _create_scaling_factor_widgets() -> list[Widget]:
for value, name, label, tooltip in zip(
widget_values, names, labels, tooltips
):
widget = magicgui.widgets.create_widget(
value=value,
name=name,
label=label,
widget_type="FloatSpinBox",
options={"tooltip": tooltip},
)
scaling_factor_widgets.append(widget)
scaling_factor_widgets[name] = QtWidgets.QDoubleSpinBox()
scaling_factor_widgets[name].setName(label)
scaling_factor_widgets[name].setToolTip(tooltip)
scaling_factor_widgets[name].setValue(value)

return scaling_factor_widgets


def _create_threshold_widgets() -> list[Widget]:
def _create_threshold_widgets() -> dict[str, QtWidgets.QWidget]:
"""Create widgets for setting thresholds for the HypothesisModel"""

distance_threshold_tooltip = (
distance_threshold = QtWidgets.QDoubleSpinBox()
distance_threshold.setName("distance threshold")
distance_threshold.setToolTip(
"A threshold distance from the edge of the field of view to add an "
"initialization or termination hypothesis."
)
distance_threshold = magicgui.widgets.create_widget(
value=20.0,
name="theta_dist",
label="distance threshold",
widget_type="FloatSpinBox",
options={"tooltip": distance_threshold_tooltip},
)
distance_threshold.setValue(20.0)
widgets = {"theta_dist": distance_threshold}

time_threshold_tooltip = (
time_threshold = QtWidgets.QDoubleSpinBox()
time_threshold.setName("time threshold")
time_threshold.setToolTip(
"A threshold time from the beginning or end of movie to add "
"an initialization or termination hypothesis."
)
time_threshold = magicgui.widgets.create_widget(
value=5.0,
name="theta_time",
label="time threshold",
widget_type="FloatSpinBox",
options={"tooltip": time_threshold_tooltip},
)
time_threshold.setValue(5.0)
widgets["theta_time"] = time_threshold

apoptosis_threshold_tooltip = (
apoptosis_threshold = QtWidgets.QSpinBox()
apoptosis_threshold.setName("apoptosis threshold")
apoptosis_threshold.setToolTip(
"Number of apoptotic detections to be considered a genuine event.\n"
"Detections are counted consecutively from the back of the track"
)
apoptosis_threshold = magicgui.widgets.create_widget(
value=5,
name="apop_thresh",
label="apoptosis threshold",
widget_type="SpinBox",
options={"tooltip": apoptosis_threshold_tooltip},
)
apoptosis_threshold.setValue(5)
widgets["apop_thresh"] = apoptosis_threshold

return [
distance_threshold,
time_threshold,
apoptosis_threshold,
]
return widgets


def _create_bin_size_widgets() -> list[Widget]:
def _create_bin_size_widgets() -> dict[str, QtWidgets.QWidget]:
"""Create widget for setting bin sizes for the HypothesisModel"""

distance_bin_size_tooltip = (
distance_bin_size = QtWidgets.QDoubleSpinBox()
distance_bin_size.setName("distance bin size")
distance_bin_size.setToolTip(
"Isotropic spatial bin size for considering hypotheses.\n"
"Larger bin sizes generate more hypothesese for each tracklet."
)
distance_bin_size = magicgui.widgets.create_widget(
value=40.0,
name="dist_thresh",
label="distance bin size",
widget_type="FloatSpinBox",
options={"tooltip": distance_bin_size_tooltip},
)
distance_bin_size.setValue(40.0)
widgets = {"dist_thresh": distance_bin_size}

time_bin_size_tooltip = (
time_bin_size = QtWidgets.QDoubleSpinBox()
time_bin_size.setName("time bin size")
time_bin_size.setToolTip(
"Temporal bin size for considering hypotheses.\n"
"Larger bin sizes generate more hypothesese for each tracklet."
)
time_bin_size = magicgui.widgets.create_widget(
value=2.0,
name="time_thresh",
label="time bin size",
widget_type="FloatSpinBox",
options={"tooltip": time_bin_size_tooltip},
)
time_bin_size.setValue(2.0)
widgets["time_thresh"] = time_bin_size

return [
distance_bin_size,
time_bin_size,
]
return widgets


def create_hypothesis_model_widgets() -> list[Widget]:
def create_hypothesis_model_widgets() -> dict[str, QtWidgets.QWidget]:
"""Create widgets for setting parameters of the MotionModel"""

hypothesis_model_label = magicgui.widgets.create_widget(
label="<b>Hypothesis model</b>", # bold label
widget_type="Label",
gui_only=True,
)
hypothesis_model_label = QtWidgets.QLabel()
hypothesis_model_label.setName("<b>Hypothesis model</b>") # bold label
widgets = {"hypothesis": hypothesis_model_label}

hypotheses_widgets = _create_hypotheses_widgets()
scaling_factor_widgets = _create_scaling_factor_widgets()
threshold_widgets = _create_threshold_widgets()
bin_size_widgets = _create_bin_size_widgets()

segmentation_miss_rate_tooltip = (
segmentation_miss_rate = QtWidgets.QDoubleSpinBox()
segmentation_miss_rate.setName("miss rate")
segmentation_miss_rate.setToolTip(
"Miss rate for the segmentation.\n"
"e.g. 1/100 segmentations incorrect gives a segmentation miss rate of 0.01."
)
segmentation_miss_rate = magicgui.widgets.create_widget(
value=0.1,
name="segmentation_miss_rate",
label="miss rate",
widget_type="FloatSpinBox",
options={"tooltip": segmentation_miss_rate_tooltip},
)
segmentation_miss_rate.setValue(0.1)
widgets["segmentation_miss_rate"] = segmentation_miss_rate

relax_tooltip = (
relax = QtWidgets.QCheckBox()
relax.setName("relax thresholds")
relax.setToolTip(
"Disable the time and distance thresholds.\n"
"This means that tracks can initialize or terminate anywhere and"
"at any time in the dataset."
)
relax = magicgui.widgets.create_widget(
value=True,
name="relax",
label="relax thresholds",
widget_type="CheckBox",
options={"tooltip": relax_tooltip},
)
relax.setCheckState(True) # noqa: FBT003
widgets["relax"] = relax

return [
hypothesis_model_label,
Expand Down
Loading

0 comments on commit 82702ac

Please sign in to comment.