11import sys
2+ import copy
3+
24import numpy as np
35
46from AnyQt .QtWidgets import (
5- QWidget , QGroupBox , QRadioButton , QPushButton , QHBoxLayout ,
6- QVBoxLayout , QStackedLayout , QComboBox , QLineEdit ,
7+ QGroupBox , QRadioButton , QPushButton , QHBoxLayout ,
8+ QVBoxLayout , QStackedWidget , QComboBox ,
79 QButtonGroup , QStyledItemDelegate , QListView , QDoubleSpinBox
810)
911from AnyQt .QtCore import Qt
@@ -75,13 +77,15 @@ class Error(OWWidget.Error):
7577 _default_method_index = settings .Setting (DO_NOT_IMPUTE )
7678 variable_methods = settings .ContextSetting ({})
7779 autocommit = settings .Setting (False )
78- default_value = settings .Setting (0. )
7980
8081 want_main_area = False
8182 resizing_enabled = False
8283
8384 def __init__ (self ):
8485 super ().__init__ ()
86+ # copy METHODS (some are modified by the widget)
87+ self .methods = copy .deepcopy (OWImpute .METHODS )
88+
8589 main_layout = QVBoxLayout ()
8690 main_layout .setContentsMargins (10 , 10 , 10 , 10 )
8791 self .controlArea .layout ().addLayout (main_layout )
@@ -92,7 +96,7 @@ def __init__(self):
9296
9397 button_group = QButtonGroup ()
9498 button_group .buttonClicked [int ].connect (self .set_default_method )
95- for i , method in enumerate (self .METHODS ):
99+ for i , method in enumerate (self .methods ):
96100 if not method .columns_only :
97101 button = QRadioButton (method .name )
98102 button .setChecked (i == self .default_method_index )
@@ -125,7 +129,7 @@ def __init__(self):
125129 horizontal_layout .addLayout (method_layout )
126130
127131 button_group = QButtonGroup ()
128- for i , method in enumerate (self .METHODS ):
132+ for i , method in enumerate (self .methods ):
129133 button = QRadioButton (text = method .name )
130134 button_group .addButton (button , i )
131135 method_layout .addWidget (button )
@@ -135,16 +139,14 @@ def __init__(self):
135139 sizeAdjustPolicy = QComboBox .AdjustToMinimumContentsLength ,
136140 activated = self ._on_value_selected
137141 )
138- self .value_combo .currentIndexChanged .connect (self ._on_value_changed )
139142 self .value_double = QDoubleSpinBox (
140143 editingFinished = self ._on_value_selected ,
141144 minimum = - 1000. , maximum = 1000. , singleStep = .1 , decimals = 3 ,
142- value = self .default_value
143145 )
144- self .value_stack = value_stack = QStackedLayout ()
146+ self .value_stack = value_stack = QStackedWidget ()
145147 value_stack .addWidget (self .value_combo )
146148 value_stack .addWidget (self .value_double )
147- method_layout .addLayout (value_stack )
149+ method_layout .addWidget (value_stack )
148150
149151 button_group .buttonClicked [int ].connect (
150152 self .set_method_for_current_selection
@@ -167,9 +169,9 @@ def __init__(self):
167169 box .layout ().insertWidget (0 , self .report_button )
168170
169171 self .data = None
172+ self .learner = None
170173 self .modified = False
171- self .default_method = self .METHODS [self .default_method_index ]
172- self .update_varview ()
174+ self .default_method = self .methods [self .default_method_index ]
173175
174176 @property
175177 def default_method_index (self ):
@@ -180,14 +182,14 @@ def default_method_index(self, index):
180182 if self ._default_method_index != index :
181183 self ._default_method_index = index
182184 self .default_button_group .button (index ).setChecked (True )
183- self .default_method = self .METHODS [self .default_method_index ]
184- self .METHODS [self .DEFAULT ].method = self .default_method
185+ self .default_method = self .methods [self .default_method_index ]
186+ self .methods [self .DEFAULT ].method = self .default_method
185187
186188 # update variable view
187189 for index in map (self .varmodel .index , range (len (self .varmodel ))):
188- self .varmodel . setData ( index ,
189- self . variable_methods . get ( index .row (), self .METHODS [self .DEFAULT ]),
190- Qt .UserRole )
190+ method = self .variable_methods . get (
191+ index .row (), self .methods [self .DEFAULT ])
192+ self . varmodel . setData ( index , method , Qt .UserRole )
191193 self ._invalidate ()
192194
193195 def set_default_method (self , index ):
@@ -212,7 +214,7 @@ def set_data(self, data):
212214
213215 def set_learner (self , learner ):
214216 self .learner = learner or self .DEFAULT_LEARNER
215- imputer = self .METHODS [self .MODEL_BASED_IMPUTER ]
217+ imputer = self .methods [self .MODEL_BASED_IMPUTER ]
216218 imputer .learner = self .learner
217219
218220 button = self .default_button_group .button (self .MODEL_BASED_IMPUTER )
@@ -224,6 +226,7 @@ def set_learner(self, learner):
224226 if learner is not None :
225227 self .default_method_index = self .MODEL_BASED_IMPUTER
226228
229+ self .update_varview ()
227230 self .commit ()
228231
229232 def get_method_for_column (self , column_index ):
@@ -233,7 +236,7 @@ def get_method_for_column(self, column_index):
233236 column_index = column_index .row ()
234237
235238 return self .variable_methods .get (column_index ,
236- self .METHODS [self .DEFAULT ])
239+ self .methods [self .DEFAULT ])
237240
238241 def _invalidate (self ):
239242 self .modified = True
@@ -267,7 +270,7 @@ def commit(self):
267270 drop_mask |= method (self .data , var )
268271 else :
269272 var = method (self .data , var )
270- except :
273+ except Exception : # pylint: disable=broad-except
271274 self .Error .imputation_failed (var .name )
272275 attributes = class_vars = None
273276 break
@@ -310,40 +313,74 @@ def send_report(self):
310313
311314 def _on_var_selection_changed (self ):
312315 indexes = self .selection .selectedIndexes ()
313- methods = set (self .get_method_for_column (i .row ()).name for i in indexes )
316+ methods = [self .get_method_for_column (i .row ()) for i in indexes ]
317+
318+ def method_key (method ):
319+ """
320+ Decompose method into its type and parameters.
321+ """
322+ # The return value should be hashable and __eq__ comparable
323+ if isinstance (method , AsDefault ):
324+ return AsDefault , (method .method ,)
325+ elif isinstance (method , impute .Model ):
326+ return impute .Model , (method .learner ,)
327+ elif isinstance (method , impute .Default ):
328+ return impute .Default , (method .default ,)
329+ else :
330+ return type (method ), None
314331
332+ methods = set (method_key (m ) for m in methods )
315333 selected_vars = [self .varmodel [index .row ()] for index in indexes ]
316334 has_discrete = any (var .is_discrete for var in selected_vars )
335+ fixed_value = None
336+ value_stack_enabled = False
337+ current_value_widget = None
317338
318339 if len (methods ) == 1 :
319- method = methods .pop ()
320- for i , m in enumerate (self .METHODS ):
321- if method == m . name :
340+ method_type , parameters = methods .pop ()
341+ for i , m in enumerate (self .methods ):
342+ if method_type == type ( m ) :
322343 self .variable_button_group .button (i ).setChecked (True )
344+
345+ if method_type is impute .Default :
346+ (fixed_value ,) = parameters
347+
323348 elif self .variable_button_group .checkedButton () is not None :
349+ # Uncheck the current button
324350 self .variable_button_group .setExclusive (False )
325351 self .variable_button_group .checkedButton ().setChecked (False )
326352 self .variable_button_group .setExclusive (True )
353+ assert self .variable_button_group .checkedButton () is None
327354
328- for method , button in zip (self .METHODS ,
355+ for method , button in zip (self .methods ,
329356 self .variable_button_group .buttons ()):
330357 enabled = all (method .supports_variable (var ) for var in
331358 selected_vars )
332359 button .setEnabled (enabled )
333360
334361 if not has_discrete :
335- self .value_stack .setEnabled (True )
336- self .value_stack .setCurrentWidget (self .value_double )
337- self ._on_value_changed ()
362+ value_stack_enabled = True
363+ current_value_widget = self .value_double
338364 elif len (selected_vars ) == 1 :
339- self . value_stack . setEnabled ( True )
340- self .value_stack . setCurrentWidget ( self . value_combo )
365+ value_stack_enabled = True
366+ current_value_widget = self .value_combo
341367 self .value_combo .clear ()
342368 self .value_combo .addItems (selected_vars [0 ].values )
343- self ._on_value_changed ()
344369 else :
370+ value_stack_enabled = False
371+ current_value_widget = None
345372 self .variable_button_group .button (self .AS_INPUT ).setEnabled (False )
346- self .value_stack .setEnabled (False )
373+
374+ self .value_stack .setEnabled (value_stack_enabled )
375+ if current_value_widget is not None :
376+ self .value_stack .setCurrentWidget (current_value_widget )
377+ if fixed_value is not None :
378+ if current_value_widget is self .value_combo :
379+ self .value_combo .setCurrentIndex (fixed_value )
380+ elif current_value_widget is self .value_double :
381+ self .value_double .setValue (fixed_value )
382+ else :
383+ assert False
347384
348385 def set_method_for_current_selection (self , method_index ):
349386 indexes = self .selection .selectedIndexes ()
@@ -352,9 +389,18 @@ def set_method_for_current_selection(self, method_index):
352389 def set_method_for_indexes (self , indexes , method_index ):
353390 if method_index == self .DEFAULT :
354391 for index in indexes :
355- self .variable_methods .pop (index , None )
392+ self .variable_methods .pop (index .row (), None )
393+ elif method_index == OWImpute .AS_INPUT :
394+ current = self .value_stack .currentWidget ()
395+ if current is self .value_combo :
396+ value = self .value_combo .currentIndex ()
397+ else :
398+ value = self .value_double .value ()
399+ for index in indexes :
400+ method = impute .Default (default = value )
401+ self .variable_methods [index .row ()] = method
356402 else :
357- method = self .METHODS [method_index ].copy ()
403+ method = self .methods [method_index ].copy ()
358404 for index in indexes :
359405 self .variable_methods [index .row ()] = method
360406
@@ -369,24 +415,12 @@ def update_varview(self, indexes=None):
369415 self .varmodel .setData (index , self .get_method_for_column (index .row ()), Qt .UserRole )
370416
371417 def _on_value_selected (self ):
418+ # The fixed 'Value' in the widget has been changed by the user.
372419 self .variable_button_group .button (self .AS_INPUT ).setChecked (True )
373- self ._on_value_changed ()
374-
375- def _on_value_changed (self ):
376- widget = self .value_stack .currentWidget ()
377- if widget is self .value_combo :
378- value = self .value_combo .currentText ()
379- else :
380- value = self .value_double .value ()
381- self .default_value = value
382-
383- self .METHODS [self .AS_INPUT ].default = value
384- index = self .variable_button_group .checkedId ()
385- if index == self .AS_INPUT :
386- self .set_method_for_current_selection (index )
420+ self .set_method_for_current_selection (self .AS_INPUT )
387421
388422 def reset_variable_methods (self ):
389- indexes = map (self .varmodel .index , range (len (self .varmodel )))
423+ indexes = list ( map (self .varmodel .index , range (len (self .varmodel ) )))
390424 self .set_method_for_indexes (indexes , self .DEFAULT )
391425 self .variable_button_group .button (self .DEFAULT ).setChecked (True )
392426
0 commit comments