Enable Toolkit Selection¶
One of the more complicated aspects of Enable is the way in which it selects
which widget toolkit (Qt, Wx, etc.) to use when constructing a Window
instance. Further, there is also a second choice of which kiva
GraphicsContext
implementation to use along with the selected widget
toolkit.
There are of course defaults for both toolkit and kiva backend, which might even depend on the packages available in the Python environment where code is running. For most users, these defaults are sufficient and stable over time. For users that have specific needs, the widget toolkit and kiva backend can be explicitly specified.
ETSConfig¶
To control toolkit selection directly from application code, one must use
ETSConfig
. Specifically, the
ETSConfig.toolkit
trait should be set.
Note
ETSConfig.toolkit
must be set before importing Enable code, directly or
indirectly (via Chaco).
Example:
from traits.etsconfig.api import ETSConfig
ETSConfig.toolkit = 'qt'
from chaco.api import Plot
from enable.api import ComponentEditor
# ...
For the example, the application would use the Qt toolkit and the default kiva
backend [currently image
].
The format of the ETSConfig.toolkit
property is
<widget toolkit>[.<kiva backend>]
. The .<kiva backend>
part is
optional. If no value is given for <kiva backend>
, the default kiva
backend is then selected by ESTConfig
. That means that if you want to use
the QPainter
backend in your Qt-based application, you should do this:
from traits.etsconfig.api import ETSConfig
ETSConfig.toolkit = 'qt.qpainter'
As another example, you might have a headless program (like a web server) which
wants to generate plots with Chaco. So you could use the null
toolkit and
image
backend:
from traits.etsconfig.api import ETSConfig
ETSConfig.toolkit = 'null.image'
ETSConfig
has a read-only property kiva_backend
which you can read if
you’d like to know which kiva backend is currently selected, in case you haven’t
explicitly selected a backend.
ETS_TOOLKIT¶
If you wish to test toolkit/backend combinations without the need for code
modifications, you can set the ETS_TOOLKIT
environment variable before
running your application. ETSConfig
will use the value of that environment
variable to initialize its toolkit
and kiva_backend
properties. Here’s
what that looks like:
$ export ETS_TOOLKIT=qt.qpainter
$ python kiva/examples/kiva/kiva_explorer.py
enable.toolkits entrypoints¶
The allowed values for ETSConfig.toolkit
are determined by the
enable.toolkits
entrypoint. An enable.toolkits
entrypoint is a
Toolkit
object which when called with the name
of a class returns the appropriate version for the selected widget toolkit and
kiva backend.
The objects which are supplied by a toolkit/backend implementation are:
CompiledPath
GraphicsContext
Window
NativeScrollBar
font_metrics_provider()
Because this is done via the setuptools entrypoint mechanism, it means that code outside of Enable can contribute a custom toolkit and backends. To create a new toolkit, you need to do the following:
Create a package for the toolkit
Add a
toolkit.py
module (its name is just a convention) which should contain aToolkit
object initialized with the details of your package.Add an
enable.toolkits
list to theentry_points
keyword argument of your package’ssetup.py
script’s call to thesetup()
function. This list should contain a string with the format:<name> = my.package.toolkit:<Toolkit object name>
. This points to theToolkit
object created in the previous step.For every kiva backend supported by your toolkit, add a
<backend>.py
module to the package. This module must contain the objects listed above (CompiledPath
,GraphicsContext
, etc.)
There’s one more wrinkle to consider. Toolkit
normally expects to be called with a <module>:<class>
format string, but
Enable’s usage of the Toolkit
instance only passes a <class>
name when
resolving objects. This is because <module>
denotes the kiva backend, which
is not known until runtime. To provide this flexibility, the following wrapper
is used in Enable’s built-in toolkits:
def _wrapper(func):
def wrapped(name):
# Prefix object lookups with the name of the configured kiva backend.
return func(f"{ETSConfig.kiva_backend}:{name}")
return wrapped
toolkit = _wrapper(Toolkit("enable", "null", "enable.null"))