11from collections import defaultdict , namedtuple
2+ from contextlib import contextmanager
23from typing import Optional
34from xml .sax .saxutils import escape
45
@@ -171,6 +172,17 @@ def paint(self, painter, _option, _index):
171172 painter .restore ()
172173
173174
175+ @contextmanager
176+ def disconnected_spin (spin ):
177+ # workaround for block spins which signals cannot be disconnected with
178+ # disconnect since they connect protected method from the spin's class
179+ spin .blockSignals (True )
180+ try :
181+ yield
182+ finally :
183+ spin .blockSignals (False )
184+
185+
174186N_ITERATIONS = 200
175187
176188
@@ -209,6 +221,12 @@ class Outputs:
209221 ("shape" , "auto_dim" , "spin_x" , "spin_y" , "initialization" , "start" )
210222 )
211223
224+ class Information (OWWidget .Information ):
225+ modified = Msg (
226+ 'The parameter settings have been changed. Press "Start" to '
227+ "rerun with the new settings."
228+ )
229+
212230 class Warning (OWWidget .Warning ):
213231 ignoring_disc_variables = Msg ("SOM ignores categorical variables." )
214232 missing_colors = \
@@ -243,34 +261,48 @@ def __init__(self):
243261 shape = gui .comboBox (
244262 box , self , "" , items = ("Hexagonal grid" , "Square grid" ))
245263 shape .setCurrentIndex (1 - self .hexagonal )
264+ shape .currentIndexChanged .connect (self .on_parameter_change )
246265
247266 box2 = gui .indentedBox (box , 10 )
248267 auto_dim = gui .checkBox (
249268 box2 , self , "auto_dimension" , "Set dimensions automatically" ,
250269 callback = self .on_auto_dimension_changed )
251270 self .manual_box = box3 = gui .hBox (box2 )
252271 spinargs = dict (
253- value = "" , widget = box3 , master = self , minv = 5 , maxv = 100 , step = 5 ,
254- alignment = Qt .AlignRight )
255- spin_x = gui .spin (** spinargs )
256- spin_x .setValue (self .size_x )
272+ value = "" ,
273+ widget = box3 ,
274+ master = self ,
275+ minv = 5 ,
276+ maxv = 100 ,
277+ step = 5 ,
278+ alignment = Qt .AlignRight ,
279+ callback = self .on_parameter_change ,
280+ )
281+ self .spin_x = gui .spin (** spinargs )
282+ with disconnected_spin (self .spin_x ):
283+ self .spin_x .setValue (self .size_x )
257284 gui .widgetLabel (box3 , "×" )
258- spin_y = gui .spin (** spinargs )
259- spin_y .setValue (self .size_y )
285+ self .spin_y = gui .spin (** spinargs )
286+ with disconnected_spin (self .spin_y ):
287+ self .spin_y .setValue (self .size_y )
260288 gui .rubber (box3 )
261289 self .manual_box .setEnabled (not self .auto_dimension )
262290
263291 initialization = gui .comboBox (
264- box , self , "initialization" ,
265- items = ("Initialize with PCA" , "Random initialization" ,
266- "Replicable random" ))
292+ box ,
293+ self ,
294+ "initialization" ,
295+ items = ("Initialize with PCA" , "Random initialization" , "Replicable random" ),
296+ callback = self .on_parameter_change ,
297+ )
267298
268299 start = gui .button (
269300 box , self , "Restart" , callback = self .restart_som_pressed ,
270301 sizePolicy = (QSizePolicy .MinimumExpanding , QSizePolicy .Fixed ))
271302
272303 self .opt_controls = self .OptControls (
273- shape , auto_dim , spin_x , spin_y , initialization , start )
304+ shape , auto_dim , self .spin_x , self .spin_y , initialization , start
305+ )
274306
275307 box = gui .vBox (self .controlArea , "Color" )
276308 gui .comboBox (
@@ -366,7 +398,8 @@ def set_warnings():
366398 self .set_color_bins ()
367399 self .create_legend ()
368400 if invalidated :
369- self .recompute_dimensions ()
401+ with disconnected_spin (self .spin_x ), disconnected_spin (self .spin_y ):
402+ self .recompute_dimensions ()
370403 self .start_som ()
371404 else :
372405 self ._redraw ()
@@ -399,6 +432,7 @@ def on_auto_dimension_changed(self):
399432 dimy = int (5 * np .round (spin_y .value () / 5 ))
400433 spin_x .setValue (dimx )
401434 spin_y .setValue (dimy )
435+ self .on_parameter_change ()
402436
403437 def on_attr_color_change (self ):
404438 self .controls .pie_charts .setEnabled (self .attr_color is not None )
@@ -413,6 +447,9 @@ def on_attr_size_change(self):
413447 def on_pie_chart_change (self ):
414448 self ._redraw ()
415449
450+ def on_parameter_change (self ):
451+ self .Information .modified ()
452+
416453 def clear_selection (self ):
417454 self .selection = None
418455 self .redraw_selection ()
@@ -498,6 +535,7 @@ def redraw_selection(self, marks=None):
498535 cell .setZValue (marked or sel_group )
499536
500537 def restart_som_pressed (self ):
538+ self .Information .modified .clear ()
501539 if self ._optimizer_thread is not None :
502540 self .stop_optimization = True
503541 self ._optimizer .stop_optimization = True
0 commit comments