Browse Source

filling out model

pull/150/head
chriskiehl 9 years ago
parent
commit
b5a1303222
9 changed files with 321 additions and 139 deletions
  1. 37
      gooey/gui/controller.py
  2. 165
      gooey/gui/model.py
  3. 105
      gooey/gui/presenter.py
  4. 12
      gooey/gui/widgets/choosers.py
  5. 25
      gooey/gui/widgets/components.py
  6. 96
      gooey/gui/widgets/widget_pack.py
  7. 16
      gooey/gui/windows/advanced_config.py
  8. 3
      gooey/gui/windows/base_window.py
  9. 1
      gooey/python_bindings/config_generator.py

37
gooey/gui/controller.py

@ -9,6 +9,7 @@ from multiprocessing.dummy import Pool
from gooey.gui.lang import i18n
from gooey.gui import events
from gooey.gui.model import MyModel
from gooey.gui.presenter import Presenter
from gooey.gui.pubsub import pub
from gooey.gui.util.taskkill import taskkill
@ -26,7 +27,7 @@ class Controller(object):
# todo: model!
self.build_spec = build_spec
self.view = BaseWindow(build_spec, layout_type=self.build_spec['layout_type'])
self.presentation = Presenter(self.view, self.build_spec)
self.presentation = Presenter(self.view, MyModel(self.build_spec))
self.presentation.initialize_view()
self._process = None
@ -34,7 +35,7 @@ class Controller(object):
# wire up all the observers
pub.subscribe(self.on_cancel, events.WINDOW_CANCEL)
pub.subscribe(self.on_stop, events.WINDOW_STOP)
pub.subscribe(self.on_start, events.WINDOW_START)
# pub.subscribe(self.on_start, events.WINDOW_START)
pub.subscribe(self.on_restart, events.WINDOW_RESTART)
pub.subscribe(self.on_close, events.WINDOW_CLOSE)
pub.subscribe(self.on_edit, events.WINDOW_EDIT)
@ -61,13 +62,15 @@ class Controller(object):
sys.exit()
def on_start(self):
if not self.skipping_config() and not self.required_section_complete():
return self.view.show_dialog(i18n._('error_title'), i18n._('error_required_fields'), wx.ICON_ERROR)
cmd_line_args = self.view.GetOptions()
command = '{} --ignore-gooey {}'.format(self.build_spec['target'], cmd_line_args)
pub.send_message(events.WINDOW_CHANGE, view_name=views.RUNNING_SCREEN)
self.run_client_code(command)
print self.presentation.view.required_section.get_values()
print self.presentation.view.optional_section.get_values()
# if not self.skipping_config() and not self.required_section_complete():
# return self.view.show_dialog(i18n._('error_title'), i18n._('error_required_fields'), wx.ICON_ERROR)
#
# cmd_line_args = self.view.GetOptions()
# command = '{} --ignore-gooey {}'.format(self.build_spec['target'], cmd_line_args)
# pub.send_message(events.WINDOW_CHANGE, view_name=views.RUNNING_SCREEN)
# self.run_client_code(command)
def on_stop(self):
self.ask_stop()
@ -154,14 +157,14 @@ class Controller(object):
pub.send_message(events.WINDOW_CHANGE, view_name=views.ERROR_SCREEN)
self.error_dialog()
def skipping_config(self):
return self.build_spec['manual_start']
def required_section_complete(self):
required_section = self.view.GetRequiredArgs()
if len(required_section) == 0:
return True # no requirements!
return not any(req == '' for req in required_section)
# def skipping_config(self):
# return self.build_spec['manual_start']
#
# def required_section_complete(self):
# required_section = self.view.GetRequiredArgs()
# if len(required_section) == 0:
# return True # no requirements!
# return not any(req == '' for req in required_section)
def success_dialog(self):
self.view.show_dialog(i18n._("execution_finished"), i18n._('success_message'), wx.ICON_INFORMATION)

165
gooey/gui/model.py

