mirror of https://github.com/chriskiehl/Gooey.git
Browse Source
Fixed issue in widget_pack that returned None for positional args. Minor code cleanup
pull/90/merge
Fixed issue in widget_pack that returned None for positional args. Minor code cleanup
pull/90/merge
6 changed files with 16 additions and 491 deletions
Unified View
Diff Options
-
464gooey/gui/components.py
-
12gooey/gui/controller.py
-
5gooey/gui/settings.py
-
8gooey/gui/widgets/widget_pack.py
-
12gooey/gui/windows/advanced_config.py
-
6gooey/gui/windows/base_window.py
@ -1,464 +0,0 @@ |
|||||
''' |
|
||||
Created on Jan 1, 2014 |
|
||||
|
|
||||
@author: Chris |
|
||||
|
|
||||
TODO: |
|
||||
Sanitize all GetValue inputs |
|
||||
(to check that there's actual data there. |
|
||||
''' |
|
||||
|
|
||||
import wx |
|
||||
from abc import ABCMeta |
|
||||
from abc import abstractmethod |
|
||||
from gooey.gui import styling |
|
||||
|
|
||||
EMPTY = '' |
|
||||
|
|
||||
|
|
||||
class BuildException(RuntimeError): |
|
||||
pass |
|
||||
|
|
||||
|
|
||||
class AbstractGuiComponent(object): |
|
||||
''' |
|
||||
Template pattern-y abstract class for the gui. |
|
||||
Children must all implement the BuildWidget and getValue |
|
||||
methods. |
|
||||
''' |
|
||||
__metaclass__ = ABCMeta |
|
||||
|
|
||||
def __init__(self): |
|
||||
self._widget = None |
|
||||
self.msg = EMPTY |
|
||||
|
|
||||
def Build(self, parent): |
|
||||
self._widget = self.BuildInputWidget(parent, self._action) |
|
||||
if self.HasHelpMsg(self._action): |
|
||||
self._msg = self.CreateHelpMsgWidget(parent, self._action) |
|
||||
else: |
|
||||
self._msg = None |
|
||||
|
|
||||
sizer = wx.BoxSizer(wx.VERTICAL) |
|
||||
sizer.Add(self.CreateNameLabelWidget(parent, self._action)) |
|
||||
sizer.AddSpacer(2) |
|
||||
|
|
||||
if self._msg: |
|
||||
sizer.Add(self._msg, 0, wx.EXPAND) |
|
||||
sizer.AddSpacer(2) |
|
||||
else: |
|
||||
sizer.AddStretchSpacer(1) |
|
||||
|
|
||||
sizer.AddStretchSpacer(1) |
|
||||
sizer.Add(self._widget, 0, wx.EXPAND) |
|
||||
return sizer |
|
||||
|
|
||||
@abstractmethod |
|
||||
def BuildInputWidget(self, parent, action): |
|
||||
''' Must construct the main widget type for the Action ''' |
|
||||
pass |
|
||||
|
|
||||
|
|
||||
def HasHelpMsg(self, action): |
|
||||
return action.help is not None |
|
||||
|
|
||||
|
|
||||
def CreateHelpMsgWidget(self, parent, action): |
|
||||
base_text = wx.StaticText(parent, label=action.help) |
|
||||
if self.HasNargs(action): |
|
||||
base_text.SetLabelText(base_text.GetLabelText() + self.CreateNargsMsg(action)) |
|
||||
styling.MakeDarkGrey(base_text) |
|
||||
return base_text |
|
||||
|
|
||||
def HasNargs(self, action): |
|
||||
return action.nargs is not None and action.nargs is not 0 |
|
||||
|
|
||||
|
|
||||
def CreateNargsMsg(self, action): |
|
||||
if isinstance(action.nargs, int): |
|
||||
return '\n(Note: exactly {0} arguments are required)'.format(action.nargs) |
|
||||
elif action.nargs == '+': |
|
||||
return '\n(Note: at least 1 or more arguments are required)' |
|
||||
return '' |
|
||||
|
|
||||
|
|
||||
def CreateNameLabelWidget(self, parent, action): |
|
||||
label = str(action.dest).title() |
|
||||
if len(action.option_strings) > 1: |
|
||||
label += ' (%s)' % action.option_strings[0] |
|
||||
text = wx.StaticText(parent, label=label) |
|
||||
styling.MakeBold(text) |
|
||||
return text |
|
||||
|
|
||||
|
|
||||
def AssertInitialization(self, clsname): |
|
||||
if not self._widget: |
|
||||
raise BuildException('%s was not correctly initialized' % clsname) |
|
||||
|
|
||||
|
|
||||
def __str__(self): |
|
||||
return str(self._action) |
|
||||
|
|
||||
|
|
||||
@abstractmethod |
|
||||
def GetValue(self): |
|
||||
''' Returns the state of the given widget ''' |
|
||||
pass |
|
||||
|
|
||||
|
|
||||
def Update(self, size): |
|
||||
''' |
|
||||
Manually word wraps the StaticText help objects which would |
|
||||
otherwise not wrap on resize |
|
||||
|
|
||||
Content area is based on each grid having two equally sized |
|
||||
columns, where the content area is defined as 87% of the halved |
|
||||
windows width. The wiggle room is the distance +- 10% of the |
|
||||
content_area. |
|
||||
|
|
||||
Wrap calculation is run only when the size of the help_msg |
|
||||
extends outside of the wiggle_room. This was done to avoid |
|
||||
the "flickering" that comes from constantly resizing a |
|
||||
StaticText object. |
|
||||
''' |
|
||||
if self._msg is None: |
|
||||
return |
|
||||
help_msg = self._msg |
|
||||
width, height = size |
|
||||
content_area = int((width / 2) * .87) |
|
||||
|
|
||||
wiggle_room = range(int(content_area - content_area * .05), int(content_area + content_area * .05)) |
|
||||
if help_msg.Size[0] not in wiggle_room: |
|
||||
self._msg.SetLabel(self._msg.GetLabelText().replace('\n', ' ')) |
|
||||
self._msg.Wrap(content_area) |
|
||||
|
|
||||
|
|
||||
|
|
||||
class AbstractComponent(object): |
|
||||
''' |
|
||||
Template pattern-y abstract class for the gui. |
|
||||
Children must all implement the BuildWidget and getValue |
|
||||
methods. |
|
||||
''' |
|
||||
__metaclass__ = ABCMeta |
|
||||
|
|
||||
def __init__(self): |
|
||||
self._widget = None |
|
||||
self.msg = EMPTY |
|
||||
|
|
||||
def Build(self, parent): |
|
||||
self._widget = self.BuildInputWidget(parent, self._action) |
|
||||
if self.HasHelpMsg(self._action): |
|
||||
self._msg = self.CreateHelpMsgWidget(parent, self._action) |
|
||||
else: |
|
||||
self._msg = None |
|
||||
|
|
||||
sizer = wx.BoxSizer(wx.VERTICAL) |
|
||||
sizer.Add(self.CreateNameLabelWidget(parent, self._action)) |
|
||||
sizer.AddSpacer(2) |
|
||||
|
|
||||
if self._msg: |
|
||||
sizer.Add(self._msg, 0, wx.EXPAND) |
|
||||
sizer.AddSpacer(2) |
|
||||
else: |
|
||||
sizer.AddStretchSpacer(1) |
|
||||
|
|
||||
sizer.AddStretchSpacer(1) |
|
||||
sizer.Add(self._widget, 0, wx.EXPAND) |
|
||||
return sizer |
|
||||
|
|
||||
@abstractmethod |
|
||||
def BuildInputWidget(self, parent, action): |
|
||||
''' Must construct the main widget type for the Action ''' |
|
||||
pass |
|
||||
|
|
||||
|
|
||||
def HasHelpMsg(self, action): |
|
||||
return action.help is not None |
|
||||
|
|
||||
|
|
||||
def CreateHelpMsgWidget(self, parent, action): |
|
||||
base_text = wx.StaticText(parent, label=action.help) |
|
||||
if self.HasNargs(action): |
|
||||
base_text.SetLabelText(base_text.GetLabelText() + self.CreateNargsMsg(action)) |
|
||||
styling.MakeDarkGrey(base_text) |
|
||||
return base_text |
|
||||
|
|
||||
def HasNargs(self, action): |
|
||||
return action.nargs is not None and action.nargs is not 0 |
|
||||
|
|
||||
|
|
||||
def CreateNargsMsg(self, action): |
|
||||
if isinstance(action.nargs, int): |
|
||||
return '\n(Note: exactly {} arguments are required)'.format(action.nargs) |
|
||||
elif action.nargs == '+': |
|
||||
return '\n(Note: at least 1 or more arguments are required)' |
|
||||
return '' |
|
||||
|
|
||||
|
|
||||
def CreateNameLabelWidget(self, parent, action): |
|
||||
label = str(action.dest).title() |
|
||||
if len(action.option_strings) > 1: |
|
||||
label += ' (%s)' % action.option_strings[0] |
|
||||
text = wx.StaticText(parent, label=label) |
|
||||
styling.MakeBold(text) |
|
||||
return text |
|
||||
|
|
||||
|
|
||||
def AssertInitialization(self, clsname): |
|
||||
if not self._widget: |
|
||||
raise BuildException('%s was not correctly initialized' % clsname) |
|
||||
|
|
||||
|
|
||||
def __str__(self): |
|
||||
return str(self._action) |
|
||||
|
|
||||
|
|
||||
@abstractmethod |
|
||||
def GetValue(self): |
|
||||
''' Returns the state of the given widget ''' |
|
||||
pass |
|
||||
|
|
||||
|
|
||||
def Update(self, size): |
|
||||
''' |
|
||||
Manually word wraps the StaticText help objects which would |
|
||||
otherwise not wrap on resize |
|
||||
|
|
||||
Content area is based on each grid having two equally sized |
|
||||
columns, where the content area is defined as 87% of the halved |
|
||||
windows width. The wiggle room is the distance +- 10% of the |
|
||||
content_area. |
|
||||
|
|
||||
Wrap calculation is run only when the size of the help_msg |
|
||||
extends outside of the wiggle_room. This was done to avoid |
|
||||
the "flickering" that comes from constantly resizing a |
|
||||
StaticText object. |
|
||||
''' |
|
||||
if self._msg is None: |
|
||||
return |
|
||||
help_msg = self._msg |
|
||||
width, height = size |
|
||||
content_area = int((width / 2) * .87) |
|
||||
|
|
||||
wiggle_room = range(int(content_area - content_area * .05), int(content_area + content_area * .05)) |
|
||||
if help_msg.Size[0] not in wiggle_room: |
|
||||
self._msg.SetLabel(self._msg.GetLabelText().replace('\n', ' ')) |
|
||||
self._msg.Wrap(content_area) |
|
||||
|
|
||||
|
|
||||
|
|
||||
class Positional(AbstractComponent): |
|
||||
""" |
|
||||
Represents a positional argument in a program |
|
||||
e.g. |
|
||||
mypyfile.py param1 <-- this guy |
|
||||
""" |
|
||||
def __init__(self, action): |
|
||||
self._action = action |
|
||||
self._widget = None |
|
||||
self.contents = None |
|
||||
|
|
||||
def BuildInputWidget(self, parent, action): |
|
||||
return wx.TextCtrl(parent) |
|
||||
|
|
||||
def GetValue(self): |
|
||||
''' |
|
||||
Positionals have no associated options_string, |
|
||||
so only the supplied arguments are returned. |
|
||||
The order is assumed to be the same as the order |
|
||||
of declaration in the client code |
|
||||
|
|
||||
Returns |
|
||||
"argument_value" |
|
||||
''' |
|
||||
self.AssertInitialization('Positional') |
|
||||
if str(self._widget.GetValue()) == EMPTY: |
|
||||
return None |
|
||||
return self._widget.GetValue() |
|
||||
|
|
||||
|
|
||||
class Choice(AbstractComponent): |
|
||||
""" A dropdown box """ |
|
||||
|
|
||||
_DEFAULT_VALUE = 'Select Option' |
|
||||
|
|
||||
def __init__(self, action): |
|
||||
self._action = action |
|
||||
self._widget = None |
|
||||
self.contents = None |
|
||||
|
|
||||
def GetValue(self): |
|
||||
''' |
|
||||
Returns |
|
||||
"--option_name argument" |
|
||||
''' |
|
||||
self.AssertInitialization('Choice') |
|
||||
if self._widget.GetValue() == self._DEFAULT_VALUE: |
|
||||
return None |
|
||||
return ' '.join( |
|
||||
[self._action.option_strings[0] if self._action.option_strings else '', # get the verbose copy if available |
|
||||
self._widget.GetValue()]) |
|
||||
|
|
||||
def BuildInputWidget(self, parent, action): |
|
||||
return wx.ComboBox( |
|
||||
parent=parent, |
|
||||
id=-1, |
|
||||
value=self._DEFAULT_VALUE, |
|
||||
choices=action.choices, |
|
||||
style=wx.CB_DROPDOWN |
|
||||
) |
|
||||
|
|
||||
|
|
||||
class Optional(AbstractComponent): |
|
||||
|
|
||||
def __init__(self, action): |
|
||||
self._action = action |
|
||||
self._widget = None |
|
||||
self.contents = None |
|
||||
|
|
||||
def BuildInputWidget(self, parent, action): |
|
||||
return wx.TextCtrl(parent) |
|
||||
|
|
||||
def GetValue(self): |
|
||||
''' |
|
||||
General options are key/value style pairs (conceptually). |
|
||||
Thus the name of the option, as well as the argument to it |
|
||||
are returned |
|
||||
e.g. |
|
||||
>>> myscript --outfile myfile.txt |
|
||||
returns |
|
||||
"--Option Value" |
|
||||
''' |
|
||||
self.AssertInitialization('Optional') |
|
||||
value = self._widget.GetValue() |
|
||||
if not value or len(value) <= 0: |
|
||||
return None |
|
||||
return ' '.join( |
|
||||
[self._action.option_strings[0], # get the verbose copy if available |
|
||||
value]) |
|
||||
|
|
||||
|
|
||||
class Flag(AbstractComponent): |
|
||||
def __init__(self, action): |
|
||||
self._action = action |
|
||||
self._widget = None |
|
||||
self.contents = None |
|
||||
|
|
||||
def Build(self, parent): |
|
||||
self._widget = self.BuildInputWidget(parent, self._action) |
|
||||
self._msg = (self.CreateHelpMsgWidget(parent, self._action) |
|
||||
if self.HasHelpMsg(self._action) |
|
||||
else None) |
|
||||
|
|
||||
sizer = wx.BoxSizer(wx.VERTICAL) |
|
||||
sizer.Add(self.CreateNameLabelWidget(parent, self._action)) |
|
||||
sizer.AddSpacer(6) |
|
||||
|
|
||||
if self.HasNargs(self._action): |
|
||||
sizer.Add(self.CreateNargsMsg(parent, self._action)) |
|
||||
|
|
||||
if self._msg: |
|
||||
hsizer = self.buildHorizonalMsgSizer(parent) |
|
||||
sizer.Add(hsizer, 1, wx.EXPAND) |
|
||||
else: |
|
||||
sizer.AddStretchSpacer(1) |
|
||||
sizer.Add(self._widget, 0, wx.EXPAND) |
|
||||
return sizer |
|
||||
|
|
||||
def BuildInputWidget(self, parent, action): |
|
||||
return wx.CheckBox(parent, -1, label='') |
|
||||
|
|
||||
def buildHorizonalMsgSizer(self, panel): |
|
||||
if not self._msg: |
|
||||
return None |
|
||||
sizer = wx.BoxSizer(wx.HORIZONTAL) |
|
||||
sizer.Add(self._widget, 0) |
|
||||
sizer.AddSpacer(6) |
|
||||
sizer.Add(self._msg, 1, wx.EXPAND) |
|
||||
return sizer |
|
||||
|
|
||||
def GetValue(self): |
|
||||
''' |
|
||||
Flag options have no param associated with them. |
|
||||
Thus we only need the name of the option. |
|
||||
e.g |
|
||||
>>> Python -v myscript |
|
||||
returns |
|
||||
Options name for argument (-v) |
|
||||
''' |
|
||||
if not self._widget.GetValue() or len(self._widget.GetValue()) <= 0: |
|
||||
return None |
|
||||
else: |
|
||||
return self._action.option_strings[0] |
|
||||
|
|
||||
def Update(self, size): |
|
||||
''' |
|
||||
Custom wrapper calculator to account for the |
|
||||
increased size of the _msg widget after being |
|
||||
inlined with the wx.CheckBox |
|
||||
''' |
|
||||
if self._msg is None: |
|
||||
return |
|
||||
help_msg = self._msg |
|
||||
width, height = size |
|
||||
content_area = int((width / 3) * .70) |
|
||||
|
|
||||
wiggle_room = range(int(content_area - content_area * .05), int(content_area + content_area * .05)) |
|
||||
if help_msg.Size[0] not in wiggle_room: |
|
||||
self._msg.SetLabel(self._msg.GetLabelText().replace('\n', ' ')) |
|
||||
self._msg.Wrap(content_area) |
|
||||
|
|
||||
|
|
||||
class Counter(AbstractComponent): |
|
||||
def __init__(self, action): |
|
||||
self._action = action |
|
||||
self._widget = None |
|
||||
self.contents = None |
|
||||
|
|
||||
def BuildInputWidget(self, parent, action): |
|
||||
levels = [str(x) for x in range(1, 7)] |
|
||||
return wx.ComboBox( |
|
||||
parent=parent, |
|
||||
id=-1, |
|
||||
value='', |
|
||||
choices=levels, |
|
||||
style=wx.CB_DROPDOWN |
|
||||
) |
|
||||
|
|
||||
def GetValue(self): |
|
||||
''' |
|
||||
NOTE: Added on plane. Cannot remember exact implementation |
|
||||
of counter objects. I believe that they count sequentail |
|
||||
pairings of options |
|
||||
e.g. |
|
||||
-vvvvv |
|
||||
But I'm not sure. That's what I'm going with for now. |
|
||||
|
|
||||
Returns |
|
||||
str(action.options_string[0]) * DropDown Value |
|
||||
''' |
|
||||
dropdown_value = self._widget.GetValue() |
|
||||
if not str(dropdown_value).isdigit(): |
|
||||
return None |
|
||||
arg = str(self._action.option_strings[0]).replace('-', '') |
|
||||
repeated_args = arg * int(dropdown_value) |
|
||||
return '-' + repeated_args |
|
||||
|
|
||||
class Group(AbstractComponent): |
|
||||
|
|
||||
def __init__(self, action): |
|
||||
self._action = action |
|
||||
self._widget = None |
|
||||
self.contents = None |
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
if __name__ == '__main__': |
|
||||
pass |
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
@ -0,0 +1,5 @@ |
|||||
|
|
||||
|
import os |
||||
|
|
||||
|
CONFIG_PATH = os.getcwd() |
||||
|
|
Write
Preview
Loading…
Cancel
Save