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
Split 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