From 6c4c087f7ae3dd0df1942c7f132f8ae2dfd18c70 Mon Sep 17 00:00:00 2001 From: chriskiehl Date: Sat, 26 Dec 2015 20:59:17 -0500 Subject: [PATCH] Decoupling views --- gooey/gui/widgets/choosers.py | 283 ++++++++++----------- gooey/gui/widgets/components.py | 162 ++++++------ gooey/gui/widgets/widget_pack.py | 124 +++++---- gooey/gui/windows/advanced_config.py | 94 ++++--- gooey/gui/windows/base_window.py | 136 ++++++++-- gooey/gui/windows/footer.py | 37 ++- gooey/gui/windows/header.py | 87 ++++--- gooey/gui/windows/layouts.py | 9 +- gooey/gui/windows/runtime_display_panel.py | 15 +- 9 files changed, 541 insertions(+), 406 deletions(-) diff --git a/gooey/gui/widgets/choosers.py b/gooey/gui/widgets/choosers.py index 8468ca3..9b969e7 100644 --- a/gooey/gui/widgets/choosers.py +++ b/gooey/gui/widgets/choosers.py @@ -1,143 +1,140 @@ -from gooey.gui.lang import i18n - -__author__ = 'Chris' - -import wx - -from gooey.gui.util import wx_util -from gooey.gui.widgets.calender_dialog import CalendarDlg - - -class AbstractChooser(object): - def __init__(self, data): - self.data = data - - # parent - self.panel = None - - self.button_text = i18n._('browse') - - # Widgets - self.title = None - self.help_msg = None - self.text_box = None - self.button = None - self.panel = None - - def build(self, parent): - return self.do_layout(parent) - - def do_layout(self, parent): - self.panel = wx.Panel(parent) - 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.button = wx.Button(self.panel, label=self.button_text, size=(73, 23)) - - vertical_container = wx.BoxSizer(wx.VERTICAL) - widget_sizer = wx.BoxSizer(wx.HORIZONTAL) - - 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) - - widget_sizer.Add(self.text_box, 1, wx.EXPAND) - widget_sizer.AddSpacer(10) - widget_sizer.Add(self.button, 0) - - vertical_container.Add(widget_sizer, 0, wx.EXPAND) - self.panel.SetSizer(vertical_container) - - self.panel.Bind(wx.EVT_SIZE, self.OnResize) - self.panel.Bind(wx.EVT_BUTTON, self.on_button, self.button) - return self.panel - - def CreateHelpMsgWidget(self, parent): - base_text = wx.StaticText(parent, label=self.data['help_msg']) - # if self.data['nargs']: - # base_text.SetLabelText(base_text.GetLabelText() + self.CreateNargsMsg(action)) - wx_util.dark_grey(base_text) - return base_text - - def CreateNameLabelWidget(self, parent): - label = self.data['title'].title() - text = wx.StaticText(parent, label=label) - wx_util.make_bold(text) - return text - - 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) - evt.Skip() - - def on_button(self, evt): - raise NotImplementedError - - - -class FileChooser(AbstractChooser): - def __init__(self, data): - AbstractChooser.__init__(self, data) - - def on_button(self, evt): - dlg = wx.FileDialog(self.panel, 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.SetLabelText(result) - - -class DirectoryChooser(AbstractChooser): - def __init__(self, data): - AbstractChooser.__init__(self, data) - - def on_button(self, evt): - dlg = wx.DirDialog(self.panel, '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 CalendarChooser(AbstractChooser): - def __init__(self, data): - AbstractChooser.__init__(self, data) - self.button_text = 'Choose Date' - - def on_button(self, evt): - dlg = CalendarDlg(self.panel) - dlg.ShowModal() - if dlg.GetPath(): - self.text_box.SetLabelText(dlg.GetPath()) - - - - - - - - - - - - - - - - - +# import wx +# +# from gooey.gui.lang import i18n +# from gooey.gui.util import wx_util +# from gooey.gui.widgets.calender_dialog import CalendarDlg +# +# +# class AbstractChooser(object): +# def __init__(self, data): +# self.data = data +# +# # parent +# self.panel = None +# +# self.button_text = i18n._('browse') +# +# # Widgets +# self.title = None +# self.help_msg = None +# self.text_box = None +# self.button = None +# self.panel = None +# +# def build(self, parent): +# return self.do_layout(parent) +# +# def do_layout(self, parent): +# self.panel = wx.Panel(parent) +# 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.button = wx.Button(self.panel, label=self.button_text, size=(73, 23)) +# +# vertical_container = wx.BoxSizer(wx.VERTICAL) +# widget_sizer = wx.BoxSizer(wx.HORIZONTAL) +# +# 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) +# +# widget_sizer.Add(self.text_box, 1, wx.EXPAND) +# widget_sizer.AddSpacer(10) +# widget_sizer.Add(self.button, 0) +# +# vertical_container.Add(widget_sizer, 0, wx.EXPAND) +# self.panel.SetSizer(vertical_container) +# +# self.panel.Bind(wx.EVT_SIZE, self.OnResize) +# self.panel.Bind(wx.EVT_BUTTON, self.on_button, self.button) +# return self.panel +# +# def CreateHelpMsgWidget(self, parent): +# base_text = wx.StaticText(parent, label=self.data['help_msg']) +# # if self.data['nargs']: +# # base_text.SetLabelText(base_text.GetLabelText() + self.CreateNargsMsg(action)) +# wx_util.dark_grey(base_text) +# return base_text +# +# def CreateNameLabelWidget(self, parent): +# label = self.data['title'].title() +# text = wx.StaticText(parent, label=label) +# wx_util.make_bold(text) +# return text +# +# 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) +# evt.Skip() +# +# def on_button(self, evt): +# raise NotImplementedError +# +# +# +# class FileChooser(AbstractChooser): +# def __init__(self, data): +# AbstractChooser.__init__(self, data) +# +# def on_button(self, evt): +# dlg = wx.FileDialog(self.panel, 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.SetLabelText(result) +# +# +# class DirectoryChooser(AbstractChooser): +# def __init__(self, data): +# AbstractChooser.__init__(self, data) +# +# def on_button(self, evt): +# dlg = wx.DirDialog(self.panel, '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 CalendarChooser(AbstractChooser): +# def __init__(self, data): +# AbstractChooser.__init__(self, data) +# self.button_text = 'Choose Date' +# +# def on_button(self, evt): +# dlg = CalendarDlg(self.panel) +# dlg.ShowModal() +# if dlg.GetPath(): +# self.text_box.SetLabelText(dlg.GetPath()) +# +# +# +# +# +# +# +# +# +# +# +# +# +# +# +# +# diff --git a/gooey/gui/widgets/components.py b/gooey/gui/widgets/components.py index 3521c26..7867775 100644 --- a/gooey/gui/widgets/components.py +++ b/gooey/gui/widgets/components.py @@ -1,3 +1,5 @@ +from functools import partial + import wx from gooey.gui.util import wx_util @@ -5,34 +7,34 @@ from gooey.gui.widgets import widget_pack class BaseGuiComponent(object): - def __init__(self, data, widget_pack): + + widget_class = None + + def __init__(self, parent, title, msg): ''' :param data: field info (title, help, etc..) :param widget_pack: internal wxWidgets to render ''' - self.data = data - # parent - self.panel = None + self.parent = parent # Widgets self.title = None self.help_msg = None - # Internal WidgetPack - self.widget_pack = widget_pack + # Internal WidgetPack set in subclasses - def build(self, parent): - return self.do_layout(parent) + self.do_layout(parent, title, msg) - def do_layout(self, parent): + def do_layout(self, parent, title, msg): self.panel = wx.Panel(parent) + self.widget_pack = self.widget_class() - self.title = self.createTitle(self.panel) - self.help_msg = self.createHelpMsgWidget(self.panel) + 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.data) + core_widget_set = self.widget_pack.build(self.panel, {}) vertical_container = wx.BoxSizer(wx.VERTICAL) @@ -48,31 +50,30 @@ class BaseGuiComponent(object): 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']) - base_text = wx.StaticText(parent, label=label_text or '') + 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 createTitle(self, parent): - text = wx.StaticText(parent, label=self.data['display_name'].title()) + def format_title(self, parent, title): + text = wx.StaticText(parent, label=title) wx_util.make_bold(text) return text - def formatExtendedHelpMsg(self, data): - base_text = data.get('help', '') - 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.panel.Freeze() @@ -97,39 +98,32 @@ class BaseGuiComponent(object): def GetValue(self): return self.widget_pack.getValue() - def HasOptionString(self): - return bool(self.widget_pack.option_string) - - def _GetWidget(self): - # used only for unittesting - return self.widget_pack.widget + # def HasOptionString(self): + # return bool(self.widget_pack.option_string) + # + # def _GetWidget(self): + # # used only for unittesting + # return self.widget_pack.widget def __repr__(self): return self.__class__.__name__ class CheckBox(BaseGuiComponent): - def __init__(self, data, widget_pack=None): - BaseGuiComponent.__init__(self, data, widget_pack) - - self.widget = None - # data - self.option_strings = data['commands'][0] - self.default_value = bool(data['default']) - def build(self, parent): - return self.do_layout(parent) + def __init__(self, parent, title, msg): + BaseGuiComponent.__init__(self, parent, title, msg) - def do_layout(self, parent): + 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.createTitle(self.panel) - self.help_msg = self.createHelpMsgWidget(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())) + # 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) @@ -144,9 +138,6 @@ class CheckBox(BaseGuiComponent): self.panel.Bind(wx.EVT_SIZE, self.onResize) return self.panel - def onSetter(self, evt): - self.GetValue() - def onResize(self, evt): msg = self.help_msg container_width, _ = self.panel.Size @@ -157,37 +148,35 @@ class CheckBox(BaseGuiComponent): msg.Wrap(container_width) evt.Skip() - def GetValue(self): - return self.option_strings if self.widget.GetValue() else '' - - def HasOptionString(self): - return bool(self.option_strings) - - def _GetWidget(self): - return self.widget + # def GetValue(self): + # return self.option_strings if self.widget.GetValue() else '' + # + # def HasOptionString(self): + # return bool(self.option_strings) + # + # def _GetWidget(self): + # return self.widget class RadioGroup(object): - def __init__(self, data): + def __init__(self, parent, title, msg): self.panel = None - self.data = data + # self.data = data self.radio_buttons = [] self.option_strings = [] self.help_msgs = [] self.btn_names = [] - def build(self, parent): - return self.do_layout(parent) + self.do_layout(parent, title, msg) - def do_layout(self, parent): + def do_layout(self, parent, titles, msgs): self.panel = wx.Panel(parent) - self.radio_buttons = [wx.RadioButton(self.panel, -1) for _ in self.data] - self.btn_names = [wx.StaticText(self.panel, label=btn_data['display_name'].title()) for btn_data in self.data] - self.help_msgs = [wx.StaticText(self.panel, label=btn_data['help'].title()) for btn_data in self.data] - self.option_strings = [btn_data['commands'] for btn_data in self.data] + 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='') @@ -208,10 +197,13 @@ class RadioGroup(object): self.panel.SetSizer(vertical_container) self.panel.Bind(wx.EVT_SIZE, self.onResize) + self.panel.Bind(wx.EVT_RADIOBUTTON, self.showz) return self.panel - def onSetter(self, evt): - self.GetValue() + def showz(self, evt): + print evt + for i in self.radio_buttons: + print i.GetValue() def onResize(self, evt): msg = self.help_msgs[0] @@ -237,14 +229,22 @@ class RadioGroup(object): return self.radio_buttons -FileChooser = lambda data: BaseGuiComponent(data=data, widget_pack=widget_pack.FileChooserPayload()) -MultiFileChooser = lambda data: BaseGuiComponent(data=data, widget_pack=widget_pack.MultiFileSaverPayload()) -DirChooser = lambda data: BaseGuiComponent(data=data, widget_pack=widget_pack.DirChooserPayload()) -FileSaver = lambda data: BaseGuiComponent(data=data, widget_pack=widget_pack.FileSaverPayload()) -DateChooser = lambda data: BaseGuiComponent(data=data, widget_pack=widget_pack.DateChooserPayload()) -TextField = lambda data: BaseGuiComponent(data=data, widget_pack=widget_pack.TextInputPayload()) -CommandField = lambda data: BaseGuiComponent(data=data, widget_pack=widget_pack.TextInputPayload(no_quoting=True)) -Dropdown = lambda data: BaseGuiComponent(data=data, widget_pack=widget_pack.DropdownPayload()) -Counter = lambda data: BaseGuiComponent(data=data, widget_pack=widget_pack.CounterPayload()) -MultiDirChooser = lambda data: BaseGuiComponent(data=data, widget_pack=widget_pack.MultiDirChooserPayload()) +def build_subclass(name, widget_class): + 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) +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) + +if __name__ == '__main__': + DirChooser = type('DirChooser', (BaseGuiComponent,), {'widget_pack': widget_pack.DirChooserPayload }) + d = DirChooser() diff --git a/gooey/gui/widgets/widget_pack.py b/gooey/gui/widgets/widget_pack.py index d566a16..7473263 100644 --- a/gooey/gui/widgets/widget_pack.py +++ b/gooey/gui/widgets/widget_pack.py @@ -25,16 +25,12 @@ class WidgetPack(object): def build(self, parent, data): pass - @abstractmethod - def getValue(self): - pass - def onResize(self, evt): pass @staticmethod def get_command(data): - return data['commands'][0] if data['commands'] else '' + return '' @staticmethod def disable_quoting(data): @@ -45,7 +41,7 @@ class WidgetPack(object): class BaseChooser(WidgetPack): - def __init__(self, button_text=''): + def __init__(self): self.button_text = i18n._('browse') self.option_string = None self.parent = None @@ -70,24 +66,24 @@ class BaseChooser(WidgetPack): parent.Bind(wx.EVT_BUTTON, self.onButton, self.button) return widget_sizer - def getValue(self): - value = self.text_box.GetValue() - if self.option_string and value: - return '{0} {1}'.format(self.option_string, quote(value)) - else: - return quote(value) if value else '' - - def onButton(self, evt): - raise NotImplementedError + # def getValue(self): + # value = self.text_box.GetValue() + # if self.option_string and value: + # return '{0} {1}'.format(self.option_string, quote(value)) + # else: + # return quote(value) if value else '' + # + # def onButton(self, evt): + # raise NotImplementedError def __repr__(self): return self.__class__.__name__ class BaseFileChooser(BaseChooser): - def __init__(self, dialog): + dialog = None + def __init__(self): BaseChooser.__init__(self) - self.dialog = dialog def onButton(self, evt): dlg = self.dialog(self.parent) @@ -103,31 +99,38 @@ class BaseFileChooser(BaseChooser): class BaseMultiFileChooser(BaseFileChooser): def __init__(self, dialog): - BaseFileChooser.__init__(self, dialog) + BaseFileChooser.__init__(self) + self.dialog = dialog - def getValue(self): - value = ' '.join(quote(x) for x in self.text_box.GetValue().split(os.pathsep) if x) - if self.option_string and value: - return '{} {}'.format(self.option_string, value) - return value or '' + # def getValue(self): + # value = ' '.join(quote(x) for x in self.text_box.GetValue().split(os.pathsep) if x) + # if self.option_string and value: + # return '{} {}'.format(self.option_string, value) + # return value or '' def get_path(self, dlg): return os.pathsep.join(dlg.GetPaths()) - -class MyMultiDirChooser(MDD.MultiDirDialog): +class MultiFileSaverPayload(BaseMultiFileChooser): def __init__(self, *args, **kwargs): - super(MyMultiDirChooser, self).__init__(*args, **kwargs) + BaseMultiFileChooser.__init__(self, build_dialog(wx.FD_MULTIPLE, False)) - def GetPaths(self): - return self.dirCtrl.GetPaths() +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) -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) class TextInputPayload(WidgetPack): @@ -148,20 +151,20 @@ class TextInputPayload(WidgetPack): self.widget.AppendText(safe_default(data, '')) return self.widget - def getValue(self): - if self.no_quoting: - _quote = lambda value: value - else: - _quote = quote - value = self.widget.GetValue() - if 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 - self.widget.SetLabelText(text) + # def getValue(self): + # if self.no_quoting: + # _quote = lambda value: value + # else: + # _quote = quote + # value = self.widget.GetValue() + # if 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 + # self.widget.SetLabelText(text) class DropdownPayload(WidgetPack): @@ -180,7 +183,7 @@ class DropdownPayload(WidgetPack): parent=parent, id=-1, value=safe_default(data, self.default_value), - choices=data['choices'], + choices=[], style=wx.CB_DROPDOWN ) return self.widget @@ -233,15 +236,26 @@ class CounterPayload(WidgetPack): repeated_args = arg * int(dropdown_value) return '-' + repeated_args +class DirDialog(wx.DirDialog): + def __init__(self, parent, *args, **kwargs): + wx.DirDialog.__init__(self, parent, 'Select Directory', style=wx.DD_DEFAULT_STYLE) + def safe_default(data, default): # str(None) is 'None'!? Whaaaaat...? - return str(data['default']) if data['default'] else default + # return str(data['default']) if data['default'] else 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}) -FileChooserPayload = partial(BaseFileChooser, dialog=build_dialog(wx.FD_OPEN)) -FileSaverPayload = partial(BaseFileChooser, dialog=build_dialog(wx.FD_SAVE, False, defaultFile="Enter Filename")) -MultiFileSaverPayload = partial(BaseMultiFileChooser, dialog=build_dialog(wx.FD_MULTIPLE, False)) -DirChooserPayload = partial(BaseFileChooser, dialog=lambda parent: wx.DirDialog(parent, 'Select Directory', style=wx.DD_DEFAULT_STYLE)) -DateChooserPayload = partial(BaseFileChooser, dialog=CalendarDlg) -MultiDirChooserPayload = partial(BaseMultiFileChooser, dialog=lambda parent: MyMultiDirChooser(parent, title="Select Directories", defaultPath=os.getcwd(), agwStyle=MDD.DD_MULTIPLE|MDD.DD_DIR_MUST_EXIST)) +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) diff --git a/gooey/gui/windows/advanced_config.py b/gooey/gui/windows/advanced_config.py index 79fac14..65005ce 100644 --- a/gooey/gui/windows/advanced_config.py +++ b/gooey/gui/windows/advanced_config.py @@ -12,13 +12,58 @@ from itertools import chain, izip_longest from gooey.gui.util import wx_util from gooey.gui.lang import i18n -from gooey.gui import component_builder from gooey.gui.option_reader import OptionReader - +from gooey.gui.widgets import components PADDING = 10 +class WidgetContainer(wx.Panel): + 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 populate(self, widgets): + for w in widgets: + widget_class = getattr(components, w.type) + self.widgets.append(widget_class(self, w.title, w.msg)) + self.layout() + + def layout(self): + 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=2) + self.container.AddSpacer(10) + + 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" + # grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx + args = [iter(iterable)] * n + return izip_longest(fillvalue=fillvalue, *args) + + class ConfigPanel(ScrolledPanel, OptionReader): def __init__(self, parent, widgets=None, req_cols=1, opt_cols=3, title=None, **kwargs): @@ -29,11 +74,12 @@ class ConfigPanel(ScrolledPanel, OptionReader): self.title = title - self.widgets = component_builder.build_components(widgets) - 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) @@ -44,41 +90,11 @@ class ConfigPanel(ScrolledPanel, OptionReader): container = wx.BoxSizer(wx.VERTICAL) container.AddSpacer(15) - - if self.title: - container.Add(wx_util.h0(self, self.title), 0, wx.LEFT | wx.RIGHT, PADDING) - container.AddSpacer(30) - - if self.widgets.required_args: - container.Add(wx_util.h1(self, i18n._("required_args_msg")), 0, wx.LEFT | wx.RIGHT, PADDING) - container.AddSpacer(5) - container.Add(wx_util.horizontal_rule(self), *STD_LAYOUT) - container.AddSpacer(20) - - self.CreateComponentGrid(container, self.widgets.required_args, cols=self._num_req_cols) - - container.AddSpacer(10) - - if self.widgets.optional_args: - # container.AddSpacer(10) - container.Add(wx_util.h1(self, i18n._("optional_args_msg")), 0, wx.LEFT | wx.RIGHT, PADDING) - container.AddSpacer(5) - container.Add(wx_util.horizontal_rule(self), *STD_LAYOUT) - container.AddSpacer(20) - - self.CreateComponentGrid(container, self.widgets.optional_args, cols=self._num_opt_cols) + container.Add(self.required_section, *STD_LAYOUT) + container.Add(self.optional_section, *STD_LAYOUT) self.SetSizer(container) - def CreateComponentGrid(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.build(self), 1, wx.EXPAND | wx.LEFT | wx.RIGHT, 10) - # hsizer.FitInside(parent_sizer) - parent_sizer.Add(hsizer, 0, wx.EXPAND) - parent_sizer.AddSpacer(20) - def OnResize(self, evt): self.SetupScrolling(scroll_x=False, scrollToTop=False) evt.Skip() @@ -97,11 +113,5 @@ class ConfigPanel(ScrolledPanel, OptionReader): def GetRequiredArgs(self): return [arg.GetValue() for arg in self.widgets.required_args] - def chunk(self, iterable, n, fillvalue=None): - "itertools recipe: Collect data into fixed-length chunks or blocks" - # grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx - args = [iter(iterable)] * n - return izip_longest(fillvalue=fillvalue, *args) - if __name__ == '__main__': pass diff --git a/gooey/gui/windows/base_window.py b/gooey/gui/windows/base_window.py index 643d4a4..e01cff8 100644 --- a/gooey/gui/windows/base_window.py +++ b/gooey/gui/windows/base_window.py @@ -4,6 +4,7 @@ Created on Jan 19, 2014 ''' import sys +from distutils import config import wx from gooey.gui.pubsub import pub @@ -17,13 +18,16 @@ from gooey.gui.windows import footer, header, layouts class BaseWindow(wx.Frame): - def __init__(self, build_spec): + def __init__(self, build_spec, layout_type): wx.Frame.__init__(self, parent=None, id=-1) self.build_spec = build_spec self.SetDoubleBuffered(True) + # type of gui to render + self.layout_type = layout_type + # Components self.icon = None self.head_panel = None @@ -39,28 +43,122 @@ class BaseWindow(wx.Frame): 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 + + + def set_display_font_style(self, style): + ''' + wx.FONTFAMILY_DEFAULT Chooses a default font. + wx.FONTFAMILY_DECORATIVE A decorative font. + wx.FONTFAMILY_ROMAN A formal, serif font. + wx.FONTFAMILY_SCRIPT A handwriting font. + wx.FONTFAMILY_SWISS A sans-serif font. + wx.FONTFAMILY_MODERN Usually a fixed pitch font. + wx.FONTFAMILY_TELETYPE A teletype font. + ''' + # 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.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 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 _init_components(self): # init gui - _desc = self.build_spec['program_description'] - self.head_panel = header.FrameHeader( - heading=i18n._("settings_title"), - subheading=_desc or '', - parent=self) - - self.runtime_display = RuntimeDisplay(self, self.build_spec) + # _desc = self.build_spec['program_description'] + # self.head_panel = header.FrameHeader( + # heading=i18n._("settings_title"), + # subheading=_desc or '', + # parent=self) + self.runtime_display = RuntimeDisplay(self) + self.head_panel = header.FrameHeader(parent=self) self.foot_panel = footer.Footer(self) - if self.build_spec['disable_stop_button']: - self.foot_panel.stop_button.Disable() - else: - self.foot_panel.stop_button.Enable() + # if self.build_spec['disable_stop_button']: + # self.foot_panel.stop_button.Disable() + # else: + # self.foot_panel.stop_button.Enable() self.panels = [self.head_panel, self.config_panel, self.foot_panel] @@ -69,12 +167,12 @@ class BaseWindow(wx.Frame): sizer.Add(self.head_panel, 0, wx.EXPAND) sizer.Add(wx_util.horizontal_rule(self), 0, wx.EXPAND) - if self.build_spec['layout_type'] == 'column': - self.config_panel = layouts.ColumnLayout(self, build_spec=self.build_spec) - sizer.Add(self.config_panel, 1, wx.EXPAND) + if self.layout_type == layouts.COLUMN: + self.config_panel = layouts.ColumnLayout(self) else: self.config_panel = layouts.FlatLayout(self, build_spec=self.build_spec) - sizer.Add(self.config_panel, 1, wx.EXPAND) + + sizer.Add(self.config_panel, 1, wx.EXPAND) sizer.Add(self.runtime_display, 1, wx.EXPAND) diff --git a/gooey/gui/windows/footer.py b/gooey/gui/windows/footer.py index 2b37754..ad5ffba 100644 --- a/gooey/gui/windows/footer.py +++ b/gooey/gui/windows/footer.py @@ -13,9 +13,14 @@ from gooey.gui.lang import i18n from gooey.gui import imageutil, events -class AbstractFooter(wx.Panel): +class Footer(wx.Panel): ''' - Abstract class for the Footer panels. + 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): @@ -39,6 +44,9 @@ class AbstractFooter(wx.Panel): pub.subscribe(self.load_view, events.WINDOW_CHANGE) + 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)) @@ -50,7 +58,13 @@ class AbstractFooter(wx.Panel): 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] + self.buttons = [self.cancel_button, self.start_button, + self.stop_button, self.close_button, + self.restart_button, self.edit_button] + + def dispatch_click(self, event): + pub.send_message(str(event.GetId())) + event.Skip() def _init_pages(self): def config(): @@ -126,24 +140,7 @@ class AbstractFooter(wx.Panel): return imageutil.resize_bitmap(self, imageutil._load_image(img_path), height) -class Footer(AbstractFooter): - ''' - 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): - AbstractFooter.__init__(self, parent, **kwargs) - for button in self.buttons: - self.Bind(wx.EVT_BUTTON, self.dispatch_click, button) - def dispatch_click(self, event): - pub.send_message(str(event.GetId())) - event.Skip() diff --git a/gooey/gui/windows/header.py b/gooey/gui/windows/header.py index 3dbca02..30dd817 100644 --- a/gooey/gui/windows/header.py +++ b/gooey/gui/windows/header.py @@ -16,43 +16,56 @@ PAD_SIZE = 10 class FrameHeader(wx.Panel): - def __init__(self, heading, subheading, **kwargs): - wx.Panel.__init__(self, **kwargs) + def __init__(self, parent=None, **kwargs): + wx.Panel.__init__(self, parent, **kwargs) self.SetDoubleBuffered(True) - self.heading_msg = heading - self.subheading_msg = subheading - self._header = None self._subheader = None - self._settings_img = None - self._running_img = None - self._check_mark = None - self._error_symbol = None + self.settings_img = None + self.running_img = None + self.check_mark = None + self.error_symbol = None self.layouts = {} self._init_properties() - self._init_components(heading, subheading) + # self._init_components(heading, subheading) + self._init_components() self._init_pages() self._do_layout() pub.subscribe(self.load_view, events.WINDOW_CHANGE) + @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, heading, subheading): - self._header = wx_util.h1(self, heading) - - self._subheader = wx.StaticText(self, label=subheading) + 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) + 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): @@ -60,13 +73,13 @@ class FrameHeader(wx.Panel): 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() + 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) @@ -86,33 +99,33 @@ class FrameHeader(wx.Panel): def config(): self._header.SetLabel(self.heading_msg) self._subheader.SetLabel(self.subheading_msg) - self._settings_img.Show() - self._check_mark.Hide() - self._running_img.Hide() - self._error_symbol.Hide() + self.settings_img.Show() + self.check_mark.Hide() + self.running_img.Hide() + self.error_symbol.Hide() self.Layout() def running(): self._header.SetLabel(i18n._("running_title")) self._subheader.SetLabel(i18n._('running_msg')) - self._check_mark.Hide() - self._settings_img.Hide() - self._running_img.Show() - self._error_symbol.Hide() + self.check_mark.Hide() + self.settings_img.Hide() + self.running_img.Show() + self.error_symbol.Hide() self.Layout() def success(): self._header.SetLabel(i18n._('finished_title')) self._subheader.SetLabel(i18n._('finished_msg')) - self._running_img.Hide() - self._check_mark.Show() + self.running_img.Hide() + self.check_mark.Show() self.Layout() def error(): self._header.SetLabel(i18n._('finished_title')) self._subheader.SetLabel(i18n._('finished_error')) - self._running_img.Hide() - self._error_symbol.Show() + self.running_img.Hide() + self.error_symbol.Show() self.Layout() self.layouts = locals() diff --git a/gooey/gui/windows/layouts.py b/gooey/gui/windows/layouts.py index 721b579..ceca1a4 100644 --- a/gooey/gui/windows/layouts.py +++ b/gooey/gui/windows/layouts.py @@ -25,13 +25,18 @@ basic_config = { } +FLAT = 'standard' +COLUMN = 'column' + + class FlatLayout(wx.Panel): def __init__(self, *args, **kwargs): - self._build_spec = kwargs.pop('build_spec') + self._build_spec = kwargs.pop('build_spec', None) super(FlatLayout, self).__init__(*args, **kwargs) self.SetDoubleBuffered(True) - self.main_content = ConfigPanel(self, widgets=self._build_spec['widgets'], opt_cols=self._build_spec['num_optional_cols']) + # self.main_content = ConfigPanel(self, widgets=self._build_spec['widgets'], opt_cols=self._build_spec['num_optional_cols']) + self.main_content = ConfigPanel(self, widgets=self._build_spec['widgets'], opt_cols=3) sizer = wx.BoxSizer(wx.HORIZONTAL) sizer.Add(self.main_content, 3, wx.EXPAND) diff --git a/gooey/gui/windows/runtime_display_panel.py b/gooey/gui/windows/runtime_display_panel.py index 94e67ba..5680665 100644 --- a/gooey/gui/windows/runtime_display_panel.py +++ b/gooey/gui/windows/runtime_display_panel.py @@ -12,13 +12,19 @@ class RuntimeDisplay(wx.Panel): ''' Textbox displayed during the client program's execution. ''' - def __init__(self, parent, build_spec, **kwargs): + def __init__(self, parent, **kwargs): wx.Panel.__init__(self, parent, **kwargs) - self.build_spec = build_spec 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, + wx.FONTWEIGHT_NORMAL, wx.FONTWEIGHT_BOLD, False) + self.cmd_textbox.SetFont(font) + def _init_properties(self): self.SetBackgroundColour('#F0F0F0') @@ -29,11 +35,6 @@ class RuntimeDisplay(wx.Panel): self, -1, "", style=wx.TE_MULTILINE | wx.TE_READONLY | wx.TE_RICH) - if self.build_spec.get('monospace_display'): - pointsize = self.cmd_textbox.GetFont().GetPointSize() - font = wx.Font(pointsize, wx.FONTFAMILY_MODERN, - wx.FONTWEIGHT_NORMAL, wx.FONTWEIGHT_BOLD, False) - self.cmd_textbox.SetFont(font) def _do_layout(self): sizer = wx.BoxSizer(wx.VERTICAL)