Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pyface Color Class and Trait Type #534

Closed
wants to merge 18 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
158 changes: 158 additions & 0 deletions docs/source/trait_types.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
===========
Trait Types
===========

Pyface defines a number of custom Trait types that represent quantities and
objects that are useful in the context of graphical user interfaces.

Colors
======

When working with user interfaces, it is common to want to be able to specify
the color to use in part of the UI. Each toolkit usually has its own way of
representing colors, and so the ability to specify a color in a
toolkit-independent way that can be converted to a toolkit-specific
representation is important. This is particularly so when you want to allow
the user to specify a color.

Pyface provides a |Color| class and a corresponding |PyfaceColor| trait-type
that allows this sort of representation. Internally, the |Color| class
stores colors as a tuple of red, green, blue and alpha values which range
from 0.0 through to 1.0, inclusive. Helper properties allow the user to
specify individual channel values, as well as specify colors in alternate
color spaces, such as HSV or HLS::

Color(rgba=(0.4, 0.2, 0.6, 0.8))
Color(red=0.4, green=0.2, blue=0.6, alpha=0.8)
Color(hls=(0.2, 0.5, 0.8))

|Color| instances can also be created via the |Color.from_str| method
which allow specification of colors via CSS-style color strings, such as::

Color.from_str("aquamarine")
Color.from_str("#624")
Color.from_str("hls(0.2, 0.5, 0.8)")

All CSS color representations should work, and the method tries to be
flexible in what it will accept.

|Color| instances are mutable, as their intended use is as values stored
in |PyfaceColor| trait classes which can be modified and listened to. The
|PyfaceColor| validator understands string descriptions of colors, and will
accept them as values when initializing or modifying the trait::

class Style(HasStrictTraits):

color = PyfaceColor("#442266FF")

@observe('color.rgba')
def color_changed(self, event):
print('The color has changed to {}'.format(self.color))

shape = Style(color='orange')
shape.color.blue = 0.8
shape.color = "hsva(0.502, 0.502, 0.0, 0.8)"

For interactions with the toolkit, the |Color.from_toolkit| and
|Color.to_toolkit| methods allow conversion to and from the appropriate
toolkit color objects, such as Qt's :py:class:`QColor` or
:py:class:`wx.Colour`. These are most likely to be needed by internal
Pyface functionality, and should not be needed by developers who are
building applications on top of Pyface.


Fonts
=====

When working with fonts, there is the frequent difficulty that the desired
font may or may not be present in the environment where the application is
being run. Additionally, the way that fonts are specified in different
toolkits are, clearly, different, although there is a lot of commonality.
Finally, while it is clearly preferable to specify fonts via a concrete
and well-defined API, it is common for developers to specify fonts via
strings like '12pt "Comic Sans"' or "bold italic 24 Arial, Helvetica, Sans".

Pyface defines a |Font| class that defines a toolkit-independent abstraction
of a font specification. This is not a particular, concrete font that can
be used to render text, but instead a specification for what font the
developer or user would like to use, but allowing for the backend toolkit to
be able to fall-back to other options, or substitute similar font faces
where it cannot exactly match the requirements. The attributes of the
font specification correspond closely to those that are described by CSS
fonts. In particular, a |Font| instance has the following traits:

``family``
A list of font family names in order of preference, such as "Helvetica"
or "Comic Sans". There are several generic font family names that can
be used as fall-backs in case all preferred fonts are unavailable. In
the case of a font that has been selected by the toolkit this list will
have one value which is the actual font family name.

``weight``
How thick or dark the font glyphs are. This value is specified by a
string, either of the form "100", "200", ..., "1000" or a number of
synonyms such as 'light' and 'bold' available for those values.
This is a mapped trait where ``weight_`` holds the corresponding
numerical value.

``stretch``
The amount of horizontal compression or expansion to apply to the glyphs.
These given by names such as 'condensed' and 'expanded', each of which is
mapped to a number between 100 and 900, available in the ``stretch_``
mapped value.

``style``
This selects either 'oblique' or 'italic' variants typefaces of the given
font family. If neither is wanted, the value is 'normal'.

``size``
The overall size of the glyphs. This can be expressed either as the
numeric size in points, or as a string such as "small" or "large".

``variants``
A set of additional font style specifiers, such as "small-caps",
"strikethrough", "underline" or "overline", where supported by the
underlying toolkit.

A |Font| object can be created in the usual way, by passing trait values as
keyword arguments, but there are classmethods :py:meth:`~!Font.from_toolkit`
and :py:meth:`~!Font.from_description` that create a |Font| from a toolkit
font specification object or a string description, respectively.

The string specification follows CSS conventions: fonts are specfied by a
string which specifies the weight, stretch, style and variants by text
synonyms (in any order), followed by size in points and font family
preferences (quoted if not a single word) and separated by commas.
Where the value is "normal" it can be omitted from the description.

For example::

'italic bold 14pt Helvetica, Arial, sans-serif'
'36pt "Comic Sans"'

are valid font descriptions, but "Helvetica bold 12pt" is not because the
order of elements is wrong.

The |Font| object also has a method :py:meth:`~!Font.to_toolkit` that
produces a toolkit font specification, which is usually what controls and
other toolkit-specific code excpect to be given.

While classes could simply use ``Instance(Font)`` whenever they want a
font specification, Pyface also provides a |PyfaceFont| trait type that
accepts either a |Font|, or a font description string. The value held
is always a |Font| object. This allows users to write code like::

class Style(HasStrictTraits):
font = PyfaceFont()

style = Style(font='bold 10pt "Comic Sans"')
style.font = "italic 12pt Arial, Helvetic, sans-serif"


.. |Color| replace:: :py:class:`~pyface.color.Color`
.. |Color.from_str| replace:: :py:meth:`~pyface.color.Color.from_str`
.. |Color.from_toolkit| replace:: :py:meth:`~pyface.color.Color.from_toolkit`
.. |Color.to_toolkit| replace:: :py:meth:`~pyface.color.Color.to_toolkit`
.. |Font| replace:: :py:class:`~pyface.font.Font`
.. |PyfaceColor| replace:: :py:class:`~pyface.color.PyfaceColor`
.. |PyfaceFont| replace:: :py:class:`~pyface.font.PyfaceFont`
4 changes: 4 additions & 0 deletions pyface/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,15 @@
from .application_window import ApplicationWindow
from .beep import beep
from .clipboard import clipboard, Clipboard
from .color import Color, PyfaceColor
from .color_dialog import ColorDialog, get_color
from .confirmation_dialog import confirm, ConfirmationDialog
from .constant import OK, CANCEL, YES, NO
from .dialog import Dialog
from .directory_dialog import DirectoryDialog
from .file_dialog import FileDialog
from .font import Font, PyfaceFont
from .font_dialog import FontDialog, get_font
from .filter import Filter
from .gui import GUI
from .gui_application import GUIApplication
Expand Down
Loading