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.

145 lines
5.6 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. self.applyStyleRules()
  21. for button in self.radioButtons:
  22. button.Bind(wx.EVT_LEFT_DOWN, self.handleButtonClick)
  23. initialSelection = getin(self.info, ['options', 'initial_selection'], None)
  24. if initialSelection is not None:
  25. self.selected = self.radioButtons[initialSelection]
  26. self.selected.SetValue(True)
  27. self.handleImplicitCheck()
  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.applyStyleRules()
  74. self.handleImplicitCheck()
  75. def isSameRadioButton(self, radioButton1, radioButton2):
  76. return (getattr(radioButton1, 'Id', 'r1-not-found') ==
  77. getattr(radioButton2, 'Id', 'r2-not-found'))
  78. def applyStyleRules(self):
  79. """
  80. Conditionally disabled/enables form fields based on the current
  81. section in the radio group
  82. """
  83. for button, widget in zip(self.radioButtons, self.widgets):
  84. if isinstance(widget, CheckBox):
  85. widget.hideInput()
  86. if not button.GetValue(): # not checked
  87. widget.widget.Disable()
  88. else:
  89. widget.widget.Enable()
  90. def handleImplicitCheck(self):
  91. """
  92. Checkboxes are hidden when inside of a RadioGroup as a selection of
  93. the Radio button is an implicit selection of the Checkbox. As such, we have
  94. to manually "check" any checkbox as needed.
  95. """
  96. for button, widget in zip(self.radioButtons, self.widgets):
  97. if isinstance(widget, CheckBox):
  98. if button.GetValue(): # checked
  99. widget.setValue(True)
  100. else:
  101. widget.setValue(False)
  102. def createRadioButtons(self):
  103. # button groups in wx are statefully determined via a style flag
  104. # on the first button (what???). All button instances are part of the
  105. # same group until a new button is created with the style flag RG_GROUP
  106. # https://wxpython.org/Phoenix/docs/html/wx.RadioButton.html
  107. # (What???)
  108. firstButton = wx.RadioButton(self, style=wx.RB_GROUP)
  109. firstButton.SetValue(False)
  110. buttons = [firstButton]
  111. for _ in getin(self.widgetInfo, ['data','widgets'], [])[1:]:
  112. buttons.append(wx.RadioButton(self))
  113. return buttons
  114. def createWidgets(self):
  115. """
  116. Instantiate the Gooey Widgets that are used within the RadioGroup
  117. """
  118. from gooey.gui.components import widgets
  119. return [getattr(widgets, item['type'])(self, item)
  120. for item in getin(self.widgetInfo, ['data', 'widgets'], [])]