diff --git a/README.md b/README.md
index 02e52a6..ff03d9e 100644
--- a/README.md
+++ b/README.md
@@ -9,6 +9,24 @@ Turn (almost) any Python 2 or 3 Console Program into a GUI application with one
# Gooey now supports Python 3!!
+# 1.0.2 Release TODO:
+
+Finish of the readonly param for all components. Maybe this 'll be passing
+totally arbitrary style around.
+
+Document all the new layout stuff
+
+* group - show_underline
+* group - marginTop
+* gooey_options.label_color
+* gooey_options.help_color
+* gooey_options.error_color
+* gooey_options.show_label
+* gooey_options.show_help
+* gooey_options.block_checkbox.checkbox_label [string]
+* gooey_options.textarea.readonly
+
+
Table of Contents
-----------------
@@ -199,6 +217,7 @@ However, by dropping in `GooeyParser` and supplying a `widget` name, you can dis
| DateChooser |

|
| PasswordField | 
|
| Listbox |  |
+| BlockCheckbox | 
The default InlineCheck box can look less than ideal if a large help text block is present. `BlockCheckbox` moves the text block to the normal position and provides a short-form `block_label` for display next to the control. Use `gooey_options.checkbox_label` to control the label text |
@@ -611,7 +630,6 @@ Screenshots
-
Wanna help?
-----------
diff --git a/gooey/gui/application.py b/gooey/gui/application.py
index eea9b97..35d4b41 100644
--- a/gooey/gui/application.py
+++ b/gooey/gui/application.py
@@ -22,7 +22,7 @@ def build_app(build_spec):
i18n.load(build_spec['language_dir'], build_spec['language'], build_spec['encoding'])
imagesPaths = image_repository.loadImages(build_spec['image_dir'])
gapp = GooeyApplication(merge(build_spec, imagesPaths))
- # wx.lib.inspection.InspectionTool().Show()
+ wx.lib.inspection.InspectionTool().Show()
gapp.Show()
return app
diff --git a/gooey/gui/components/config.py b/gooey/gui/components/config.py
index 62c4b46..ee74c80 100644
--- a/gooey/gui/components/config.py
+++ b/gooey/gui/components/config.py
@@ -121,7 +121,7 @@ class ConfigPage(ScrolledPanel):
widget = self.reifyWidget(parent, item)
# !Mutate the reifiedWidgets instance variable in place
self.reifiedWidgets.append(widget)
- sizer.Add(widget, 1, wx.ALL, 5)
+ sizer.Add(widget, 1, wx.ALL | wx.EXPAND, 5)
boxSizer.Add(sizer, 0, wx.ALL | wx.EXPAND, 5)
# apply the same layout rules recursively for subgroups
diff --git a/gooey/gui/components/util/__init__.py b/gooey/gui/components/util/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/gooey/gui/components/util/wrapped_static_text.py b/gooey/gui/components/util/wrapped_static_text.py
new file mode 100644
index 0000000..4f8770a
--- /dev/null
+++ b/gooey/gui/components/util/wrapped_static_text.py
@@ -0,0 +1,83 @@
+import wx
+from wx.lib.wordwrap import wordwrap
+
+
+
+class AutoWrappedStaticText(wx.StaticText):
+ """
+ Copy/pasta of wx.lib.agw.infobar.AutoWrapStaticText with two modifications:
+
+ 1. Extends wx.StaticText rather than GenStaticText
+ 2. Does not set the fore/background colors to sys defaults
+
+ The behavior of GenStaticText's background color is pretty buggy cross-
+ platform. It doesn't reliably match its parent components background
+ colors[0] (for instance when rendered inside of a Notebook) which leads to
+ ugly 'boxing' around the text components.
+
+ [0] more specifically, they'll match 1:1 on paper, but still ultimately
+ render differently.
+ """
+
+ def __init__(self, parent, *args, **kwargs):
+ super(AutoWrappedStaticText, self).__init__(parent, *args, **kwargs)
+ self.label = kwargs.get('label')
+ self.Bind(wx.EVT_SIZE, self.OnSize)
+
+
+ def OnSize(self, event):
+ """
+ Handles the ``wx.EVT_SIZE`` event for :class:`AutoWrapStaticText`.
+
+ :param `event`: a :class:`wx.SizeEvent` event to be processed.
+ """
+
+ event.Skip()
+ self.Wrap(event.GetSize().width)
+
+ def Wrap(self, width):
+ """
+ This functions wraps the controls label so that each of its lines becomes at
+ most `width` pixels wide if possible (the lines are broken at words boundaries
+ so it might not be the case if words are too long).
+
+ If `width` is negative, no wrapping is done.
+
+ :param integer `width`: the maximum available width for the text, in pixels.
+
+ :note: Note that this `width` is not necessarily the total width of the control,
+ since a few pixels for the border (depending on the controls border style) may be added.
+ """
+
+ if width < 0:
+ return
+
+ self.Freeze()
+
+ dc = wx.ClientDC(self)
+ dc.SetFont(self.GetFont())
+ text = wordwrap(self.label, width, dc)
+ self.SetLabel(text, wrapped=True)
+
+ self.Thaw()
+
+ def SetLabel(self, label, wrapped=False):
+ """
+ Sets the :class:`AutoWrapStaticText` label.
+
+ All "&" characters in the label are special and indicate that the following character is
+ a mnemonic for this control and can be used to activate it from the keyboard (typically
+ by using ``Alt`` key in combination with it). To insert a literal ampersand character, you
+ need to double it, i.e. use "&&". If this behaviour is undesirable, use :meth:`~Control.SetLabelText` instead.
+
+ :param string `label`: the new :class:`AutoWrapStaticText` text label;
+ :param bool `wrapped`: ``True`` if this method was called by the developer using :meth:`~AutoWrapStaticText.SetLabel`,
+ ``False`` if it comes from the :meth:`~AutoWrapStaticText.OnSize` event handler.
+
+ :note: Reimplemented from :class:`wx.Control`.
+ """
+
+ if not wrapped:
+ self.label = label
+
+ wx.StaticText.SetLabel(self, label)
\ No newline at end of file
diff --git a/gooey/gui/components/widgets/__init__.py b/gooey/gui/components/widgets/__init__.py
index ac3184d..5789312 100644
--- a/gooey/gui/components/widgets/__init__.py
+++ b/gooey/gui/components/widgets/__init__.py
@@ -7,6 +7,7 @@ from .command import CommandField
from .dropdown import Dropdown
from .listbox import Listbox
from .checkbox import CheckBox
+from .checkbox import BlockCheckbox
from .counter import Counter
from .radio_group import RadioGroup
from .choosers import *
diff --git a/gooey/gui/components/widgets/bases.py b/gooey/gui/components/widgets/bases.py
index e70122c..dd0e5b0 100644
--- a/gooey/gui/components/widgets/bases.py
+++ b/gooey/gui/components/widgets/bases.py
@@ -1,11 +1,12 @@
+from functools import reduce
+
import wx
-# from rx.subjects import Subject
from gooey.gui import formatters, events
-from gooey.gui.pubsub import pub
from gooey.gui.util import wx_util
from gooey.util.functional import getin, ifPresent
from gooey.gui.validators import runValidator
+from gooey.gui.components.util.wrapped_static_text import AutoWrappedStaticText
class BaseWidget(wx.Panel):
@@ -47,11 +48,12 @@ class TextContainer(BaseWidget):
self._meta = widgetInfo['data']
self._options = widgetInfo['options']
self.label = wx.StaticText(self, label=widgetInfo['data']['display_name'])
- self.help_text = wx.StaticText(self, label=widgetInfo['data']['help'] or '')
- self.error = wx.StaticText(self, label='')
+ self.help_text = AutoWrappedStaticText(self, label=widgetInfo['data']['help'] or '')
+ self.error = AutoWrappedStaticText(self, label='')
self.error.Hide()
self.widget = self.getWidget(self)
self.layout = self.arrange(*args, **kwargs)
+ self.setColors()
self.SetSizer(self.layout)
self.Bind(wx.EVT_SIZE, self.onSize)
if self._meta['default']:
@@ -69,7 +71,7 @@ class TextContainer(BaseWidget):
layout = wx.BoxSizer(wx.VERTICAL)
if self._options.get('show_label', True):
- layout.Add(self.label)
+ layout.Add(self.label, 0, wx.EXPAND)
else:
layout.AddStretchSpacer(1)
@@ -80,10 +82,24 @@ class TextContainer(BaseWidget):
else:
layout.AddStretchSpacer(1)
layout.Add(self.getSublayout(), 0, wx.EXPAND)
- layout.Add(self.error)
+ layout.Add(self.error, 1, wx.EXPAND)
+
self.error.Hide()
return layout
+
+ def setColors(self):
+ wx_util.make_bold(self.label)
+ wx_util.withColor(self.label, self._options['label_color'])
+ wx_util.withColor(self.help_text, self._options['help_color'])
+ wx_util.withColor(self.error, self._options['error_color'])
+ if self._options.get('label_bg_color'):
+ self.label.SetBackgroundColour(self._options.get('label_bg_color'))
+ if self._options.get('help_bg_color'):
+ self.help_text.SetBackgroundColour(self._options.get('help_bg_color'))
+ if self._options.get('error_bg_color'):
+ self.error.SetBackgroundColour(self._options.get('error_bg_color'))
+
def getWidget(self, *args, **options):
return self.widget_class(*args, **options)
@@ -96,9 +112,13 @@ class TextContainer(BaseWidget):
return layout
def onSize(self, event):
- self.error.Wrap(self.GetSize().width)
+ # print(self.GetSize())
+ # self.error.Wrap(self.GetSize().width)
+ # self.help_text.Wrap(500)
+ # self.Layout()
event.Skip()
+
def getValue(self):
userValidator = getin(self._options, ['validator', 'test'], 'True')
message = getin(self._options, ['validator', 'message'], '')
diff --git a/gooey/gui/components/widgets/checkbox.py b/gooey/gui/components/widgets/checkbox.py
index 15fec6f..5b3da66 100644
--- a/gooey/gui/components/widgets/checkbox.py
+++ b/gooey/gui/components/widgets/checkbox.py
@@ -1,10 +1,9 @@
import wx
-from gooey.gui import formatters, events
+from gooey.gui import formatters
from gooey.gui.components.widgets.bases import TextContainer
-from gooey.gui.pubsub import pub
+from gooey.gui.lang.i18n import _
from gooey.gui.util import wx_util
-from gooey.util.functional import getin
class CheckBox(TextContainer):
@@ -49,3 +48,57 @@ class CheckBox(TextContainer):
def hideInput(self):
self.widget.Hide()
+
+
+
+
+
+class BlockCheckbox(CheckBox):
+ """
+ A block style layout which places the help text in the normal
+ location rather than inline next to the checkbox. A replacement label
+ called `block_label` is shown next to the checkbox control.
+
+ +-----------------+
+ |label |
+ |help_text |
+ |[ ] block_label |
+ +-----------------+
+ This option tends to look better when there is a large amount of
+ help text.
+ """
+
+
+ def arrange(self, *args, **kwargs):
+ wx_util.make_bold(self.label)
+ wx_util.withColor(self.label, self._options['label_color'])
+ wx_util.withColor(self.help_text, self._options['help_color'])
+ wx_util.withColor(self.error, self._options['error_color'])
+ self.error.Hide()
+
+ self.help_text.SetMinSize((0,-1))
+
+ layout = wx.BoxSizer(wx.VERTICAL)
+
+ if self._options.get('show_label', True):
+ layout.Add(self.label, 0, wx.EXPAND)
+ else:
+ layout.AddStretchSpacer(1)
+
+ layout.AddSpacer(2)
+ if self.help_text and self._options.get('show_help', True):
+ layout.Add(self.help_text, 1, wx.EXPAND)
+ layout.AddSpacer(2)
+ else:
+ layout.AddStretchSpacer(1)
+
+ layout.AddSpacer(2)
+
+ block_label = self._options.get('checkbox_label', _('checkbox_label'))
+ hsizer = wx.BoxSizer(wx.HORIZONTAL)
+ hsizer.Add(self.widget, 0)
+ hsizer.Add(wx.StaticText(self, label=block_label), 1)
+ layout.Add(hsizer, 1, wx.EXPAND)
+ layout.AddSpacer(2)
+
+ return layout
\ No newline at end of file
diff --git a/gooey/gui/components/widgets/textarea.py b/gooey/gui/components/widgets/textarea.py
index 608c6e4..fe1cd40 100644
--- a/gooey/gui/components/widgets/textarea.py
+++ b/gooey/gui/components/widgets/textarea.py
@@ -1,4 +1,6 @@
import wx
+from functools import reduce
+
from gooey.gui.components.widgets.core.text_input import MultilineTextInput
from gooey.gui.components.widgets.textfield import TextField
from gooey.gui.components.widgets.bases import TextContainer
@@ -12,9 +14,16 @@ class Textarea(TextContainer):
return wx.TextCtrl(
parent=parent,
size=(-1, widgetHeight),
- style=wx.TE_MULTILINE | wx.TE_READONLY
+ style=self.getModifiers()
)
+ def getModifiers(self):
+ readonly = (wx.TE_READONLY
+ if self._options.get('readonly', False)
+ # using TE_MUTLI as a safe OR-able no-op value
+ else wx.TE_MULTILINE)
+ return reduce(lambda acc, val: acc | val, [wx.TE_MULTILINE, readonly])
+
def getWidgetValue(self):
return self.widget.GetValue()
diff --git a/gooey/gui/containers/application.py b/gooey/gui/containers/application.py
index 6cc21c3..ced3944 100644
--- a/gooey/gui/containers/application.py
+++ b/gooey/gui/containers/application.py
@@ -3,27 +3,26 @@ Primary orchestration and control point for Gooey.
"""
import sys
-from itertools import chain
import wx
+from gooey.gui import cli
from gooey.gui import events
-from gooey.gui.components.header import FrameHeader
-from gooey.gui.components.footer import Footer
-from gooey.gui.util import wx_util
+from gooey.gui import seeder
+from gooey.gui.components import modals
from gooey.gui.components.config import ConfigPage, TabbedConfigPage
+from gooey.gui.components.console import Console
+from gooey.gui.components.footer import Footer
+from gooey.gui.components.header import FrameHeader
+from gooey.gui.components.menubar import MenuBar
from gooey.gui.components.sidebar import Sidebar
from gooey.gui.components.tabbar import Tabbar
-from gooey.util.functional import getin, assoc, flatmap, compact
-from gooey.python_bindings import constants
-from gooey.gui.pubsub import pub
-from gooey.gui import cli
-from gooey.gui.components.console import Console
from gooey.gui.lang.i18n import _
from gooey.gui.processor import ProcessController
+from gooey.gui.pubsub import pub
+from gooey.gui.util import wx_util
from gooey.gui.util.wx_util import transactUI
-from gooey.gui.components import modals
-from gooey.gui import seeder
+from gooey.python_bindings import constants
class GooeyApplication(wx.Frame):
@@ -36,7 +35,9 @@ class GooeyApplication(wx.Frame):
self._state = {}
self.buildSpec = buildSpec
- self.SetTitle(self.buildSpec['program_name'])
+ self.applyConfiguration()
+ self.menuBar = MenuBar(buildSpec)
+ self.SetMenuBar(self.menuBar)
self.header = FrameHeader(self, buildSpec)
self.configs = self.buildConfigPanels(self)
self.navbar = self.buildNavigation()
@@ -70,6 +71,10 @@ class GooeyApplication(wx.Frame):
self.onStart()
+ def applyConfiguration(self):
+ self.SetTitle(self.buildSpec['program_name'])
+ self.SetBackgroundColour(self.buildSpec.get('body_bg_color'))
+
def onStart(self, *args, **kwarg):
"""
Verify user input and kick off the client's program if valid
diff --git a/gooey/languages/english.json b/gooey/languages/english.json
index c49ba4f..5133725 100644
--- a/gooey/languages/english.json
+++ b/gooey/languages/english.json
@@ -1,6 +1,7 @@
{
"browse": "Browse",
"cancel": "Cancel",
+ "checkbox_label": "Enable",
"choose_date": "Choose Date",
"choose_file": "Choose File",
"choose_folder": "Choose Folder",
diff --git a/gooey/python_bindings/argparse_to_json.py b/gooey/python_bindings/argparse_to_json.py
index c668f9b..a2793ee 100644
--- a/gooey/python_bindings/argparse_to_json.py
+++ b/gooey/python_bindings/argparse_to_json.py
@@ -1,8 +1,6 @@
"""
Converts argparse parser actions into json "Build Specs"
"""
-import functools
-import pprint
import argparse
import os
import sys
@@ -19,6 +17,7 @@ from uuid import uuid4
from gooey.util.functional import merge, getin
+
VALID_WIDGETS = (
'FileChooser',
'MultiFileChooser',
@@ -30,6 +29,7 @@ VALID_WIDGETS = (
'Counter',
'RadioGroup',
'CheckBox',
+ 'BlockCheckbox',
'MultiDirChooser',
'Textarea',
'PasswordField',
@@ -45,16 +45,17 @@ class UnsupportedConfiguration(Exception):
pass
-
group_defaults = {
'columns': 2,
'padding': 10,
'show_border': False
}
+# TODO: merge the default foreground and bg colors from the
+# baseline build_spec
item_default = {
'error_color': '#ea7878',
- 'label_color': '#ff1111',
+ 'label_color': '#000000',
'help_color': '#363636',
'validator': {
'type': 'local',
@@ -93,6 +94,7 @@ def process(parser, widget_dict, options):
return categorize2(strip_empty(corrected_action_groups), widget_dict, options)
+
def strip_empty(groups):
return [group for group in groups if group['items']]