Browse Source

nightly work. Added unittests from advanced_config. Fixed bug in ActionSorter, tweaked display elements, fixed wrapping

pull/1/head
Chris Kiehl 11 years ago
parent
commit
e8ef53d3af
13 changed files with 324 additions and 56 deletions
  1. 9
      src/app/dialogs/action_sorter.py
  2. 80
      src/app/dialogs/action_sorter_unittest.py
  3. 74
      src/app/dialogs/advanced_config.py
  4. BIN
      src/app/dialogs/advanced_config.pyc
  5. 26
      src/app/dialogs/advanced_config_unittest.py
  6. 22
      src/app/dialogs/argparse_test_data.py
  7. 29
      src/app/dialogs/component_factory.py
  8. BIN
      src/app/dialogs/component_factory.pyc
  9. 105
      src/app/dialogs/components.py
  10. 10
      src/app/dialogs/components_unittest.py
  11. 3
      src/app/dialogs/simple_config_panel.py
  12. 0
      src/experiments/__init__.py
  13. 22
      src/experiments/command.py

9
src/app/dialogs/action_sorter.py

@ -9,7 +9,7 @@ import wx
from argparse import (
_StoreAction, _StoreConstAction,
_StoreFalseAction, _StoreTrueAction,
_CountAction, _AppendAction)
_CountAction, _AppendAction, _HelpAction)
DEBUG = 1
@ -58,7 +58,7 @@ class ActionSorter(object):
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._flags = self.get_flag_style_optionals(self._actions)
self._flags = self.get_flag_style_optionals(self._actions)
self._counters = self.get_counter_actions(self._actions)
if DEBUG:
@ -67,6 +67,7 @@ class ActionSorter(object):
self._display('ActionSorter: optionals', self._optionals)
self._display('ActionSorter: booleans', self._flags)
self._display('ActionSorter: counters', self._counters)
print '|-------------------------'
def _display(self, _type, something):
for i in something:
@ -106,7 +107,9 @@ class ActionSorter(object):
for action in actions
if action.option_strings
and not action.choices
and action not in boolean_actions]
and not isinstance(action, _CountAction)
and not isinstance(action, _HelpAction)
and type(action) not in boolean_actions]
def get_optionals_with_choices(self, actions):
'''

80
src/app/dialogs/action_sorter_unittest.py

@ -0,0 +1,80 @@
'''
Created on Jan 16, 2014
@author: Chris
'''
import time
import unittest
import argparse_test_data
from functools import partial
from argparse import _StoreAction, _HelpAction
from action_sorter import ActionSorter
class Test(unittest.TestCase):
def setUp(self):
self._actions = argparse_test_data.parser._actions
self.sorted_actions = ActionSorter(self._actions)
# pain in the A... PEP8 be damned!
self.expected_positionals = [
"_StoreAction(option_strings=[], dest='filename', nargs=None, const=None, default=None, type=None, choices=None, help='Name of the file you want to read', metavar=None)",
'''_StoreAction(option_strings=[], dest='outfile', nargs=None, const=None, default=None, type=None, choices=None, help="Name of the file where you'll save the output", metavar=None)'''
]
self.expected_choices = [
'''_StoreAction(option_strings=['-T', '--tester'], dest='tester', nargs=None, const=None, default=None, type=None, choices=['yes', 'no'], help="Yo, what's up man? I'm a help message!", metavar=None)'''
]
self.expected_optionals = [
'''_StoreAction(option_strings=['-o', '--outfile'], dest='outfile', nargs=None, const=None, default=None, type=None, choices=None, help='Redirects output to the file specified by you, the awesome user', metavar=None)''',
'''_StoreAction(option_strings=['-v', '--verbose'], dest='verbose', nargs=None, const=None, default=None, type=None, choices=None, help='Toggles verbosity off', metavar=None)''',
'''_StoreAction(option_strings=['-s', '--schimzammy'], dest='schimzammy', nargs=None, const=None, default=None, type=None, choices=None, help='Add in an optional shimzammy parameter', metavar=None)'''
]
self.expected_counters = [
'''_CountAction(option_strings=['-e', '--repeat'], dest='repeat', nargs=0, const=None, default=None, type=None, choices=None, help='Set the number of times to repeat', metavar=None)'''
]
self.expected_flags = [
'''_StoreConstAction(option_strings=['-c', '--constoption'], dest='constoption', nargs=0, const='myconstant', default=None, type=None, choices=None, help='Make sure the const action is correctly sorted', metavar=None)''',
'''_StoreTrueAction(option_strings=['-t', '--truify'], dest='truify', nargs=0, const=True, default=False, type=None, choices=None, help='Ensure the store_true actions are sorted', metavar=None)''',
'''_StoreFalseAction(option_strings=['-f', '--falsificle'], dest='falsificle', nargs=0, const=False, default=True, type=None, choices=None, help='Ensure the store_false actions are sorted', metavar=None)'''
]
def testPositionalsReturnsOnlyPositionalActions(self):
positionals = self.sorted_actions._positionals
self.assertEqual(len(positionals), 2)
self.assertForAllActionsInList(positionals,self.expected_positionals)
def testHelpActionNotInOptionals(self):
_isinstance = lambda x: isinstance(x, _HelpAction)
self.assertFalse(any(map(_isinstance, self.sorted_actions._optionals)))
def testChoicesOnlyReturnsChoices(self):
self.assertForAllActionsInList(self.sorted_actions._choices,
self.expected_choices)
def testOptionalsOnlyReturnsOptionals(self):
self.assertForAllActionsInList(self.sorted_actions._optionals,
self.expected_optionals)
def testCounterSortOnlyReturnsCounters(self):
self.assertForAllActionsInList(self.sorted_actions._counters,
self.expected_counters)
def testFlagSortReturnsOnlyFlags(self):
self.assertForAllActionsInList(self.sorted_actions._flags,
self.expected_flags)
def assertForAllActionsInList(self, actions, expected_actions):
for index, action in enumerate(actions):
self.assertEqual(str(action), expected_actions[index])
if __name__ == "__main__":
#import sys;sys.argv = ['', 'Test.testName']
unittest.main()

74
src/app/dialogs/advanced_config.py

@ -5,11 +5,13 @@ Created on Dec 28, 2013
'''
import wx
import components
from wx.lib import wordwrap
from itertools import chain
from component_factory import ComponentFactory
from wx.lib.scrolledpanel import ScrolledPanel
PADDING = 10
class AdvancedConfigPanel(ScrolledPanel):
@ -23,25 +25,67 @@ class AdvancedConfigPanel(ScrolledPanel):
self.components = ComponentFactory(parser)
self.container = wx.BoxSizer(wx.VERTICAL)
self.container.AddSpacer(15)
self.AddHeaderMsg("Required Arguments")
self.container.AddSpacer(10)
self.AddRequiredArgsHeaderMsg()
self.AddWidgets(self.components.positionals)
box = wx.StaticBox(self, label="")
boxsizer = wx.StaticBoxSizer(box, wx.VERTICAL)
self.AddWidgets(self.container, self.components.positionals, add_space=True)
self.container.AddSpacer(10)
self.container.Add(self._draw_horizontal_line(),
0, wx.LEFT | wx.RIGHT | wx.EXPAND, PADDING)
self.container.AddSpacer(10)
self.AddHeaderMsg("Optional Arguments")
self.container.AddSpacer(15)
flag_grids = self.CreateComponentGrid(self.components.flags, vgap=15)
opt_choice_counter_grid = self.CreateComponentGrid(c for c in self.components
if not isinstance(c, components.Flag)
and not isinstance(c, components.Positional))
self.container.Add(opt_choice_counter_grid, 0, wx.LEFT | wx.RIGHT | wx.EXPAND, PADDING)
self.container.AddSpacer(30)
self.container.Add(flag_grids, 0, wx.LEFT | wx.RIGHT | wx.EXPAND, PADDING)
# sizer_params = [(grid, 0, wx.LEFT|wx.RIGHT|wx.EXPAND, PADDING)
# for grid in component_grids]
# self.container.AddMany(sizer_params)
self.SetSizer(self.container)
self.Bind(wx.EVT_SIZE, self.OnResize)
def AddRequiredArgsHeaderMsg(self):
required_msg = wx.StaticText(self, label="Required Arguments")
self.container.Add(required_msg, 0, wx.LEFT | wx.RIGHT, 20)
def AddWidgets(self, components):
if not components:
return
component = components[0]
widget_group = component.Build(parent=self)
self.container.Add(widget_group)
self.AddWidgets(components[1:])
def AddHeaderMsg(self, label):
required_msg = wx.StaticText(self, label=label)
font_size = required_msg.GetFont().GetPointSize()
bold = wx.Font(font_size*1.2, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD)
required_msg.SetFont(bold)
self.container.Add(required_msg, 0, wx.LEFT | wx.RIGHT, PADDING)
def AddWidgets(self, sizer, components, add_space=False, padding=PADDING):
for component in components:
widget_group = component.Build(parent=self)
sizer.Add(widget_group, 0, wx.LEFT | wx.RIGHT | wx.EXPAND, padding)
if add_space:
sizer.AddSpacer(8)
def CreateComponentGrid(self, components, cols=2, vgap=10):
gridsizer = wx.GridSizer(rows=0, cols=cols, vgap=vgap,hgap=4)
self.AddWidgets(gridsizer, components)
return gridsizer
def _draw_horizontal_line(self):
line = wx.StaticLine(self, -1, style=wx.LI_HORIZONTAL)
line.SetSize((10, 10))
return line
def OnResize(self, evt):
print evt.m_size
for component in self.components:
component.Update(evt.m_size)

