@@ -274,6 +274,35 @@ def _focusout_validate(self, **kwargs):
274274
275275    return  valid 
276276
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+     self .button_args  =  button_args  or  dict ()
289+ 
290+     for  v  in  self .values :
291+       button  =  ttk .Radiobutton (
292+         self , value = v , text = v ,
293+         variable = self .variable , ** self .button_args 
294+       )
295+       button .pack (
296+         side = tk .LEFT , ipadx = 10 , ipady = 2 , expand = True , fill = 'x' 
297+       )
298+     self .bind ('<FocusOut>' , self .trigger_focusout_validation )
299+ 
300+   def  trigger_focusout_validation (self , * _ ):
301+     self .error .set ('' )
302+     if  not  self .variable .get ():
303+       self .error .set ('A value is required' )
304+ 
305+ 
277306class  BoundText (tk .Text ):
278307  """A Text widget with a bound variable.""" 
279308
@@ -310,7 +339,7 @@ class LabelInput(ttk.Frame):
310339  field_types  =  {
311340    FT .string : RequiredEntry ,
312341    FT .string_list : ValidatedCombobox ,
313-     FT .short_string_list : ttk . Radiobutton ,
342+     FT .short_string_list : ValidatedRadioGroup ,
314343    FT .iso_date_string : DateEntry ,
315344    FT .long_string : BoundText ,
316345    FT .decimal : ValidatedSpinbox ,
@@ -353,7 +382,9 @@ def __init__(
353382      self .label .grid (row = 0 , column = 0 , sticky = (tk .W  +  tk .E ))
354383
355384    # setup the variable 
356-     if  input_class  in  (ttk .Checkbutton , ttk .Button , ttk .Radiobutton ):
385+     if  input_class  in  (
386+         ttk .Checkbutton , ttk .Button , ttk .Radiobutton , ValidatedRadioGroup 
387+     ):
357388      input_args ["variable" ] =  self .variable 
358389    else :
359390      input_args ["textvariable" ] =  self .variable 
@@ -363,12 +394,12 @@ def __init__(
363394      # for Radiobutton, create one input per value 
364395      self .input  =  tk .Frame (self )
365396      for  v  in  input_args .pop ('values' , []):
366-         button  =  ttk .Radiobutton (
367-           self .input , value = v , text = v , ** input_args )
397+         button  =  input_class (
398+           self .input , value = v , text = v , ** input_args 
399+         )
368400        button .pack (side = tk .LEFT , ipadx = 10 , ipady = 2 , expand = True , fill = 'x' )
369401    else :
370402      self .input  =  input_class (self , ** input_args )
371- 
372403    self .input .grid (row = 1 , column = 0 , sticky = (tk .W  +  tk .E ))
373404    self .columnconfigure (0 , weight = 1 )
374405
0 commit comments