You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

142 lines
5.5 KiB

  1. import wx
  2. from gooey.gui.components.widgets.bases import BaseWidget
  3. from gooey.gui.lang.i18n import _
  4. from gooey.gui.util import wx_util
  5. from gooey.gui.components.widgets import CheckBox
  6. from gooey.util.functional import getin, findfirst, merge
  7. class RadioGroup(BaseWidget):
  8. def __init__(self, parent, widgetInfo, *args, **kwargs):
  9. super(RadioGroup, self).__init__(parent, *args, **kwargs)
  10. self._parent = parent
  11. self.info = widgetInfo
  12. self._id = widgetInfo['id']
  13. self.widgetInfo = widgetInfo
  14. self.error = wx.StaticText(self, label='')
  15. self.radioButtons = self.createRadioButtons()
  16. self.selected = None
  17. self.widgets = self.createWidgets()
  18. self.arrange()
  19. self.applyStyleRules()
  20. for button in self.radioButtons:
  21. button.Bind(wx.EVT_LEFT_DOWN, self.handleButtonClick)
  22. initialSelection = getin(self.info, ['options', 'initial_selection'], None)
  23. if initialSelection is not None:
  24. self.selected = self.radioButtons[initialSelection]
  25. self.selected.SetValue(True)
  26. self.handleImplicitCheck()
  27. def getValue(self):
  28. for button, widget in zip(self.radioButtons, self.widgets):
  29. if button.GetValue(): # is Checked
  30. return merge(widget.getValue(), {'id': self._id})
  31. else:
  32. # just return the first widget's value even though it's
  33. # not active so that the expected interface is satisfied
  34. return self.widgets[0].getValue()
  35. def setErrorString(self, message):
  36. for button, widget in zip(self.radioButtons, self.widgets):
  37. if button.GetValue(): # is Checked
  38. widget.setErrorString(message)
  39. self.Layout()
  40. def showErrorString(self, b):
  41. for button, widget in zip(self.radioButtons, self.widgets):
  42. if button.GetValue(): # is Checked
  43. widget.showErrorString(b)
  44. def arrange(self, *args, **kwargs):
  45. title = getin(self.widgetInfo, ['options', 'title'], _('choose_one'))
  46. if getin(self.widgetInfo, ['options', 'show_border'], False):
  47. boxDetails = wx.StaticBox(self, -1, title)
  48. boxSizer = wx.StaticBoxSizer(boxDetails, wx.VERTICAL)
  49. else:
  50. boxSizer = wx.BoxSizer(wx.VERTICAL)
  51. boxSizer.AddSpacer(10)
  52. boxSizer.Add(wx_util.h1(self, title), 0)
  53. for btn, widget in zip(self.radioButtons, self.widgets):
  54. sizer = wx.BoxSizer(wx.HORIZONTAL)
  55. sizer.Add(btn,0, wx.RIGHT, 4)
  56. sizer.Add(widget, 1, wx.EXPAND)
  57. boxSizer.Add(sizer, 1, wx.ALL | wx.EXPAND, 5)
  58. self.SetSizer(boxSizer)
  59. def handleButtonClick(self, event):
  60. currentSelection = self.selected
  61. nextSelection = event.EventObject
  62. if not self.isSameRadioButton(currentSelection, nextSelection):
  63. self.selected = nextSelection
  64. self.selected.SetValue(True)
  65. else:
  66. # user clicked on an already enabled radio button.
  67. # if it is not in the required section, allow it to be deselected
  68. if not self.widgetInfo['required']:
  69. self.selected.SetValue(False)
  70. self.applyStyleRules()
  71. self.handleImplicitCheck()
  72. def isSameRadioButton(self, radioButton1, radioButton2):
  73. return (getattr(radioButton1, 'Id', 'r1-not-found') ==
  74. getattr(radioButton2, 'Id', 'r2-not-found'))
  75. def applyStyleRules(self):
  76. """
  77. Conditionally disabled/enables form fields based on the current
  78. section in the radio group
  79. """
  80. for button, widget in zip(self.radioButtons, self.widgets):
  81. if isinstance(widget, CheckBox):
  82. widget.hideInput()
  83. if not button.GetValue(): # not checked
  84. widget.widget.Disable()
  85. else:
  86. widget.widget.Enable()
  87. def handleImplicitCheck(self):
  88. """
  89. Checkboxes are hidden when inside of a RadioGroup as a selection of
  90. the Radio button is an implicit selection of the Checkbox. As such, we have
  91. to manually "check" any checkbox as needed.
  92. """
  93. for button, widget in zip(self.radioButtons, self.widgets):
  94. if isinstance(widget, CheckBox):
  95. if button.GetValue(): # checked
  96. widget.setValue(True)
  97. else:
  98. widget.setValue(False)
  99. def createRadioButtons(self):
  100. # button groups in wx are statefully determined via a style flag
  101. # on the first button (what???). All button instances are part of the
  102. # same group until a new button is created with the style flag RG_GROUP
  103. # https://wxpython.org/Phoenix/docs/html/wx.RadioButton.html
  104. # (What???)
  105. firstButton = wx.RadioButton(self, style=wx.RB_GROUP)
  106. firstButton.SetValue(False)
  107. buttons = [firstButton]
  108. for _ in getin(self.widgetInfo, ['data','widgets'], [])[1:]:
  109. buttons.append(wx.RadioButton(self))
  110. return buttons
  111. def createWidgets(self):
  112. """
  113. Instantiate the Gooey Widgets that are used within the RadioGroup
  114. """
  115. from gooey.gui.components import widgets
  116. return [getattr(widgets, item['type'])(self, item)
  117. for item in getin(self.widgetInfo, ['data', 'widgets'], [])]