Browse Source

Added WidgetPack interface, added components2 - experimental structure for gui elements

pull/61/head
chriskiehl 10 years ago
parent
commit
62bfaa23c8
6 changed files with 404 additions and 1 deletions
  1. 2
      gooey/gui/chooser_runner.py
  2. 0
      gooey/gui/choosers.py
  3. 26
      gooey/gui/componenets2_runner.py
  4. 116
      gooey/gui/components.py
  5. 101
      gooey/gui/components2.py
  6. 160
      gooey/gui/widget_pack.py

gooey/gui/ChooserRunner.py → gooey/gui/chooser_runner.py

@ -3,7 +3,7 @@ __author__ = 'Chris'
import wx
from wx.lib import wordwrap
from Chooser import FileChooser, DirectoryChooser, CalendarChooser
from choosers import FileChooser, DirectoryChooser, CalendarChooser
class MyFrame(wx.Frame):
def __init__(self, parent):

gooey/gui/Chooser.py → gooey/gui/choosers.py

26
gooey/gui/componenets2_runner.py

@ -0,0 +1,26 @@
__author__ = 'Chris'
import wx
import components2
class MyFrame(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent, title="test", size=(320, 240))
self.SetBackgroundColour('#ffffff')
sizer = wx.BoxSizer(wx.VERTICAL)
f = components2.Counter({
'title': 'cool title',
'help_msg': 'cool help msg that is super long and intense andd has lots of words!', 'nargs': '+',
'option_strings': ['-f', '--fudger'],
'choices': ['choice 1', 'choice 2', 'choice 3']
})
sizer.Add(f.build(self), 0, wx.EXPAND)
self.SetSizer(sizer)
if __name__ == '__main__':
app = wx.App(False)
frame = MyFrame(None)
frame.Show(True)
app.MainLoop()

116
gooey/gui/components.py

