mirror of https://github.com/chriskiehl/Gooey.git
24 changed files with 0 additions and 2252 deletions
Split View
Diff Options
-
32gooey/TODO.txt
-
17gooey/gui/controller.py
-
277gooey/gui/model.py
-
190gooey/gui/presenter.py
-
17gooey/gui/subpresenter.py
-
1gooey/gui/widgets/__init__.py
-
50gooey/gui/widgets/calender_dialog.py
-
256gooey/gui/widgets/components.py
-
268gooey/gui/widgets/widget_pack.py
-
1gooey/gui/windows/__init__.py
-
114gooey/gui/windows/advanced_config.py
-
229gooey/gui/windows/base_window.py
-
112gooey/gui/windows/footer.py
-
88gooey/gui/windows/header.py
-
53gooey/gui/windows/layouts.py
-
52gooey/gui/windows/runtime_display_panel.py
-
45gooey/gui/windows/sidebar.py
-
6gooey/gui/windows/views.py
-
67gooey/tests/conftest.py
-
58gooey/tests/test_image_repositoy.py
-
89gooey/tests/test_presentation.py
-
227gooey_config.json
-
2pytest.ini
-
1version.txt
@ -1,32 +0,0 @@ |
|||
|
|||
|
|||
TODO: |
|||
- Update parser to catch all argparse import styles |
|||
- Investigate Docopt |
|||
- better graphics |
|||
|
|||
Restart Button Change: |
|||
- need different strategy for everything added. |
|||
|
|||
- Fix vertical stacking of restart button |
|||
- system for supplying custom widgets to the GUI -- e.g. a FileChooser, rather than just a TextBox |
|||
- Remove debug statements current printing from program |
|||
|
|||
- add optional cancel button. |
|||
- allow NoConfig to run without argparse (Issue #43) |
|||
- display warning when this happens (could be a misfire on Gooey's end) |
|||
- add suppress warnings flag |
|||
|
|||
|
|||
- Meta Info |
|||
- Program Name |
|||
- Program Description |
|||
- Client Program |
|||
- Desired Size |
|||
- Desired Columns (required) |
|||
- Desired Columns (optional) |
|||
|
|||
|
|||
|
|||
|
|||
|
@ -1,17 +0,0 @@ |
|||
from gooey.gui.model import MyModel |
|||
from gooey.gui.presenter import Presenter |
|||
from gooey.gui.windows.base_window import BaseWindow |
|||
|
|||
|
|||
class Controller(object): |
|||
|
|||
def __init__(self, build_spec): |
|||
self.model = MyModel(build_spec) |
|||
self.view = BaseWindow(layout_type=self.model.layout_type) |
|||
self.presentation = Presenter(self.view, self.model) |
|||
self.presentation.initialize_view() |
|||
|
|||
|
|||
def run(self): |
|||
self.view.Show(True) |
|||
|
@ -1,277 +0,0 @@ |
|||
import os |
|||
from collections import OrderedDict, namedtuple |
|||
from itertools import chain |
|||
from gooey.gui.lang.i18n import _ |
|||
from gooey.gui.util.quoting import quote |
|||
|
|||
import wx |
|||
|
|||
ArgumentGroup = namedtuple('ArgumentGroup', 'name command required_args optional_args') |
|||
|
|||
|
|||
class MyWidget(object): |
|||
# TODO: Undumbify damn |
|||
# TODO: Undumbify _value/value access |
|||
|
|||
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 u'{} {}'.format(self.commands[0], value) |
|||
return value or None |
|||
if self.type == 'Textarea': |
|||
if self.commands and self._value: |
|||
return '{} {}'.format(self.commands[0], quote(self._value.encode('unicode_escape'))) |
|||
else: |
|||
return quote(self._value.encode('unicode_escape')) if self._value else '' |
|||
if self.type == 'CommandField': |
|||
if self.commands and self._value: |
|||
return u'{} {}'.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 == 'Listbox': |
|||
if self.commands and self._value: |
|||
return u'{} {}'.format(self.commands[0], ' '.join(map(quote, self._value))) |
|||
else: |
|||
return ' '.join(map(quote, self._value)) if self._value else '' |
|||
if self.type == 'Dropdown': |
|||
if self._value == 'Select Option': |
|||
return None |
|||
elif self.commands and self._value: |
|||
return u'{} {}'.format(self.commands[0], quote(self._value)) |
|||
else: |
|||
return quote(self._value) if self._value else '' |
|||
else: |
|||
if self.commands and self._value: |
|||
if not self.nargs: |
|||
v = quote(self._value) |
|||
else: |
|||
v = self._value |
|||
return u'{0} {1}'.format(self.commands[0], v) |
|||
else: |
|||
if not self._value: |
|||
return None |
|||
elif not self.nargs: |
|||
return quote(self._value) |
|||
else: |
|||
return self._value |
|||
|
|||
@value.setter |
|||
def value(self, val): |
|||
self._value = val |
|||
|
|||
@classmethod |
|||
def from_dict(cls, data): |
|||
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 |
|||
|
|||
details = data['data'] |
|||
return cls( |
|||
data['type'], |
|||
maybe_unpack(details, 'display_name'), |
|||
maybe_unpack(details, 'help'), |
|||
maybe_unpack(details, 'default'), |
|||
maybe_unpack(details, 'nargs'), |
|||
maybe_unpack(details, 'commands'), |
|||
maybe_unpack(details, 'choices') |
|||
) |
|||
|
|||
|
|||
|
|||
|
|||
class States(object): |
|||
CONFIGURING = 'configuring' |
|||
RUNNNING = 'running' |
|||
SUCCESS = 'success' |
|||
ERROR = 'error' |
|||
STOPPED = 'stopped' |
|||
|
|||
|
|||
|
|||
class MyModel(object): |
|||
''' |
|||
''' |
|||
|
|||
def wrap(self, groups): |
|||
output = OrderedDict() |
|||
for name, group in groups.items(): |
|||
output[name] = ArgumentGroup( |
|||
name, |
|||
group['command'], |
|||
*self.group_arguments(group['contents']) |
|||
) |
|||
return output |
|||
|
|||
|
|||
def __init__(self, build_spec): |
|||
|
|||
self.current_state = States.CONFIGURING |
|||
|
|||
self.build_spec = build_spec |
|||
self.layout_type = self.build_spec.get('layout_type') |
|||
|
|||
self.auto_start = self.build_spec.get('auto_start') |
|||
self.progress_regex = self.build_spec.get('progress_regex') |
|||
self.progress_expr = self.build_spec.get('progress_expr') |
|||
self.disable_progress_bar_animation = self.build_spec['disable_progress_bar_animation'] |
|||
|
|||
self.program_name = self.build_spec.get('program_name') |
|||
self.default_size = self.build_spec.get('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'] |
|||
|
|||
self.argument_groups = self.wrap(self.build_spec.get('widgets', {})) |
|||
self.active_group = next(iter(self.argument_groups)) |
|||
|
|||
self.num_required_cols = self.build_spec['num_required_cols'] |
|||
self.num_optional_cols = self.build_spec['num_optional_cols'] |
|||
|
|||
self.text_states = { |
|||
States.CONFIGURING: { |
|||
'title': _("settings_title"), |
|||
'subtitle': self.build_spec['program_description'] or '' |
|||
}, |
|||
States.RUNNNING: { |
|||
'title': _("running_title"), |
|||
'subtitle': _('running_msg') |
|||
}, |
|||
States.SUCCESS: { |
|||
'title': _('finished_title'), |
|||
'subtitle': _('finished_msg') |
|||
}, |
|||
States.ERROR: { |
|||
'title': _('finished_title'), |
|||
'subtitle': _('finished_error') |
|||
} |
|||
} |
|||
|
|||
@property |
|||
def required_args(self): |
|||
return self.argument_groups[self.active_group].required_args |
|||
|
|||
@property |
|||
def optional_args(self): |
|||
return self.argument_groups[self.active_group].optional_args |
|||
|
|||
def update_state(self, state): |
|||
self.current_state = state |
|||
|
|||
text = self.text_states[state] |
|||
self.heading_title = text['title'] |
|||
self.heading_subtitle = text['subtitle'] |
|||
|
|||
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): |
|||
error_found = False |
|||
for arg in self.required_args: |
|||
widget = arg.widget_instance.widget_pack.widget |
|||
if arg.value: |
|||
widget.SetBackgroundColour(wx.NullColour) |
|||
else: |
|||
if not error_found: |
|||
error_found = True |
|||
widget.SetFocus() |
|||
widget.SetBackgroundColour("#EF9A9A") |
|||
|
|||
widget.Refresh() |
|||
|
|||
return not error_found |
|||
|
|||
def build_command_line_string(self): |
|||
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, "--") |
|||
cmd_string = ' '.join(list(filter(None, chain(required_args, optional_args, position_args)))) |
|||
if self.layout_type == 'column': |
|||
cmd_string = u'{} {}'.format(self.argument_groups[self.active_group].command, cmd_string) |
|||
return u'{} --ignore-gooey {}'.format(self.build_spec['target'], cmd_string) |
|||
|
|||
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 list(map(self.to_object, required_args)), list(map(self.to_object, optional_args)) |
|||
|
|||
@staticmethod |
|||
def partition(collection, condition): |
|||
return list(filter(condition, collection)), list(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 |
|||
|
|||
|
|||
|
@ -1,190 +0,0 @@ |
|||
import sys |
|||
|
|||
from gooey.gui.processor import ProcessController |
|||
from gooey.gui.model import States |
|||
from gooey.gui.pubsub import pub |
|||
from gooey.gui import events |
|||
from gooey.gui.windows import layouts |
|||
|
|||
import wx |
|||
|
|||
class Presenter(object): |
|||
def __init__(self, view, model): |
|||
self.view = view |
|||
self.model = model |
|||
|
|||
self.client_runner = ProcessController( |
|||
self.model.progress_regex, |
|||
self.model.progress_expr |
|||
) |
|||
|
|||
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_restart, events.WINDOW_RESTART) |
|||
pub.subscribe(self.on_edit, events.WINDOW_EDIT) |
|||
pub.subscribe(self.on_close, events.WINDOW_CLOSE) |
|||
|
|||
# console statuses from the other thread |
|||
pub.subscribe(self.on_new_message, 'console_update') |
|||
pub.subscribe(self.on_progress_change, 'progress_update') |
|||
pub.subscribe(self.on_client_done, 'execution_complete') |
|||
|
|||
|
|||
pub.subscribe(self.on_selection_change, events.LIST_BOX) |
|||
|
|||
|
|||
def on_selection_change(self, selection): |
|||
self.update_model() |
|||
self.model.active_group = selection |
|||
self.redraw_from_model() |
|||
self.syncronize_from_model() |
|||
|
|||
def initialize_view(self): |
|||
self.view.window_title = self.model.program_name |
|||
self.view.window_size = self.model.default_size |
|||
|
|||
self.view.required_section.clear() |
|||
self.view.optional_section.clear() |
|||
self.view.required_section.populate(self.model.required_args, self.model.num_required_cols) |
|||
self.view.optional_section.populate(self.model.optional_args, self.model.num_optional_cols) |
|||
|
|||
if self.model.use_monospace_font: |
|||
self.view.set_display_font_style('monospace') |
|||
|
|||
if self.should_disable_stop_button(): |
|||
self.view.disable_stop_button() |
|||
else: |
|||
self.view.enable_stop_button() |
|||
|
|||
if self.model.layout_type == layouts.COLUMN: |
|||
self.view.set_list_contents(list(self.model.argument_groups.keys())) |
|||
|
|||
if self.model.auto_start: |
|||
self.model.update_state(States.RUNNNING) |
|||
self.on_start() |
|||
self.syncronize_from_model() |
|||
|
|||
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()) |
|||
self.syncronize_from_model() |
|||
|
|||
def syncronize_from_model(self): |
|||
#TODO move this out of the presenter |
|||
#TODO Make all view interactions thread safe |
|||
wx.CallAfter(self.syncronize_from_model_async) |
|||
|
|||
def syncronize_from_model_async(self): |
|||
# update heading titles |
|||
self.view.heading_title = self.model.heading_title |
|||
self.view.heading_subtitle = self.model.heading_subtitle |
|||
|
|||
# refresh the widgets |
|||
for index, widget in enumerate(self.view.required_section): |
|||
widget.set_value(self.model.required_args[index]._value) |
|||
for index, widget in enumerate(self.view.optional_section): |
|||
widget.set_value(self.model.optional_args[index]._value) |
|||
|
|||
# swap the views |
|||
getattr(self, self.model.current_state)() |
|||
|
|||
def redraw_from_model(self): |
|||
self.view.freeze() |
|||
self.view.required_section.clear() |
|||
self.view.optional_section.clear() |
|||
self.view.required_section.populate(self.model.required_args, self.model.num_required_cols) |
|||
self.view.optional_section.populate(self.model.optional_args, self.model.num_optional_cols) |
|||
getattr(self, self.model.current_state)() |
|||
self.view.thaw() |
|||
|
|||
def should_disable_stop_button(self): |
|||
return self.model.stop_button_disabled |
|||
|
|||
def on_start(self): |
|||
self.update_model() |
|||
if not self.model.is_valid(): |
|||
return self.view.show_missing_args_dialog() |
|||
command = self.model.build_command_line_string() |
|||
self.client_runner.run(command) |
|||
self.model.update_state(States.RUNNNING) |
|||
self.syncronize_from_model() |
|||
|
|||
def on_stop(self): |
|||
self.ask_stop() |
|||
|
|||
def on_edit(self): |
|||
self.model.update_state(States.CONFIGURING) |
|||
self.syncronize_from_model() |
|||
|
|||
def on_restart(self): |
|||
self.on_start() |
|||
|
|||
def on_cancel(self): |
|||
if self.view.confirm_exit_dialog(): |
|||
self.view.Destroy() |
|||
sys.exit() |
|||
|
|||
def on_close(self): |
|||
self.view.Destroy() |
|||
sys.exit() |
|||
|
|||
def on_new_message(self, msg): |
|||
# observes changes coming from the subprocess |
|||
self.view.update_console_async(msg) |
|||
|
|||
def on_progress_change(self, progress): |
|||
# observes changes coming from the subprocess |
|||
self.view.update_progress_aync(progress, self.model.disable_progress_bar_animation) |
|||
|
|||
def on_client_done(self): |
|||
if self.client_runner.was_success(): |
|||
self.model.update_state(States.SUCCESS) |
|||
else: |
|||
self.model.update_state(States.ERROR) |
|||
self.syncronize_from_model() |
|||
|
|||
def ask_stop(self): |
|||
if self.view.confirm_stop_dialog(): |
|||
self.stop() |
|||
return True |
|||
return False |
|||
|
|||
def stop(self): |
|||
self.client_runner.stop() |
|||
|
|||
def configuring(self): |
|||
self.view.hide_all_buttons() |
|||
self.view.hide('check_mark', 'running_img', 'error_symbol', 'runtime_display') |
|||
self.view.show('settings_img', 'cancel_button', 'start_button', 'config_panel') |
|||
self.view.Layout() |
|||
|
|||
def running(self): |
|||
self.view.hide_all_buttons() |
|||
self.view.hide('check_mark', 'settings_img', 'error_symbol', 'config_panel') |
|||
self.view.show('running_img', 'stop_button', 'progress_bar', 'runtime_display') |
|||
self.view.progress_bar.Pulse() |
|||
self.view.Layout() |
|||
|
|||
def success(self): |
|||
self.view.hide_all_buttons() |
|||
self.view.hide('running_img', 'progress_bar', 'config_panel') |
|||
self.view.show('check_mark', 'edit_button', 'restart_button', 'close_button', 'runtime_display') |
|||
self.view.Layout() |
|||
|
|||
def error(self): |
|||
self.view.hide_all_buttons() |
|||
self.view.hide('running_img', 'progress_bar', 'config_panel') |
|||
self.view.show('error_symbol', 'edit_button', 'restart_button', 'close_button', 'runtime_display') |
|||
self.view.Layout() |
|||
|
|||
@staticmethod |
|||
def partition(collection, condition): |
|||
return filter(condition, collection), filter(lambda x: not condition(x), collection) |
|||
|
|||
def update_list(self, collection, new_values): |
|||
# convenience method for syncronizing the model -> widget list collections |
|||
for index, val in enumerate(new_values): |
|||
collection[index].value = val |
|||
|
|||
|
@ -1,17 +0,0 @@ |
|||
|
|||
|
|||
class SubModel(object): |
|||
|
|||
def __init__(self): |
|||
self.section_title = None |
|||
|
|||
|
|||
class Presenter(object): |
|||
def __init__(self, view, model): |
|||
self.view = view |
|||
self.model = model |
|||
|
|||
|
|||
def on_selection_change(self): |
|||
self.view.refresh |
|||
|
@ -1 +0,0 @@ |
|||
__author__ = 'Chris' |
@ -1,50 +0,0 @@ |
|||
__author__ = 'Chris' |
|||
|
|||
import wx |
|||
|
|||
from gooey.gui.util import wx_util |
|||
|
|||
from gooey.gui.three_to_four import Classes, Constants |
|||
|
|||
|
|||
class CalendarDlg(wx.Dialog): |
|||
def __init__(self, parent): |
|||
wx.Dialog.__init__(self, parent) |
|||
|
|||
self.SetBackgroundColour('#ffffff') |
|||
|
|||
self.ok_button = wx.Button(self, wx.ID_OK, label='Ok') |
|||
self.datepicker = Classes.DatePickerCtrl(self, style=Constants.WX_DP_DROPDOWN) |
|||
|
|||
vertical_container = wx.BoxSizer(wx.VERTICAL) |
|||
vertical_container.AddSpacer(10) |
|||
vertical_container.Add(wx_util.h1(self, label='Select a Date'), 0, wx.LEFT | wx.RIGHT, 15) |
|||
vertical_container.AddSpacer(10) |
|||
vertical_container.Add(self.datepicker, 0, wx.EXPAND | wx.LEFT | wx.RIGHT, 15) |
|||
|
|||
vertical_container.AddSpacer(10) |
|||
button_sizer = wx.BoxSizer(wx.HORIZONTAL) |
|||
button_sizer.AddStretchSpacer(1) |
|||
button_sizer.Add(self.ok_button, 0) |
|||
|
|||
vertical_container.Add(button_sizer, 0, wx.LEFT | wx.RIGHT, 15) |
|||
vertical_container.AddSpacer(20) |
|||
self.SetSizerAndFit(vertical_container) |
|||
|
|||
self.Bind(wx.EVT_BUTTON, self.OnOkButton, self.ok_button) |
|||
|
|||
def OnOkButton(self, event): |
|||
self.EndModal(wx.ID_OK) |
|||
event.Skip() |
|||
|
|||
def OnCancellButton(self, event): |
|||
try: |
|||
return None |
|||
except: |
|||
self.Close() |
|||
|
|||
def GetPath(self): |
|||
return self.datepicker.GetValue().FormatISODate() |
|||
|
|||
|
|||
|
@ -1,256 +0,0 @@ |
|||
from functools import partial |
|||
|
|||
import wx |
|||
|
|||
from gooey.gui.util import wx_util |
|||
from gooey.gui.widgets import widget_pack |
|||
|
|||
|
|||
class BaseGuiComponent(object): |
|||
|
|||
widget_class = None |
|||
|
|||
def __init__(self, parent, title, msg, choices=None): |
|||
''' |
|||
:param data: field info (title, help, etc..) |
|||
:param widget_pack: internal wxWidgets to render |
|||
''' |
|||
# parent |
|||
self.parent = parent |
|||
|
|||
# Widgets |
|||
self.title = None |
|||
self.help_msg = None |
|||
self.choices = choices |
|||
|
|||
# Internal WidgetPack set in subclasses |
|||
|
|||
self.do_layout(parent, title, msg) |
|||
|
|||
def do_layout(self, parent, title, msg): |
|||
self.panel = wx.Panel(parent) |
|||
|
|||
self.widget_pack = self.widget_class() |
|||
|
|||
self.title = self.format_title(self.panel, title) |
|||
self.help_msg = self.format_help_msg(self.panel, msg) |
|||
self.help_msg.SetMinSize((0, -1)) |
|||
core_widget_set = self.widget_pack.build(self.panel, {}, self.choices) |
|||
|
|||
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) |
|||
|
|||
return self.panel |
|||
|
|||
def bind(self, *args, **kwargs): |
|||
print(self.widget_pack.widget.Bind(*args, **kwargs)) |
|||
|
|||
def get_title(self): |
|||
return self.title.GetLabel() |
|||
|
|||
def set_title(self, text): |
|||
self.title.SetLabel(text) |
|||
|
|||
def get_help_msg(self): |
|||
return self.help_msg.GetLabelText() |
|||
|
|||
def set_label_text(self, text): |
|||
self.help_msg.SetLabel(text) |
|||
|
|||
def format_help_msg(self, parent, msg): |
|||
base_text = wx.StaticText(parent, label=msg or '') |
|||
wx_util.dark_grey(base_text) |
|||
return base_text |
|||
|
|||
def format_title(self, parent, title): |
|||
text = wx.StaticText(parent, label=title) |
|||
wx_util.make_bold(text) |
|||
return text |
|||
|
|||
def onResize(self, evt): |
|||
# handle internal widgets |
|||
# self.panel.Freeze() |
|||
self._onResize(evt) |
|||
# propagate event to child widgets |
|||
self.widget_pack.onResize(evt) |
|||
evt.Skip() |
|||
# self.panel.Thaw() |
|||
|
|||
def _onResize(self, evt): |
|||
if not self.help_msg: |
|||
return |
|||
self.panel.Size = evt.GetSize() |
|||
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) |
|||
evt.Skip() |
|||
|
|||
def get_value(self): |
|||
return self.widget_pack.get_value() |
|||
|
|||
def set_value(self, val): |
|||
if val: |
|||
self.widget_pack.widget.SetValue(str(val)) |
|||
|
|||
def __repr__(self): |
|||
return self.__class__.__name__ |
|||
|
|||
|
|||
class CheckBox(BaseGuiComponent): |
|||
|
|||
def __init__(self, parent, title, msg, choices=None): |
|||
BaseGuiComponent.__init__(self, parent, title, msg) |
|||
|
|||
def do_layout(self, parent, title, msg): |
|||
self.panel = wx.Panel(parent) |
|||
|
|||
self.widget = wx.CheckBox(self.panel) |
|||
# self.widget.SetValue(self.default_value) |
|||
self.title = self.format_title(self.panel, title) |
|||
self.help_msg = self.format_help_msg(self.panel, msg) |
|||
self.help_msg.SetMinSize((0, -1)) |
|||
|
|||
# self.help_msg.Bind(wx.EVT_LEFT_UP, lambda event: self.widget.SetValue(not self.widget.GetValue())) |
|||
|
|||
vertical_container = wx.BoxSizer(wx.VERTICAL) |
|||
vertical_container.Add(self.title) |
|||
|
|||
horizontal_sizer = wx.BoxSizer(wx.HORIZONTAL) |
|||
horizontal_sizer.Add(self.widget, 0, wx.EXPAND | wx.RIGHT, 10) |
|||
horizontal_sizer.Add(self.help_msg, 1, wx.EXPAND) |
|||
|
|||
vertical_container.Add(horizontal_sizer, 0, wx.EXPAND) |
|||
|
|||
self.panel.SetSizer(vertical_container) |
|||
self.panel.Bind(wx.EVT_SIZE, self.onResize) |
|||
return self.panel |
|||
|
|||
def onResize(self, evt): |
|||
msg = self.help_msg |
|||
container_width, _ = self.panel.Size |
|||
text_width, _ = msg.Size |
|||
|
|||
if text_width != container_width: |
|||
msg.SetLabel(msg.GetLabelText().replace('\n', ' ')) |
|||
msg.Wrap(container_width) |
|||
evt.Skip() |
|||
|
|||
def get_value(self): |
|||
return self.widget.GetValue() |
|||
|
|||
def set_value(self, val): |
|||
self.widget.SetValue(val) |
|||
|
|||
|
|||
class RadioGroup(object): |
|||
def __init__(self, parent, title, msg, choices=None): |
|||
self.panel = None |
|||
|
|||
self.radio_buttons = [] |
|||
self.option_strings = [] |
|||
self.help_msgs = [] |
|||
self.btn_names = [] |
|||
|
|||
self.do_layout(parent, title, msg) |
|||
|
|||
self.selected_button = None |
|||
|
|||
def do_layout(self, parent, titles, msgs): |
|||
self.panel = wx.Panel(parent) |
|||
|
|||
self.radio_buttons = [wx.RadioButton(self.panel, -1) for _ in titles] |
|||
self.btn_names = [wx.StaticText(self.panel, label=title.title()) for title in titles] |
|||
self.help_msgs = [wx.StaticText(self.panel, label=msg.title()) for msg in msgs] |
|||
|
|||
# box = wx.StaticBox(self.panel, -1, label=self.data['group_name']) |
|||
box = wx.StaticBox(self.panel, -1, label='') |
|||
vertical_container = wx.StaticBoxSizer(box, wx.VERTICAL) |
|||
|
|||
for button, name, help in zip(self.radio_buttons, self.btn_names, self.help_msgs): |
|||
|
|||
hbox = wx.BoxSizer(wx.HORIZONTAL) |
|||
|
|||
hbox.Add(button, 0, wx.ALIGN_TOP | wx.ALIGN_LEFT) |
|||
hbox.Add(name, 0, wx.LEFT, 10) |
|||
|
|||
vertical_container.Add(hbox, 0, wx.EXPAND) |
|||
|
|||
vertical_container.Add(help, 1, wx.EXPAND | wx.LEFT, 25) |
|||
vertical_container.AddSpacer(5) |
|||
|
|||
self.panel.SetSizer(vertical_container) |
|||
self.panel.Bind(wx.EVT_SIZE, self.onResize) |
|||
|
|||
for button in self.radio_buttons: |
|||
button.Bind(wx.EVT_LEFT_DOWN, self.handle_selection) |
|||
|
|||
return self.panel |
|||
|
|||
def handle_selection(self, event): |
|||
if event.EventObject.Id == getattr(self.selected_button, 'Id', None): |
|||
# if it is already selected, manually deselect it |
|||
self.selected_button.SetValue(False) |
|||
self.selected_button = None |
|||
else: |
|||
event.Skip() |
|||
self.selected_button = event.EventObject |
|||
|
|||
def onResize(self, evt): |
|||
msg = self.help_msgs[0] |
|||
container_width, _ = self.panel.Size |
|||
text_width, _ = msg.Size |
|||
|
|||
if text_width != container_width: |
|||
msg.SetLabel(msg.GetLabelText().replace('\n', ' ')) |
|||
msg.Wrap(container_width) |
|||
evt.Skip() |
|||
|
|||
def get_value(self): |
|||
return [button.GetValue() for button in self.radio_buttons] |
|||
|
|||
def set_value(self, val): |
|||
pass |
|||
|
|||
|
|||
class Listbox(BaseGuiComponent): |
|||
widget_class = widget_pack.ListboxPayload |
|||
|
|||
def set_value(self, val): |
|||
if val: |
|||
self.widget_pack.set_value(val) |
|||
|
|||
|
|||
def build_subclass(name, widget_class): |
|||
# this seemed faster than typing class X a bunch |
|||
return type(name, (BaseGuiComponent,), {'widget_class': widget_class}) |
|||
|
|||
|
|||
|
|||
|
|||
|
|||
FileChooser = build_subclass('FileChooser', widget_pack.FileChooserPayload) |
|||
MultiFileChooser = build_subclass('MultiFileChooser', widget_pack.MultiFileSaverPayload) |
|||
DirChooser = build_subclass('DirChooser', widget_pack.DirChooserPayload) |
|||
FileSaver = build_subclass('FileSaver', widget_pack.FileSaverPayload) |
|||
DateChooser = build_subclass('DateChooser', widget_pack.DateChooserPayload) |
|||
TextField = build_subclass('TextField', widget_pack.TextInputPayload) |
|||
Textarea = build_subclass('TextField', widget_pack.TextAreaPayload) |
|||
CommandField = build_subclass('CommandField', widget_pack.TextInputPayload(no_quoting=True)) |
|||
Dropdown = build_subclass('Dropdown', widget_pack.DropdownPayload) |
|||
Counter = build_subclass('Counter', widget_pack.CounterPayload) |
|||
MultiDirChooser = build_subclass('MultiDirChooser', widget_pack.MultiDirChooserPayload) |
|||
PasswordField = build_subclass('PasswordField', widget_pack.PasswordInputPayload) |
@ -1,268 +0,0 @@ |
|||
import os |
|||
import wx |
|||
|
|||
import wx.lib.agw.multidirdialog as MDD |
|||
from abc import ABCMeta, abstractmethod |
|||
|
|||
from gooey.gui.lang import i18n |
|||
from gooey.gui.util.filedrop import FileDrop |
|||
from gooey.gui.widgets.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, choices=None): |
|||
pass |
|||
|
|||
def onResize(self, evt): |
|||
pass |
|||
|
|||
@staticmethod |
|||
def get_command(data): |
|||
return '' |
|||
|
|||
@staticmethod |
|||
def disable_quoting(data): |
|||
nargs = data.get('nargs', None) |
|||
if not nargs: |
|||
return False |
|||
return nargs not in (1, '?') |
|||
|
|||
|
|||
class BaseChooser(WidgetPack): |
|||
def __init__(self): |
|||
self.button_text = i18n._('browse') |
|||
self.parent = None |
|||
self.widget = None |
|||
self.button = None |
|||
|
|||
def build(self, parent, data, choices=None): |
|||
self.parent = parent |
|||
self.widget = wx.TextCtrl(self.parent) |
|||
self.widget.AppendText('') |
|||
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.widget, 1, wx.EXPAND) |
|||
widget_sizer.AddSpacer(10) |
|||
widget_sizer.Add(self.button, 0, wx.ALIGN_CENTER_VERTICAL) |
|||
|
|||
parent.Bind(wx.EVT_BUTTON, self.on_button, self.button) |
|||
|
|||
return widget_sizer |
|||
|
|||
def get_value(self): |
|||
return self.widget.GetValue() |
|||
|
|||
def __repr__(self): |
|||
return self.__class__.__name__ |
|||
|
|||
|
|||
class BaseFileChooser(BaseChooser): |
|||
dialog = None |
|||
def __init__(self): |
|||
BaseChooser.__init__(self) |
|||
|
|||
def on_button(self, evt): |
|||
dlg = self.dialog(self.parent) |
|||
result = (self.get_path(dlg) |
|||
if dlg.ShowModal() == wx.ID_OK |
|||
else None) |
|||
if result: |
|||
self.widget.SetValue(result) |
|||
|
|||
def get_path(self, dlg): |
|||
return dlg.GetPath() |
|||
|
|||
|
|||
class BaseMultiFileChooser(BaseFileChooser): |
|||
def __init__(self, dialog): |
|||
BaseFileChooser.__init__(self) |
|||
self.dialog = dialog |
|||
|
|||
def get_path(self, dlg): |
|||
return os.pathsep.join(dlg.GetPaths()) |
|||
|
|||
|
|||
class MultiFileSaverPayload(BaseMultiFileChooser): |
|||
def __init__(self, *args, **kwargs): |
|||
BaseMultiFileChooser.__init__(self, build_dialog(wx.FD_MULTIPLE, False)) |
|||
|
|||
|
|||
class MultiDirChooserPayload(BaseMultiFileChooser): |
|||
class MyMultiDirChooser(MDD.MultiDirDialog): |
|||
def __init__(self, *args, **kwargs): |
|||
kwargs.update({ |
|||
'title': "Select Directories", |
|||
'defaultPath': os.getcwd(), |
|||
'agwStyle': MDD.DD_MULTIPLE|MDD.DD_DIR_MUST_EXIST |
|||
}) |
|||
MDD.MultiDirDialog.__init__(self, *args, **kwargs) |
|||
|
|||
def GetPaths(self): |
|||
return self.dirCtrl.GetPaths() |
|||
|
|||
def __init__(self, *args, **kwargs): |
|||
BaseMultiFileChooser.__init__(self, MultiDirChooserPayload.MyMultiDirChooser) |
|||
|
|||
|
|||
class TextInputPayload(WidgetPack): |
|||
def __init__(self, no_quoting=False): |
|||
self.widget = None |
|||
self.option_string = None |
|||
self.no_quoting = no_quoting |
|||
|
|||
def build(self, parent, data, choices=None): |
|||
self.widget = wx.TextCtrl(parent) |
|||
dt = FileDrop(self.widget) |
|||
self.widget.SetDropTarget(dt) |
|||
self.widget.SetMinSize((0, -1)) |
|||
self.widget.SetDoubleBuffered(True) |
|||
self.widget.AppendText('') |
|||
return self.widget |
|||
|
|||
def get_value(self): |
|||
return self.widget.GetValue() |
|||
|
|||
|
|||
class TextAreaPayload(WidgetPack): |
|||
def __init__(self, no_quoting=False): |
|||
self.widget = None |
|||
self.option_string = None |
|||
self.no_quoting = no_quoting |
|||
|
|||
def build(self, parent, data, choices=None): |
|||
self.widget = wx.TextCtrl(parent, style=wx.TE_MULTILINE) |
|||
dt = FileDrop(self.widget) |
|||
self.widget.SetDropTarget(dt) |
|||
self.widget.SetMinSize((0, -1)) |
|||
self.widget.SetDoubleBuffered(True) |
|||
self.widget.AppendText('') |
|||
return self.widget |
|||
|
|||
def get_value(self): |
|||
return self.widget.GetValue() |
|||
|
|||
|
|||
class DropdownPayload(WidgetPack): |
|||
default_value = 'Select Option' |
|||
|
|||
def __init__(self, no_quoting=False): |
|||
self.widget = None |
|||
self.option_string = None |
|||
self.no_quoting = no_quoting |
|||
|
|||
def build(self, parent, data, choices=None): |
|||
self.widget = wx.ComboBox( |
|||
parent=parent, |
|||
id=-1, |
|||
value=self.default_value, |
|||
choices=[self.default_value] + choices, |
|||
style=wx.CB_DROPDOWN |
|||
) |
|||
return self.widget |
|||
|
|||
def get_value(self): |
|||
return self.widget.GetValue() |
|||
|
|||
def set_value(self, text): |
|||
self.widget.SetValue(text) |
|||
|
|||
|
|||
class ListboxPayload(WidgetPack): |
|||
default_value = 'Select Option' |
|||
|
|||
def __init__(self, no_quoting=False): |
|||
self.widget = None |
|||
self.option_string = None |
|||
self.no_quoting = no_quoting |
|||
|
|||
def build(self, parent, data, choices=None): |
|||
self.widget = wx.ListBox( |
|||
parent=parent, |
|||
choices=choices, |
|||
size=(-1,60), |
|||
style=wx.LB_MULTIPLE |
|||
) |
|||
return self.widget |
|||
|
|||
def get_value(self): |
|||
return [self.widget.GetString(index) |
|||
for index in self.widget.GetSelections()] |
|||
|
|||
def set_value(self, strings): |
|||
for s in strings: |
|||
self.widget.SetStringSelection(s) |
|||
|
|||
|
|||
class CounterPayload(WidgetPack): |
|||
def __init__(self): |
|||
self.widget = None |
|||
|
|||
def build(self, parent, data, choices=None): |
|||
self.widget = wx.ComboBox( |
|||
parent=parent, |
|||
id=-1, |
|||
value='', |
|||
choices=list(map(str, range(1, 11))), |
|||
style=wx.CB_DROPDOWN |
|||
) |
|||
return self.widget |
|||
|
|||
def get_value(self): |
|||
return self.widget.GetValue() |
|||
|
|||
|
|||
class DirDialog(wx.DirDialog): |
|||
def __init__(self, parent, *args, **kwargs): |
|||
wx.DirDialog.__init__(self, parent, 'Select Directory', style=wx.DD_DEFAULT_STYLE) |
|||
|
|||
|
|||
class PasswordInputPayload(WidgetPack): |
|||
def __init__(self, no_quoting=False): |
|||
self.widget = None |
|||
self.option_string = None |
|||
self.no_quoting = no_quoting |
|||
|
|||
def build(self, parent, data, choices=None): |
|||
self.widget = wx.TextCtrl(parent, style=wx.TE_PASSWORD) |
|||
dt = FileDrop(self.widget) |
|||
self.widget.SetDropTarget(dt) |
|||
self.widget.SetMinSize((0, -1)) |
|||
self.widget.SetDoubleBuffered(True) |
|||
self.widget.AppendText('') |
|||
return self.widget |
|||
|
|||
def get_value(self): |
|||
return self.widget.GetValue() |
|||
|
|||
|
|||
def safe_default(data, default): |
|||
return '' |
|||
|
|||
|
|||
def build_dialog(style, exist_constraint=True, **kwargs): |
|||
if exist_constraint: |
|||
return lambda panel: wx.FileDialog(panel, style=style | wx.FD_FILE_MUST_EXIST, **kwargs) |
|||
else: |
|||
return lambda panel: wx.FileDialog(panel, style=style, **kwargs) |
|||
|
|||
|
|||
def build_subclass(subclass, dialog): |
|||
return type(subclass, (BaseFileChooser,), {'dialog': dialog}) |
|||
|
|||
|
|||
FileSaverPayload = build_subclass('FileSaverPayload', staticmethod(build_dialog(wx.FD_SAVE, False, defaultFile="Enter Filename"))) |
|||
FileChooserPayload = build_subclass('FileChooserPayload', staticmethod(build_dialog(wx.FD_OPEN))) |
|||
DirChooserPayload = build_subclass('DirChooserPayload', DirDialog) |
|||
DateChooserPayload = build_subclass('DateChooserPayload', CalendarDlg) |
@ -1 +0,0 @@ |
|||
__author__ = 'Chris' |
@ -1,114 +0,0 @@ |
|||
""" |
|||
Managed the internal layout for configuration options |
|||
|
|||
@author: Chris |
|||
""" |
|||
|
|||
|
|||
import wx |
|||
from wx.lib.scrolledpanel import ScrolledPanel |
|||
try: |
|||
from itertools import zip_longest |
|||
except ImportError: |
|||
from itertools import izip_longest as zip_longest |
|||
|
|||
from gooey.gui.util import wx_util |
|||
from gooey.gui.lang import i18n |
|||
from gooey.gui.widgets import components |
|||
|
|||
PADDING = 10 |
|||
|
|||
|
|||
class WidgetContainer(wx.Panel): |
|||
''' |
|||
Collection of widgets |
|||
''' |
|||
def __init__(self, parent, section_name, *args, **kwargs): |
|||
wx.Panel.__init__(self, parent, *args, **kwargs) |
|||
self.section_name = section_name |
|||
self.title = None |
|||
self.widgets = [] |
|||
|
|||
self.container = wx.BoxSizer(wx.VERTICAL) |
|||
self.SetSizer(self.container) |
|||
|
|||
def layout(self, num_columns): |
|||
STD_LAYOUT = (0, wx.LEFT | wx.RIGHT | wx.EXPAND, PADDING) |
|||
|
|||
if self.title: |
|||
self.container.Add(wx_util.h0(self, self.title), 0, wx.LEFT | wx.RIGHT, PADDING) |
|||
self.container.AddSpacer(30) |
|||
|
|||
if self.widgets: |
|||
self.container.Add(wx_util.h1(self, self.section_name), 0, wx.LEFT | wx.RIGHT, PADDING) |
|||
self.container.AddSpacer(5) |
|||
self.container.Add(wx_util.horizontal_rule(self), *STD_LAYOUT) |
|||
self.container.AddSpacer(20) |
|||
self.create_component_grid(self.container, self.widgets, cols=num_columns) |
|||
self.container.AddSpacer(10) |
|||
|
|||
def populate(self, widgets, num_columns): |
|||
for index, widget in enumerate(widgets): |
|||
widget_class = getattr(components, widget.type) |
|||
widget_instance = widget_class(self, widget.title, widget.help, widget.choices) |
|||
widget.widget_instance = widget_instance |
|||
self.widgets.append(widget_instance) |
|||
self.layout(num_columns) |
|||
|
|||
def get_values(self): |
|||
return [x.get_value() for x in self.widgets] |
|||
|
|||
def clear(self): |
|||
self.container.Clear(True) |
|||
self.widgets = [] |
|||
|
|||
def create_component_grid(self, parent_sizer, components, cols=2): |
|||
for row in self.chunk(components, cols): |
|||
hsizer = wx.BoxSizer(wx.HORIZONTAL) |
|||
for widget in filter(None, row): |
|||
hsizer.Add(widget.panel, 1, wx.EXPAND | wx.LEFT | wx.RIGHT, 10) |
|||
parent_sizer.Add(hsizer, 0, wx.EXPAND) |
|||
parent_sizer.AddSpacer(20) |
|||
|
|||
def chunk(self, iterable, n, fillvalue=None): |
|||
"itertools recipe: Collect data into fixed-length chunks or blocks" |
|||
args = [iter(iterable)] * n |
|||
return zip_longest(fillvalue=fillvalue, *args) |
|||
|
|||
def __iter__(self): |
|||
return iter(self.widgets) |
|||
|
|||
|
|||
class ConfigPanel(ScrolledPanel): |
|||
|
|||
def __init__(self, parent, req_cols=1, opt_cols=3, title=None, **kwargs): |
|||
ScrolledPanel.__init__(self, parent, **kwargs) |
|||
self.SetupScrolling(scroll_x=False, scrollToTop=False) |
|||
self.SetDoubleBuffered(True) |
|||
|
|||
self.title = title |
|||
self._num_req_cols = req_cols |
|||
self._num_opt_cols = opt_cols |
|||
self.required_section = WidgetContainer(self, i18n._("required_args_msg")) |
|||
self.optional_section = WidgetContainer(self, i18n._("optional_args_msg")) |
|||
|
|||
self._do_layout() |
|||
self.Bind(wx.EVT_SIZE, self.OnResize) |
|||
|
|||
def _do_layout(self): |
|||
STD_LAYOUT = (0, wx.LEFT | wx.RIGHT | wx.EXPAND, PADDING) |
|||
|
|||
container = wx.BoxSizer(wx.VERTICAL) |
|||
container.AddSpacer(15) |
|||
container.Add(self.required_section, *STD_LAYOUT) |
|||
container.Add(self.optional_section, *STD_LAYOUT) |
|||
self.SetSizer(container) |
|||
|
|||
def OnResize(self, evt): |
|||
self.SetupScrolling(scroll_x=False, scrollToTop=False) |
|||
evt.Skip() |
|||
|
|||
def clear(self): |
|||
self.required_section.clear() |
|||
self.optional_section.clear() |
|||
|
@ -1,229 +0,0 @@ |
|||
''' |
|||
Created on Jan 19, 2014 |
|||
@author: Chris |
|||
''' |
|||
|
|||
import sys |
|||
|
|||
import wx |
|||
|
|||
from gooey.gui import image_repository, events |
|||
from gooey.gui.lang.i18n import _ |
|||
from gooey.gui.pubsub import pub |
|||
from gooey.gui.util import wx_util |
|||
from gooey.gui.windows import footer, header, layouts |
|||
from gooey.gui.windows.runtime_display_panel import RuntimeDisplay |
|||
|
|||
YES = 5103 |
|||
NO = 5104 |
|||
|
|||
class BaseWindow(wx.Frame): |
|||
''' |
|||
Primary Frame under which all sub-Panels are organized. |
|||
''' |
|||
|
|||
def __init__(self, layout_type): |
|||
wx.Frame.__init__(self, parent=None, id=-1) |
|||
|
|||
self.SetDoubleBuffered(True) |
|||
|
|||
# type of gui to render |
|||
self.layout_type = layout_type |
|||
|
|||
# Components |
|||
self.icon = None |
|||
self.head_panel = None |
|||
self.config_panel = None |
|||
self.runtime_display = None |
|||
self.foot_panel = None |
|||
self.panels = None |
|||
|
|||
self._init_properties() |
|||
self._init_components() |
|||
self._do_layout() |
|||
|
|||
self.Bind(wx.EVT_SIZE, self.onResize) |
|||
self.Bind(wx.EVT_CLOSE, self.onClose) |
|||
|
|||
@property |
|||
def window_size(self): |
|||
return self.GetSize() |
|||
|
|||
@window_size.setter |
|||
def window_size(self, size_tuple): |
|||
self.SetSize(size_tuple) |
|||
|
|||
@property |
|||
def window_title(self): |
|||
return self.GetTitle() |
|||
|
|||
@window_title.setter |
|||
def window_title(self, title): |
|||
self.SetTitle(title) |
|||
|
|||
@property |
|||
def heading_title(self): |
|||
return self.head_panel.title |
|||
|
|||
@heading_title.setter |
|||
def heading_title(self, text): |
|||
self.head_panel.title = text |
|||
|
|||
@property |
|||
def heading_subtitle(self): |
|||
return self.head_panel.subtitle |
|||
|
|||
@heading_subtitle.setter |
|||
def heading_subtitle(self, text): |
|||
self.head_panel.subtitle = text |
|||
|
|||
@property |
|||
def required_section(self): |
|||
return self.config_panel.main_content.required_section |
|||
|
|||
@property |
|||
def optional_section(self): |
|||
return self.config_panel.main_content.optional_section |
|||
|
|||
@property |
|||
def progress_bar(self): |
|||
return self.foot_panel.progress_bar |
|||
|
|||
def set_list_contents(self, contents): |
|||
self.config_panel.sidebar.set_list_contents(contents) |
|||
|
|||
def set_display_font_style(self, style): |
|||
# TODO: make this not stupid |
|||
# TODO: _actual_ font support |
|||
self.runtime_display.set_font_style( |
|||
wx.MODERN if style == 'monospace' else wx.DEFAULT) |
|||
|
|||
def _init_properties(self): |
|||
# self.SetTitle(self.build_spec['program_name']) |
|||
# self.SetSize(self.build_spec['default_size']) |
|||
# # self.SetMinSize((400, 300)) |
|||
self.icon = wx.Icon(image_repository.program_icon, wx.BITMAP_TYPE_ICO) |
|||
self.SetIcon(self.icon) |
|||
|
|||
def _init_components(self): |
|||
self.runtime_display = RuntimeDisplay(self) |
|||
self.head_panel = header.FrameHeader(parent=self) |
|||
self.foot_panel = footer.Footer(self) |
|||
self.panels = [self.head_panel, self.config_panel, self.foot_panel] |
|||
|
|||
def _do_layout(self): |
|||
sizer = wx.BoxSizer(wx.VERTICAL) |
|||
sizer.Add(self.head_panel, 0, wx.EXPAND) |
|||
sizer.Add(wx_util.horizontal_rule(self), 0, wx.EXPAND) |
|||
|
|||
if self.layout_type == layouts.COLUMN: |
|||
self.config_panel = layouts.ColumnLayout(self) |
|||
else: |
|||
self.config_panel = layouts.FlatLayout(self) |
|||
|
|||
sizer.Add(self.config_panel, 1, wx.EXPAND) |
|||
|
|||
sizer.Add(self.runtime_display, 1, wx.EXPAND) |
|||
|
|||
self.runtime_display.Hide() |
|||
sizer.Add(wx_util.horizontal_rule(self), 0, wx.EXPAND) |
|||
sizer.Add(self.foot_panel, 0, wx.EXPAND) |
|||
self.SetSizer(sizer) |
|||
|
|||
self.sizer = sizer |
|||
|
|||
def freeze(self): |
|||
self.Freeze() |
|||
|
|||
def thaw(self): |
|||
self.Thaw() |
|||
|
|||
def enable_stop_button(self): |
|||
self.foot_panel.stop_button.Enable() |
|||
|
|||
def disable_stop_button(self): |
|||
self.foot_panel.stop_button.Disable() |
|||
|
|||
def show(self, *args): |
|||
''' |
|||
Looks up the attribute across all available |
|||
panels and calls `Show()` |
|||
''' |
|||
self._set_visibility('Show', *args) |
|||
|
|||
def hide(self, *args): |
|||
''' |
|||
Looks up the attribute across all available |
|||
panels and calls `Show()` |
|||
''' |
|||
self._set_visibility('Hide', *args) |
|||
|
|||
def _set_visibility(self, action, *args): |
|||
''' |
|||
Checks for the existence `attr` on a given `panel` and |
|||
performs `action` if found |
|||
''' |
|||
def _set_visibility(obj, attrs): |
|||
for attr in attrs: |
|||
if hasattr(obj, attr): |
|||
instance = getattr(obj, attr) |
|||
getattr(instance, action)() |
|||
instance.Layout() |
|||
for panel in [self, self.head_panel, self.foot_panel, self.config_panel]: |
|||
_set_visibility(panel, args) |
|||
|
|||
def hide_all_buttons(self): |
|||
self.foot_panel.hide_all_buttons() |
|||
|
|||
def update_console_async(self, msg): |
|||
wx.CallAfter(self.runtime_display.append_text, msg) |
|||
|
|||
def update_progress_aync(self, progress, disable_animation=False): |
|||
wx.CallAfter(self.UpdateProgressBar, progress, disable_animation) |
|||
|
|||
def onResize(self, evt): |
|||
evt.Skip() |
|||
|
|||
def onClose(self, evt): |
|||
if evt.CanVeto(): |
|||
evt.Veto() |
|||
pub.send_message(str(events.WINDOW_CLOSE)) |
|||
|
|||
def UpdateProgressBar(self, value, disable_animation=False): |
|||
pb = self.foot_panel.progress_bar |
|||
if value is None: |
|||
return |
|||
if value < 0: |
|||
pb.Pulse() |
|||
else: |
|||
value = min(int(value), pb.GetRange()) |
|||
if pb.GetValue() != value: |
|||
# Windows 7 progress bar animation hack |
|||
# http://stackoverflow.com/questions/5332616/disabling-net-progressbar-animation-when-changing-value |
|||
if disable_animation and sys.platform.startswith("win"): |
|||
if pb.GetRange() == value: |
|||
pb.SetValue(value) |
|||
pb.SetValue(value-1) |
|||
else: |
|||
pb.SetValue(value+1) |
|||
pb.SetValue(value) |
|||
|
|||
def show_dialog(self, title, content, style): |
|||
dlg = wx.MessageDialog(None, content, title, style) |
|||
result = dlg.ShowModal() |
|||
dlg.Destroy() |
|||
return result |
|||
|
|||
def show_missing_args_dialog(self): |
|||
self.show_dialog(_('error_title'), _('error_required_fields'), wx.ICON_ERROR) |
|||
|
|||
def confirm_exit_dialog(self): |
|||
result = self.show_dialog(_('sure_you_want_to_exit'), _('close_program'), wx.YES_NO) |
|||
return result == YES |
|||
|
|||
def confirm_stop_dialog(self): |
|||
result = self.show_dialog(_('sure_you_want_to_stop'), _('stop_task'), wx.YES_NO) |
|||
return result == YES |
|||
|
|||
if __name__ == '__main__': |
|||
pass |
@ -1,112 +0,0 @@ |
|||
''' |
|||
Created on Dec 23, 2013 |
|||
|
|||
@author: Chris |
|||
''' |
|||
|
|||
import wx |
|||
|
|||
from gooey.gui.pubsub import pub |
|||
|
|||
from gooey.gui.lang import i18n |
|||
from gooey.gui import imageutil, events |
|||
|
|||
|
|||
class Footer(wx.Panel): |
|||
''' |
|||
Footer section used on the configuration |
|||
screen of the application |
|||
|
|||
args: |
|||
parent: wxPython parent windows |
|||
controller: controller class used in delagating all the commands |
|||
''' |
|||
|
|||
def __init__(self, parent, **kwargs): |
|||
wx.Panel.__init__(self, parent, **kwargs) |
|||
self.SetMinSize((30, 53)) |
|||
|
|||
# components |
|||
self.cancel_button = None |
|||
self.start_button = None |
|||
self.progress_bar = None |
|||
self.close_button = None |
|||
self.stop_button = None |
|||
self.restart_button = None |
|||
self.buttons = None |
|||
|
|||
self.layouts = {} |
|||
|
|||
self._init_components() |
|||
self._do_layout() |
|||
|
|||
for button in self.buttons: |
|||
self.Bind(wx.EVT_BUTTON, self.dispatch_click, button) |
|||
|
|||
|
|||
def _init_components(self): |
|||
self.cancel_button = self.button(i18n._('cancel'), wx.ID_CANCEL, event_id=int(events.WINDOW_CANCEL)) |
|||
self.stop_button = self.button(i18n._('stop'), wx.ID_OK, event_id=int(events.WINDOW_STOP)) |
|||
self.start_button = self.button(i18n._('start'), wx.ID_OK, event_id=int(events.WINDOW_START)) |
|||
self.close_button = self.button(i18n._("close"), wx.ID_OK, event_id=int(events.WINDOW_CLOSE)) |
|||
self.restart_button = self.button(i18n._('restart'), wx.ID_OK, event_id=int(events.WINDOW_RESTART)) |
|||
self.edit_button = self.button(i18n._('edit'), wx.ID_OK, event_id=int(events.WINDOW_EDIT)) |
|||
|
|||
self.progress_bar = wx.Gauge(self, range=100) |
|||
|
|||
self.buttons = [self.cancel_button, self.start_button, |
|||
self.stop_button, self.close_button, |
|||
self.restart_button, self.edit_button] |
|||
|
|||
def _do_layout(self): |
|||
self.stop_button.Hide() |
|||
self.restart_button.Hide() |
|||
|
|||
v_sizer = wx.BoxSizer(wx.VERTICAL) |
|||
h_sizer = wx.BoxSizer(wx.HORIZONTAL) |
|||
|
|||
h_sizer.Add(self.progress_bar, 0, wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 20) |
|||
self.progress_bar.Hide() |
|||
|
|||
h_sizer.AddStretchSpacer(1) |
|||
h_sizer.Add(self.cancel_button, 0, wx.ALIGN_RIGHT | wx.RIGHT, 20) |
|||
h_sizer.Add(self.start_button, 0, wx.ALIGN_RIGHT | wx.RIGHT, 20) |
|||
h_sizer.Add(self.stop_button, 0, wx.ALIGN_RIGHT | wx.RIGHT, 20) |
|||
|
|||
v_sizer.AddStretchSpacer(1) |
|||
v_sizer.Add(h_sizer, 0, wx.ALIGN_CENTER_VERTICAL | wx.EXPAND) |
|||
|
|||
h_sizer.Add(self.edit_button, 0, wx.ALIGN_RIGHT | wx.RIGHT, 10) |
|||
h_sizer.Add(self.restart_button, 0, wx.ALIGN_RIGHT | wx.RIGHT, 10) |
|||
h_sizer.Add(self.close_button, 0, wx.ALIGN_RIGHT | wx.RIGHT, 20) |
|||
self.edit_button.Hide() |
|||
self.restart_button.Hide() |
|||
self.close_button.Hide() |
|||
|
|||
v_sizer.AddStretchSpacer(1) |
|||
self.SetSizer(v_sizer) |
|||
|
|||
def button(self, label=None, style=None, event_id=-1): |
|||
return wx.Button( |
|||
parent=self, |
|||
id=event_id, |
|||
size=(90, 24), |
|||
label=label, |
|||
style=style) |
|||
|
|||
def dispatch_click(self, event): |
|||
pub.send_message(str(event.GetId())) |
|||
event.Skip() |
|||
|
|||
def hide_all_buttons(self): |
|||
for button in self.buttons: |
|||
button.Hide() |
|||
|
|||
def _load_image(self, img_path, height=70): |
|||
return imageutil.resize_bitmap(self, imageutil._load_image(img_path), height) |
|||
|
|||
|
|||
|
|||
|
|||
|
|||
|
@ -1,88 +0,0 @@ |
|||
''' |
|||
Created on Dec 23, 2013 |
|||
|
|||
@author: Chris |
|||
''' |
|||
|
|||
import wx |
|||
|
|||
from gooey.gui import imageutil, image_repository |
|||
from gooey.gui.util import wx_util |
|||
|
|||
|
|||
PAD_SIZE = 10 |
|||
|
|||
|
|||
class FrameHeader(wx.Panel): |
|||
def __init__(self, parent=None, **kwargs): |
|||
wx.Panel.__init__(self, parent, **kwargs) |
|||
self.SetDoubleBuffered(True) |
|||
|
|||
self._header = None |
|||
self._subheader = None |
|||
self.settings_img = None |
|||
self.running_img = None |
|||
self.check_mark = None |
|||
self.error_symbol = None |
|||
|
|||
self._init_properties() |
|||
self._init_components() |
|||
self._do_layout() |
|||
|
|||
@property |
|||
def title(self): |
|||
return self._header.GetLabel() |
|||
|
|||
@title.setter |
|||
def title(self, text): |
|||
self._header.SetLabel(text) |
|||
|
|||
@property |
|||
def subtitle(self): |
|||
return self._subheader.GetLabel() |
|||
|
|||
@subtitle.setter |
|||
def subtitle(self, text): |
|||
self._subheader.SetLabel(text) |
|||
|
|||
def _init_properties(self): |
|||
self.SetBackgroundColour('#ffffff') |
|||
self.SetSize((30, 90)) |
|||
self.SetMinSize((120, 80)) |
|||
|
|||
def _init_components(self): |
|||
self._header = wx_util.h1(self, '') |
|||
self._subheader = wx.StaticText(self, label='') |
|||
|
|||
self.settings_img = self._load_image(image_repository.config_icon, height=79) |
|||
self.running_img = self._load_image(image_repository.running_icon, 79) |
|||
self.check_mark = self._load_image(image_repository.success_icon, height=75) |
|||
self.error_symbol = self._load_image(image_repository.error_icon, height=75) |
|||
|
|||
|
|||
def _do_layout(self): |
|||
vsizer = wx.BoxSizer(wx.VERTICAL) |
|||
sizer = wx.BoxSizer(wx.HORIZONTAL) |
|||
headings_sizer = self.build_heading_sizer() |
|||
sizer.Add(headings_sizer, 1, wx.ALIGN_LEFT | wx.ALIGN_CENTER_HORIZONTAL | wx.EXPAND | wx.LEFT, PAD_SIZE) |
|||
sizer.Add(self.settings_img, 0, wx.ALIGN_RIGHT | wx.EXPAND | wx.RIGHT, PAD_SIZE) |
|||
sizer.Add(self.running_img, 0, wx.ALIGN_RIGHT | wx.EXPAND | wx.RIGHT, PAD_SIZE) |
|||
sizer.Add(self.check_mark, 0, wx.ALIGN_RIGHT | wx.EXPAND | wx.RIGHT, PAD_SIZE) |
|||
sizer.Add(self.error_symbol, 0, wx.ALIGN_RIGHT | wx.EXPAND | wx.RIGHT, PAD_SIZE) |
|||
self.running_img.Hide() |
|||
self.check_mark.Hide() |
|||
self.error_symbol.Hide() |
|||
vsizer.Add(sizer, 1, wx.EXPAND) |
|||
self.SetSizer(vsizer) |
|||
|
|||
def _load_image(self, img_path, height=70): |
|||
return imageutil.resize_bitmap(self, imageutil._load_image(img_path), height) |
|||
|
|||
def build_heading_sizer(self): |
|||
sizer = wx.BoxSizer(wx.VERTICAL) |
|||
sizer.AddStretchSpacer(1) |
|||
sizer.Add(self._header, 0) |
|||
sizer.Add(self._subheader, 0) |
|||
sizer.AddStretchSpacer(1) |
|||
return sizer |
|||
|
@ -1,53 +0,0 @@ |
|||
import wx |
|||
|
|||
from gooey.gui.util import wx_util |
|||
from gooey.gui.windows.advanced_config import ConfigPanel |
|||
from gooey.gui.windows.sidebar import Sidebar |
|||
|
|||
basic_config = { |
|||
'widgets': [{ |
|||
'type': 'CommandField', |
|||
'required': True, |
|||
'data': { |
|||
'display_name': 'Enter Commands', |
|||
'help': 'Enter command line arguments', |
|||
'nargs': '', |
|||
'commands': '', |
|||
'choices': [], |
|||
'default': None, |
|||
} |
|||
}], |
|||
} |
|||
|
|||
|
|||
FLAT = 'standard' |
|||
COLUMN = 'column' |
|||
|
|||
|
|||
class FlatLayout(wx.Panel): |
|||
def __init__(self, *args, **kwargs): |
|||
super(FlatLayout, self).__init__(*args, **kwargs) |
|||
self.SetDoubleBuffered(True) |
|||
|
|||
self.main_content = ConfigPanel(self, opt_cols=3) |
|||
|
|||
sizer = wx.BoxSizer(wx.HORIZONTAL) |
|||
sizer.Add(self.main_content, 3, wx.EXPAND) |
|||
self.SetSizer(sizer) |
|||
|
|||
|
|||
class ColumnLayout(wx.Panel): |
|||
def __init__(self, *args, **kwargs): |
|||
super(ColumnLayout, self).__init__(*args, **kwargs) |
|||
self.SetDoubleBuffered(True) |
|||
|
|||
self.sidebar = Sidebar(self) |
|||
self.main_content = ConfigPanel(self, opt_cols=2) |
|||
|
|||
sizer = wx.BoxSizer(wx.HORIZONTAL) |
|||
sizer.Add(self.sidebar, 1, wx.EXPAND) |
|||
sizer.Add(wx_util.vertical_rule(self), 0, wx.EXPAND) |
|||
sizer.Add(self.main_content, 3, wx.EXPAND) |
|||
self.SetSizer(sizer) |
|||
|
|||
|
@ -1,52 +0,0 @@ |
|||
''' |
|||
Created on Dec 23, 2013 |
|||
|
|||
@author: Chris |
|||
''' |
|||
|
|||
import wx |
|||
from gooey.gui.lang import i18n |
|||
|
|||
from gooey.gui.three_to_four import Constants |
|||
|
|||
|
|||
class RuntimeDisplay(wx.Panel): |
|||
''' |
|||
Textbox displayed during the client program's execution. |
|||
''' |
|||
def __init__(self, parent, **kwargs): |
|||
wx.Panel.__init__(self, parent, **kwargs) |
|||
self._init_properties() |
|||
self._init_components() |
|||
self._do_layout() |
|||
|
|||
|
|||
def set_font_style(self, style): |
|||
pointsize = self.cmd_textbox.GetFont().GetPointSize() |
|||
font = wx.Font(pointsize, style, |
|||
Constants.WX_FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD, False) |
|||
self.cmd_textbox.SetFont(font) |
|||
|
|||
def _init_properties(self): |
|||
self.SetBackgroundColour('#F0F0F0') |
|||
|
|||
def _init_components(self): |
|||
self.text = wx.StaticText(self, label=i18n._("status")) |
|||
|
|||
self.cmd_textbox = wx.TextCtrl( |
|||
self, -1, "", |
|||
style=wx.TE_MULTILINE | wx.TE_READONLY | wx.TE_RICH) |
|||
|
|||
|
|||
def _do_layout(self): |
|||
sizer = wx.BoxSizer(wx.VERTICAL) |
|||
sizer.AddSpacer(10) |
|||
sizer.Add(self.text, 0, wx.LEFT, 30) |
|||
sizer.AddSpacer(10) |
|||
sizer.Add(self.cmd_textbox, 1, wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, 30) |
|||
sizer.AddSpacer(20) |
|||
self.SetSizer(sizer) |
|||
|
|||
def append_text(self, txt): |
|||
self.cmd_textbox.AppendText(txt) |
|||
|
@ -1,45 +0,0 @@ |
|||
import wx |
|||
from gooey.gui.pubsub import pub |
|||
from gooey.gui import events |
|||
|
|||
from gooey.gui.util import wx_util |
|||
|
|||
|
|||
class Sidebar(wx.Panel): |
|||
|
|||
def __init__(self, parent, *args, **kwargs): |
|||
super(Sidebar, self).__init__(parent, *args, **kwargs) |
|||
self.SetDoubleBuffered(True) |
|||
|
|||
self._parent = parent |
|||
|
|||
self._do_layout() |
|||
|
|||
def _do_layout(self): |
|||
self.SetDoubleBuffered(True) |
|||
self.SetBackgroundColour('#f2f2f2') |
|||
self.SetSize((180, 0)) |
|||
self.SetMinSize((180, 0)) |
|||
|
|||
STD_LAYOUT = (0, wx.LEFT | wx.RIGHT | wx.EXPAND, 10) |
|||
|
|||
container = wx.BoxSizer(wx.VERTICAL) |
|||
container.AddSpacer(15) |
|||
container.Add(wx_util.h1(self, 'Actions'), *STD_LAYOUT) |
|||
container.AddSpacer(5) |
|||
self.listbox = wx.ListBox(self, -1) |
|||
container.Add(self.listbox, 1, wx.LEFT | wx.RIGHT | wx.EXPAND, 10) |
|||
container.AddSpacer(20) |
|||
self.SetSizer(container) |
|||
|
|||
self.Bind(wx.EVT_LISTBOX, self.selection_change, self.listbox) |
|||
|
|||
def set_list_contents(self, contents): |
|||
self.listbox.AppendItems(contents) |
|||
self.listbox.SetSelection(0) |
|||
|
|||
def selection_change(self, evt): |
|||
pub.send_message( |
|||
events.LIST_BOX, |
|||
selection=self.listbox.GetItems()[self.listbox.GetSelection()]) |
|||
evt.Skip() |
@ -1,6 +0,0 @@ |
|||
|
|||
|
|||
CONFIG_SCREEN = 'config' |
|||
RUNNING_SCREEN = 'running' |
|||
SUCCESS_SCREEN = 'success' |
|||
ERROR_SCREEN = 'error' |
@ -1,67 +0,0 @@ |
|||
import argparse |
|||
import pytest |
|||
|
|||
@pytest.fixture |
|||
def empty_parser(): |
|||
return argparse.ArgumentParser(description='description') |
|||
|
|||
|
|||
@pytest.fixture |
|||
def complete_parser(): |
|||
parser = argparse.ArgumentParser(description='description') |
|||
parser.add_argument("req1", help='filename help msg') # positional |
|||
parser.add_argument("req2", help="Name of the file where you'll save the output") # positional |
|||
parser.add_argument('-r', dest="req3", default=10, type=int, help='sets the time to count down from', required=True) |
|||
parser.add_argument('--req4', dest="req4", default=10, type=int, help='sets the time to count down from', required=True) |
|||
|
|||
parser.add_argument("-a", "--aa", action="store_true", help="aaa") |
|||
parser.add_argument("-b", "--bb", action="store_true", help="bbb") |
|||
parser.add_argument('-c', '--cc', action='count') |
|||
parser.add_argument("-d", "--dd", action="store_true", help="ddd") |
|||
parser.add_argument('-e', '--ee', choices=['yes', 'no'], help='eee') |
|||
parser.add_argument("-f", "--ff", default="0000", help="fff") |
|||
parser.add_argument("-g", "--gg", action="store_true", help="ggg") |
|||
verbosity = parser.add_mutually_exclusive_group() |
|||
verbosity.add_argument('-i', '--ii', action="store_true", help="iii") |
|||
verbosity.add_argument('-j', '--jj', action="store_true", help="hhh") |
|||
return parser |
|||
|
|||
|
|||
@pytest.fixture |
|||
def subparser(): |
|||
parser = argparse.ArgumentParser(description='qidev') |
|||
parser.add_argument('--verbose', help='be verbose', dest='verbose', action='store_true', default=False) |
|||
subs = parser.add_subparsers(help='commands', dest='command') |
|||
|
|||
config_parser = subs.add_parser('config', help='configure defaults for qidev') |
|||
config_parser.add_argument('field', help='the field to configure', type=str) |
|||
config_parser.add_argument('value', help='set field to value', type=str) |
|||
|
|||
# ######################################################## |
|||
connect_parser = subs.add_parser('connect', help='connect to a robot (ip/hostname)') |
|||
connect_parser.add_argument('hostname', help='hostname or IP address of the robot', type=str) |
|||
|
|||
# ######################################################## |
|||
install_parser = subs.add_parser('install', help='package and install a project directory on a robot') |
|||
install_parser.add_argument('path', help='path to the project directory (containing manifest.xml', type=str) |
|||
install_parser.add_argument('--ip', nargs='*', type=str, dest='ip', help='specify hostname(es)/IP address(es)') |
|||
return parser |
|||
|
|||
|
|||
@pytest.fixture |
|||
def exclusive_group(): |
|||
parser = argparse.ArgumentParser(description='description') |
|||
verbosity = parser.add_mutually_exclusive_group() |
|||
verbosity.add_argument('-i', dest="option1", action="store_true", help="iii") |
|||
verbosity.add_argument('-j', dest="option2", action="store_true", help="hhh") |
|||
|
|||
mutually_exclusive_group = [mutex_action |
|||
for group_actions in parser._mutually_exclusive_groups |
|||
for mutex_action in group_actions._group_actions] |
|||
return mutually_exclusive_group |
|||
|
|||
|
|||
@pytest.fixture |
|||
def expected_attrs(): |
|||
return ('program_icon', 'success_icon', 'running_icon', |
|||
'loading_icon', 'config_icon', 'error_icon') |
@ -1,58 +0,0 @@ |
|||
''' |
|||
Image Repository acts as a funky dynamic singlton module. |
|||
''' |
|||
import os |
|||
import pytest |
|||
import tempfile |
|||
|
|||
|
|||
def test_variable_names_are_pushed_to_module_scope(expected_attrs): |
|||
''' |
|||
The dynamically initialized Globals() should contain the expected images at runtime |
|||
''' |
|||
from gooey.gui import image_repository |
|||
assert all((attr in image_repository.__dict__) for attr in expected_attrs) |
|||
|
|||
|
|||
def test_patch_returns_error_on_invalid_dir(): |
|||
''' |
|||
patch should explode with a helpful message if it |
|||
cannot find the supplied directory |
|||
''' |
|||
from gooey.gui import image_repository |
|||
|
|||
with pytest.raises(IOError) as kaboom: |
|||
image_repository.patch_images('foo/bar/not/a/path') |
|||
|
|||
# our error |
|||
assert ' user supplied' in str(kaboom.value) |
|||
assert 'foo/bar/not/a/path' in str(kaboom.value) |
|||
|
|||
|
|||
def test_module_scope_is_updated_on_patch(expected_attrs): |
|||
''' |
|||
Patch should update the module's globals() on success |
|||
''' |
|||
from gooey.gui import image_repository |
|||
testing_icons = ('config_icon.png', 'success_icon.png') |
|||
try: |
|||
# setup |
|||
make_user_files(*testing_icons) |
|||
old_icon = image_repository.config_icon |
|||
# load up our new icon(s) |
|||
image_repository.patch_images(tempfile.tempdir) |
|||
new_icon = image_repository.config_icon |
|||
assert old_icon != new_icon |
|||
finally: |
|||
cleanup_temp(*testing_icons) |
|||
|
|||
|
|||
# helpers |
|||
def make_user_files(*filenames): |
|||
for filename in filenames: |
|||
with open(os.path.join(tempfile.gettempdir(), filename), 'w') as f: |
|||
f.write('temp') |
|||
|
|||
def cleanup_temp(*filenames): |
|||
for filename in filenames: |
|||
os.remove(os.path.join(tempfile.gettempdir(), filename)) |
@ -1,89 +0,0 @@ |
|||
import sys |
|||
|
|||
import pytest |
|||
import wx |
|||
from mock import MagicMock |
|||
|
|||
from gooey.gui.lang import i18n |
|||
from gooey.gui.model import MyModel |
|||
from gooey.gui.presenter import Presenter |
|||
from gooey.gui.util.freeze import get_resource_path |
|||
from gooey.python_bindings import config_generator |
|||
|
|||
|
|||
@pytest.fixture |
|||
def build_spec(complete_parser): |
|||
return config_generator.create_from_parser(complete_parser, sys.argv[0]) |
|||
|
|||
@pytest.fixture |
|||
def build_spec_subparser(subparser): |
|||
return config_generator.create_from_parser(subparser, sys.argv[0]) |
|||
|
|||
@pytest.fixture |
|||
def presentation_model(build_spec): |
|||
app = wx.App(False) |
|||
i18n.load(get_resource_path('languages'), build_spec['language']) |
|||
model = MyModel(build_spec) |
|||
view = MagicMock() |
|||
presentation = Presenter(view, model) |
|||
return presentation |
|||
|
|||
@pytest.fixture |
|||
def subparser_presentation_model(build_spec_subparser): |
|||
app = wx.App(False) |
|||
i18n.load(get_resource_path('languages'), 'english') |
|||
model = MyModel(build_spec_subparser) |
|||
view = MagicMock() |
|||
presentation = Presenter(view, model) |
|||
return presentation |
|||
|
|||
|
|||
|
|||
|
|||
# ---------------------------- |
|||
# Tests # |
|||
# ---------------------------- |
|||
|
|||
def test_presentation_init(presentation_model): |
|||
'''Sanity check that the primary fields are set on init ''' |
|||
presentation = presentation_model |
|||
presentation.initialize_view() |
|||
assert presentation.view.heading_title == presentation.model.heading_title |
|||
assert presentation.view.heading_subtitle == presentation.model.heading_subtitle |
|||
assert presentation.view.required_section.populate.called |
|||
assert presentation.view.optional_section.populate.called |
|||
# should not call when not running in column format |
|||
assert not presentation.view.set_list_contents.called |
|||
|
|||
def test_subparser_presentation_init_sets_sidebar(subparser_presentation_model): |
|||
presentation = subparser_presentation_model |
|||
presentation.initialize_view() |
|||
# should be called to initialize the sidebar |
|||
assert presentation.view.set_list_contents.called |
|||
|
|||
def test_on_start_shows_err_dlg_if_missing_args(presentation_model): |
|||
presentation = presentation_model |
|||
presentation.initialize_view() |
|||
presentation.on_start() |
|||
assert presentation.view.show_missing_args_dialog.called |
|||
presentation.view.show_missing_args_dialog.reset_mock() |
|||
|
|||
# the inverse: |
|||
# fill the missing args |
|||
for arg in presentation.model.required_args: |
|||
arg.value = 'foo' |
|||
# patch the methods we don't need |
|||
presentation.client_runner = MagicMock() |
|||
presentation.update_model = MagicMock() |
|||
presentation.model.build_command_line_string = MagicMock() |
|||
|
|||
presentation.on_start() |
|||
# should no longer show the dialog |
|||
assert not presentation.view.show_missing_args_dialog.called |
|||
|
|||
|
|||
|
|||
|
|||
|
|||
|
|||
|
@ -1,227 +0,0 @@ |
|||
{ |
|||
"show_advanced": true, |
|||
"language": "english", |
|||
"manual_start": false, |
|||
"optionals_cols": 3, |
|||
"required": [ |
|||
{ |
|||
"data": { |
|||
"nargs": "", |
|||
"commands": [], |
|||
"display_name": "FileChooser", |
|||
"help": "Name of the file you want to process", |
|||
"choices": [] |
|||
}, |
|||
"type": "FileChooser" |
|||
}, |
|||
{ |
|||
"data": { |
|||
"nargs": "", |
|||
"commands": [], |
|||
"display_name": "DirectoryChooser", |
|||
"help": "Name of the file you want to process", |
|||
"choices": [] |
|||
}, |
|||
"type": "DirChooser" |
|||
}, |
|||
{ |
|||
"data": { |
|||
"nargs": "", |
|||
"commands": [], |
|||
"display_name": "FileSaver", |
|||
"help": "Name of the file you want to process", |
|||
"choices": [] |
|||
}, |
|||
"type": "FileSaver" |
|||
}, |
|||
{ |
|||
"data": { |
|||
"nargs": "", |
|||
"commands": [], |
|||
"display_name": "MultiFileSaver", |
|||
"help": "Name of the file you want to process", |
|||
"choices": [] |
|||
}, |
|||
"type": "MultiFileChooser" |
|||
}, |
|||
{ |
|||
"data": { |
|||
"nargs": "", |
|||
"commands": [], |
|||
"display_name": "directory", |
|||
"help": "Directory to store output", |
|||
"choices": [] |
|||
}, |
|||
"type": "TextField" |
|||
} |
|||
], |
|||
"requireds_cols": 3, |
|||
"show_config": true, |
|||
"default_size": [ |
|||
610, |
|||
530 |
|||
], |
|||
"optional": [ |
|||
{ |
|||
"data": { |
|||
"nargs": "", |
|||
"commands": [ |
|||
"-r", |
|||
"--recursive" |
|||
], |
|||
"display_name": "recursive", |
|||
"help": "Recurse into subfolders", |
|||
"choices": [ |
|||
"yes", |
|||
"no" |
|||
] |
|||
}, |
|||
"type": "Dropdown" |
|||
}, |
|||
{ |
|||
"data": { |
|||
"nargs": "", |
|||
"commands": [ |
|||
"-c", |
|||
"--countdown" |
|||
], |
|||
"display_name": "countdown", |
|||
"help": "sets the time to count down from you see its quite simple!", |
|||
"choices": [] |
|||
}, |
|||
"type": "TextField" |
|||
}, |
|||
{ |
|||
"data": { |
|||
"nargs": "", |
|||
"commands": [ |
|||
"-j", |
|||
"--cron-schedule" |
|||
], |
|||
"display_name": "cron_schedule", |
|||
"help": "Set the datetime when the cron should begin", |
|||
"choices": [] |
|||
}, |
|||
"type": "DateChooser" |
|||
}, |
|||
{ |
|||
"data": { |
|||
"nargs": "", |
|||
"commands": [ |
|||
"-w", |
|||
"--writelog" |
|||
], |
|||
"display_name": "writelog", |
|||
"help": "write log to some file or something", |
|||
"choices": [] |
|||
}, |
|||
"type": "TextField" |
|||
}, |
|||
{ |
|||
"data": { |
|||
"nargs": "", |
|||
"commands": [ |
|||
"-v", |
|||
"--verbose" |
|||
], |
|||
"display_name": "verbose", |
|||
"help": null, |
|||
"choices": [] |
|||
}, |
|||
"type": "Counter", |
|||
"choices": [ |
|||
0, |
|||
1, |
|||
2, |
|||
3, |
|||
4, |
|||
5, |
|||
6, |
|||
7, |
|||
8, |
|||
9 |
|||
] |
|||
}, |
|||
{ |
|||
"data": [ |
|||
{ |
|||
"nargs": "", |
|||
"commands": [ |
|||
"-t", |
|||
"--verbozze" |
|||
], |
|||
"display_name": "verbose", |
|||
"help": "Show more details", |
|||
"choices": null |
|||
}, |
|||
{ |
|||
"nargs": "", |
|||
"commands": [ |
|||
"-q", |
|||
"--quiet" |
|||
], |
|||
"display_name": "quiet", |
|||
"help": "Only output on error", |
|||
"choices": null |
|||
} |
|||
], |
|||
"type": "RadioGroup", |
|||
"group_name": "Choose Option" |
|||
}, |
|||
{ |
|||
"data": { |
|||
"nargs": "", |
|||
"commands": [ |
|||
"-s", |
|||
"--showtime" |
|||
], |
|||
"display_name": "showtime", |
|||
"help": "display the countdown timer", |
|||
"choices": [] |
|||
}, |
|||
"type": "CheckBox" |
|||
}, |
|||
{ |
|||
"data": { |
|||
"nargs": "", |
|||
"commands": [ |
|||
"-d", |
|||
"--delay" |
|||
], |
|||
"display_name": "delay", |
|||
"help": "Delay execution for a bit", |
|||
"choices": [] |
|||
}, |
|||
"type": "CheckBox" |
|||
}, |
|||
{ |
|||
"data": { |
|||
"nargs": "", |
|||
"commands": [ |
|||
"-o", |
|||
"--obfuscate" |
|||
], |
|||
"display_name": "obfuscate", |
|||
"help": "obfuscate the countdown timer!", |
|||
"choices": [] |
|||
}, |
|||
"type": "CheckBox" |
|||
}, |
|||
{ |
|||
"data": { |
|||
"nargs": "", |
|||
"commands": [ |
|||
"-e", |
|||
"--expandAll" |
|||
], |
|||
"display_name": "expandAll", |
|||
"help": "expand all processes", |
|||
"choices": [] |
|||
}, |
|||
"type": "CheckBox" |
|||
} |
|||
], |
|||
"program_name": "widget_demo", |
|||
"program_description": "Example application to show Gooey's various widgetz", |
|||
"target": "python C:\\Users\\Chris\\Dropbox\\pretty_gui\\Gooey\\gooey\\examples\\widget_demo.py" |
|||
} |
@ -1,2 +0,0 @@ |
|||
[pytest] |
|||
python_files=*unittest*.py |
@ -1 +0,0 @@ |
|||
0.9.5.2 |
Write
Preview
Loading…
Cancel
Save