@ -0,0 +1,165 @@
import os
from collections import namedtuple
from itertools import chain
from gooey.gui.lang.i18n import _
# MyWidget = namedtuple('MyWidget', 'type title help nargs commands choices')
from gooey.gui.util.quoting import quote
class MyWidget(object):
def __init__(self, type, title, help, default, nargs, commands, choices):
self.type = type
self.title = title
self.help = help
self.default = default
self._value = default
self.nargs = nargs
self.commands = commands
self.choices = choices
@property
def value(self):
# TODO: split into stategy or subclass thingie
if self.type == 'CheckBox':
return self.commands[0] if self._value else None
if self.type == 'RadioGroup':
try:
return self.commands[self._value.index(True)][0]
except ValueError:
return None
if self.type == 'MultiFileChooser':
value = ' '.join(quote(x) for x in self._value.split(os.pathsep) if x)
if self.commands and value:
return '{} {}'.format(self.commands[0], value)
return value or None
# if self.type == 'TextField':
# if self.commands and self._value:
# return '{} {}'.format(self.commands[0], quote(self._value))
# else:
# return quote(self._value) if self._value else ''
if self.type == 'CommandField':
if self.commands and self._value:
return '{} {}'.format(self.commands[0], self._value)
else:
return self._value or None
if self.type == 'Counter':
'''
Returns
str(option_string * DropDown Value)
e.g.
-vvvvv
'''
if not str(self._value).isdigit():
return None
arg = str(self.commands[0]).replace('-', '')
repeated_args = arg * int(self._value)
return '-' + repeated_args
if self.type == 'Dropdown':
if self._value == self.default:
return ''
elif self.commands and self._value:
return '{} {}'.format(self.commands[0], quote(self._value))
else:
return quote(self._value) if self._value else ''
else:
if self.commands and self._value:
return '{0} {1}'.format(self.commands[0], quote(self._value))
else:
return quote(self._value) if self._value else None
@value.setter
def value(self, val):
self._value = val
class MyModel(object):
'''
Needs to:
- sort the args based on a strategy
-
'''
def __init__(self, build_spec):
self.build_spec = build_spec
self.program_name = self.build_spec['program_name']
self.default_size = self.build_spec['default_size']
self.heading_title = _("settings_title")
self.heading_subtitle = self.build_spec['program_description'] or ''
self.use_monospace_font = self.build_spec.get('monospace_display')
self.stop_button_disabled = self.build_spec['disable_stop_button']
reqs, opts = self.group_arguments(self.build_spec['widgets'])
self.required_args = reqs
self.optional_args = opts
def is_valid(self):
# TODO: fix skipping_config.. whatever that did
# currently breaks when you supply it as a decorator option
# return self.skipping_config() and self.required_section_complete()
return self.is_required_section_complete()
def skipping_config(self):
return self.build_spec['manual_start']
def is_required_section_complete(self):
completed_values = filter(None, [arg.value for arg in self.required_args])
return len(self.required_args) == len(completed_values)
def build_command_line_string(self):
"""
returns the collective values from all of the
widgets contained in the panel"""
# _f = lambda lst: [x for x in lst if x is not None]
optional_args = [arg.value for arg in self.optional_args]
required_args = [c.value for c in self.required_args if c.commands]
position_args = [c.value for c in self.required_args if not c.commands]
if position_args:
position_args.insert(0, "--")
return ' '.join(filter(None, chain(required_args, optional_args, position_args)))
def group_arguments(self, widget_list):
is_required = lambda widget: widget['required']
not_checkbox = lambda widget: widget['type'] != 'CheckBox'
required_args, optional_args = self.partition(widget_list, is_required)
if self.build_spec['group_by_type']:
optional_args = chain(*self.partition(optional_args, not_checkbox))
return map(self.to_object, required_args), map(self.to_object, optional_args)
@staticmethod
def partition(collection, condition):
return filter(condition, collection), filter(lambda x: not condition(x), collection)
def to_object(self, data):
details = data['data']
return MyWidget(
data['type'],
self.maybe_unpack(details, 'display_name'),
self.maybe_unpack(details, 'help'),
self.maybe_unpack(details, 'default'),
self.maybe_unpack(details, 'nargs'),
self.maybe_unpack(details, 'commands'),
self.maybe_unpack(details, 'choices')
)
@staticmethod
def maybe_unpack(collection, attr):
# TODO: RadioGroups need to support defaults
try:
if isinstance(collection, list):
return [item[attr] for item in collection]
return collection[attr]
except:
return None

105
gooey/gui/presenter.py