BIN
src/app/dialogs/advanced_config.pyc

26
src/app/dialogs/advanced_config_unittest.py

@ -9,24 +9,18 @@ import os
import sys
import unittest
import advanced_config
import argparse_test_data
from argparse import ArgumentParser
class Test(unittest.TestCase):
class TestAdvancedConfigPanel(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'])
parser.add_argument('-o', '--outfile', help='Redirects output to the specified file')
parser.add_argument('-v', '--verbose', help='Toggles verbosity off')
parser.add_argument('-e', '--repeat', action='count', help='Set the number of times to repeat')
self.parser = parser
self.parser = argparse_test_data.parser
def buildWindow(self):
app = wx.PySimpleApp()
module_name = os.path.split(sys.argv[0])[-1]
frame = wx.Frame(None, -1, module_name)
frame = wx.Frame(None, -1, module_name, size=(640,480))
panel = advanced_config.AdvancedConfigPanel(frame, self.parser)
frame.Show()
@ -38,4 +32,14 @@ class Test(unittest.TestCase):
if __name__ == "__main__":
#import sys;sys.argv = ['', 'Test.testName']
unittest.main()
unittest.main()

22
src/app/dialogs/argparse_test_data.py

@ -0,0 +1,22 @@
'''
Created on Jan 16, 2014
@author: Chris
'''
from argparse import ArgumentParser
parser = ArgumentParser(description='Example Argparse Program')
parser.add_argument("filename", help="Name of the file you want to read") # positional
parser.add_argument("outfile", help="Name of the file where you'll save the output") # positional
parser.add_argument('-T', '--tester', choices=['yes','no'], help="Yo, what's up man? I'm a help message!") # Choice
parser.add_argument('-o', '--outfile', help='Redirects output to the file specified by you, the awesome user') # Optional
parser.add_argument('-v', '--verbose', help='Toggles verbosity off') # Optional
parser.add_argument('-s', '--schimzammy', help='Add in an optional shimzammy parameter') # Optional
parser.add_argument('-e', '--repeat', action='count', help='Set the number of times to repeat') # Counter
parser.add_argument('-c', '--constoption', action="store_const", const="myconstant", help='Make sure the const action is correctly sorted') # Flag
parser.add_argument('-t', '--truify', action="store_true", help='Ensure the store_true actions are sorted') # Flag
parser.add_argument('-f', '--falsificle', action="store_false", help='Ensure the store_false actions are sorted') # Flag

29
src/app/dialogs/component_factory.py

@ -4,9 +4,10 @@ Created on Dec 8, 2013
@author: Chris
'''
import wx
import itertools
import components
import action_sorter
import argparse_test_data
class ComponentFactory(object):
@ -22,16 +23,16 @@ class ComponentFactory(object):
self.positionals = self.BuildPositionals(self._actions)
self.choices = self.BuildChoices(self._actions)
self.optionals = self.BuildOptionals(self._actions)
self.booleans = self.BuildFlags(self._actions)
self.flags = self.BuildFlags(self._actions)
self.counters = self.BuildCounters(self._actions)
self._components = [
self.positionals,
self.choices,
self.optionals,
self.booleans,
self.counters
]
self.positionals,
self.choices,
self.optionals,
self.flags,
self.counters
]
def BuildPositionals(self, actions):
return self._AssembleWidgetsFromActions(actions, 'Positional', '_positionals')
@ -53,11 +54,19 @@ class ComponentFactory(object):
actions_list = getattr(actions, actiontype)
return [cls(action)
for action in actions_list]
# def __getitem__(self, slice):
# return self._components[slice]
def __iter__(self):
'''
return an iterator for all of the contained
components
'''
return itertools.chain(*self._components)
if __name__ == '__main__':
pass
a = ComponentFactory(argparse_test_data.parser)

BIN
src/app/dialogs/component_factory.pyc

105
src/app/dialogs/components.py

@ -26,21 +26,26 @@ class AbstractComponent(object):
self._widget = None
def Build(self, parent):
self._widget = self.BuildWidget(parent, self._action)
sizer = wx.BoxSizer(wx.VERTICAL)
self._msg = (self.CreateHelpMsgWidget(parent, self._action)
if self.HasHelpMsg(self._action)
else None)
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))
if self._msg:
sizer.Add(self._msg, 0, wx.EXPAND)
sizer.AddSpacer(2)
else:
sizer.AddSpacer(10)
sizer.AddStretchSpacer(1)
if self.HasNargs(self._action):
sizer.Add(self.AddNargsMsg(parent, self._action))
sizer.AddStretchSpacer(1)
sizer.Add(self._widget, 0, wx.EXPAND)
return sizer
@ -80,6 +85,9 @@ class AbstractComponent(object):
darkgray = (54,54,54)
statictext.SetForegroundColour(darkgray)
def __str__(self):
return str(self._action)
@abstractmethod
def BuildWidget(self, parent, action):
@ -90,6 +98,36 @@ class AbstractComponent(object):
def GetValue(self):
''' Returns the state of the given widget '''
pass
# @abstractmethod
def Update(self, size):
'''
Manually word wraps the StaticText help objects which would
otherwise not wrap on resize
Content area is based on each grid having two equally sized
columns, where the content area is defined as 87% of the halved
window width. The wiggle room is the distance +- 10% of the
content_area.
Wrap calculation is run only when the size of the help_msg
extends outside of the wiggle_room. This was done to avoid
the "flickering" that comes from constantly resizing a
StaticText object.
'''
if self._msg is None:
return
help_msg = self._msg
width, height = size
content_area = int((width/2)*.87)
print 'wiget size', help_msg.Size[0]
wiggle_room = range(int(content_area - content_area * .05), int(content_area + content_area * .05))
print '(',int(content_area - content_area * .05), int(content_area + content_area * .05),')'
if help_msg.Size[0] not in wiggle_room:
self._msg.SetLabel(self._msg.GetLabelText().replace('\n',' '))
self._msg.Wrap(content_area)
@ -148,16 +186,61 @@ class Flag(AbstractComponent):
self._widget = None
self.contents = None
def Build(self, parent):
self._widget = self.BuildWidget(parent, self._action)
self._msg = (self.CreateHelpMsgWidget(parent, self._action)
if self.HasHelpMsg(self._action)
else None)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.CreateDestNameWidget(parent, self._action))
sizer.AddSpacer(6)
if self.HasNargs(self._action):
sizer.Add(self.AddNargsMsg(parent, self._action))
if self._msg:
hsizer = self.buildHorizonalMsgSizer(parent)
sizer.Add(hsizer, 1, wx.EXPAND)
else:
sizer.AddStretchSpacer(1)
sizer.Add(self._widget, 0, wx.EXPAND)
return sizer
def BuildWidget(self, parent, action):
if len(action.option_strings) > 1:
label = action.option_strings[0]
else:
label = ''
return wx.CheckBox(parent, -1, label=label)
return wx.CheckBox(parent, -1, label='')
def buildHorizonalMsgSizer(self, panel):
if not self._msg:
return None
sizer = wx.BoxSizer(wx.HORIZONTAL)
sizer.Add(self._widget, 0)
sizer.AddSpacer(6)
sizer.Add(self._msg, 1, wx.EXPAND)
return sizer
def GetValue(self):
return self._widget.GetValue()
def Update(self, size):
'''
Custom wrapper calculator to account for the
increased size of the _msg widget after being
inlined with the wx.CheckBox
'''
if self._msg is None:
return
help_msg = self._msg
width, height = size
content_area = int((width/2)*.70)
print 'wiget size', help_msg.Size[0]
wiggle_room = range(int(content_area - content_area * .05), int(content_area + content_area * .05))
print '(',int(content_area - content_area * .05), int(content_area + content_area * .05),')'
if help_msg.Size[0] not in wiggle_room:
self._msg.SetLabel(self._msg.GetLabelText().replace('\n',' '))
self._msg.Wrap(content_area)
class Counter(AbstractComponent):

10
src/app/dialogs/components_unittest.py

@ -24,7 +24,7 @@ class ComponentsTest(unittest.TestCase):
parser.add_argument('-T', '--tester', choices=['yes','no'])
parser.add_argument('-o', '--outfile', help='Redirects output to the specified file')
parser.add_argument('-v', '--verbose', help='Toggles verbosity off')
parser.add_argument('-e', '--repeat', action='count', help='Set the number of times to repeat')
parser.add_argument('-e', '--repeat', action='count')
action = parser._actions
self.actions = {
'help' : action[0],
@ -36,10 +36,10 @@ class ComponentsTest(unittest.TestCase):
}
def BuildWindow(self, component):
def BuildWindow(self, component, _type):
app = wx.PySimpleApp()
module_name = os.path.split(sys.argv[0])[-1]
frame = wx.Frame(None, -1, module_name)
frame = wx.Frame(None, -1, _type)
panel = wx.Panel(frame, -1, size=(320,240))
component_sizer = component.Build(panel)
@ -65,10 +65,10 @@ class ComponentsTest(unittest.TestCase):
def testCounterWidgetBuild(self):
self.SetupWidgetAndBuildWindow('Counter')
def SetupWidgetAndBuildWindow(self, _type):
component = getattr(components, _type)(self.actions[_type])
self.BuildWindow(component)
print component
self.BuildWindow(component, _type)
if __name__ == "__main__":

3
src/app/dialogs/simple_config_panel.py

@ -45,8 +45,9 @@ class BodyDisplayPanel(wx.Panel):
return self.cmd_textbox.GetValue()
def _bold_static_text(self, text_label):
bold = wx.Font(8, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD)
text = wx.StaticText(self, label=text_label)
font_size = text.GetFont().GetPointSize()
bold = wx.Font(font_size, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD)
text.SetFont(bold)
return text

0
src/experiments/__init__.py

22
src/experiments/command.py

@ -0,0 +1,22 @@
'''
Created on Jan 7, 2014
@author: Chris
'''
class Command(object):
def __init__(self):
pass
def execute(self):
pass
class NextButton(Command):
def execute(self):
print "Next Button"
class CancelButton(Command):
def execute(self):
print 'Cancel button!'
Loading…
Cancel
Save