diff --git a/docs/source/getting_started.rst b/docs/source/getting_started.rst index 5a67de3..98289ea 100644 --- a/docs/source/getting_started.rst +++ b/docs/source/getting_started.rst @@ -104,8 +104,8 @@ It contains the following: If your input was a Points layer, the ``node_id`` is simply the index of the node in the list of points. - Note: This does not save the output segmentation. If you want to save - the relabeled segmentation, you can do so through napari by selecting the - layer and then selecting ``File``-> ``Save selected layers`` + the relabeled segmentation, you can do so through napari by selecting the + layer and then selecting ``File``-> ``Save selected layers`` - The ``Back to editing`` button, which will return you to the ``Run Editor`` in its previous state. - The ``Edit this run`` button. This button will take you back to the ``Run Editor``, @@ -126,11 +126,7 @@ previous sessions do not appear here until you load them from disk with the The tracking results can also be visualized as a lineage tree. You can open the lineage tree widget via ``Plugins`` > ``Motile`` > ``Lineage View``. -For more details, go to `this docs page`_. +For more details, go to the :doc:`Tree View ` documentation. - - -.. _here: docs/source/key_bindings.rst -.. _this docs page: docs/source/tree_view.rst .. _Issue #48: https://github.com/funkelab/motile_napari_plugin/issues/48 .. _Cell Tracking Challenge: https://celltrackingchallenge.net/ diff --git a/src/motile_plugin/layers/track_graph.py b/src/motile_plugin/layers/track_graph.py index 4e30cd8..7de161f 100644 --- a/src/motile_plugin/layers/track_graph.py +++ b/src/motile_plugin/layers/track_graph.py @@ -20,11 +20,7 @@ def __init__( name: str, colormap: CyclicLabelColormap, ): - if tracks is None or tracks.graph is None: - graph = nx.DiGraph() - else: - graph = tracks.graph - + graph = nx.DiGraph() if tracks.graph is None else tracks.graph track_data, track_props, track_edges = to_napari_tracks_layer( graph, frame_key=tracks.time_attr, location_key=tracks.pos_attr ) @@ -40,6 +36,7 @@ def __init__( self.viewer = viewer self.colormaps_dict["track_id"] = colormap + self.colormap = "turbo" # just to refresh the colormap self.tracks_layer_graph = copy.deepcopy(self.graph) # for restoring graph later @@ -63,6 +60,7 @@ def update_track_visibility(self, visible: list[int] | str) -> None: self.track_colors[:, 3] = 0 self.track_colors[track_id_mask, 3] = 1 if len(self.graph.items()) == 0: - self.display_graph = False # empty dicts to not trigger update (bug?) so disable the graph entirely as a workaround + self.display_graph = False # empty dicts to not trigger update (bug?) + # so disable the graph entirely as a workaround else: self.display_graph = True diff --git a/src/motile_plugin/utils/relabel_segmentation.py b/src/motile_plugin/utils/relabel_segmentation.py index aed9b65..05f1e8a 100644 --- a/src/motile_plugin/utils/relabel_segmentation.py +++ b/src/motile_plugin/utils/relabel_segmentation.py @@ -30,7 +30,7 @@ def relabel_segmentation( previous_seg_id = solution_nx_graph.nodes[node][NodeAttr.SEG_ID.value] assert previous_seg_id != 0 tracklet_id = solution_nx_graph.nodes[node]["tracklet_id"] - hypothesis_id = solution_nx_graph.nodes[node].get([NodeAttr.SEG_HYPO.value], 0) + hypothesis_id = solution_nx_graph.nodes[node].get(NodeAttr.SEG_HYPO.value, 0) previous_seg_mask = segmentation[time_frame, hypothesis_id] == previous_seg_id tracked_masks[time_frame, 0][previous_seg_mask] = tracklet_id return tracked_masks diff --git a/src/motile_plugin/widgets/tracks_view/tree_widget.py b/src/motile_plugin/widgets/tracks_view/tree_widget.py index 60d7c2e..850fb6b 100644 --- a/src/motile_plugin/widgets/tracks_view/tree_widget.py +++ b/src/motile_plugin/widgets/tracks_view/tree_widget.py @@ -47,10 +47,7 @@ def mouseDragEvent(self, ev, axis=None): # Otherwise, disable rectangular zoom mode self.setMouseMode(self.PanMode) - if ( - axis is not None - and ev.button() == QtCore.Qt.MouseButton.RightButton - ): + if axis is not None and ev.button() == QtCore.Qt.MouseButton.RightButton: ev.ignore() else: pg.ViewBox.mouseDragEvent(self, ev, axis=axis) @@ -99,8 +96,8 @@ def update( feature (str): The feature to be plotted ('tree' or 'area') selected_nodes (list[Any]): The currently selected nodes to be highlighted """ - self.set_data(track_df, feature) self.set_view(view_direction, feature) + self.set_data(track_df, feature) self._update_viewed_data() # this can be expensive self.set_selection(selected_nodes, feature) @@ -112,7 +109,7 @@ def set_view(self, view_direction: str, feature: str): Args: view_direction (str): "horizontal" or "vertical" - feature (str): the feature being displayed, it can be either 'tree' or 'area' + feature (str): the feature being displayed, it can be 'tree' or 'area' """ if view_direction == self.view_direction and feature == self.feature: @@ -127,7 +124,7 @@ def set_view(self, view_direction: str, feature: str): self.setLabel("bottom", text="") else: self.getAxis("bottom").setStyle(showValues=True) - self.setLabel("bottom", text="Number of pixels") + self.setLabel("bottom", text="Area in Scaled Pixels") self.invertY(True) # to show tracks from top to bottom elif view_direction == "horizontal": self.setLabel("bottom", text="Time Point") @@ -136,7 +133,7 @@ def set_view(self, view_direction: str, feature: str): self.setLabel("left", text="") self.getAxis("left").setStyle(showValues=False) else: - self.setLabel("left", text="Number of pixels") + self.setLabel("left", text="Area in Scaled Pixels") self.getAxis("left").setStyle(showValues=True) self.invertY(False) @@ -167,9 +164,9 @@ def set_data(self, track_df: pd.DataFrame, feature: str) -> None: self._create_pyqtgraph_content(track_df, feature) def _update_viewed_data(self): - self.g.scatter.setPen( - pg.mkPen(QColor(150, 150, 150)) - ) # first reset the pen to avoid problems with length mismatch between the different properties + # first reset the pen to avoid problems with length mismatch between the + # different properties + self.g.scatter.setPen(pg.mkPen(QColor(150, 150, 150))) self.g.scatter.setSize(10) if len(self._pos) == 0 or self.view_direction == "vertical": pos_data = self._pos @@ -188,9 +185,7 @@ def _update_viewed_data(self): self.g.scatter.setSize(self.sizes) self.autoRange() - def _create_pyqtgraph_content( - self, track_df: pd.DataFrame, feature: str - ) -> None: + def _create_pyqtgraph_content(self, track_df: pd.DataFrame, feature: str) -> None: """Parse the given track_df into the format that pyqtgraph expects and save the information as attributes. @@ -320,9 +315,7 @@ class TreeWidget(QWidget): def __init__(self, viewer: napari.Viewer): super().__init__() self.track_df = pd.DataFrame() # all tracks - self.lineage_df = ( - pd.DataFrame() - ) # the currently viewed subset of lineages + self.lineage_df = pd.DataFrame() # the currently viewed subset of lineages self.graph = None self.mode = "all" # options: "all", "lineage" self.feature = "tree" # options: "tree", "area" @@ -436,8 +429,11 @@ def _update_track_data(self) -> None: ) self.graph = self.tracks_viewer.tracks.graph - # check whether we have area measurements and therefore should activate the area button + # check whether we have area measurements and therefore should activate the area + # button if "area" not in self.track_df.columns: + if self.feature_widget.feature == "area": + self.feature_widget._toggle_feature_mode() self.feature_widget.show_area_radio.setEnabled(False) else: self.feature_widget.show_area_radio.setEnabled(True) @@ -483,15 +479,14 @@ def _set_mode(self, mode: str) -> None: ) def _set_feature(self, feature: str) -> None: - """Set the feature mode to 'tree' or 'area'. For this the view is always horizontal. + """Set the feature mode to 'tree' or 'area'. For this the view is always + horizontal. Args: feature (str): The feature to plot. Options are "tree" or "area" """ if feature not in ["tree", "area"]: - raise ValueError( - f"Feature must be 'tree' or 'area', got {feature}" - ) + raise ValueError(f"Feature must be 'tree' or 'area', got {feature}") self.feature = feature if feature == "tree" and self.mode == "all":