From 0fc14219eb5accd750273c5f10e42ed1bfc35883 Mon Sep 17 00:00:00 2001 From: John Wiggins Date: Fri, 19 Mar 2021 16:43:36 +0100 Subject: [PATCH] Rearrange the documentation --- docs/source/enable/abstract_window.rst | 28 -- docs/source/enable/basic_tools.rst | 71 ---- docs/source/enable/drag_and_drop.rst | 64 ---- docs/source/enable/events.rst | 209 +++++++++++ docs/source/enable/key_events.rst | 85 ----- docs/source/enable/mouse_events.rst | 53 --- docs/source/enable/overview.rst | 59 +++ .../enable/{undo_redo.rst => tools.rst} | 85 ++++- docs/source/enable/traitsui.rst | 27 -- docs/source/index.rst | 24 +- docs/source/kiva/backends.rst | 50 --- docs/source/kiva/compiled_path.rst | 35 -- docs/source/kiva/drawing_details.rst | 343 ++++++++++++++++++ docs/source/kiva/fonts.rst | 126 ------- docs/source/kiva/images.rst | 57 --- docs/source/kiva/overview.rst | 65 +++- docs/source/kiva/state.rst | 115 ------ 17 files changed, 762 insertions(+), 734 deletions(-) delete mode 100644 docs/source/enable/abstract_window.rst delete mode 100644 docs/source/enable/basic_tools.rst delete mode 100644 docs/source/enable/drag_and_drop.rst create mode 100644 docs/source/enable/events.rst delete mode 100644 docs/source/enable/key_events.rst delete mode 100644 docs/source/enable/mouse_events.rst rename docs/source/enable/{undo_redo.rst => tools.rst} (75%) delete mode 100644 docs/source/enable/traitsui.rst delete mode 100644 docs/source/kiva/backends.rst delete mode 100644 docs/source/kiva/compiled_path.rst create mode 100644 docs/source/kiva/drawing_details.rst delete mode 100644 docs/source/kiva/fonts.rst delete mode 100644 docs/source/kiva/images.rst delete mode 100644 docs/source/kiva/state.rst diff --git a/docs/source/enable/abstract_window.rst b/docs/source/enable/abstract_window.rst deleted file mode 100644 index 132fa5fd7..000000000 --- a/docs/source/enable/abstract_window.rst +++ /dev/null @@ -1,28 +0,0 @@ -Top-level Windows -================= -When a component is shown on screen via a GUI toolkit, its :attr:`window` trait -contains an instance of :class:`~.AbstractWindow` which serves as a delegate -between the underlying window system and the component. - -For the most part, code doesn't need to interact with the underlying window. -However one common exception is tools which want to set a custom cursor. This -is accomplished via the :py:meth:`set_pointer` method. - -AbstractWindow --------------- -The following methods are the public interface of :class:`AbstractWindow`. - -.. automethod:: enable.abstract_window.AbstractWindow.get_pointer_position - :noindex: - -.. automethod:: enable.abstract_window.AbstractWindow.redraw - :noindex: - -.. automethod:: enable.abstract_window.AbstractWindow.set_mouse_owner - :noindex: - -.. automethod:: enable.abstract_window.AbstractWindow.set_pointer - :noindex: - -.. automethod:: enable.abstract_window.AbstractWindow.set_tooltip - :noindex: diff --git a/docs/source/enable/basic_tools.rst b/docs/source/enable/basic_tools.rst deleted file mode 100644 index f75711995..000000000 --- a/docs/source/enable/basic_tools.rst +++ /dev/null @@ -1,71 +0,0 @@ -Enable Tools -============ - -Enable ``Tools`` are ``Interator`` subclasses that do not have to have any -visual representation, and which can be dynamically added and removed from -components by adding or removing them from the component's ``tools`` list. -This permits developers to quickly build up complex behaviours from simple, -reproducible parts without having complex inheritance hierarchies. - -Basic Tools ------------ - -Enable provides a number of basic tools for common interactions. - -ButtonTool -~~~~~~~~~~ - -The :py:class:`ButtonTool` provides basic push-button or checkbox -interactions, depending on how it is configured. The primary interface it -provides is a :py:attr:`clicked` event which is fired when the user clicks in -the region of the underlying component, or when the :py:meth:`click` method is -called. The :py:attr:`clicked` event is fired on mouse up. - -To get checkbox-style behaviour, set :py:attr:`togglable` to ``True`` and -then every click will invert the :py:attr:`checked` trait. The toggle state -can also be changed via the :py:meth:`toggle` method, which does not fire the -:py:attr:`clicked` event when called. For buttons with multi-state toggles, -subclasses can override the :py:meth:`toggle` method to perform more complex -state changes. - -By default, the tool responds to clicks that are within the associated -component, but subclasses can override this behaviour by replacing the -:py:meth:`is_clickable` method with something else. - -It will commonly be the case that components or :py:class:`ButtonTool` -subclasses which draw may wish to respond to user interactions by drawing -themselves in a highlighted or selected mode when the mouse is down inside -the button region. The :py:attr:`down` trait provides this information -conveniently, so that users of the tool can change their drawing state and -request redraws when it changes. - -DragTool -~~~~~~~~ - -The :py:class:`DragTool` is an abstract base class that provides basic -interaction support for draging within Enable. Many other tools within -Enable and Chaco use it. - -HoverTool -~~~~~~~~~ - -The :py:class:`HoverTool` is a simple tool that calls a callback when the -mouse has been held steadily over the component for a period of time. - -MoveTool -~~~~~~~~ - -A :py:class:`DragTool` subclass that allows a user to move a component around -its container by dragging. - -ResizeTool -~~~~~~~~~~ - -A :py:class:`DragTool` subclass that allows a user to resize a component by -dragging from the edges of the component. - -ValueDragTool -~~~~~~~~~~~~~ - -A :py:class:`DragTool` subclass that allows a drag operation to set an -arbitrary value. diff --git a/docs/source/enable/drag_and_drop.rst b/docs/source/enable/drag_and_drop.rst deleted file mode 100644 index 6cf873cb7..000000000 --- a/docs/source/enable/drag_and_drop.rst +++ /dev/null @@ -1,64 +0,0 @@ -Enable Drag and Drop Events -=========================== - -Enable has support for objects being dropped onto components built on top of -the backend's (and, in practice, PyFace's) support for drag and drop. - -All drag-and-drop related events have type ``DragEvent`` and provide the -``x`` and ``y`` coordinates of the event, and the object being dragged or -dropped (if it is available from the backend, otherwise this will be ``None``). - -The system generates 3 types of drag and drop events: - -``drag_over`` - These events are generated when the user is moving a dragged object over - the Enable window. Tools or other Interactors which want to indicate that - they can accept the drag should indicate this by calling the - ``set_drag_result()`` method of the Enable window indicating the type of - operation that will be performed on the object (the default is a "copy", - but other possibilties include "move" or "link", the full list of - possibilites is found in the appropriate ``constants`` module). The value - of the drag result influences the way that the operating system displays - the dragged objects and cursor whil dragging. - -``drag_leave`` - This event is generated when the user drags objects out of the window. - -``dropped_on`` - This event is generated when the user releases the mouse button over the - Enable window while dragging. Tools or other Interactors should handle - this event to perform whatever operations need to be performed with the - dropped objects. - -BaseDropTool ------------- - -As a convenience, there is a ``BaseDropTool`` class which handles most of the -drag and drop interactions for you correctly. To use this, you need to -subclass and override at least the ``accept_drop`` and ``handle_drop`` methods. - -``accept_drop`` - This method is given the position and object instance and should return - ``True`` if the drop is accepted or ``False`` if it is not. - -``handle_drop`` - If the drop is accepted, this method is called with the position and the - objects, and should perform whatever actions are required with the dropped - objects. - -The behaviour is slightly different between the Wx and Qt backends: Qt provides -a reference to the dragged objects during ``drag_over`` events, and so the -``BaseDropTool`` will call ``accept_drop`` during ``drag_over`` events to give -better feedback about the state of the drag and drop operation; whereas Wx does -not provide that information, so will always indicate to the operating system -that a drop is possible. - -The type of drag result returned during ``drag_over`` events is controlled by -the ``default_drag_result`` attribute. - -If you want more control over the response to ``drag_over`` events, then you -can additionally override the ``get_drag_result`` method to return one of the -drag result constants dependin on the position and (possibly) the objects -being dragged. If you want cross-toolkit compatibility, you must handle the -case where the ``get_drag_result`` method is called with the object being -``None``, which indicates that the object is not known yet. diff --git a/docs/source/enable/events.rst b/docs/source/enable/events.rst new file mode 100644 index 000000000..9b485f84e --- /dev/null +++ b/docs/source/enable/events.rst @@ -0,0 +1,209 @@ +================= +Enable GUI Events +================= + +Mouse Events +============ + +Enable mouse events are represented by the :class:`~.MouseEvent` type and their +event names (which are the suffixes used by +:py:meth:`enable.interactor.Interactor.dispatch`) can be divided into two +groups: mouse clicks and mouse movements. The mouse click events have names +ending in ``_down``, ``_up``, or ``_dclick`` and names beginning with ``left``, +``right``, or ``middle``. This means that Enable only supports three mouse +buttons (plus wheel events). + +Mouse Event types +----------------- + +\*_down +~~~~~~~ +A mouse button was pressed. Dispatched as ``left_down``, ``right_down``, or +``middle_down``. + +\*_up +~~~~~ +A mouse button was released. Dispatched as ``left_up``, ``right_up``, or +``middle_up``. + +\*_dclick +~~~~~~~~~ +A mouse button was double-clicked. Dispatched as ``left_dclick``, +``right_dclick``, or ``middle_dclick``. + +move +~~~~ +The mouse moved within a component. + +enter +~~~~~ +The mouse moved into a component's bounds. + +leave +~~~~~ +The mouse moved out of a component's bounds. + +wheel +~~~~~ +The mouse wheel moved. + +MouseEvent +---------- +Below is a listing of the traits on a `MouseEvent` instance. + +.. autoclass:: enable.events.MouseEvent + :members: alt_down, control_down, shift_down, left_down, middle_down, right_down, mouse_wheel, mouse_wheel_axis, mouse_wheel_delta + :noindex: + + +Keyboard Events +=============== + +Previous versions of Enable had just one keyboard event, a 'key_pressed' +event. This event conflated which key(s) were physically being pressed +with the text which should be generated by that keypress. All the underlying +windowing/event systems have a way of distinguishing these two pieces of +information, either by issuing separate events for each (Wx, Pyglet, Vtk), or +by storing them as different attributes on the event object (Qt). + +In addition, there was no way to detect a key-up event. This is a comparatively +minor use case, but potentially useful for doing things like toggling pointer +states to indicate different mouse behaviour when modifiers keys are pressed. + +New Events +---------- + +key_pressed: + We will keep the 'key_pressed' event. However the semantics will change so + that it will keep track of the physical key being pressed rather than the + text being entered. Thus pressing the 'a' key with the shift key down will + result in an event with the 'character' attribute set to 'a', instead of 'A' + as it would be now. The lower-case version of the character will be the + canonical representation of the key, since that will cause minimal problems + with existing event handlers in Enable and Chaco. + + The character mapping will be used as it is now to map special keys to + standard strings (like right arrow generating the string 'right'). + + In addition, events will now be generated by pressing modifier keys by + themselves (eg. pressing shift will generate an event). + + Most Chaco code will continue to use key_pressed as the primary event to + listen to, it will just work more consistently than it did before. Some + code may need to change to reflect the change in what the character + attribute returns (XXX or we add a 'key' attribute and deprecate the + 'character' attribute). + + On Wx this event will be generated by EVT_KEY_DOWN, on Qt, this event + will be generated by keyPressEvent, but will access the 'key()' attribute + rather than the 'text()' attribute. + +key_released: + We will introduce a new 'key_released' event, which is essentially the + mirror image of the 'key_pressed' event. + + On Wx this event will be generated by EVT_KEY_UP, on Qt, this event + will be generated by keyReleaseEvent, but will access the 'key()' attribute + rather than the 'text()' attribute. + +character: + We will introduce a new 'character' event, which will hold the + unicode characters, if any, that are generated by the key press + event. It is possible that this event may also be generated by + other mechanisms in the future, like pasting text. The event may + hold multiple characters. + + This event will not be generated if the key_pressed event is handled. + This is largely because this is the way that wx works, but it also makes + a certain amount of sense from an interaction modelling point of view. + + This will never do key mapping to standard Enable names for non-printable + characters: if there is no appropriate unicode representation, no event + will be emitted. + + The handful of Enable widgets which expect actual text input will be + changed to use the character event instead of the key_pressed event. + + On Wx this event will be generated by EVT_CHAR, on Qt, this event + will be generated by keyPressEvent, but will access the 'text()' attribute + rather than the 'key()' attribute. + +Why Three Events? +----------------- + +Qt's two-event model is generally nicer and cleaner, but adapting the other +backends to use that model would require holding global state from EVT_KEY_DOWN +(or its equivalent) to fold in to the Enable event generated from EVT_CHAR. +This seems potentially fragile. The other alternative would be to try to work +out from the EVT_KEY_DOWN what text would be generated, and that seems even more +fragile. + +On the other hand, adapting Qt to a 3-event model is straightforward: on +keyPressedEvent we generate a 'key_pressed' event, and if text() is non-empty, +we subsequently also emit a 'character' event. + + +Drag and Drop Events +==================== + +Enable has support for objects being dropped onto components built on top of +the backend's (and, in practice, PyFace's) support for drag and drop. + +All drag-and-drop related events have type ``DragEvent`` and provide the +``x`` and ``y`` coordinates of the event, and the object being dragged or +dropped (if it is available from the backend, otherwise this will be ``None``). + +The system generates 3 types of drag and drop events: + +``drag_over`` + These events are generated when the user is moving a dragged object over + the Enable window. Tools or other Interactors which want to indicate that + they can accept the drag should indicate this by calling the + ``set_drag_result()`` method of the Enable window indicating the type of + operation that will be performed on the object (the default is a "copy", + but other possibilties include "move" or "link", the full list of + possibilites is found in the appropriate ``constants`` module). The value + of the drag result influences the way that the operating system displays + the dragged objects and cursor whil dragging. + +``drag_leave`` + This event is generated when the user drags objects out of the window. + +``dropped_on`` + This event is generated when the user releases the mouse button over the + Enable window while dragging. Tools or other Interactors should handle + this event to perform whatever operations need to be performed with the + dropped objects. + +BaseDropTool +------------ + +As a convenience, there is a ``BaseDropTool`` class which handles most of the +drag and drop interactions for you correctly. To use this, you need to +subclass and override at least the ``accept_drop`` and ``handle_drop`` methods. + +``accept_drop`` + This method is given the position and object instance and should return + ``True`` if the drop is accepted or ``False`` if it is not. + +``handle_drop`` + If the drop is accepted, this method is called with the position and the + objects, and should perform whatever actions are required with the dropped + objects. + +The behaviour is slightly different between the Wx and Qt backends: Qt provides +a reference to the dragged objects during ``drag_over`` events, and so the +``BaseDropTool`` will call ``accept_drop`` during ``drag_over`` events to give +better feedback about the state of the drag and drop operation; whereas Wx does +not provide that information, so will always indicate to the operating system +that a drop is possible. + +The type of drag result returned during ``drag_over`` events is controlled by +the ``default_drag_result`` attribute. + +If you want more control over the response to ``drag_over`` events, then you +can additionally override the ``get_drag_result`` method to return one of the +drag result constants dependin on the position and (possibly) the objects +being dragged. If you want cross-toolkit compatibility, you must handle the +case where the ``get_drag_result`` method is called with the object being +``None``, which indicates that the object is not known yet. diff --git a/docs/source/enable/key_events.rst b/docs/source/enable/key_events.rst deleted file mode 100644 index 2f3ae10d0..000000000 --- a/docs/source/enable/key_events.rst +++ /dev/null @@ -1,85 +0,0 @@ -Enable Keyboard Events -====================== - -Previous versions of Enable had just one keyboard event, a 'key_pressed' -event. This event conflated which key(s) were physically being pressed -with the text which should be generated by that keypress. All the underlying -windowing/event systems have a way of distinguishing these two pieces of -information, either by issuing separate events for each (Wx, Pyglet, Vtk), or -by storing them as different attributes on the event object (Qt). - -In addition, there was no way to detect a key-up event. This is a comparatively -minor use case, but potentially useful for doing things like toggling pointer -states to indicate different mouse behaviour when modifiers keys are pressed. - -New Events ----------- - -key_pressed: - We will keep the 'key_pressed' event. However the semantics will change so - that it will keep track of the physical key being pressed rather than the - text being entered. Thus pressing the 'a' key with the shift key down will - result in an event with the 'character' attribute set to 'a', instead of 'A' - as it would be now. The lower-case version of the character will be the - canonical representation of the key, since that will cause minimal problems - with existing event handlers in Enable and Chaco. - - The character mapping will be used as it is now to map special keys to - standard strings (like right arrow generating the string 'right'). - - In addition, events will now be generated by pressing modifier keys by - themselves (eg. pressing shift will generate an event). - - Most Chaco code will continue to use key_pressed as the primary event to - listen to, it will just work more consistently than it did before. Some - code may need to change to reflect the change in what the character - attribute returns (XXX or we add a 'key' attribute and deprecate the - 'character' attribute). - - On Wx this event will be generated by EVT_KEY_DOWN, on Qt, this event - will be generated by keyPressEvent, but will access the 'key()' attribute - rather than the 'text()' attribute. - -key_released: - We will introduce a new 'key_released' event, which is essentially the - mirror image of the 'key_pressed' event. - - On Wx this event will be generated by EVT_KEY_UP, on Qt, this event - will be generated by keyReleaseEvent, but will access the 'key()' attribute - rather than the 'text()' attribute. - -character: - We will introduce a new 'character' event, which will hold the - unicode characters, if any, that are generated by the key press - event. It is possible that this event may also be generated by - other mechanisms in the future, like pasting text. The event may - hold multiple characters. - - This event will not be generated if the key_pressed event is handled. - This is largely because this is the way that wx works, but it also makes - a certain amount of sense from an interaction modelling point of view. - - This will never do key mapping to standard Enable names for non-printable - characters: if there is no appropriate unicode representation, no event - will be emitted. - - The handful of Enable widgets which expect actual text input will be - changed to use the character event instead of the key_pressed event. - - On Wx this event will be generated by EVT_CHAR, on Qt, this event - will be generated by keyPressEvent, but will access the 'text()' attribute - rather than the 'key()' attribute. - -Why Three Events? ------------------ - -Qt's two-event model is generally nicer and cleaner, but adapting the other -backends to use that model would require holding global state from EVT_KEY_DOWN -(or its equivalent) to fold in to the Enable event generated from EVT_CHAR. -This seems potentially fragile. The other alternative would be to try to work -out from the EVT_KEY_DOWN what text would be generated, and that seems even more -fragile. - -On the other hand, adapting Qt to a 3-event model is straightforward: on -keyPressedEvent we generate a 'key_pressed' event, and if text() is non-empty, -we subsequently also emit a 'character' event. diff --git a/docs/source/enable/mouse_events.rst b/docs/source/enable/mouse_events.rst deleted file mode 100644 index bea8eebfa..000000000 --- a/docs/source/enable/mouse_events.rst +++ /dev/null @@ -1,53 +0,0 @@ -Enable Mouse Events -=================== - -Enable mouse events are represented by the :class:`~.MouseEvent` type and their -event names (which are the suffixes used by -:py:meth:`enable.interactor.Interactor.dispatch`) can be divided into two -groups: mouse clicks and mouse movements. The mouse click events have names -ending in ``_down``, ``_up``, or ``_dclick`` and names beginning with ``left``, -``right``, or ``middle``. This means that Enable only supports three mouse -buttons (plus wheel events). - -Event types ------------ - -\*_down -~~~~~~~ -A mouse button was pressed. Dispatched as ``left_down``, ``right_down``, or -``middle_down``. - -\*_up -~~~~~ -A mouse button was released. Dispatched as ``left_up``, ``right_up``, or -``middle_up``. - -\*_dclick -~~~~~~~~~ -A mouse button was double-clicked. Dispatched as ``left_dclick``, -``right_dclick``, or ``middle_dclick``. - -move -~~~~ -The mouse moved within a component. - -enter -~~~~~ -The mouse moved into a component's bounds. - -leave -~~~~~ -The mouse moved out of a component's bounds. - -wheel -~~~~~ -The mouse wheel moved. - -MouseEvent ----------- -Below is a listing of the traits on a `MouseEvent` instance. - -.. autoclass:: enable.events.MouseEvent - :members: alt_down, control_down, shift_down, left_down, middle_down, right_down, mouse_wheel, mouse_wheel_axis, mouse_wheel_delta - :noindex: - diff --git a/docs/source/enable/overview.rst b/docs/source/enable/overview.rst index 3e4777f7c..5b7bc2ba9 100644 --- a/docs/source/enable/overview.rst +++ b/docs/source/enable/overview.rst @@ -277,3 +277,62 @@ Container nested. Containers are responsible for event dispatch, draw dispatch, and layout. Containers override a lot of Component methods, so that they behave more like containers than plain components do. + +Top-level Windows +----------------- +When a component is shown on screen via a GUI toolkit, its :attr:`window` trait +contains an instance of :class:`~.AbstractWindow` which serves as a delegate +between the underlying window system and the component. + +For the most part, code doesn't need to interact with the underlying window. +However one common exception is tools which want to set a custom cursor. This +is accomplished via the :py:meth:`set_pointer` method. + +AbstractWindow +~~~~~~~~~~~~~~ +The following methods are the public interface of :class:`AbstractWindow`. + +.. automethod:: enable.abstract_window.AbstractWindow.get_pointer_position + :noindex: + +.. automethod:: enable.abstract_window.AbstractWindow.redraw + :noindex: + +.. automethod:: enable.abstract_window.AbstractWindow.set_mouse_owner + :noindex: + +.. automethod:: enable.abstract_window.AbstractWindow.set_pointer + :noindex: + +.. automethod:: enable.abstract_window.AbstractWindow.set_tooltip + :noindex: + + +Enable TraitsUI Editors +----------------------- + +To facilitate the inclusion of Enable :class:`~.Component` objects in +`TraitsUI GUIs `_, Enable provides +:class:`~.ComponentEditor`. + +ComponentEditor +~~~~~~~~~~~~~~~ +:class:`~.ComponentEditor` is a fairly simple editor. It only has a few traits +which are of interest to users: + +bgcolor +^^^^^^^ +``bgcolor`` is a :class:`ColorTrait` which can be used to specify the background +color of the component. The default value is ``"sys_window"``, which may or may +not match the default window background color of the GUI toolkit you are using. + +high_resolution +^^^^^^^^^^^^^^^ +``high_resolution`` is a boolean which, if True, tells Enable that you would +like your component to take advantage of HiDPI displays if the GUI toolkit +supports it. The default value is True. + +size +^^^^ +``size`` is a tuple of integers which can be used to specify the initial size of +the component in a GUI. The default value is ``(400, 400)``. diff --git a/docs/source/enable/undo_redo.rst b/docs/source/enable/tools.rst similarity index 75% rename from docs/source/enable/undo_redo.rst rename to docs/source/enable/tools.rst index 7427d5080..af08f7a86 100644 --- a/docs/source/enable/undo_redo.rst +++ b/docs/source/enable/tools.rst @@ -1,5 +1,79 @@ +============ +Enable Tools +============ + +Enable ``Tools`` are ``Interator`` subclasses that do not have to have any +visual representation, and which can be dynamically added and removed from +components by adding or removing them from the component's ``tools`` list. +This permits developers to quickly build up complex behaviours from simple, +reproducible parts without having complex inheritance hierarchies. + +Basic Tools +----------- + +Enable provides a number of basic tools for common interactions. + +ButtonTool +~~~~~~~~~~ + +The :py:class:`ButtonTool` provides basic push-button or checkbox +interactions, depending on how it is configured. The primary interface it +provides is a :py:attr:`clicked` event which is fired when the user clicks in +the region of the underlying component, or when the :py:meth:`click` method is +called. The :py:attr:`clicked` event is fired on mouse up. + +To get checkbox-style behaviour, set :py:attr:`togglable` to ``True`` and +then every click will invert the :py:attr:`checked` trait. The toggle state +can also be changed via the :py:meth:`toggle` method, which does not fire the +:py:attr:`clicked` event when called. For buttons with multi-state toggles, +subclasses can override the :py:meth:`toggle` method to perform more complex +state changes. + +By default, the tool responds to clicks that are within the associated +component, but subclasses can override this behaviour by replacing the +:py:meth:`is_clickable` method with something else. + +It will commonly be the case that components or :py:class:`ButtonTool` +subclasses which draw may wish to respond to user interactions by drawing +themselves in a highlighted or selected mode when the mouse is down inside +the button region. The :py:attr:`down` trait provides this information +conveniently, so that users of the tool can change their drawing state and +request redraws when it changes. + +DragTool +~~~~~~~~ + +The :py:class:`DragTool` is an abstract base class that provides basic +interaction support for draging within Enable. Many other tools within +Enable and Chaco use it. + +HoverTool +~~~~~~~~~ + +The :py:class:`HoverTool` is a simple tool that calls a callback when the +mouse has been held steadily over the component for a period of time. + +MoveTool +~~~~~~~~ + +A :py:class:`DragTool` subclass that allows a user to move a component around +its container by dragging. + +ResizeTool +~~~~~~~~~~ + +A :py:class:`DragTool` subclass that allows a user to resize a component by +dragging from the edges of the component. + +ValueDragTool +~~~~~~~~~~~~~ + +A :py:class:`DragTool` subclass that allows a drag operation to set an +arbitrary value. + + Undo/Redo Support -================= +----------------- The `enable.tools.pyface` package has a number of modules that provide classes for working with Pyface's Undo/Redo stack. This permits Enable @@ -15,7 +89,8 @@ High-Level Tools There are three tools that provide convenient facilities and reference implementations of interacting with the undo/redo stack. -``UndoTool`` +UndoTool +^^^^^^^^ The ``UndoTool`` binds keystrokes to undo and redo operations. The ``undo_keys`` and ``redo_keys`` attributes each take a list of ``KeySpec`` @@ -36,7 +111,8 @@ implementations of interacting with the undo/redo stack. ) my_component.tools.append(undo_tool) -``MoveCommandTool`` +MoveCommandTool +^^^^^^^^^^^^^^^ The ``MoveCommandTool`` is a subclass of ``MoveTool`` that by default issues a ``MoveCommand`` at the end of every successful drag move. @@ -59,7 +135,8 @@ implementations of interacting with the undo/redo stack. that should expect keyword arguments ``component``, ``data`` (the new position), ``previous_position``, and ``mergeable``. -``ResizeCommandTool`` +ResizeCommandTool +^^^^^^^^^^^^^^^^^ The ``ResizeCommandTool`` is a subclass of ``ResizeTool`` that issues ``ResizeCommand`` s at the end of every successful drag move. diff --git a/docs/source/enable/traitsui.rst b/docs/source/enable/traitsui.rst deleted file mode 100644 index dc923554b..000000000 --- a/docs/source/enable/traitsui.rst +++ /dev/null @@ -1,27 +0,0 @@ -Enable TraitsUI Editors -======================= -To facilitate the inclusion of Enable :class:`~.Component` objects in -`TraitsUI GUIs `_, Enable provides -:class:`~.ComponentEditor`. - -ComponentEditor ---------------- -:class:`~.ComponentEditor` is a fairly simple editor. It only has a few traits -which are of interest to users: - -bgcolor -~~~~~~~ -``bgcolor`` is a :class:`ColorTrait` which can be used to specify the background -color of the component. The default value is ``"sys_window"``, which may or may -not match the default window background color of the GUI toolkit you are using. - -high_resolution -~~~~~~~~~~~~~~~ -``high_resolution`` is a boolean which, if True, tells Enable that you would -like your component to take advantage of HiDPI displays if the GUI toolkit -supports it. The default value is True. - -size -~~~~ -``size`` is a tuple of integers which can be used to specify the initial size of -the component in a GUI. The default value is ``(400, 400)``. diff --git a/docs/source/index.rst b/docs/source/index.rst index 35bb9b5a4..a6895bce4 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -1,6 +1,14 @@ Enable Documentation ==================== +Tutorials +--------- + +.. toctree:: + :maxdepth: 2 + + kiva_tutorial/index + Kiva ---- @@ -8,13 +16,8 @@ Kiva :maxdepth: 2 kiva/overview - kiva/backends - kiva/fonts - kiva/compiled_path - kiva/images - kiva/state kiva/quickref - kiva_tutorial/index + kiva/drawing_details Enable ------ @@ -23,15 +26,10 @@ Enable :maxdepth: 2 enable/overview - enable/abstract_window + enable/tools + enable/events enable/constraints_layout - enable/key_events - enable/mouse_events - enable/basic_tools - enable/drag_and_drop enable/traits - enable/traitsui - enable/undo_redo enable/toolkit_selection credits diff --git a/docs/source/kiva/backends.rst b/docs/source/kiva/backends.rst deleted file mode 100644 index 6679f80f0..000000000 --- a/docs/source/kiva/backends.rst +++ /dev/null @@ -1,50 +0,0 @@ -Kiva Backends -============= - -GUI-capable ------------ -Each of these backends can be used to draw the contents of windows in a -graphical user interface. - -kiva.agg/image -~~~~~~~~~~~~~~ -This is a wrapper of the popular Anti-Grain Geometry C++ library. It is the -current default backend. - -cairo -~~~~~ -A backend based on the `Cairo graphics library `_. - -celiagg -~~~~~~~ -A newer wrapper of Anti-Grain Geometry which is maintained outside of -kiva/enable. - -gl -~~ -OpenGL drawing. This backend is quite limited compared to others. - -qpainter -~~~~~~~~ -Qt ``QPainter`` drawing. This is only availble with the Qt toolkit. - -quartz -~~~~~~ -macOS Quartz graphics (ie `CGContext `_). -This is only available on macOS. - -File-only ---------- -Each of these backends can be used to create an output file. - -pdf -~~~ -A backend which writes PDF files. - -ps -~~ -A backend which writes PostScript files. - -svg -~~~ -A backend which writes SVG files. diff --git a/docs/source/kiva/compiled_path.rst b/docs/source/kiva/compiled_path.rst deleted file mode 100644 index 14e236943..000000000 --- a/docs/source/kiva/compiled_path.rst +++ /dev/null @@ -1,35 +0,0 @@ -CompiledPath -============ - -A path is a collection of geometric objects that can be drawn in a graphics -context with coloring and an affine transformation applied to it. It is the -basic unit of drawing in a graphics context. - -Every graphics context instance has a current path which can be manipulated by -the :ref:`kiva_path_functions`. However, some drawing operations are easier to -implement with an independent path instance -(specifically :py:meth:`draw_path_at_points`). - -An independent path instance can be created in two ways. The first is via the -:py:meth:`GraphicsContext.get_empty_path` method. The second method is to use -the :class:`CompiledPath` class imported from the backend being used. The -interface of a :class:`CompiledPath` instance is the same as the -:ref:`kiva_path_functions` (modulo :py:meth:`get_empty_path`). - -Once you have a path object, it can be drawn by adding it to the graphics -context with the :py:meth:`GraphicsContext.add_path` method (which adds the path -to the current path) and then calling any of the :ref:`kiva_drawing_functions` -which operate on the current path. - -For certain backends which support it, the -:py:meth:`GraphicsContext.draw_path_at_points` method can be used to draw a -path object at many different positions with a single function call. - -Example -------- -.. image:: images/compiled_path_ex.png - :width: 300 - :height: 300 - -.. literalinclude:: compiled_path_ex.py - :linenos: diff --git a/docs/source/kiva/drawing_details.rst b/docs/source/kiva/drawing_details.rst new file mode 100644 index 000000000..a05ddc56c --- /dev/null +++ b/docs/source/kiva/drawing_details.rst @@ -0,0 +1,343 @@ +===================== +Kiva Drawing In-depth +===================== + +Kiva State +========== +Kiva is a "stateful" drawing API. What this means is that the graphics context +has a collection of state which affects the results of its drawing actions. +Furthermore, Kiva enables this state to be managed with a stack such that state +can be "pushed" onto the stack before making some temporary changes and then +"popped" off the stack to restore the state to a version which no longer +includes those changes. + +State Components +---------------- +Here is a list of all the pieces of state tracked by a Kiva graphics context, +along with the methods which operate on them: + +* Affine transformation (:py:meth:`translate_ctm`, :py:meth:`rotate_ctm`, + :py:meth:`scale_ctm`, :py:meth:`concat_ctm`, :py:meth:`set_ctm`, + :py:meth:`get_ctm`) +* Clipping (:py:meth:`clip_to_rect`, :py:meth:`clip_to_rects`, :py:meth:`clip`, + :py:meth:`even_odd_clip`) +* Fill color (:py:meth:`set_fill_color`, :py:meth:`get_fill_color`, + :py:meth:`linear_gradient`, :py:meth:`radial_gradient`) +* Stroke color (:py:meth:`set_stroke_color`, :py:meth:`get_stroke_color`) +* Line width (:py:meth:`set_line_width`) +* Line join style (:py:meth:`set_line_join`) +* Line cap style (:py:meth:`set_line_cap`) +* Line dashing (:py:meth:`set_line_dash`) +* Global transparency (:py:meth:`set_alpha`, :py:meth:`get_alpha`) +* Anti-aliasing (:py:meth:`set_antialias`, :py:meth:`get_antialias`) +* Miter limit (:py:meth:`set_miter_limit`) +* Flatness (:py:meth:`set_flatness`) +* Image interpolation (:py:meth:`set_image_interpolation`, :py:meth:`get_image_interpolation`) +* Text drawing mode (:py:meth:`set_text_drawing_mode`) + +Color +----- +Kiva has two colors in its graphics state: stroke color and fill color. Stroke +color is used for the lines in paths when the drawing mode is ``STROKE``, +``FILL_STROKE`` or ``EOF_FILL_STROKE``. Fill color is used for text and for +the enclosed sections of paths when the drawing mode is ``FILL``, ``EOF_FILL``, +``FILL_STROKE``, or ``EOF_FILL_STROKE``. Additionally, the fill color can be +set by the :py:meth:`linear_gradient` and :py:meth:`radial_gradient` methods. + +.. note:: + Even though text uses the fill color, text will not be filled with a + gradient *unless* the text drawing mode is ``TEXT_FILL_STROKE`` and even that + will only work if the backend supports it. + +Color values should always be passed in as 3- or 4- tuples. The order of the +color components is ``(R, G, B[, A])`` and values must be floating point numbers +in the range [0, 1]. Even if a graphics context is not able to draw with alpha +blending, it's still OK to pass a 4 component color value when setting state. + +State Stack Management +---------------------- +Graphics context instances have two methods for saving and restoring the state, +:py:meth:`save_state` ("push") and :py:meth:`restore_state` ("pop"). That said, +it isn't recommended practice to call the methods directly. Instead, you can +treat the graphics context object as a +`context manager `_ +and use the ``with`` keyword to create a block of code where the graphics state +is temporarily modified. Using the context manager approach provides safety from +"temporary" modifications becoming permanent if an uncaught exception is raised +while drawing. + +In Enable and Chaco, it is frequently the case that a graphics context instance +will be passed into a method for the purpose of some drawing. Because it is not +reasonable to push the responsibility of state management "up" the call stack, +the onus is on the code making state modifications to do them safely so that +other changes don't leak into other code. + +**Well behaved code should take care to only modify graphics state inside a** +``with`` **block**. + +Example +------- +.. image:: images/state_ex.png + :width: 300 + :height: 300 + +First, the whole example: + +.. literalinclude:: state_ex.py + :linenos: + +The first part sets up the default graphics state. Here, that includes a scale +of 2 in X and Y, a translation of (150, 150) which is affected by the +preceeding scale transformation, and some line properties: stroke color, width, +join, and cap: + +.. literalinclude:: state_ex.py + :lines: 7-13 + :linenos: + :lineno-match: + +Then in a loop, we draw twice (the two :py:meth:`stroke_path` calls). The first +draw uses a ``with`` block to temporarily modify the drawing state. It adds more +affine transformations: a rotate and a translate. It also changes some line +properties: stroke color, width, and cap. A rectangle is then added to the +current path and stroked. + +.. literalinclude:: state_ex.py + :lines: 17-24 + :linenos: + :lineno-match: + +After leaving the first ``with`` block, the state is now restored to its +default. A new ``with`` block is entered and the current transformation matrix +is modified with the same rotation as the first drawing block, but a +*different* translation is applied. The line properties are unchanged +and so use the defaults set at the top. + +.. literalinclude:: state_ex.py + :lines: 26-31 + :linenos: + :lineno-match: + + +CompiledPath +============ + +A path is a collection of geometric objects that can be drawn in a graphics +context with coloring and an affine transformation applied to it. It is the +basic unit of drawing in a graphics context. + +Every graphics context instance has a current path which can be manipulated by +the :ref:`kiva_path_functions`. However, some drawing operations are easier to +implement with an independent path instance +(specifically :py:meth:`draw_path_at_points`). + +An independent path instance can be created in two ways. The first is via the +:py:meth:`GraphicsContext.get_empty_path` method. The second method is to use +the :class:`CompiledPath` class imported from the backend being used. The +interface of a :class:`CompiledPath` instance is the same as the +:ref:`kiva_path_functions` (modulo :py:meth:`get_empty_path`). + +Once you have a path object, it can be drawn by adding it to the graphics +context with the :py:meth:`GraphicsContext.add_path` method (which adds the path +to the current path) and then calling any of the :ref:`kiva_drawing_functions` +which operate on the current path. + +For certain backends which support it, the +:py:meth:`GraphicsContext.draw_path_at_points` method can be used to draw a +path object at many different positions with a single function call. + +Example +------- +.. image:: images/compiled_path_ex.png + :width: 300 + :height: 300 + +.. literalinclude:: compiled_path_ex.py + :linenos: + + +Kiva Image Rendering +==================== + +Drawing images in kiva is accomplished via +:py:meth:`GraphicsContext.draw_image`. A unique feature of drawing images +(relative to path drawing) is that you can apply an arbitrary translation and +scaling to the image without involving the current transformation matrix. + +The signature for :py:meth:`draw_image` is straightforward: + +.. automethod:: kiva.abstract_graphics_context.AbstractGraphicsContext.draw_image + :noindex: + +The ``image`` object that is passed to :py:meth:`draw_image` can be a numpy +array, a `PIL `_ ``Image`` instance, +or another ``GraphicsContext`` instance of the same backend. If ``image`` is a +numpy array, it is typically converted to a more convenient format via +`PIL.Image.fromarray `_. +Therefore, one must be careful about the expected pixel format of the image. If +your image is rendering with incorrect colors, this might be the problem. +Passing the other allowed versions of ``image`` should give a more consistent +result. + +If ``image`` contains an alpha channel and transparent or translucent pixels, +this transparency should also be honored by the destination graphics context. +However, not all backends may support this. + +Regarding the ``rect`` argument to :py:meth:`draw_image`, if it is not +specified then the bounding rectangle of the graphics context will be used. As +mentioned before, ``rect`` can be used to apply an arbitrary translation and +scaling to an image. The translation is the x,y position of the rectangle and +the scaling is the ratio of the image's width and height to those of the +rectangle. In every case, ``rect`` will be transformed by the current +transformation matrix. + +Special considerations +---------------------- +If you only want to draw a subset of an image, you should pass only that subset +to :py:meth:`draw_image`. The Kiva API does not support defining a "source" +rectangle when drawing images, only a "destination". + +If drawing images with some scaling applied, one might wish to have control +over the interpolation used when drawing the image. This can be accomplished +with the :py:meth:`set_image_interpolation` method. + +.. note:: + :py:meth:`set_image_interpolation` is currently only implemented by the + ``kiva.agg`` backend. Other backends may have the method, but it is + effectively a no-op. + +Saving images +------------- +One can also save the contents of a graphics context to an image. This is done +via the :py:meth:`save` method: + +.. automethod:: kiva.abstract_graphics_context.AbstractGraphicsContext.save + :noindex: + + +Kiva Text Rendering +=================== + +Drawing text in kiva is accomplished via a few methods on +:class:`GraphicsContext`. There are three basic topics: selecting a font, +measuring the size of rendered text, and drawing the text. + +Font Selection +++++++++++++++ + +Font selection for use with the text rendering capabilities of +:class:`GraphicsContext` can be accomplished in a few different ways depending +on the amount of control needed by your drawing code. + +Simplest: ``select_font`` +------------------------- + +The simplest form of font selection is the +:py:meth:`GraphicsContext.select_font` method. The tradeoff for this simplicity +is that you're at the mercy of the backend's font lookup. If your desired font +isn't available from the system you're using, it's not defined what you will end +up with. + +``select_font(name, size=12)`` + +``name`` is the name of the desired font: "Helvetica Regular", +"Futura Medium Italic", etc. + +``size`` is the size in points. + +**Supported backends**: cairo, celiagg, pdf, ps, qpainter, quartz, svg. + + +The ``KivaFont`` trait and ``set_font`` +--------------------------------------- + +If you're already doing your drawing within an application using traits, you can +use the :class:`kiva.trait_defs.api.KivaFont` trait. + +``KivaFont`` traits are initialized with a string which describes the font: +"Times Italic 18", "Courier Bold 10", etc. The *value* of the trait is a +:class:`kiva.fonttools.font.Font` instance which can be passed to the +:py:meth:`GraphicsContext.set_font` method. + +**Supported backends**: all backends + + +``Font`` objects +---------------- + +If you don't want to rely on the font description parsing in ``KivaFont``, you +can also manually construct a :class:`kiva.fonttools.font.Font` instance. Once +you have a ``Font`` instance, it can be passed to the +:py:meth:`GraphicsContext.set_font` method. + +``Font(face_name="", size=12, family=SWISS, weight=NORMAL, style=NORMAL)`` + +``face_name`` is the font's name: "Arial", "Webdings", "Verdana", etc. + +``size`` is the size in points + +``family`` is a constant from :py:mod:`kiva.constants`. Pick from ``DEFAULT``, +``SWISS``, ``ROMAN``, ``MODERN``, ``DECORATIVE``, ``SCRIPT``, or ``TELETYPE``. +If ``face_name`` is empty, the value of ``family`` will be used to select the +desired font. + +``weight`` is a constant from :py:mod:`kiva.constants`. Pick from ``NORMAL`` or +``BOLD``. + +``style`` is a constant from :py:mod:`kiva.constants`. Pick from ``NORMAL`` or +``ITALIC``. + + +Measuring Text +++++++++++++++ + +Before drawing text, one often wants to know what the bounding rectangle of the +rendered text will be so that the text can be positioned correctly. To do this, +the :py:meth:`GraphicsContext.get_text_extent` method is used. + +``get_text_extent(text) -> (x, y, width, height)`` + +``text`` is the string that you want to measure. The currently selected font +will be used, *so it's important to set the font before calling this method.* + +The return value is a ``tuple`` which describes a rectangle with its bottom-left +corner at (x, y) and a width and height. The rectangle is relative to the +origin and not affected by the currently set text transform. The bottom of the +rectangle won't always be 0, depending on the font. It might be a negative +number in the situation where glyphs hang below the baseline. In any case, +``y = 0`` is the baseline for the rendered glyphs. + +.. note:: + ``get_text_extent`` does not respect endline characters. It is assumed that + ``text`` describes a single line of text. To render multiple lines, one + should split the text into individual lines first and then measure and draw + each line in sequence. A blank line's height should be the same as the + height of the selected font. + + +Drawing Text +++++++++++++ + +Text can be drawn in a graphics context with the +:py:meth:`GraphicsContext.show_text` and +:py:meth:`GraphicsContext.show_text_at_point` methods. + +``show_text(text, point=None)`` + +``show_text_at_point(text, x, y)`` + +``show_text_at_point`` or ``show_text`` with a ``point=(x, y)`` argument both +do the same thing: Draw a line of text at the given (x, y) coordinate, which +represents the horizontal position of the first glyph and the baseline position, +respectively. + +If ``show_text`` is used *without* a ``point`` argument, then the current text +position of the graphics context is used. This position can be set via the +:py:meth:`GraphicsContext.set_text_position` method. Relatedly, the text +position can be retrieved with the :py:meth:`GraphicsContext.get_text_position` +method. + +.. note:: + There is also a :py:meth:`GraphicsContext.set_text_matrix` method which + allows a text-specific affine transform to be set. Unfortunately it's not + implemented uniformly across backends, so it's recommended not to use it. diff --git a/docs/source/kiva/fonts.rst b/docs/source/kiva/fonts.rst deleted file mode 100644 index 21c723de3..000000000 --- a/docs/source/kiva/fonts.rst +++ /dev/null @@ -1,126 +0,0 @@ -Kiva Text Rendering -=================== - -Drawing text in kiva is accomplished via a few methods on -:class:`GraphicsContext`. There are three basic topics: selecting a font, -measuring the size of rendered text, and drawing the text. - -Font Selection -++++++++++++++ - -Font selection for use with the text rendering capabilities of -:class:`GraphicsContext` can be accomplished in a few different ways depending -on the amount of control needed by your drawing code. - -Simplest: ``select_font`` -------------------------- - -The simplest form of font selection is the -:py:meth:`GraphicsContext.select_font` method. The tradeoff for this simplicity -is that you're at the mercy of the backend's font lookup. If your desired font -isn't available from the system you're using, it's not defined what you will end -up with. - -``select_font(name, size=12)`` - -``name`` is the name of the desired font: "Helvetica Regular", -"Futura Medium Italic", etc. - -``size`` is the size in points. - -**Supported backends**: cairo, celiagg, pdf, ps, qpainter, quartz, svg. - - -The ``KivaFont`` trait and ``set_font`` ---------------------------------------- - -If you're already doing your drawing within an application using traits, you can -use the :class:`kiva.trait_defs.api.KivaFont` trait. - -``KivaFont`` traits are initialized with a string which describes the font: -"Times Italic 18", "Courier Bold 10", etc. The *value* of the trait is a -:class:`kiva.fonttools.font.Font` instance which can be passed to the -:py:meth:`GraphicsContext.set_font` method. - -**Supported backends**: all backends - - -``Font`` objects ----------------- - -If you don't want to rely on the font description parsing in ``KivaFont``, you -can also manually construct a :class:`kiva.fonttools.font.Font` instance. Once -you have a ``Font`` instance, it can be passed to the -:py:meth:`GraphicsContext.set_font` method. - -``Font(face_name="", size=12, family=SWISS, weight=NORMAL, style=NORMAL)`` - -``face_name`` is the font's name: "Arial", "Webdings", "Verdana", etc. - -``size`` is the size in points - -``family`` is a constant from :py:mod:`kiva.constants`. Pick from ``DEFAULT``, -``SWISS``, ``ROMAN``, ``MODERN``, ``DECORATIVE``, ``SCRIPT``, or ``TELETYPE``. -If ``face_name`` is empty, the value of ``family`` will be used to select the -desired font. - -``weight`` is a constant from :py:mod:`kiva.constants`. Pick from ``NORMAL`` or -``BOLD``. - -``style`` is a constant from :py:mod:`kiva.constants`. Pick from ``NORMAL`` or -``ITALIC``. - - -Measuring Text -++++++++++++++ - -Before drawing text, one often wants to know what the bounding rectangle of the -rendered text will be so that the text can be positioned correctly. To do this, -the :py:meth:`GraphicsContext.get_text_extent` method is used. - -``get_text_extent(text) -> (x, y, width, height)`` - -``text`` is the string that you want to measure. The currently selected font -will be used, *so it's important to set the font before calling this method.* - -The return value is a ``tuple`` which describes a rectangle with its bottom-left -corner at (x, y) and a width and height. The rectangle is relative to the -origin and not affected by the currently set text transform. The bottom of the -rectangle won't always be 0, depending on the font. It might be a negative -number in the situation where glyphs hang below the baseline. In any case, -``y = 0`` is the baseline for the rendered glyphs. - -.. note:: - ``get_text_extent`` does not respect endline characters. It is assumed that - ``text`` describes a single line of text. To render multiple lines, one - should split the text into individual lines first and then measure and draw - each line in sequence. A blank line's height should be the same as the - height of the selected font. - - -Drawing Text -++++++++++++ - -Text can be drawn in a graphics context with the -:py:meth:`GraphicsContext.show_text` and -:py:meth:`GraphicsContext.show_text_at_point` methods. - -``show_text(text, point=None)`` - -``show_text_at_point(text, x, y)`` - -``show_text_at_point`` or ``show_text`` with a ``point=(x, y)`` argument both -do the same thing: Draw a line of text at the given (x, y) coordinate, which -represents the horizontal position of the first glyph and the baseline position, -respectively. - -If ``show_text`` is used *without* a ``point`` argument, then the current text -position of the graphics context is used. This position can be set via the -:py:meth:`GraphicsContext.set_text_position` method. Relatedly, the text -position can be retrieved with the :py:meth:`GraphicsContext.get_text_position` -method. - -.. note:: - There is also a :py:meth:`GraphicsContext.set_text_matrix` method which - allows a text-specific affine transform to be set. Unfortunately it's not - implemented uniformly across backends, so it's recommended not to use it. diff --git a/docs/source/kiva/images.rst b/docs/source/kiva/images.rst deleted file mode 100644 index 8402ad6bd..000000000 --- a/docs/source/kiva/images.rst +++ /dev/null @@ -1,57 +0,0 @@ -Kiva Image Rendering -==================== - -Drawing images in kiva is accomplished via -:py:meth:`GraphicsContext.draw_image`. A unique feature of drawing images -(relative to path drawing) is that you can apply an arbitrary translation and -scaling to the image without involving the current transformation matrix. - -The signature for :py:meth:`draw_image` is straightforward: - -.. automethod:: kiva.abstract_graphics_context.AbstractGraphicsContext.draw_image - :noindex: - -The ``image`` object that is passed to :py:meth:`draw_image` can be a numpy -array, a `PIL `_ ``Image`` instance, -or another ``GraphicsContext`` instance of the same backend. If ``image`` is a -numpy array, it is typically converted to a more convenient format via -`PIL.Image.fromarray `_. -Therefore, one must be careful about the expected pixel format of the image. If -your image is rendering with incorrect colors, this might be the problem. -Passing the other allowed versions of ``image`` should give a more consistent -result. - -If ``image`` contains an alpha channel and transparent or translucent pixels, -this transparency should also be honored by the destination graphics context. -However, not all backends may support this. - -Regarding the ``rect`` argument to :py:meth:`draw_image`, if it is not -specified then the bounding rectangle of the graphics context will be used. As -mentioned before, ``rect`` can be used to apply an arbitrary translation and -scaling to an image. The translation is the x,y position of the rectangle and -the scaling is the ratio of the image's width and height to those of the -rectangle. In every case, ``rect`` will be transformed by the current -transformation matrix. - -Special considerations ----------------------- -If you only want to draw a subset of an image, you should pass only that subset -to :py:meth:`draw_image`. The Kiva API does not support defining a "source" -rectangle when drawing images, only a "destination". - -If drawing images with some scaling applied, one might wish to have control -over the interpolation used when drawing the image. This can be accomplished -with the :py:meth:`set_image_interpolation` method. - -.. note:: - :py:meth:`set_image_interpolation` is currently only implemented by the - ``kiva.agg`` backend. Other backends may have the method, but it is - effectively a no-op. - -Saving images -------------- -One can also save the contents of a graphics context to an image. This is done -via the :py:meth:`save` method: - -.. automethod:: kiva.abstract_graphics_context.AbstractGraphicsContext.save - :noindex: diff --git a/docs/source/kiva/overview.rst b/docs/source/kiva/overview.rst index cd77c7fb7..19b935f38 100644 --- a/docs/source/kiva/overview.rst +++ b/docs/source/kiva/overview.rst @@ -1,5 +1,6 @@ .. _kiva_overview: +==== Kiva ==== @@ -34,13 +35,13 @@ available on any platform, and should work even if there is no GUI or windowing system available. Kiva Concepts -------------- +============= This section gives a whirlwind tour of the concepts involved with drawing with Kiva. The Graphics Context -~~~~~~~~~~~~~~~~~~~~ +-------------------- The heart of the Kiva drawing API is the "graphics context", frequently abbreviated as ``gc`` in code. The graphics context holds the current drawing @@ -97,7 +98,7 @@ performed in C where possible:: gc.stroke_path() Coordinate Model -~~~~~~~~~~~~~~~~ +---------------- Kiva uses mathematical axes direction conventions as opposed to framebuffer axes conventions. In other words, the origin is always at the *bottom* @@ -132,7 +133,7 @@ is drawn through the center of the pixels: .. image:: images/pixel_coordinates.png The Coordinate Transform Matrix -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +------------------------------- The Kiva API allows arbitrary affine transforms to be applied to the graphics context during drawing. The API provides convenience methods for common @@ -186,7 +187,7 @@ transformations, such as rotation and scaling:: If desired, the user can also supply their own transformations directly. Paths ------ +===== The basic drawing operations are performed by building a path out of primitive operations, and then performing stroking and/or filling operations with it. @@ -339,7 +340,7 @@ Winding vs. Even-Odd Fill:: .. image:: images/fill.png Text -~~~~ +---- Text can be rendered at a point by first setting the font to use, then setting the text location using ``set_text_position()`` and then ``show_text()`` to @@ -359,3 +360,55 @@ render the text:: .. image:: images/text.png Text defaults to being rendered filled, but can be rendered with an outline. + + +Kiva Backends +============= + +GUI-capable +----------- +Each of these backends can be used to draw the contents of windows in a +graphical user interface. + +kiva.agg/image +~~~~~~~~~~~~~~ +This is a wrapper of the popular Anti-Grain Geometry C++ library. It is the +current default backend. + +cairo +~~~~~ +A backend based on the `Cairo graphics library `_. + +celiagg +~~~~~~~ +A newer wrapper of Anti-Grain Geometry which is maintained outside of +kiva/enable. + +gl +~~ +OpenGL drawing. This backend is quite limited compared to others. + +qpainter +~~~~~~~~ +Qt ``QPainter`` drawing. This is only availble with the Qt toolkit. + +quartz +~~~~~~ +macOS Quartz graphics (ie `CGContext `_). +This is only available on macOS. + +File-only +--------- +Each of these backends can be used to create an output file. + +pdf +~~~ +A backend which writes PDF files. + +ps +~~ +A backend which writes PostScript files. + +svg +~~~ +A backend which writes SVG files. diff --git a/docs/source/kiva/state.rst b/docs/source/kiva/state.rst deleted file mode 100644 index c4044577e..000000000 --- a/docs/source/kiva/state.rst +++ /dev/null @@ -1,115 +0,0 @@ -Kiva State -========== -Kiva is a "stateful" drawing API. What this means is that the graphics context -has a collection of state which affects the results of its drawing actions. -Furthermore, Kiva enables this state to be managed with a stack such that state -can be "pushed" onto the stack before making some temporary changes and then -"popped" off the stack to restore the state to a version which no longer -includes those changes. - -State Components ----------------- -Here is a list of all the pieces of state tracked by a Kiva graphics context, -along with the methods which operate on them: - -* Affine transformation (:py:meth:`translate_ctm`, :py:meth:`rotate_ctm`, - :py:meth:`scale_ctm`, :py:meth:`concat_ctm`, :py:meth:`set_ctm`, - :py:meth:`get_ctm`) -* Clipping (:py:meth:`clip_to_rect`, :py:meth:`clip_to_rects`, :py:meth:`clip`, - :py:meth:`even_odd_clip`) -* Fill color (:py:meth:`set_fill_color`, :py:meth:`get_fill_color`, - :py:meth:`linear_gradient`, :py:meth:`radial_gradient`) -* Stroke color (:py:meth:`set_stroke_color`, :py:meth:`get_stroke_color`) -* Line width (:py:meth:`set_line_width`) -* Line join style (:py:meth:`set_line_join`) -* Line cap style (:py:meth:`set_line_cap`) -* Line dashing (:py:meth:`set_line_dash`) -* Global transparency (:py:meth:`set_alpha`, :py:meth:`get_alpha`) -* Anti-aliasing (:py:meth:`set_antialias`, :py:meth:`get_antialias`) -* Miter limit (:py:meth:`set_miter_limit`) -* Flatness (:py:meth:`set_flatness`) -* Image interpolation (:py:meth:`set_image_interpolation`, :py:meth:`get_image_interpolation`) -* Text drawing mode (:py:meth:`set_text_drawing_mode`) - -Color ------ -Kiva has two colors in its graphics state: stroke color and fill color. Stroke -color is used for the lines in paths when the drawing mode is ``STROKE``, -``FILL_STROKE`` or ``EOF_FILL_STROKE``. Fill color is used for text and for -the enclosed sections of paths when the drawing mode is ``FILL``, ``EOF_FILL``, -``FILL_STROKE``, or ``EOF_FILL_STROKE``. Additionally, the fill color can be -set by the :py:meth:`linear_gradient` and :py:meth:`radial_gradient` methods. - -.. note:: - Even though text uses the fill color, text will not be filled with a - gradient *unless* the text drawing mode is ``TEXT_FILL_STROKE`` and even that - will only work if the backend supports it. - -Color values should always be passed in as 3- or 4- tuples. The order of the -color components is ``(R, G, B[, A])`` and values must be floating point numbers -in the range [0, 1]. Even if a graphics context is not able to draw with alpha -blending, it's still OK to pass a 4 component color value when setting state. - -State Stack Management ----------------------- -Graphics context instances have two methods for saving and restoring the state, -:py:meth:`save_state` ("push") and :py:meth:`restore_state` ("pop"). That said, -it isn't recommended practice to call the methods directly. Instead, you can -treat the graphics context object as a -`context manager `_ -and use the ``with`` keyword to create a block of code where the graphics state -is temporarily modified. Using the context manager approach provides safety from -"temporary" modifications becoming permanent if an uncaught exception is raised -while drawing. - -In Enable and Chaco, it is frequently the case that a graphics context instance -will be passed into a method for the purpose of some drawing. Because it is not -reasonable to push the responsibility of state management "up" the call stack, -the onus is on the code making state modifications to do them safely so that -other changes don't leak into other code. - -**Well behaved code should take care to only modify graphics state inside a** -``with`` **block**. - -Example -------- -.. image:: images/state_ex.png - :width: 300 - :height: 300 - -First, the whole example: - -.. literalinclude:: state_ex.py - :linenos: - -The first part sets up the default graphics state. Here, that includes a scale -of 2 in X and Y, a translation of (150, 150) which is affected by the -preceeding scale transformation, and some line properties: stroke color, width, -join, and cap: - -.. literalinclude:: state_ex.py - :lines: 7-13 - :linenos: - :lineno-match: - -Then in a loop, we draw twice (the two :py:meth:`stroke_path` calls). The first -draw uses a ``with`` block to temporarily modify the drawing state. It adds more -affine transformations: a rotate and a translate. It also changes some line -properties: stroke color, width, and cap. A rectangle is then added to the -current path and stroked. - -.. literalinclude:: state_ex.py - :lines: 17-24 - :linenos: - :lineno-match: - -After leaving the first ``with`` block, the state is now restored to its -default. A new ``with`` block is entered and the current transformation matrix -is modified with the same rotation as the first drawing block, but a -*different* translation is applied. The line properties are unchanged -and so use the defaults set at the top. - -.. literalinclude:: state_ex.py - :lines: 26-31 - :linenos: - :lineno-match: