Browse Source

improved style, font, and color controls

pull/473/head
Chris 6 years ago
parent
commit
4ce19a7ac8
12 changed files with 222 additions and 30 deletions
  1. 20
      README.md
  2. 2
      gooey/gui/application.py
  3. 2
      gooey/gui/components/config.py
  4. 0
      gooey/gui/components/util/__init__.py
  5. 83
      gooey/gui/components/util/wrapped_static_text.py
  6. 1
      gooey/gui/components/widgets/__init__.py
  7. 34
      gooey/gui/components/widgets/bases.py
  8. 59
      gooey/gui/components/widgets/checkbox.py
  9. 11
      gooey/gui/components/widgets/textarea.py
  10. 29
      gooey/gui/containers/application.py
  11. 1
      gooey/languages/english.json
  12. 10
      gooey/python_bindings/argparse_to_json.py

20
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 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;| <p align="center"><img src="https://cloud.githubusercontent.com/assets/1408720/7904376/f544756a-07c5-11e5-86d6-862ac146ad35.gif" width="400"></p> |
| PasswordField | <p align="center"><img src="https://user-images.githubusercontent.com/1408720/28953722-eae72cca-788e-11e7-8fa1-9a1ef332a053.png" width="400"></p> |
| Listbox | ![image](https://user-images.githubusercontent.com/1408720/31590191-fadd06f2-b1c0-11e7-9a49-7cbf0c6d33d1.png) |
| BlockCheckbox | ![image](https://user-images.githubusercontent.com/1408720/46922288-9296f200-cfbb-11e8-8b0d-ddde08064247.png) <br/> 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?
-----------

2
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

2
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

0
gooey/gui/components/util/__init__.py

83
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)

1
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 *

34
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'], '')

59
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

11
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()

29
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

1
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",

10
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']]

Loading…
Cancel
Save