@ -2,41 +2,24 @@ from collections import namedtuple
from gooey.gui import component_builder
from gooey.gui.lang.i18n import _
is_required = lambda widget: widget['required']
SimpleArg = namedtuple('SimpleArg', 'title msg type')
from gooey.gui.pubsub import pub
from gooey.gui import events
class Presenter(object):
def __init__(self, view, build_spec):
def __init__(self, view, model):
self.view = view
self.build_spec = build_spec
def initialize_view(self):
self.view.window_title = self.build_spec['program_name']
self.view.window_size = self.build_spec['default_size']
# widgets = component_builder.build_components(self.build_spec['widgets'])
required_args, optional_args = self.partition(self.build_spec['widgets'], is_required)
self.view.required_section.populate([SimpleArg(x['data']['display_name'], x['data']['help'], x['type']) for x in required_args])
self.model = model
optionals = []
for x in optional_args:
if x['type'] != 'RadioGroup':
optionals.append(SimpleArg(x['data']['display_name'], x['data']['help'], x['type']))
else:
names = [y['display_name'] for y in x['data']]
msgs = [y['help'] for y in x['data']]
optionals.append(SimpleArg(names, msgs, x['type']))
pub.subscribe(self.on_start, events.WINDOW_START)
self.view.optional_section.populate(optionals)
def initialize_view(self):
self.view.window_title = self.model.program_name
self.view.window_size = self.model.default_size
# self.view.optional_section.populate(widgets.optional_args)
self.view.required_section.populate(self.model.required_args)
self.view.optional_section.populate(self.model.optional_args)
if self.build_spec.get('monospace_display'):
if self.model.use_monospace_font:
self.view.set_display_font_style('monospace')
if self.should_disable_stop_button():
@ -46,54 +29,38 @@ class Presenter(object):
self.syncronize_from_model()
def on_start(self):
self.update_model()
if not self.model.is_valid():
self.view.show_missing_args_dialog()
cmd_line_args = self.model.build_command_line_string()
print cmd_line_args
# cmd_line_args = self.view.GetOptions()
# command = '{} --ignore-gooey {}'.format(self.build_spec['target'], cmd_line_args)
# pub.send_message(events.WINDOW_CHANGE, view_name=views.RUNNING_SCREEN)
# self.run_client_code(command)
def update_model(self):
self.update_list(self.model.required_args, self.view.required_section.get_values())
self.update_list(self.model.optional_args, self.view.optional_section.get_values())
def update_list(self, collection, new_values):
for index, val in enumerate(new_values):
collection[index].value = val
@staticmethod
def partition(collection, condition):
return filter(condition, collection), filter(lambda x: not condition(x), collection)
def syncronize_from_model(self):
self.view.heading_title = _("settings_title")
self.view.heading_subtitle = self.build_spec['program_description'] or ''
#
# def _init_pages(self):
# def config():
# self.view.heading_title = 'asdf'
# self.view.heading_subtitle = 'asdf'
# self.view.show('settings_img')
# self.view.hide('check_mark', 'running_img', 'error_symbol')
#
# def running():
# self.view._header.SetLabel(_("running_title"))
# self.view._subheader.SetLabel(_('running_msg'))
# self.view._check_mark.Hide()
# self.view._settings_img.Hide()
# self.view._running_img.Show()
# self.view._error_symbol.Hide()
# self.view.Layout()
#
# def success():
# self.view._header.SetLabel(_('finished_title'))
# self.view._subheader.SetLabel(_('finished_msg'))
# self.view._running_img.Hide()
# self.view._check_mark.Show()
# self.view.Layout()
#
# def error():
# self.view._header.SetLabel(_('finished_title'))
# self.view._subheader.SetLabel(_('finished_error'))
# self.view._running_img.Hide()
# self.view._error_symbol.Show()
# self.view.Layout()
#
# self.layouts = locals()
def is_column_layout(self):
return self.build_spec['layout_type'] == 'column'
self.view.heading_title = self.model.heading_title
self.view.heading_subtitle = self.model.heading_subtitle
def should_disable_stop_button(self):
return self.build_spec['disable_stop_button']
return self.model.stop_button_disabled

12
gooey/gui/widgets/choosers.py

