1- from typing import Optional
1+ from typing import Optional , List , Tuple
22
33import napari
44import numpy as np
55from qtpy .QtWidgets import QWidget
6+ from magicgui import magicgui , ComboBox
67
78from .base import NapariMPLWidget
89from .util import Interval
910
10- __all__ = ["HistogramWidget" ]
11+ __all__ = ["HistogramWidget" , "FeaturesHistogramWidget" ]
1112
1213_COLORS = {"r" : "tab:red" , "g" : "tab:green" , "b" : "tab:blue" }
1314
@@ -63,3 +64,124 @@ def draw(self) -> None:
6364 self .axes .hist (data .ravel (), bins = bins , label = layer .name )
6465
6566 self .axes .legend ()
67+
68+
69+ class FeaturesHistogramWidget (NapariMPLWidget ):
70+ n_layers_input = Interval (1 , 1 )
71+ # All layers that have a .features attributes
72+ input_layer_types = (
73+ napari .layers .Labels ,
74+ napari .layers .Points ,
75+ napari .layers .Shapes ,
76+ napari .layers .Tracks ,
77+ napari .layers .Vectors ,
78+ )
79+
80+ def __init__ (self , napari_viewer : napari .viewer .Viewer ):
81+ super ().__init__ (napari_viewer )
82+ self .axes = self .canvas .figure .subplots ()
83+
84+ self ._key_selection_widget = magicgui (
85+ self ._set_axis_keys ,
86+ x_axis_key = {"choices" : self ._get_valid_axis_keys },
87+ call_button = "plot" ,
88+ )
89+ self .layout ().addWidget (self ._key_selection_widget .native )
90+
91+ self .update_layers (None )
92+
93+ def clear (self ) -> None :
94+ """
95+ Clear the axes.
96+ """
97+ self .axes .clear ()
98+
99+ self .layout ().addWidget (self ._key_selection_widget .native )
100+
101+ @property
102+ def x_axis_key (self ) -> Optional [str ]:
103+ """Key to access x axis data from the FeaturesTable"""
104+ return self ._x_axis_key
105+
106+ @x_axis_key .setter
107+ def x_axis_key (self , key : Optional [str ]) -> None :
108+ self ._x_axis_key = key
109+ self ._draw ()
110+
111+ def _set_axis_keys (self , x_axis_key : str ) -> None :
112+ """Set both axis keys and then redraw the plot"""
113+ self ._x_axis_key = x_axis_key
114+ self ._draw ()
115+
116+ def _get_valid_axis_keys (
117+ self , combo_widget : Optional [ComboBox ] = None
118+ ) -> List [str ]:
119+ """
120+ Get the valid axis keys from the layer FeatureTable.
121+
122+ Returns
123+ -------
124+ axis_keys : List[str]
125+ The valid axis keys in the FeatureTable. If the table is empty
126+ or there isn't a table, returns an empty list.
127+ """
128+ if len (self .layers ) == 0 or not (hasattr (self .layers [0 ], "features" )):
129+ return []
130+ else :
131+ return self .layers [0 ].features .keys ()
132+
133+ def _get_data (self ) -> Tuple [np .ndarray , str ]:
134+ """Get the plot data.
135+
136+ Returns
137+ -------
138+ data : List[np.ndarray]
139+ List contains X and Y columns from the FeatureTable. Returns
140+ an empty array if nothing to plot.
141+ x_axis_name : str
142+ The title to display on the x axis. Returns
143+ an empty string if nothing to plot.
144+ """
145+ if not hasattr (self .layers [0 ], "features" ):
146+ # if the selected layer doesn't have a featuretable,
147+ # skip draw
148+ return [], ""
149+
150+ feature_table = self .layers [0 ].features
151+
152+ if (
153+ (len (feature_table ) == 0 )
154+ or (self .x_axis_key is None )
155+ ):
156+ return [], ""
157+
158+ data = feature_table [self .x_axis_key ]
159+ x_axis_name = self .x_axis_key .replace ("_" , " " )
160+
161+ return data , x_axis_name
162+
163+ def _on_update_layers (self ) -> None :
164+ """
165+ This is called when the layer selection changes by
166+ ``self.update_layers()``.
167+ """
168+ if hasattr (self , "_key_selection_widget" ):
169+ self ._key_selection_widget .reset_choices ()
170+
171+ # reset the axis keys
172+ self ._x_axis_key = None
173+
174+ def draw (self ) -> None :
175+ """Clear the axes and histogram the currently selected layer/slice."""
176+
177+ data , x_axis_name = self ._get_data ()
178+
179+ if len (data ) == 0 :
180+ return
181+
182+ self .axes .hist (data , bins = 50 , edgecolor = 'white' ,
183+ linewidth = 0.3 )
184+
185+ # set ax labels
186+ self .axes .set_xlabel (x_axis_name )
187+ self .axes .set_ylabel ('Counts [#]' )
0 commit comments