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.

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