@ -17,7 +17,7 @@
# # Widgets
# self.title = None
# self.help_msg = None
# self.text_box = None
# self.widget = None
# self.button = None
# self.panel = None
#
@ -29,7 +29,7 @@
# self.panel.SetDoubleBuffered(True)
# self.title = self.CreateNameLabelWidget(self.panel)
# self.help_msg = self.CreateHelpMsgWidget(self.panel)
# self.text_box = wx.TextCtrl(self.panel)
# self.widget = wx.TextCtrl(self.panel)
# self.button = wx.Button(self.panel, label=self.button_text, size=(73, 23))
#
# vertical_container = wx.BoxSizer(wx.VERTICAL)
@ -44,7 +44,7 @@
# else:
# vertical_container.AddStretchSpacer(1)
#
# widget_sizer.Add(self.text_box, 1, wx.EXPAND)
# widget_sizer.Add(self.widget, 1, wx.EXPAND)
# widget_sizer.AddSpacer(10)
# widget_sizer.Add(self.button, 0)
#
@ -95,7 +95,7 @@
# if dlg.ShowModal() == wx.ID_OK
# else None)
# if result:
# self.text_box.SetLabelText(result)
# self.widget.SetLabelText(result)
#
#
# class DirectoryChooser(AbstractChooser):
@ -108,7 +108,7 @@
# if dlg.ShowModal() == wx.ID_OK
# else None)
# if result:
# self.text_box.SetLabelText(result)
# self.widget.SetLabelText(result)
#
#
# class CalendarChooser(AbstractChooser):
@ -120,7 +120,7 @@
# dlg = CalendarDlg(self.panel)
# dlg.ShowModal()
# if dlg.GetPath():
# self.text_box.SetLabelText(dlg.GetPath())
# self.widget.SetLabelText(dlg.GetPath())
#
#
#

25
gooey/gui/widgets/components.py

@ -52,6 +52,9 @@ class BaseGuiComponent(object):
return self.panel
def bind(self, *args, **kwargs):
print self.widget_pack.widget.Bind(*args, **kwargs)
def get_title(self):
return self.title.GetLabel()
@ -95,8 +98,8 @@ class BaseGuiComponent(object):
self.help_msg.Wrap(container_width)
evt.Skip()
def GetValue(self):
return self.widget_pack.getValue()
def get_value(self):
return self.widget_pack.get_value()
# def HasOptionString(self):
# return bool(self.widget_pack.option_string)
@ -148,6 +151,8 @@ class CheckBox(BaseGuiComponent):
msg.Wrap(container_width)
evt.Skip()
def get_value(self):
return self.widget.GetValue()
# def GetValue(self):
# return self.option_strings if self.widget.GetValue() else ''
#
@ -215,12 +220,15 @@ class RadioGroup(object):
msg.Wrap(container_width)
evt.Skip()
def GetValue(self):
vals = [button.GetValue() for button in self.radio_buttons]
try:
return self.option_strings[vals.index(True)][0]
except:
return ''
def get_value(self):
return [button.GetValue() for button in self.radio_buttons]
# def GetValue(self):
# vals = [button.GetValue() for button in self.radio_buttons]
# try:
# return self.option_strings[vals.index(True)][0]
# except:
# return ''
def HasOptionString(self):
return bool(self.option_strings)
@ -230,6 +238,7 @@ class RadioGroup(object):
def build_subclass(name, widget_class):
# this seemed faster than typing class X a bunch
return type(name, (BaseGuiComponent,), {'widget_class': widget_class})

96
gooey/gui/widgets/widget_pack.py

