Add weakref-friendly syntax sugar for observers

Python decorators for defining observer list interfaces in a way that's a
bit more public and formal and documentable than a plain old list member.
Uses some cute C#-like syntax for tacking on callbacks and additional
behaviour as tersely as possible.

Observable "@event"s set up this way store weakrefs to their observer
objects if the callback is a bound method, to allow them to die off
naturally.  Not needed right now, but it'll be necessary later on to allow
dynamic (parameterized, and shorter-lived) tool widgets to observe the
brushmanager, for example.
brushmanager: replace *_observers []s with @events

Replace the bm's observer lists with the new interface, to allow dynamic
tool widgets to represent one brush group per tab (at some point).
workspace: parameterizable building from layouts

Allow tool widget "tabs" in the workspace to be constructed with static
parameters. These parameters can't be changed over the lifetime of the
widget, and get saved and restored in the usual way.

This is intended to allow the brush and colour selectors to be split up
across multiple tabs: parameters will hold the brush group name, or the
colour selector subtype.
logging: pretty colours, and --logfile now copies

Update logging: pretty ANSI colours with a specialised formatter, for POSIX
console (isatty()) logs only. Colourize by level, assign attributes for
interpolated strings and numbers in the message templates.

The standard Python logging module supports more than one handler, so
reimplement --logfile so it sends a (plain, uncoloured) copy of the log
messages to its output file rather than just reallocating sys.stderr and
sys.stdout. However we still do the reallocation trick to catch GTK
assertions on the Windows platform, or any other installation that logs to
files by default.
palette: better handling of names
Color adjusters: minor tweaks (NFC)

Title case, members.
drawwindow: code tidy (NFC)
adjbases: use weakset for registered adjusters

Allows them to be a little more dynamic, in theory. In practice, PyGI may
have some leaks that requires us to be a little more careful anyway.
Add a standardized gobject factory

Allows construction of classes on the fly, based on their class's GType
system name (which is auto-registered in modern Python GTK+ code), plus a
bundle of parameters which further identify the widget needed.

Rationale: support dynamic tool widget tabs, including ones for individual
brush collections.
workspace: support parameterized construction

Move object creation out to a separately defined factory+cache to keep
things simpler in workspace.py.

Convert tool widget lifetime signals to @events.

Tool widgets are now long-lived. Turns out that making GTK widgets
short-lived will just leak with the version of PyGI I'm using, so use a
cache for the tool widgets and convert the API to hide/show.  Further
encapsulate direct access to the tool widgets too: shouldn't be needed;
tool widgets should have no app-specific magic embedded in them.

Docstring and test updates.
brushselectionwin: fix an exception on re-show
paletteview: fix some sizing issues
workspace: tool-widget size consts and base class

Add constants for sizing tool widgets attractively, and a base class for
the common case where a tool widget is implemented as a VBox.
workspace: sizing fixes for various things
tool widgets: nicer default sizes
palette: convert to GTK3, some code love

GTK3 conversion, making sure the tests still run.

Comments and docs.  Make member variable init a little more conventional /
safe.  Split up API into sections. Makes sense for my folding editor
anyway, and it's good to think systematically about it.
uicolor: add interpolation methods
observable: fix class vs. instance test

Fix use of boolean testing when distinguishing between descriptor access on
a class instance and descriptor access on the class itself. Testing with
``is None`` is better in this case.
palette: never test false

Palettes are more than just arrays: they should be treatable as objects
because they have more fields and much more behaviour.  In particular,
bool() should return what "is not None" does.
Split out colour selectors

Split up the color selector tool to allow the tabs to positioned side by
side in the Workspace.