@ -20,6 +20,122 @@ class BuildException(RuntimeError):
pass
class AbstractGuiComponent(object):
'''
Template pattern-y abstract class for the gui.
Children must all implement the BuildWidget and getValue
methods.
'''
__metaclass__ = ABCMeta
def __init__(self):
self._widget = None
self.msg = EMPTY
def Build(self, parent):
self._widget = self.BuildInputWidget(parent, self._action)
if self.HasHelpMsg(self._action):
self._msg = self.CreateHelpMsgWidget(parent, self._action)
else:
self._msg = None
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.CreateNameLabelWidget(parent, self._action))
sizer.AddSpacer(2)
if self._msg:
sizer.Add(self._msg, 0, wx.EXPAND)
sizer.AddSpacer(2)
else:
sizer.AddStretchSpacer(1)
sizer.AddStretchSpacer(1)
sizer.Add(self._widget, 0, wx.EXPAND)
return sizer
@abstractmethod
def BuildInputWidget(self, parent, action):
''' Must construct the main widget type for the Action '''
pass
def HasHelpMsg(self, action):
return action.help is not None
def CreateHelpMsgWidget(self, parent, action):
base_text = wx.StaticText(parent, label=action.help)
if self.HasNargs(action):
base_text.SetLabelText(base_text.GetLabelText() + self.CreateNargsMsg(action))
styling.MakeDarkGrey(base_text)
return base_text
def HasNargs(self, action):
return action.nargs is not None and action.nargs is not 0
def CreateNargsMsg(self, action):
if isinstance(action.nargs, int):
return '\n(Note: exactly {0} arguments are required)'.format(action.nargs)
elif action.nargs == '+':
return '\n(Note: at least 1 or more arguments are required)'
return ''
def CreateNameLabelWidget(self, parent, action):
label = str(action.dest).title()
if len(action.option_strings) > 1:
label += ' (%s)' % action.option_strings[0]
text = wx.StaticText(parent, label=label)
styling.MakeBold(text)
return text
def AssertInitialization(self, clsname):
if not self._widget:
raise BuildException('%s was not correctly initialized' % clsname)
def __str__(self):
return str(self._action)
@abstractmethod
def GetValue(self):
''' Returns the state of the given widget '''
pass
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)
class AbstractComponent(object):
'''
Template pattern-y abstract class for the gui.

101
gooey/gui/components2.py

@ -0,0 +1,101 @@
__author__ = 'Chris'
import wx
import styling
import widget_pack
class BaseGuiComponent(object):
def __init__(self, data, widget_pack):
self.data = data
# parent
self.panel = None
# Widgets
self.title = None
self.help_msg = None
# Internal WidgetPack
self.widget_pack = widget_pack
def build(self, parent):
return self.do_layout(parent)
def do_layout(self, parent):
self.panel = wx.Panel(parent)
self.title = self.createTitle(self.panel)
self.help_msg = self.createHelpMsgWidget(self.panel)
core_widget_set = self.widget_pack.build(self.panel, self.data)
vertical_container = wx.BoxSizer(wx.VERTICAL)
vertical_container.Add(self.title)
vertical_container.AddSpacer(2)
if self.help_msg.GetLabelText():
vertical_container.Add(self.help_msg, 1, wx.EXPAND)
vertical_container.AddSpacer(2)
else:
vertical_container.AddStretchSpacer(1)
vertical_container.Add(core_widget_set, 0, wx.EXPAND)
self.panel.SetSizer(vertical_container)
self.panel.Bind(wx.EVT_SIZE, self.onResize)
return self.panel
def createHelpMsgWidget(self, parent):
label_text = (self.formatExtendedHelpMsg(self.data)
if self.data['nargs']
else self.data['help_msg'])
base_text = wx.StaticText(parent, label=label_text)
styling.MakeDarkGrey(base_text)
return base_text
def createTitle(self, parent):
text = wx.StaticText(parent, label=self.data['title'].title())
styling.MakeBold(text)
return text
def formatExtendedHelpMsg(self, data):
base_text = data['help_msg']
nargs = data['nargs']
if isinstance(nargs, int):
return '{base}\n(Note: exactly {nargs} arguments are required)'.format(base=base_text, nargs=nargs)
elif nargs == '+':
return '{base}\n(Note: at least 1 or more arguments are required)'.format(base=base_text)
return base_text
def onResize(self, evt):
# handle internal widgets
self._onResize(evt)
# propagate event to child widgets
self.widget_pack.onResize(evt)
evt.Skip()
def _onResize(self, evt):
if self.help_msg is None:
return
container_width, _ = self.panel.Size
text_width, _ = self.help_msg.Size
if text_width != container_width:
self.help_msg.SetLabel(self.help_msg.GetLabelText().replace('\n', ' '))
self.help_msg.Wrap(container_width)
def getValue(self):
return self.widget_pack.getValue()
FileChooser = lambda data: BaseGuiComponent(data=data, widget_pack=widget_pack.FileChooserPayload())
DirChooser = lambda data: BaseGuiComponent(data=data, widget_pack=widget_pack.DirChooserPayload())
DateChooser = lambda data: BaseGuiComponent(data=data, widget_pack=widget_pack.DateChooserPayload())
TextField = lambda data: BaseGuiComponent(data=data, widget_pack=widget_pack.TextInputPayload())
Dropdown = lambda data: BaseGuiComponent(data=data, widget_pack=widget_pack.DropdownPayload())
Counter = lambda data: BaseGuiComponent(data=data, widget_pack=widget_pack.CounterPayload())

160
gooey/gui/widget_pack.py

@ -0,0 +1,160 @@
__author__ = 'Chris'
import wx
from abc import ABCMeta, abstractmethod
from calender_dialog import CalendarDlg
class WidgetPack(object):
"""
Interface specifying the contract to which
all `WidgetPack`s will adhere
"""
__metaclass__ = ABCMeta
@abstractmethod
def build(self, parent, data):
pass
@abstractmethod
def getValue(self):
pass
def onResize(self, evt):
pass
class BaseChooser(WidgetPack):
def __init__(self, button_text='Browse'):
self.button_text = button_text
self.parent = None
self.text_box = None
self.button = None
def build(self, parent, data=None):
self.parent = parent
self.text_box = wx.TextCtrl(self.parent)
self.button = wx.Button(self.parent, label=self.button_text, size=(73, 23))
widget_sizer = wx.BoxSizer(wx.HORIZONTAL)
widget_sizer.Add(self.text_box, 1, wx.EXPAND)
widget_sizer.AddSpacer(10)
widget_sizer.Add(self.button, 0)
parent.Bind(wx.EVT_BUTTON, self.onButton, self.button)
return widget_sizer
def getValue(self):
return self.text_box.GetValue()
def onButton(self, evt):
raise NotImplementedError
class FileChooserPayload(BaseChooser):
def __init__(self):
BaseChooser.__init__(self)
def onButton(self, evt):
dlg = wx.FileDialog(self.parent, style=wx.FD_OPEN | wx.FD_FILE_MUST_EXIST)
result = (dlg.GetPath()
if dlg.ShowModal() == wx.ID_OK
else None)
if result:
# self.text_box references a field on the class this is passed into
# kinda hacky, but avoided a buncha boilerplate
self.text_box.SetLabelText(result)
class DirChooserPayload(BaseChooser):
def __init__(self):
BaseChooser.__init__(self)
def onButton(self, evt):
dlg = wx.DirDialog(self.parent, 'Select directory', style=wx.DD_DEFAULT_STYLE)
result = (dlg.GetPath()
if dlg.ShowModal() == wx.ID_OK
else None)
if result:
self.text_box.SetLabelText(result)
class DateChooserPayload(BaseChooser):
def __init__(self):
BaseChooser.__init__(self, button_text='Pick Date')
def onButton(self, evt):
dlg = CalendarDlg(self.parent)
dlg.ShowModal()
if dlg.GetPath():
self.text_box.SetLabelText(dlg.GetPath())
class TextInputPayload(WidgetPack):
def __init__(self):
self.widget = None
def build(self, parent, data):
self.widget = wx.TextCtrl(parent)
return self.widget
def getValue(self):
return self.widget.GetValue()
class DropdownPayload(WidgetPack):
default_value = 'Select Option'
def __init__(self):
self.option_string = None
self.widget = None
def build(self, parent, data):
self.option_string = data['option_strings'][0]
self.widget = wx.ComboBox(
parent=parent,
id=-1,
value=self.default_value,
choices=data['choices'],
style=wx.CB_DROPDOWN
)
return self.widget
def getValue(self):
if self.widget.GetValue() == self.default_value:
return None
return ' '.join([self.option_string, self.widget.GetValue()])
class CounterPayload(WidgetPack):
def __init__(self):
self.option_string = None
self.widget = None
def build(self, parent, data):
self.option_string = data['option_strings'][0]
self.widget = wx.ComboBox(
parent=parent,
id=-1,
value='',
choices=[str(x) for x in range(1, 7)],
style=wx.CB_DROPDOWN
)
return self.widget
def getValue(self):
'''
Returns
str(option_string * DropDown Value)
e.g.
-vvvvv
'''
dropdown_value = self.widget.GetValue()
if not str(dropdown_value).isdigit():
return None
arg = str(self.option_string).replace('-', '')
repeated_args = arg * int(dropdown_value)
return '-' + repeated_args
Loading…
Cancel
Save