@ -45,29 +45,32 @@ class BaseChooser(WidgetPack):
self.button_text = i18n._('browse')
self.option_string = None
self.parent = None
self.text_box = None
self.widget = None
self.button = None
def build(self, parent, data):
self.parent = parent
self.option_string = self.get_command(data)
self.text_box = wx.TextCtrl(self.parent)
self.text_box.AppendText(safe_default(data, ''))
self.text_box.SetMinSize((0, -1))
dt = FileDrop(self.text_box)
self.text_box.SetDropTarget(dt)
self.widget = wx.TextCtrl(self.parent)
self.widget.AppendText(safe_default(data, ''))
self.widget.SetMinSize((0, -1))
dt = FileDrop(self.widget)
self.widget.SetDropTarget(dt)
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.Add(self.widget, 1, wx.EXPAND)
widget_sizer.AddSpacer(10)
widget_sizer.Add(self.button, 0, wx.ALIGN_CENTER_VERTICAL)
parent.Bind(wx.EVT_BUTTON, self.onButton, self.button)
return widget_sizer
def get_value(self):
return self.widget.GetValue()
# def getValue(self):
# value = self.text_box.GetValue()
# value = self.widget.GetValue()
# if self.option_string and value:
# return '{0} {1}'.format(self.option_string, quote(value))
# else:
@ -91,7 +94,7 @@ class BaseFileChooser(BaseChooser):
if dlg.ShowModal() == wx.ID_OK
else None)
if result:
self.text_box.SetValue(result)
self.widget.SetValue(result)
def get_path(self, dlg):
return dlg.GetPath()
@ -103,7 +106,7 @@ class BaseMultiFileChooser(BaseFileChooser):
self.dialog = dialog
# def getValue(self):
# value = ' '.join(quote(x) for x in self.text_box.GetValue().split(os.pathsep) if x)
# value = ' '.join(quote(x) for x in self.widget.GetValue().split(os.pathsep) if x)
# if self.option_string and value:
# return '{} {}'.format(self.option_string, value)
# return value or ''
@ -151,6 +154,9 @@ class TextInputPayload(WidgetPack):
self.widget.AppendText(safe_default(data, ''))
return self.widget
def get_value(self):
return self.widget.GetValue()
# def getValue(self):
# if self.no_quoting:
# _quote = lambda value: value
@ -188,18 +194,21 @@ class DropdownPayload(WidgetPack):
)
return self.widget
def getValue(self):
if self.no_quoting:
_quote = lambda value: value
else:
_quote = quote
value = self.widget.GetValue()
if value == self.default_value:
return ''
elif value and self.option_string:
return '{} {}'.format(self.option_string, _quote(value))
else:
return _quote(value) if value else ''
def get_value(self):
return self.widget.GetValue()
# def getValue(self):
# if self.no_quoting:
# _quote = lambda value: value
# else:
# _quote = quote
# value = self.widget.GetValue()
# if value == self.default_value:
# return ''
# elif value and self.option_string:
# return '{} {}'.format(self.option_string, _quote(value))
# else:
# return _quote(value) if value else ''
def _SetValue(self, text):
# used for testing
@ -222,19 +231,36 @@ class CounterPayload(WidgetPack):
)
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 ''
arg = str(self.option_string).replace('-', '')
repeated_args = arg * int(dropdown_value)
return '-' + repeated_args
def get_value(self):
return self.widget.GetValue()
# def getValue(self):
# '''
# Returns
# str(option_string * DropDown Value)
# e.g.
# -vvvvv
# '''
# return self.widget.GetValue()
# if not str(dropdown_value).isdigit():
# return ''
# arg = str(self.option_string).replace('-', '')
# repeated_args = arg * int(dropdown_value)
# return '-' + repeated_args
# def getValue(self):
# '''
# Returns
# str(option_string * DropDown Value)
# e.g.
# -vvvvv
# '''
# dropdown_value = self.widget.GetValue()
# if not str(dropdown_value).isdigit():
# return ''
# arg = str(self.option_string).replace('-', '')
# repeated_args = arg * int(dropdown_value)
# return '-' + repeated_args
class DirDialog(wx.DirDialog):
def __init__(self, parent, *args, **kwargs):

16
gooey/gui/windows/advanced_config.py

@ -6,7 +6,7 @@ Managed the internal layout for configuration options
import wx
from bson import ObjectId
from wx.lib.scrolledpanel import ScrolledPanel
from itertools import chain, izip_longest
@ -29,11 +29,19 @@ class WidgetContainer(wx.Panel):
self.SetSizer(self.container)
def populate(self, widgets):
for w in widgets:
widget_class = getattr(components, w.type)
self.widgets.append(widget_class(self, w.title, w.msg))
for index, widget in enumerate(widgets):
widget_class = getattr(components, widget.type)
widget_instance = widget_class(self, widget.title, widget.help)
# widget_instance.bind(wx.EVT_TEXT, self.publish_change)
self.widgets.append(widget_instance)
self.layout()
def publish_change(self, evt):
evt.Skip()
def get_values(self):
return [x.get_value() for x in self.widgets]
def layout(self):
STD_LAYOUT = (0, wx.LEFT | wx.RIGHT | wx.EXPAND, PADDING)

3
gooey/gui/windows/base_window.py

@ -258,6 +258,9 @@ class BaseWindow(wx.Frame):
dlg.Destroy()
return result
def show_missing_args_dialog(self):
self.show_dialog(i18n._('error_title'), i18n._('error_required_fields'), wx.ICON_ERROR)
if __name__ == '__main__':
pass

1
gooey/python_bindings/config_generator.py

@ -32,6 +32,7 @@ def create_from_parser(parser, source_path, **kwargs):
'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 show_config:

Loading…
Cancel
Save