Browse Source

partial re-write to allow better user customization

pull/240/head
chriskiehl 7 years ago
parent
commit
4c61c29b65
6 changed files with 451 additions and 219 deletions
  1. 471
      gooey/python_bindings/argparse_to_json.py
  2. 118
      gooey/python_bindings/config_generator.py
  3. 5
      gooey/python_bindings/constants.py
  4. 29
      gooey/python_bindings/gooey_decorator.py
  5. 47
      gooey/python_bindings/gooey_parser.py
  6. 0
      gooey/python_bindings/parser/gooey_parser.py

471
gooey/python_bindings/argparse_to_json.py

@ -1,237 +1,362 @@
"""
Converts argparse parser actions into json "Build Specs"
"""
import functools
import pprint
import argparse
import os
import sys
from argparse import (
_CountAction,
_HelpAction,
_StoreConstAction,
_StoreFalseAction,
_StoreTrueAction,
ArgumentParser,
_SubParsersAction)
_CountAction,
_HelpAction,
_StoreConstAction,
_StoreFalseAction,
_StoreTrueAction,
_SubParsersAction)
from collections import OrderedDict
from functools import partial
from itertools import chain
from uuid import uuid4
import sys
from gooey.util.functional import merge, getin
VALID_WIDGETS = (
'FileChooser',
'MultiFileChooser',
'FileSaver',
'DirChooser',
'DateChooser',
'TextField',
'Dropdown',
'Counter',
'RadioGroup',
'CheckBox',
'MultiDirChooser',
'Textarea',
'PasswordField',
'Listbox'
'FileChooser',
'MultiFileChooser',
'FileSaver',
'DirChooser',
'DateChooser',
'TextField',
'Dropdown',
'Counter',
'RadioGroup',
'CheckBox',
'MultiDirChooser',
'Textarea',
'PasswordField',
'Listbox'
)
class UnknownWidgetType(Exception):
pass
pass
class UnsupportedConfiguration(Exception):
pass
pass
{
'siege': {
'command': 'siege',
'display_name': 'Siege',
'contents': []
}
group_defaults = {
'columns': 2,
'padding': 10,
'show_border': False
}
item_default = {
'error_color': '#ea7878',
'validator': {
'type': 'local',
'test': 'lambda x: True',
'message': ''
},
'external_validator': {
'cmd': '',
}
}
def convert(parser):
widget_dict = getattr(parser, 'widgets', {})
actions = parser._actions
if has_subparsers(actions):
if has_required(actions):
raise UnsupportedConfiguration("Gooey doesn't currently support required arguments when subparsers are present.")
layout_type = 'column'
layout_data = OrderedDict(
(choose_name(name, sub_parser), {
'command': name,
'contents': process(sub_parser, getattr(sub_parser, 'widgets', {}))
}) for name, sub_parser in get_subparser(actions).choices.items())
else:
layout_type = 'standard'
layout_data = OrderedDict([
('primary', {
'command': None,
'contents': process(parser, widget_dict)
})
])
return {
'layout_type': layout_type,
'widgets': layout_data
}
def convert(parser, **kwargs):
assert_subparser_constraints(parser)
x = {
'layout': 'standard',
'widgets': OrderedDict(
(choose_name(name, sub_parser), {
'command': name,
'contents': process(sub_parser,
getattr(sub_parser, 'widgets', {}),
getattr(sub_parser, 'options', {}))
}) for name, sub_parser in iter_parsers(parser))
}
if kwargs.get('use_legacy_titles'):
return apply_default_rewrites(x)
return x
def process(parser, widget_dict, options):
mutex_groups = parser._mutually_exclusive_groups
raw_action_groups = [extract_groups(group) for group in parser._action_groups
if group._group_actions]
corrected_action_groups = reapply_mutex_groups(mutex_groups, raw_action_groups)
return categorize2(strip_empty(corrected_action_groups), widget_dict, options)
def strip_empty(groups):
return [group for group in groups if group['items']]
def assert_subparser_constraints(parser):
if has_subparsers(parser._actions):
if has_required(parser._actions):
raise UnsupportedConfiguration(
"Gooey doesn't currently support top level required arguments "
"when subparsers are present.")
def iter_parsers(parser):
''' Iterate over name, parser pairs '''
try:
return get_subparser(parser._actions).choices.items()
except:
return iter([('::gooey/default', parser)])
def extract_groups(action_group):
'''
Recursively extract argument groups and associated actions
from ParserGroup objects
'''
return {
'name': action_group.title,
'description': action_group.description,
'items': [action for action in action_group._group_actions
if not is_help_message(action)],
'groups': [extract_groups(group)
for group in action_group._action_groups],
'options': merge(group_defaults,
getattr(action_group, 'gooey_options', {}))
}
def process(parser, widget_dict):
mutually_exclusive_groups = [
[mutex_action for mutex_action in group_actions._group_actions]
for group_actions in parser._mutually_exclusive_groups]
group_options = list(chain(*mutually_exclusive_groups))
base_actions = [action for action in parser._actions
if action not in group_options
and action.dest != 'help']
required_actions = filter(is_required, base_actions)
optional_actions = filter(is_optional, base_actions)
return list(categorize(required_actions, widget_dict, required=True)) + \
list(categorize(optional_actions, widget_dict)) + \
list(map(build_radio_group, mutually_exclusive_groups))
def categorize(actions, widget_dict, required=False):
_get_widget = partial(get_widget, widgets=widget_dict)
for action in actions:
if is_standard(action):
yield as_json(action, _get_widget(action) or 'TextField', required)
elif is_choice(action):
yield as_json(action, _get_widget(action) or 'Dropdown', required)
elif is_flag(action):
yield as_json(action, _get_widget(action) or 'CheckBox', required)
elif is_counter(action):
_json = as_json(action, _get_widget(action) or 'Counter', required)
# pre-fill the 'counter' dropdown
_json['data']['choices'] = list(map(str, range(1, 11)))
yield _json
else:
raise UnknownWidgetType(action)
def get_widget(action, widgets):
supplied_widget = widgets.get(action.dest, None)
type_arg_widget = 'FileChooser' if action.type == argparse.FileType else None
return supplied_widget or type_arg_widget or None
def apply_default_rewrites(spec):
top_level_subgroups = list(spec['widgets'].keys())
for subgroup in top_level_subgroups:
path = ['widgets', subgroup, 'contents']
contents = getin(spec, path)
for group in contents:
if group['name'] == 'positional arguments':
group['name'] = 'Required Arguments'
if group['name'] == 'optional arguments':
group['name'] = 'Optional Arguments'
return spec
def contains_actions(a, b):
''' check if any actions(a) are present in actions(b) '''
return set(a).intersection(set(b))
def reapply_mutex_groups(mutex_groups, action_groups):
# argparse stores mutually exclusive groups independently
# of all other groups. So, they must be manually re-combined
# with the groups/subgroups to which they were originally declared
# in order to have them appear in the correct location in the UI.
#
# Order is attempted to be preserved by inserting the MutexGroup
# into the _actions list at the first occurrence of any item
# where the two groups intersect
def swap_actions(actions):
for mutexgroup in mutex_groups:
mutex_actions = mutexgroup._group_actions
if contains_actions(mutex_actions, actions):
# make a best guess as to where we should store the group
targetindex = actions.index(mutexgroup._group_actions[0])
# insert the _ArgumentGroup container
actions[targetindex] = mutexgroup
# remove the duplicated individual actions
return [action for action in actions
if action not in mutex_actions]
return actions
return [group.update({'items': swap_actions(group['items'])}) or group
for group in action_groups]
def categorize2(groups, widget_dict, options):
return [{
'name': group['name'],
'items': list(categorize(group['items'], widget_dict, options)),
'groups': categorize2(group['groups'], widget_dict, options),
'description': group['description'],
'options': group['options']
} for group in groups]
def categorize(actions, widget_dict, options):
_get_widget = partial(get_widget, widget_dict)
for action in actions:
if is_mutex(action):
yield build_radio_group(action, widget_dict, options)
elif is_standard(action):
yield action_to_json(action, _get_widget(action, 'TextField'), options)
elif is_choice(action):
yield action_to_json(action, _get_widget(action, 'Dropdown'), options)
elif is_flag(action):
yield action_to_json(action, _get_widget(action, 'CheckBox'), options)
elif is_counter(action):
_json = action_to_json(action, _get_widget(action, 'Counter'), options)
# pre-fill the 'counter' dropdown
_json['data']['choices'] = list(map(str, range(1, 11)))
yield _json
else:
raise UnknownWidgetType(action)
def get_widget(widgets, action, default):
supplied_widget = widgets.get(action.dest, None)
type_arg_widget = 'FileChooser' if action.type == argparse.FileType else None
return supplied_widget or type_arg_widget or default
def is_required(action):
'''
_actions possessing the `required` flag and not implicitly optional
through `nargs` being '*' or '?'
'''
return not isinstance(action, _SubParsersAction) and (action.required == True and action.nargs not in ['*', '?'])
'''
_actions possessing the `required` flag and not implicitly optional
through `nargs` being '*' or '?'
'''
return not isinstance(action, _SubParsersAction) and (
action.required == True and action.nargs not in ['*', '?'])
def is_mutex(action):
return isinstance(action, argparse._MutuallyExclusiveGroup)
def has_required(actions):
return list(filter(None, list(filter(is_required, actions))))
return list(filter(None, list(filter(is_required, actions))))
def is_subparser(action):
return isinstance(action,_SubParsersAction)
return isinstance(action, _SubParsersAction)
def has_subparsers(actions):
return list(filter(is_subparser, actions))
def get_subparser(actions):
return list(filter(is_subparser, actions))[0]
def is_optional(action):
'''
_actions either not possessing the `required` flag or implicitly optional through `nargs` being '*' or '?'
'''
return (not action.required) or action.nargs in ['*', '?']
'''
_actions either not possessing the `required` flag or implicitly optional
through `nargs` being '*' or '?'
'''
return (not action.required) or action.nargs in ['*', '?']
def is_choice(action):
''' action with choices supplied '''
return action.choices
''' action with choices supplied '''
return action.choices
def is_standard(action):
""" actions which are general "store" instructions.
e.g. anything which has an argument style like:
$ script.py -f myfilename.txt
"""
boolean_actions = (
_StoreConstAction, _StoreFalseAction,
_StoreTrueAction
)
return (not action.choices
and not isinstance(action, _CountAction)
and not isinstance(action, _HelpAction)
and type(action) not in boolean_actions)
""" actions which are general "store" instructions.
e.g. anything which has an argument style like:
$ script.py -f myfilename.txt
"""
boolean_actions = (
_StoreConstAction, _StoreFalseAction,
_StoreTrueAction
)
return (not action.choices
and not isinstance(action, _CountAction)
and not isinstance(action, _HelpAction)
and type(action) not in boolean_actions)
def is_flag(action):
""" _actions which are either storeconst, store_bool, etc.. """
action_types = [_StoreTrueAction, _StoreFalseAction, _StoreConstAction]
return any(list(map(lambda Action: isinstance(action, Action), action_types)))
""" _actions which are either storeconst, store_bool, etc.. """
action_types = [_StoreTrueAction, _StoreFalseAction, _StoreConstAction]
return any(list(map(lambda Action: isinstance(action, Action), action_types)))
def is_counter(action):
""" _actions which are of type _CountAction """
return isinstance(action, _CountAction)
""" _actions which are of type _CountAction """
return isinstance(action, _CountAction)
def is_default_progname(name, subparser):
return subparser.prog == '{} {}'.format(os.path.split(sys.argv[0])[-1], name)
return subparser.prog == '{} {}'.format(os.path.split(sys.argv[0])[-1], name)
def is_help_message(action):
return isinstance(action, _HelpAction)
def choose_name(name, subparser):
return name if is_default_progname(name, subparser) else subparser.prog
def build_radio_group(mutex_group):
if not mutex_group:
return []
options = [
{
'display_name': mutex_arg.metavar or mutex_arg.dest,
'help': mutex_arg.help,
'nargs': mutex_arg.nargs or '',
'commands': mutex_arg.option_strings,
'choices': mutex_arg.choices,
} for mutex_arg in mutex_group
]
return name if is_default_progname(name, subparser) else subparser.prog
def build_radio_group(mutex_group, widget_group, options):
return {
'id': str(uuid4()),
'type': 'RadioGroup',
'cli_type': 'optional',
'group_name': 'Choose Option',
'required': False,
'data': options
'required': mutex_group.required,
'options': getattr(mutex_group, 'gooey_options', {}),
'data': {
'commands': [action.option_strings for action in mutex_group._group_actions],
'widgets': list(categorize(mutex_group._group_actions, widget_group, options))
}
}
def as_json(action, widget, required):
if widget not in VALID_WIDGETS:
raise UnknownWidgetType('Widget Type {0} is unrecognized'.format(widget))
return {
'type': widget,
'required': required,
'data': {
'display_name': action.metavar or action.dest,
'help': action.help,
'nargs': action.nargs or '',
'commands': action.option_strings,
'choices': action.choices or [],
'default': clean_default(widget, action.default)
def action_to_json(action, widget, options):
if action.required:
# check that it's present and not just spaces
validator = 'user_input and not user_input.isspace()'
error_msg = 'This field is required'
else:
# not required; do nothing;
validator = 'True'
error_msg = ''
base = merge(item_default, {
'validator': {
'test': validator,
'message': error_msg
},
})
return {
'id': action.option_strings[0] if action.option_strings else action.dest,
'type': widget,
'cli_type': choose_cli_type(action),
'required': action.required,
'data': {
'display_name': action.metavar or action.dest,
'help': action.help,
'required': action.required,
'nargs': action.nargs or '',
'commands': action.option_strings,
'choices': action.choices or [],
'default': clean_default(action.default),
'dest': action.dest,
},
'options': merge(base, options.get(action.dest) or {})
}
}
def clean_default(widget_type, default):
'''
Attemps to safely coalesce the default value down to
a valid JSON type.
See: Issue #147.
function references supplied as arguments to the
`default` parameter in Argparse cause errors in Gooey.
'''
if widget_type != 'CheckBox':
return default.__name__ if callable(default) else default
# checkboxes must be handled differently, as they
# must be forced down to a boolean value
return default if isinstance(default, bool) else False
def choose_cli_type(action):
return 'positional' \
if action.required and not action.option_strings \
else 'optional'
def clean_default(default):
'''
Attemps to safely coalesce the default value down to
a valid JSON type.
See: Issue #147.
function references supplied as arguments to the
`default` parameter in Argparse cause errors in Gooey.
'''
return default.__name__ if callable(default) else default

118
gooey/python_bindings/config_generator.py

@ -1,12 +1,28 @@
import os
import sys
from gooey.gui.windows import layouts
import warnings
from gooey.python_bindings import argparse_to_json
from gooey.gui.util.quoting import quote
from gooey.python_bindings import constants
default_layout = {
'widgets': [{
'type': 'CommandField',
'required': True,
'data': {
'display_name': 'Enter Commands',
'help': 'Enter command line arguments',
'nargs': '',
'commands': '',
'choices': [],
'default': None,
}
}],
}
def create_from_parser(parser, source_path, **kwargs):
auto_start = kwargs.get('auto_start', False)
run_cmd = kwargs.get('target')
if run_cmd is None:
@ -15,32 +31,76 @@ def create_from_parser(parser, source_path, **kwargs):
else:
run_cmd = '{} -u {}'.format(quote(sys.executable), quote(source_path))
build_spec = {
'language': kwargs.get('language', 'english'),
'target': run_cmd,
'program_name': kwargs.get('program_name') or os.path.basename(sys.argv[0]).replace('.py', ''),
'program_description': kwargs.get('program_description', ''),
'auto_start': kwargs.get('auto_start', False),
'show_advanced': kwargs.get('advanced', True),
'default_size': kwargs.get('default_size', (610, 530)),
'num_required_cols': kwargs.get('required_cols', 1),
'num_optional_cols': kwargs.get('optional_cols', 3),
'manual_start': False,
'layout_type': 'flat',
'monospace_display': kwargs.get('monospace_display', False),
'image_dir': kwargs.get('image_dir'),
'language_dir': kwargs.get('language_dir'),
'progress_regex': kwargs.get('progress_regex'),
'progress_expr': kwargs.get('progress_expr'),
'disable_progress_bar_animation': kwargs.get('disable_progress_bar_animation'),
'disable_stop_button': kwargs.get('disable_stop_button'),
'group_by_type': kwargs.get('group_by_type', True)
}
if not auto_start:
build_spec['program_description'] = parser.description or build_spec['program_description']
layout_data = argparse_to_json.convert(parser) if build_spec['show_advanced'] else layouts.basic_config.items()
build_spec.update(layout_data)
build_spec = {
'language': kwargs.get('language', 'english'),
'target': run_cmd,
'program_name': kwargs.get('program_name') or os.path.basename(sys.argv[0]).replace('.py', ''),
'program_description': kwargs.get('program_description') or '',
'sidebar_title': kwargs.get('sidebar_title', 'Actions'),
'default_size': kwargs.get('default_size', (610, 530)),
'auto_start': kwargs.get('auto_start', False),
'show_advanced': kwargs.get('advanced', True),
'run_validators': kwargs.get('run_validators', True),
'encoding': kwargs.get('encoding', 'utf-8'),
'show_stop_warning': kwargs.get('show_stop_warning', True),
'show_success_modal': kwargs.get('show_success_modal', True),
'force_stop_is_error': kwargs.get('force_stop_is_error', True),
'poll_external_updates':kwargs.get('poll_external_updates', False),
# Legacy/Backward compatibility interop
'use_legacy_titles': kwargs.get('use_legacy_titles', True),
'num_required_cols': kwargs.get('required_cols', 1),
'num_optional_cols': kwargs.get('optional_cols', 3),
'manual_start': False,
'monospace_display': kwargs.get('monospace_display', False),
'image_dir': kwargs.get('image_dir'),
'language_dir': kwargs.get('language_dir'),
'progress_regex': kwargs.get('progress_regex'),
'progress_expr': kwargs.get('progress_expr'),
'disable_progress_bar_animation': kwargs.get('disable_progress_bar_animation'),
'disable_stop_button': kwargs.get('disable_stop_button'),
# Layouts
'navigation': kwargs.get('navigation', constants.SIDEBAR),
'show_sidebar': kwargs.get('show_sidebar', False),
'tabbed_groups': kwargs.get('tabbed_groups', False),
'group_by_type': kwargs.get('group_by_type', True),
# styles
'body_bg_color': kwargs.get('body_bg_color', '#f0f0f0'),
'header_bg_color': kwargs.get('header_bg_color', '#ffffff'),
'header_height': kwargs.get('header_height', 90),
'header_show_title': kwargs.get('header_show_title', True),
'header_show_subtitle': kwargs.get('header_show_subtitle', True),
'header_image_center': kwargs.get('header_image_center', False),
'footer_bg_color': kwargs.get('footer_bg_color', '#f0f0f0'),
'sidebar_bg_color': kwargs.get('sidebar_bg_color', '#f2f2f2'),
# font family, weight, and size are determined at runtime
'terminal_panel_color': kwargs.get('terminal_panel_color', '#F0F0F0'),
'terminal_font_color': kwargs.get('terminal_font_color', '#000000'),
'terminal_font_family': kwargs.get('terminal_font_family', None),
'terminal_font_weight': kwargs.get('terminal_font_weight', None),
'terminal_font_size': kwargs.get('terminal_font_size', None),
'error_color': kwargs.get('error_color', '#ea7878')
}
if build_spec['monospace_display']:
warnings.warn('Gooey Option `monospace_display` is a legacy option.\n'
'See the terminal_font_x options for more flexible control '
'over Gooey\'s text formatting')
build_spec['program_description'] = parser.description or build_spec['program_description']
layout_data = (argparse_to_json.convert(parser, **build_spec)
if build_spec['show_advanced']
else default_layout.items())
build_spec.update(layout_data)
if len(build_spec['widgets']) > 1:
# there are subparsers involved
build_spec['show_sidebar'] = True
return build_spec

5
gooey/python_bindings/constants.py

@ -0,0 +1,5 @@
SIDEBAR = 'SIDEBAR'
TABBED = 'TABBED'
INLINE = 'INLINE'
HIDDEN = 'HIDDEN'

29
gooey/python_bindings/gooey_decorator.py

@ -12,11 +12,13 @@ import sys
from argparse import ArgumentParser
from gooey.gui import application
from gooey.gui.util.freeze import get_resource_path
from gooey.gui.util.freeze import getResourcePath
from gooey.util.functional import merge
from . import config_generator
IGNORE_COMMAND = '--ignore-gooey'
# TODO: kwargs all the things
def Gooey(f=None,
advanced=True,
language='english',
@ -25,24 +27,29 @@ def Gooey(f=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='default',
language_dir=get_resource_path('languages'),
progress_regex=None, # TODO: add this to the docs
progress_expr=None, # TODO: add this to the docs
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
disable_progress_bar_animation=False,
disable_stop_button=False,
group_by_type=True): # TODO: add this to the docs
group_by_type=True,
header_height=80,
navigation='SIDEBAR', # TODO: add this to the docs
tabbed_groups=False,
**kwargs):
'''
Decorator for client code's main function.
Serializes argparse data to JSON for use with the Gooey front end
'''
params = locals()
params = merge(locals(), locals()['kwargs'])
def build(payload):
def run_gooey(self, args=None, namespace=None):
@ -57,7 +64,11 @@ def Gooey(f=None,
sys.exit(1)
if not build_spec:
build_spec = config_generator.create_from_parser(self, source_path, payload_name=payload.__name__, **params)
build_spec = config_generator.create_from_parser(
self,
source_path,
payload_name=payload.__name__,
**params)
if dump_build_config:
config_path = os.path.join(os.getcwd(), 'gooey_config.json')

47
gooey/python_bindings/gooey_parser.py

@ -8,39 +8,64 @@ class GooeySubParser(_SubParsersAction):
def __init__(self, *args, **kwargs):
super(GooeySubParser, self).__init__(*args, **kwargs)
# TODO: dedupe code
# TODO: figure out how to correctly dispatch all of these
# so that the individual wrappers aren't needed
class GooeyArgumentGroup(_ArgumentGroup):
def __init__(self, parser, widgets, *args, **kwargs):
def __init__(self, parser, widgets, options, *args, **kwargs):
self.parser = parser
self.widgets = widgets
self.options = options
super(GooeyArgumentGroup, self).__init__(self.parser, *args, **kwargs)
def add_argument(self, *args, **kwargs):
widget = kwargs.pop('widget', None)
metavar = kwargs.pop('metavar', None)
options = kwargs.pop('gooey_options', None)
super(GooeyArgumentGroup, self).add_argument(*args, **kwargs)
self.parser._actions[-1].metavar = metavar
self.widgets[self.parser._actions[-1].dest] = widget
self.options[self.parser._actions[-1].dest] = options
def add_argument_group(self, *args, **kwargs):
options = kwargs.pop('gooey_options', {})
group = GooeyArgumentGroup(self.parser, self.widgets, self.options, *args, **kwargs)
group.gooey_options = options
self._action_groups.append(group)
return group
def add_mutually_exclusive_group(self, *args, **kwargs):
options = kwargs.pop('gooey_options', {})
container = self
group = GooeyMutuallyExclusiveGroup(container, self.parser, self.widgets, self.options, *args, **kwargs)
group.gooey_options = options
self.parser._mutually_exclusive_groups.append(group)
return group
class GooeyMutuallyExclusiveGroup(_MutuallyExclusiveGroup):
def __init__(self, parser, widgets, *args, **kwargs):
def __init__(self, container, parser, widgets, options, *args, **kwargs):
self.parser = parser
self.widgets = widgets
super(GooeyMutuallyExclusiveGroup, self).__init__(self.parser, *args, **kwargs)
self.options = options
super(GooeyMutuallyExclusiveGroup, self).__init__(container, *args, **kwargs)
def add_argument(self, *args, **kwargs):
widget = kwargs.pop('widget', None)
metavar = kwargs.pop('metavar', None)
options = kwargs.pop('gooey_options', None)
super(GooeyMutuallyExclusiveGroup, self).add_argument(*args, **kwargs)
self.parser._actions[-1].metavar = metavar
self.widgets[self.parser._actions[-1].dest] = widget
self.options[self.parser._actions[-1].dest] = options
class GooeyParser(object):
def __init__(self, **kwargs):
self.__dict__['parser'] = ArgumentParser(**kwargs)
self.widgets = {}
self.options = {}
@property
def _mutually_exclusive_groups(self):
@ -57,6 +82,7 @@ class GooeyParser(object):
def add_argument(self, *args, **kwargs):
widget = kwargs.pop('widget', None)
metavar = kwargs.pop('metavar', None)
options = kwargs.pop('gooey_options', None)
if widget and widget == 'Listbox':
if not 'nargs' in kwargs or kwargs['nargs'] not in ['*', '+']:
@ -67,15 +93,20 @@ class GooeyParser(object):
self.parser.add_argument(*args, **kwargs)
self.parser._actions[-1].metavar = metavar
self.widgets[self.parser._actions[-1].dest] = widget
self.options[self.parser._actions[-1].dest] = options
def add_mutually_exclusive_group(self, **kwargs):
group = GooeyMutuallyExclusiveGroup(self.parser, self.widgets, **kwargs)
def add_mutually_exclusive_group(self, *args, **kwargs):
options = kwargs.pop('gooey_options', {})
group = GooeyMutuallyExclusiveGroup(self, self.parser, self.widgets, self.options, *args, **kwargs)
group.gooey_options = options
self.parser._mutually_exclusive_groups.append(group)
return group
def add_argument_group(self, *args, **kwargs):
group = GooeyArgumentGroup(self.parser, self.widgets, **kwargs)
self.parser.add_argument_group(*args, **kwargs)
options = kwargs.pop('gooey_options', {})
group = GooeyArgumentGroup(self.parser, self.widgets, self.options, *args, **kwargs)
group.gooey_options = options
self.parser._action_groups.append(group)
return group
def parse_args(self, args=None, namespace=None):

0
gooey/python_bindings/parser/gooey_parser.py

Loading…
Cancel
Save