@@ -32,7 +32,7 @@ def __init__(self, data):
3232
3333 # we need to create these variables in case someone doesn't "quit"
3434 # the plot appropriately (i.e., clicks X instead of pressing ctrl+q)
35- self .deleted , self .rejected , self .included = set (), set (), set ()
35+ self .deleted , self .rejected , self .included , self . annotated = set (), set (), set (), set ()
3636
3737 # make main plot objects depending on supplementary data
3838 if self .suppdata is None :
@@ -57,8 +57,8 @@ def __init__(self, data):
5757 # three selectors for:
5858 # 1. rejection (central mouse),
5959 # 2. addition (right mouse), and
60- # 3. deletion (left mouse)
61- delete = functools . partial ( self .on_edit , method = "delete" )
60+ # 3. deletion or annotation (left mouse)
61+ self .mode = "delete"
6262 reject = functools .partial (self .on_edit , method = "reject" )
6363 insert = functools .partial (self .on_edit , method = "insert" )
6464
@@ -71,7 +71,7 @@ def __init__(self, data):
7171
7272 self .span2 = SpanSelector (
7373 self .ax ,
74- delete ,
74+ self . _span_callback ,
7575 "horizontal" ,
7676 button = 1 ,
7777 useblit = True ,
@@ -118,6 +118,21 @@ def plot_signals(self, plot=True):
118118 ".g" ,
119119 )
120120
121+ if self .data .segments .shape [0 ]> 0 :
122+ for segment in self .data .segments :
123+ ymin , ymax = self .ax .get_ylim ()
124+ height = ymax - ymin
125+ width = self .time [int (segment [1 ])] - self .time [int (segment [0 ])]
126+
127+ rect = plt .Rectangle (
128+ [self .time [int (segment [0 ])], ymin ],
129+ width ,
130+ height ,
131+ alpha = 0.3 ,
132+ facecolor = 'y'
133+ )
134+ self .ax .add_patch (rect )
135+
121136 if self .suppdata is not None :
122137 self ._ax [1 ].plot (self .time , self .suppdata , "k" , linewidth = 0.7 )
123138 self ._ax [1 ].set_ylim (- 0.5 , 0.5 )
@@ -142,6 +157,13 @@ def on_key(self, event):
142157 self .undo ()
143158 elif event .key in ["ctrl+q" , "super+d" ]:
144159 self .quit ()
160+ elif event .key in ["ctrl+a" ]:
161+ self .annotate_mode ()
162+ elif event .key in ["ctrl+e" ]:
163+ self .edit_mode ()
164+
165+ def _span_callback (self , xmin , xmax ):
166+ self .on_edit (xmin , xmax , method = self .mode )
145167
146168 def on_edit (self , xmin , xmax , * , method ):
147169 """
@@ -150,10 +172,10 @@ def on_edit(self, xmin, xmax, *, method):
150172 Removes specified peaks by either rejection / deletion, OR
151173 Include one peak by finding the max in the selection.
152174
153- method accepts 'insert', 'reject', 'delete'
175+ method accepts 'insert', 'reject', 'delete', 'annotate'
154176 """
155177 logger .debug ("Edited peaks with action: {}" , method )
156- if method not in ["insert" , "reject" , "delete" ]:
178+ if method not in ["insert" , "reject" , "delete" , "annotate" ]:
157179 raise ValueError (f'Action "{ method } " not supported.' )
158180
159181 tmin , tmax = np .searchsorted (self .time , (xmin , xmax ))
@@ -165,6 +187,8 @@ def on_edit(self, xmin, xmax, *, method):
165187 if newpeak == tmin :
166188 self .plot_signals ()
167189 return
190+ elif method == "annotate" :
191+ segment = (tmin , tmax )
168192 else :
169193 bad = np .arange (pmin , pmax , dtype = int )
170194 if len (bad ) == 0 :
@@ -180,6 +204,11 @@ def on_edit(self, xmin, xmax, *, method):
180204 if method == "insert" :
181205 self .included .add (newpeak )
182206 self .data = operations .add_peaks (self .data , newpeak )
207+ elif method == "annotate" :
208+ self .data = operations .annotate_segments (self .data , segment )
209+ self .annotated = set ()
210+ for s in self .data .segments :
211+ self .annotated .add ((s [0 ], s [1 ]))
183212 else :
184213 rej .update (self .data .peaks [bad ].tolist ())
185214 self .data = fcn (self .data , self .data .peaks [bad ])
@@ -189,7 +218,7 @@ def on_edit(self, xmin, xmax, *, method):
189218 def undo (self ):
190219 """Reset last span select peak removal."""
191220 # check if last history entry was a manual reject / delete
192- relevant = ["reject_peaks" , "delete_peaks" , "add_peaks" ]
221+ relevant = ["reject_peaks" , "delete_peaks" , "add_peaks" , "annotate_segments" ]
193222 if self .data ._history [- 1 ][0 ] not in relevant :
194223 return
195224
@@ -215,7 +244,20 @@ def undo(self):
215244 np .searchsorted (self .data ._metadata ["peaks" ], peaks ["add" ]),
216245 )
217246 self .included .remove (peaks ["add" ])
247+ elif func == "annotate_segments" :
248+ self .data ._metadata ["segments" ] = np .delete (
249+ self .data ._metadata ["segments" ],
250+ - 1 ,
251+ axis = 0
252+ )
253+ self .annotated .pop ()
218254 self .data ._metadata ["troughs" ] = utils .check_troughs (
219255 self .data , self .data .peaks , self .data .troughs
220256 )
221257 self .plot_signals ()
258+
259+ def annotate_mode (self ):
260+ self .mode = "annotate"
261+
262+ def edit_mode (self ):
263+ self .mode = "delete"
0 commit comments