diff --git a/src/app/dialogs/action_sorter.py b/src/app/dialogs/action_sorter.py new file mode 100644 index 0000000..48ef84e --- /dev/null +++ b/src/app/dialogs/action_sorter.py @@ -0,0 +1,132 @@ +''' +Created on Dec 8, 2013 + +@author: Chris +''' + +import wx +from argparse import ( + _StoreAction, _StoreConstAction, + _StoreFalseAction, _StoreTrueAction, + _CountAction, _AppendAction) + +class ActionSorter(object): + ''' + So this thing is the thing that will + pull out all of the "types" from the + argparse object and turn them into the + correct wx components + + COMPONENT MAP + Action WxWidget + -------------------------- + store TextCtrl + store_const CheckBox + store_true CheckBox + store_False CheckBox + append TextCtrl + count DropDown + choice DropDown + + _HelpAction(option_strings=['-h', '--help'], dest='help', nargs=0, const=None, default='==SUPPRESS==', type=None, choices=None, help='show this help message and exit', metavar=None) + _StoreAction(option_strings=[], dest='filename', nargs=None, const=None, default=None, type=None, choices=None, help='filename', metavar=None) + _StoreTrueAction(option_strings=['-r', '--recursive'], dest='recurse', nargs=0, const=True, default=False, type=None, choices=None, help='recurse into subfolders [default: %(default)s]', metavar=None) + _CountAction(option_strings=['-v', '--verbose'], dest='verbose', nargs=0, const=None, default=None, type=None, choices=None, help='set verbosity level [default: %(default)s]', metavar=None) + _AppendAction(option_strings=['-i', '--include'], dest='include', nargs=None, const=None, default=None, type=None, choices=None, help='only include paths matching this regex pattern. Note: exclude is given preference over include. [default: %(default)s]', metavar='RE') + _StoreAction(option_strings=['-e', '--exclude'], dest='exclude', nargs=None, const=None, default=None, type=None, choices=None, help='exclude paths matching this regex pattern. [default: %(default)s]', metavar='RE') + _VersionAction(option_strings=['-V', '--version'], dest='version', nargs=0, const=None, default='==SUPPRESS==', type=None, choices=None, help="show program's version number and exit", metavar=None) + _StoreAction(option_strings=['-T', '--tester'], dest='tester', nargs=None, const=None, default=None, type=None, choices=['yes', 'no'], help=None, metavar=None) + _StoreAction(option_strings=[], dest='paths', nargs='+', const=None, default=None, type=None, choices=None, help='paths to folder(s) with source file(s) [default: %(default)s]', metavar='path') + usage: example_argparse_souce.py [-h] [-r] [-v] [-i RE] [-e RE] [-V] + ''' + + def __init__(self, parser): + self._parser = parser + self._actions = self._parser._actions[:] # Copy all of the actions + + self._positionals = self.get_positionals(self._actions) + self._choices = self.get_optionals_with_choices(self._actions) + self._optionals = self.get_optionals_without_choices(self._actions) + self._booleans = self.get_flag_style_optionals(self._actions) + self._counters = self.get_counter_actions(self._actions) + + self._display('ActionSorter: positionals', self._positionals) + self._display('ActionSorter: choices', self._choices) + self._display('ActionSorter: optionals', self._optionals) + self._display('ActionSorter: booleans', self._booleans) + self._display('ActionSorter: counters', self._counters) + + def _display(self, _type, something): + for i in something: + print _type, i + + def get_counter_actions(self, actions): + ''' + Returns all instances of type _CountAction + ''' + return [action + for action in actions + if isinstance(action, _CountAction)] + + def get_positionals(self, actions): + ''' + Get all required (positional) actions + ''' + return [action + for action in actions + if not action.option_strings] + + def get_optionals_without_choices(self, actions): + ''' + All actions which are + (a) Optional, but without required choices + (b) Not of a "boolean" type (storeTrue, etc..) + (c) Of type _AppendAction + + e.g. anything which has an argument style like: + >>> -f myfilename.txt + ''' + boolean_actions = ( + _StoreConstAction, _StoreFalseAction, + _StoreTrueAction + ) + return [action + for action in actions + if action.option_strings + and not action.choices + and action not in boolean_actions] + + def get_optionals_with_choices(self, actions): + ''' + All optional arguments which are constrained + to specific choices. + ''' + return [action + for action in actions + if action.choices] + + def get_flag_style_optionals(self, actions): + ''' + Gets all instances of "flag" type options. + i.e. options which either store a const, or + store boolean style options (e.g. StoreTrue). + Types: + _StoreTrueAction + _StoreFalseAction + _StoreConst + ''' + return [action + for action in actions + if isinstance(action, _StoreTrueAction) + or isinstance(action, _StoreFalseAction) + or isinstance(action, _StoreConstAction)] + + +if __name__ == '__main__': + pass + + + + + + diff --git a/src/app/dialogs/advanced_config.py b/src/app/dialogs/advanced_config.py index 56c0a80..d688968 100644 --- a/src/app/dialogs/advanced_config.py +++ b/src/app/dialogs/advanced_config.py @@ -21,6 +21,7 @@ class AdvancedConfigPanel(ScrolledPanel): self.container = wx.BoxSizer(wx.VERTICAL) self.container.AddSpacer(10) self.AddRequiredArgsHeaderMsg() + self.Add @@ -28,11 +29,34 @@ class AdvancedConfigPanel(ScrolledPanel): required_msg = wx.StaticText(self, label="Required Arguments") self.container.Add(required_msg, 0, wx.LEFT | wx.RIGHT, 20) - def AddRequiredWidgets(self, factory): - widgets = factory._positionals - for widget in widgets: - + def AddWidgets(self, actions, widget_type): + if len(actions) == 0: + return + action = actions.pop(0) + self.CreateHelpMsgWidget(action) + if self.hasNargs(action): + self.AddNargsMsg(action) + self.AddWidget(widget_type) + self.AddWidgets(actions, widget_type) + + def CreateHelpMsgWidget(self, action): + help_msg = action.help + return wx.StaticText(self, label=help_msg) + + def HasNargs(self, action): + return action.nargs > 0 + def AddNargsMsg(self, action): + msg = action.nargs + return wx.StaticText(self, label=msg) + + def AddWidget(self, _type): + widget = getattr(wx, _type) + + +if __name__ == '__main__': + a = getattr(wx, 'StaticText') + print a diff --git a/src/app/dialogs/component_factory.py b/src/app/dialogs/component_factory.py index ab91944..443bcbc 100644 --- a/src/app/dialogs/component_factory.py +++ b/src/app/dialogs/component_factory.py @@ -12,10 +12,7 @@ from argparse import ( class ComponentFactory(object): ''' - So this thing is the thing that will - pull out all of the "types" from the - argparse object and turn them into the - correct wx components + Generates Wx Components from the argparse action types COMPONENT MAP Action WxWidget @@ -50,76 +47,6 @@ class ComponentFactory(object): self._booleans = self.get_flag_style_optionals(self._actions) self._counters = self.get_counter_actions(self._actions) - self._display('positionals', self._positionals) - self._display('choices', self._choices) - self._display('optionals', self._optionals) - self._display('booleans', self._booleans) - self._display('counters', self._counters) - - def _display(self, _type, something): - for i in something: - print _type, i - - def get_counter_actions(self, actions): - ''' - Returns all instances of type _CountAction - ''' - return [action - for action in actions - if isinstance(action, _CountAction)] - - def get_positionals(self, actions): - ''' - Get all required (positional) actions - ''' - return [action - for action in actions - if not action.option_strings] - - def get_optionals_without_choices(self, actions): - ''' - All actions which are - (a) Optional, but without required choices - (b) Not of a "boolean" type (storeTrue, etc..) - (c) Of type _AppendAction - - e.g. anything which has an argument style like: - >>> -f myfilename.txt - ''' - boolean_actions = ( - _StoreConstAction, _StoreFalseAction, - _StoreTrueAction - ) - return [action - for action in actions - if action.option_strings - and not action.choices - and action not in boolean_actions] - - def get_optionals_with_choices(self, actions): - ''' - All optional arguments which are constrained - to specific choices. - ''' - return [action - for action in actions - if action.choices] - - def get_flag_style_optionals(self, actions): - ''' - Gets all instances of "flag" type options. - i.e. options which either store a const, or - store boolean style options (e.g. StoreTrue). - Types: - _StoreTrueAction - _StoreFalseAction - _StoreConst - ''' - return [action - for action in actions - if isinstance(action, _StoreTrueAction) - or isinstance(action, _StoreFalseAction) - or isinstance(action, _StoreConstAction)] if __name__ == '__main__': diff --git a/src/app/dialogs/components.py b/src/app/dialogs/components.py new file mode 100644 index 0000000..b571da5 --- /dev/null +++ b/src/app/dialogs/components.py @@ -0,0 +1,152 @@ +''' +Created on Jan 1, 2014 + +@author: Chris + +''' + +import wx +from argparse import ArgumentParser +from abc import ABCMeta, abstractmethod + + +class BuildException(RuntimeError): + pass + + +class AbstractComponent(object): + ''' + Template pattern-y abstract class for the components. + Children must all implement the BuildWidget and getValue + methods. + ''' + __metaclass__ = ABCMeta + + def __init__(self): + self._widget = None + + def Build(self, parent): + self._widget = self.BuildWidget(parent, self._action) + sizer = wx.BoxSizer(wx.VERTICAL) + + sizer.Add(self.CreateDestNameWidget(parent, self._action)) + sizer.AddSpacer(2) + + if self.HasHelpMsg(self._action): + sizer.Add(self.CreateHelpMsgWidget(parent, self._action)) + sizer.AddSpacer(2) + + if self.HasNargs(self._action): + sizer.Add(self.AddNargsMsg(parent, self._action)) + + sizer.Add(self._widget, 0, wx.EXPAND) + return sizer + + def HasHelpMsg(self, action): + return action.help is not None + + def HasNargs(self, action): + return action.nargs == '+' + + def CreateHelpMsgWidget(self, parent, action): + text = wx.StaticText(parent, label=action.help) + self.MakeDarkGrey(text) + return text + + def AddNargsMsg(self, parent, action): + msg = 'Note: at least 1 or more arguments are required' + return wx.StaticText(parent, label=msg) + + def CreateDestNameWidget(self, parent, action): + text = wx.StaticText(parent, label=str(action.dest).title()) + self.MakeBold(text) + return text + + def AssertInitialization(self, widget, clsname): + print self._widget + if not self._widget: + raise BuildException('%s was not correctly initialized' % clsname) + + def MakeBold(self, statictext): + pointsize = statictext.GetFont().GetPointSize() + statictext.SetFont(wx.Font(pointsize, wx.FONTFAMILY_DEFAULT, + wx.FONTWEIGHT_NORMAL, wx.FONTWEIGHT_BOLD, False)) + + def MakeDarkGrey(self, statictext): + darkgray = (54,54,54) + statictext.SetForegroundColour(darkgray) + + + @abstractmethod + def BuildWidget(self, parent, action): + ''' + Must construct the main widget type for the Action + ''' + pass + + @abstractmethod + def GetValue(self): + ''' + Returns the state of the given widget + ''' + pass + + + +class Positional(AbstractComponent): + def __init__(self, action): + self._action = action + self._widget = None + self.contents = None + + def BuildWidget(self, parent, action): + return wx.TextCtrl(parent) + + def GetValue(self): + self.AssertInitialization(self._widget, 'Positional') + return self._widget.GetValue() + + +class Choice(AbstractComponent): + def __init__(self, action): + self._action = action + self._widget = None + self.contents = None + + def GetValue(self): + self.AssertInitialization() + return self._widget.GetValue() + + def BuildWidget(self, parent, action): + return wx.ComboBox( + parent=parent, + id=-1, + value='Select Option', + choices=action.choices, + style=wx.CB_DROPDOWN + ) + + +class Optional(AbstractComponent): + def __init__(self, action): + pass + + + + + + + + +if __name__ == '__main__': + parser = ArgumentParser(description='Example Argparse Program') + parser.add_argument("filename", help="filename") + action = parser._actions[1] + positional = Positional(action) + + a = getattr + + + + + \ No newline at end of file diff --git a/src/app/dialogs/components_unittest.py b/src/app/dialogs/components_unittest.py new file mode 100644 index 0000000..289a265 --- /dev/null +++ b/src/app/dialogs/components_unittest.py @@ -0,0 +1,71 @@ +''' +Created on Jan 4, 2014 + +@author: Chris + + + + +''' + +import wx +import os +import sys +import unittest +import components +from argparse import ArgumentParser + + +class ComponentsTest(unittest.TestCase): + + def setUp(self): + parser = ArgumentParser(description='Example Argparse Program') + parser.add_argument("filename", help="Name of the file you want to read") + parser.add_argument('-T', '--tester', choices=['yes','no']) + action = parser._actions + self.actions = { + 'help' : action[0], + 'Positional' : action[1], + 'Choice' : action[2] + } + + + def BuildWindow(self, component): + + app = wx.PySimpleApp() + module_name = os.path.split(sys.argv[0])[-1] + frame = wx.Frame(None, -1, module_name) + + panel = wx.Panel(frame, -1, size=(320,240)) + component_sizer = component.Build(panel) + panel.SetSizer(component_sizer) + + frame.Show(True) + + print component.GetValue() + app.MainLoop() + + def testPositionalWidgetBuild(self): + self.SetupWidgetAndBuildWindow('Positional') +# component = components.Positional(self.actions['positional']) +# self.BuildWindow(component) + + def testChoiceWidgetBuild(self): + self.SetupWidgetAndBuildWindow('Choice') + + def SetupWidgetAndBuildWindow(self, _type): + component = getattr(components, _type)(self.actions[_type]) + self.BuildWindow(component) + + +if __name__ == "__main__": + # import sys;sys.argv = ['', 'Test.testName'] + unittest.main() + + + + + + + + diff --git a/src/model/example_argparse_souce.py b/src/model/example_argparse_souce.py index 9d48847..f0b7059 100644 --- a/src/model/example_argparse_souce.py +++ b/src/model/example_argparse_souce.py @@ -76,6 +76,7 @@ USAGE parser.add_argument("-r", "--recursive", dest="recurse", action="store_true", help="recurse into subfolders [default: %(default)s]") parser.add_argument("-v", "--verbose", dest="verbose", action="count", help="set verbosity level [default: %(default)s]") parser.add_argument("-i", "--include", action="append", help="only include paths matching this regex pattern. Note: exclude is given preference over include. [default: %(default)s]", metavar="RE" ) + parser.add_argument("-m", "--mycoolargument", help="mycoolargument") parser.add_argument("-e", "--exclude", dest="exclude", help="exclude paths matching this regex pattern. [default: %(default)s]", metavar="RE" ) parser.add_argument('-V', '--version', action='version') parser.add_argument('-T', '--tester', choices=['yes','no']) diff --git a/src/model/source_parser.py b/src/model/source_parser.py index e814f34..7cd0c64 100644 --- a/src/model/source_parser.py +++ b/src/model/source_parser.py @@ -22,7 +22,7 @@ from functools import partial from numpy.lib.utils import _split_line from argparse import ArgumentParser from argparse import RawDescriptionHelpFormatter -from app.dialogs.component_factory import ComponentFactory +from app.dialogs.action_sorter import ActionSorter class ParserError(Exception): @@ -269,7 +269,8 @@ if __name__ == '__main__': ast_source = parse_source_file('example_argparse_souce.py') python_code = convert_to_python(ast_source) parser = ParserFromSource(python_code) - factory = ComponentFactory(parser) + factory = ActionSorter(parser) + print factory._positionals #