@@ -190,7 +190,7 @@ def _set_focus_update_var(self, event):
190190    if  self .focus_update_var  and  not  self .error .get ():
191191      self .focus_update_var .set (value )
192192
193-   def  _set_minimum (self , * args ):
193+   def  _set_minimum (self , * _ ):
194194    current  =  self .get ()
195195    try :
196196      new_min  =  self .min_var .get ()
@@ -203,7 +203,7 @@ def _set_minimum(self, *args):
203203      self .variable .set (current )
204204    self .trigger_focusout_validation ()
205205
206-   def  _set_maximum (self , * args ):
206+   def  _set_maximum (self , * _ ):
207207    current  =  self .get ()
208208    try :
209209      new_max  =  self .max_var .get ()
@@ -219,13 +219,13 @@ def _set_maximum(self, *args):
219219  def  _key_validate (
220220    self , char , index , current , proposed , action , ** kwargs 
221221  ):
222+     if  action  ==  '0' :
223+       return  True 
222224    valid  =  True 
223225    min_val  =  self .cget ('from' )
224226    max_val  =  self .cget ('to' )
225227    no_negative  =  min_val  >=  0 
226228    no_decimal  =  self .precision  >=  0 
227-     if  action  ==  '0' :
228-       return  True 
229229
230230    # First, filter out obviously invalid keystrokes 
231231    if  any ([
@@ -260,55 +260,69 @@ def _focusout_validate(self, **kwargs):
260260    max_val  =  self .cget ('to' )
261261
262262    try :
263-       value  =  Decimal (value )
263+       d_value  =  Decimal (value )
264264    except  InvalidOperation :
265-       self .error .set ('Invalid number string: {}'  . format ( value ) )
265+       self .error .set (f 'Invalid number string: { value } '  )
266266      return  False 
267267
268-     if  value  <  min_val :
269-       self .error .set ('Value is too low (min {})' .format (min_val ))
268+     if  d_value  <  min_val :
269+       self .error .set (f'Value is too low (min { min_val }  )' )
270+       valid  =  False 
271+     if  d_value  >  max_val :
272+       self .error .set (f'Value is too high (max { max_val }  )' )
270273      valid  =  False 
271-     if  value  >  max_val :
272-       self .error .set ('Value is too high (max {})' .format (max_val ))
273274
274275    return  valid 
275276
277+ class  ValidatedRadioGroup (ttk .Frame ):
278+   """A validated radio button group""" 
279+ 
280+   def  __init__ (
281+     self , * args , variable = None , error_var = None ,
282+     values = None , button_args = None , ** kwargs 
283+   ):
284+     super ().__init__ (* args , ** kwargs )
285+     self .variable  =  variable  or  tk .StringVar ()
286+     self .error  =  error_var  or  tk .StringVar ()
287+     self .values  =  values  or  list ()
288+     button_args  =  button_args  or  dict ()
289+ 
290+     for  v  in  self .values :
291+       button  =  ttk .Radiobutton (
292+         self , value = v , text = v , variable = self .variable , ** button_args 
293+       )
294+       button .pack (side = tk .LEFT , ipadx = 10 , ipady = 2 , expand = True , fill = 'x' )
295+     self .bind ('<FocusOut>' , self .trigger_focusout_validation )
296+ 
297+   def  trigger_focusout_validation (self , * _ ):
298+     self .error .set ('' )
299+     if  not  self .variable .get ():
300+       self .error .set ('A value is required' )
301+ 
302+ 
276303class  BoundText (tk .Text ):
277304  """A Text widget with a bound variable.""" 
278305
279306  def  __init__ (self , * args , textvariable = None , ** kwargs ):
280307    super ().__init__ (* args , ** kwargs )
281308    self ._variable  =  textvariable 
282-     self ._modifying  =  False 
283309    if  self ._variable :
284310      # insert any default value 
285311      self .insert ('1.0' , self ._variable .get ())
286312      self ._variable .trace_add ('write' , self ._set_content )
287313      self .bind ('<<Modified>>' , self ._set_var )
288314
289-   def  _clear_modified_flag (self ):
290-     # This also triggers a '<<Modified>>' Event 
291-     self .tk .call (self ._w , 'edit' , 'modified' , 0 )
292- 
293315  def  _set_var (self , * _ ):
294316    """Set the variable to the text contents""" 
295-     if  self ._modifying :
296-       return 
297-     self ._modifying  =  True 
298-     # remove trailing newline from content 
299-     content  =  self .get ('1.0' , tk .END )[:- 1 ]
300-     self ._variable .set (content )
301-     self ._clear_modified_flag ()
302-     self ._modifying  =  False 
317+     if  self .edit_modified ():
318+       content  =  self .get ('1.0' , 'end-1chars' )
319+       self ._variable .set (content )
320+       self .edit_modified (False )
303321
304322  def  _set_content (self , * _ ):
305323    """Set the text contents to the variable""" 
306-     if  self ._modifying :
307-       return 
308-     self ._modifying  =  True 
309324    self .delete ('1.0' , tk .END )
310325    self .insert ('1.0' , self ._variable .get ())
311-     self ._modifying  =  False 
312326
313327
314328########################### 
@@ -322,7 +336,7 @@ class LabelInput(ttk.Frame):
322336  field_types  =  {
323337    FT .string : RequiredEntry ,
324338    FT .string_list : ValidatedCombobox ,
325-     FT .short_string_list : ttk . Radiobutton ,
339+     FT .short_string_list : ValidatedRadioGroup ,
326340    FT .iso_date_string : DateEntry ,
327341    FT .long_string : BoundText ,
328342    FT .decimal : ValidatedSpinbox ,
@@ -365,7 +379,9 @@ def __init__(
365379      self .label .grid (row = 0 , column = 0 , sticky = (tk .W  +  tk .E ))
366380
367381    # setup the variable 
368-     if  input_class  in  (ttk .Checkbutton , ttk .Button , ttk .Radiobutton ):
382+     if  input_class  in  (
383+         ttk .Checkbutton , ttk .Button , ttk .Radiobutton , ValidatedRadioGroup 
384+     ):
369385      input_args ["variable" ] =  self .variable 
370386    else :
371387      input_args ["textvariable" ] =  self .variable 
@@ -375,12 +391,12 @@ def __init__(
375391      # for Radiobutton, create one input per value 
376392      self .input  =  tk .Frame (self )
377393      for  v  in  input_args .pop ('values' , []):
378-         button  =  ttk .Radiobutton (
379-           self .input , value = v , text = v , ** input_args )
394+         button  =  input_class (
395+           self .input , value = v , text = v , ** input_args 
396+         )
380397        button .pack (side = tk .LEFT , ipadx = 10 , ipady = 2 , expand = True , fill = 'x' )
381398    else :
382399      self .input  =  input_class (self , ** input_args )
383- 
384400    self .input .grid (row = 1 , column = 0 , sticky = (tk .W  +  tk .E ))
385401    self .columnconfigure (0 , weight = 1 )
386402
0 commit comments