Browse Source

closes #517 - radio group not enabling widget when initial_selection is set

1.0.4-release--issue-470
Chris 4 years ago
parent
commit
91d0fad421
5 changed files with 258 additions and 10 deletions
  1. 7
      gooey/gui/application.py
  2. 18
      gooey/gui/components/widgets/radio_group.py
  3. 32
      gooey/python_bindings/gooey_decorator.py
  4. 22
      gooey/tests/harness.py
  5. 189
      gooey/tests/test_radiogroup.py

7
gooey/gui/application.py

@ -18,7 +18,7 @@ from gooey.util.functional import merge
def run(build_spec): def run(build_spec):
app = build_app(build_spec)
app, _ = build_app(build_spec)
app.MainLoop() app.MainLoop()
@ -29,8 +29,5 @@ def build_app(build_spec):
imagesPaths = image_repository.loadImages(build_spec['image_dir']) imagesPaths = image_repository.loadImages(build_spec['image_dir'])
gapp = GooeyApplication(merge(build_spec, imagesPaths)) gapp = GooeyApplication(merge(build_spec, imagesPaths))
gapp.Show() gapp.Show()
return app
return (app, gapp)

18
gooey/gui/components/widgets/radio_group.py

@ -20,7 +20,7 @@ class RadioGroup(BaseWidget):
self.selected = None self.selected = None
self.widgets = self.createWidgets() self.widgets = self.createWidgets()
self.arrange() self.arrange()
self.applyStyleRules()
for button in self.radioButtons: for button in self.radioButtons:
button.Bind(wx.EVT_LEFT_DOWN, self.handleButtonClick) button.Bind(wx.EVT_LEFT_DOWN, self.handleButtonClick)
@ -31,6 +31,8 @@ class RadioGroup(BaseWidget):
self.selected.SetValue(True) self.selected.SetValue(True)
self.handleImplicitCheck() self.handleImplicitCheck()
self.applyStyleRules()
def getValue(self): def getValue(self):
for button, widget in zip(self.radioButtons, self.widgets): for button, widget in zip(self.radioButtons, self.widgets):
@ -85,6 +87,7 @@ class RadioGroup(BaseWidget):
# if it is not in the required section, allow it to be deselected # if it is not in the required section, allow it to be deselected
if not self.widgetInfo['required']: if not self.widgetInfo['required']:
self.selected.SetValue(False) self.selected.SetValue(False)
self.selected = None
self.applyStyleRules() self.applyStyleRules()
self.handleImplicitCheck() self.handleImplicitCheck()
@ -104,9 +107,9 @@ class RadioGroup(BaseWidget):
if isinstance(widget, CheckBox): if isinstance(widget, CheckBox):
widget.hideInput() widget.hideInput()
if not button.GetValue(): # not checked if not button.GetValue(): # not checked
widget.widget.Disable()
widget.Disable()
else: else:
widget.widget.Enable()
widget.Enable()
def handleImplicitCheck(self): def handleImplicitCheck(self):
""" """
@ -141,5 +144,10 @@ class RadioGroup(BaseWidget):
Instantiate the Gooey Widgets that are used within the RadioGroup Instantiate the Gooey Widgets that are used within the RadioGroup
""" """
from gooey.gui.components import widgets from gooey.gui.components import widgets
return [getattr(widgets, item['type'])(self, item)
for item in getin(self.widgetInfo, ['data', 'widgets'], [])]
widgets = [getattr(widgets, item['type'])(self, item)
for item in getin(self.widgetInfo, ['data', 'widgets'], [])]
# widgets should be disabled unless
# explicitly selected
for widget in widgets:
widget.Disable()
return widgets

32
gooey/python_bindings/gooey_decorator.py

@ -18,6 +18,38 @@ from . import cmd_args
IGNORE_COMMAND = '--ignore-gooey' IGNORE_COMMAND = '--ignore-gooey'
# TODO: use these defaults in the decorator and migrate to a flat **kwargs
# They're pulled out here for wiring up instances in the tests.
# Some fiddling is needed before I can make the changes to make the swap to
# `defaults` + **kwargs overrides.
defaults = {
'advanced': True,
'language': 'english',
'auto_start': False, # TODO: add this to the docs. Used to be `show_config=True`
'target': None,
'program_name': None,
'program_description': None,
'default_size': (610, 530),
'use_legacy_titles': True,
'required_cols': 2,
'optional_cols': 2,
'dump_build_config': False,
'load_build_config': None,
'monospace_display': False, # TODO: add this to the docs
'image_dir': '::gooey/default',
'language_dir': getResourcePath('languages'),
'progress_regex': None, # TODO: add this to the docs
'progress_expr': None, # TODO: add this to the docs
'hide_progress_msg': False, # TODO: add this to the docs
'disable_progress_bar_animation': False,
'disable_stop_button': False,
'group_by_type': True,
'header_height': 80,
'navigation': 'SIDEBAR', # TODO: add this to the docs
'tabbed_groups': False,
'use_cmd_args': False,
}
# TODO: kwargs all the things # TODO: kwargs all the things
def Gooey(f=None, def Gooey(f=None,
advanced=True, advanced=True,

22
gooey/tests/harness.py

@ -0,0 +1,22 @@
from contextlib import contextmanager
import wx
from gui import application
from python_bindings.config_generator import create_from_parser
from python_bindings.gooey_decorator import defaults
@contextmanager
def instrumentGooey(parser, **kwargs):
"""
Context manager used during testing for setup/tear down of the
WX infrastructure during subTests.
"""
buildspec = create_from_parser(parser, "", **defaults)
app, gooey = application.build_app(buildspec)
try:
yield (app, gooey)
finally:
wx.CallAfter(app.ExitMainLoop)
app.Destroy()

189
gooey/tests/test_radiogroup.py

@ -0,0 +1,189 @@
import unittest
import wx
from gooey import GooeyParser
from tests.harness import instrumentGooey
class TestRadioGroupBehavior(unittest.TestCase):
def mutext_group(self, options):
"""
Basic radio group consisting of two options.
"""
parser = GooeyParser()
group = parser.add_mutually_exclusive_group(**options)
group.add_argument("-b", type=str)
group.add_argument("-d", type=str, widget="DateChooser")
return parser
def test_initial_selection_options(self):
"""
Ensure that the initial_selection GooeyOption behaves as expected.
"""
# each pair in the below datastructure represents input/output
# First position: kwargs which will be supplied to the parser
# Second position: expected indices which buttons/widgets should be enabled/disabled
testCases = [
[{'required': True, 'gooey_options': {}},
{'selected': None, 'enabled': [], 'disabled': [0, 1]}],
# Issue #517 - initial section with required=True was not enabling
# the inner widget
[{'required': True, 'gooey_options': {"initial_selection": 0}},
{'selected': 0, 'enabled': [0], 'disabled': [1]}],
[{'required': True, 'gooey_options': {"initial_selection": 1}},
{'selected': 1, 'enabled': [1], 'disabled': [0]}],
[{'required': False, 'gooey_options': {}},
{'selected': None, 'enabled': [], 'disabled': [0, 1]}],
[{'required': False, 'gooey_options': {"initial_selection": 0}},
{'selected': 0, 'enabled': [0], 'disabled': [1]}],
[{'required': False, 'gooey_options': {"initial_selection": 1}},
{'selected': 1, 'enabled': [1], 'disabled': [0]}],
]
for options, expected in testCases:
parser = self.mutext_group(options)
with self.subTest(options):
with instrumentGooey(parser) as (app, gooeyApp):
radioGroup = gooeyApp.configs[0].reifiedWidgets[0]
# verify that the checkboxes themselves are correct
if expected['selected'] is not None:
self.assertEqual(
radioGroup.selected,
radioGroup.radioButtons[expected['selected']])
else:
self.assertEqual(radioGroup.selected, None)
# verify the widgets contained in the radio group
# are in the correct state
for enabled in expected['enabled']:
# The widget contained within the group should be enabled
self.assertTrue(radioGroup.widgets[enabled].IsEnabled())
# make sure all widgets other than the selected
# are disabled
for enabled in expected['disabled']:
self.assertFalse(radioGroup.widgets[enabled].IsEnabled())
def test_optional_radiogroup_click_behavior(self):
"""
Testing that select/deselect behaves as expected
"""
testcases = [
self.click_scenarios_optional_widget(),
self.click_scenarios_required_widget(),
self.click_scenarios_initial_selection()
]
for testcase in testcases:
with self.subTest(testcase['name']):
# wire up the parse with our test case options
parser = self.mutext_group(testcase['input'])
with instrumentGooey(parser) as (app, gooeyApp):
radioGroup = gooeyApp.configs[0].reifiedWidgets[0]
for scenario in testcase['scenario']:
targetButton = scenario['clickButton']
event = wx.CommandEvent(wx.wxEVT_LEFT_DOWN, wx.NewId())
event.SetEventObject(radioGroup.radioButtons[targetButton])
radioGroup.radioButtons[targetButton].ProcessEvent(event)
expectedEnabled, expectedDisabled = scenario['postState']
for index in expectedEnabled:
self.assertEqual(radioGroup.selected, radioGroup.radioButtons[index])
self.assertTrue(radioGroup.widgets[index].IsEnabled())
for index in expectedDisabled:
self.assertNotEqual(radioGroup.selected, radioGroup.radioButtons[index])
self.assertFalse(radioGroup.widgets[index].IsEnabled())
def click_scenarios_optional_widget(self):
return {
'name': 'click_scenarios_optional_widget',
'input': {'required': False},
'scenario': [
# clicking enabled the button
{'clickButton': 0,
'postState': [[0], [1]]},
# clicking again disables the button (*when not required*)
{'clickButton': 0,
'postState': [[], [0, 1]]},
# clicking group 2 enabled it
{'clickButton': 1,
'postState': [[1], [0]]},
# and similarly clicking group 2 again disables it
{'clickButton': 1,
'postState': [[], [0, 1]]},
# enable second group
{'clickButton': 1,
'postState': [[1], [0]]},
# can switch to group one
{'clickButton': 0,
'postState': [[0], [1]]},
]
}
def click_scenarios_required_widget(self):
return {
'name': 'click_scenarios_required_widget',
'input': {'required': True},
'scenario': [
# clicking enables the button
{'clickButton': 0,
'postState': [[0], [1]]},
# unlike the the optional case, this
# has no effect. You cannot _not_ select something
# when it is required.
{'clickButton': 0,
'postState': [[0], [1]]},
# we can select a different button
{'clickButton': 1,
'postState': [[1], [0]]},
# again, if we click it again, we cannot deselect it
{'clickButton': 1,
'postState': [[1], [0]]},
# we can click back to the other group
{'clickButton': 0,
'postState': [[0], [1]]},
]}
def click_scenarios_initial_selection(self):
return {
'name': 'click_scenarios_initial_selection',
'input': {'required': False, 'gooey_options': {'initial_selection': 0}},
'scenario': [
# we start already selected via GooeyOptions. As such,
# clicking on the radiobutton should deselect it
{'clickButton': 0,
'postState': [[], [0, 1]]},
# clicking again reselected it
{'clickButton': 0,
'postState': [[0], [1]]},
]}
if __name__ == '__main__':
unittest.main()
Loading…
Cancel
Save