Skip to content

Commit

Permalink
Merge branch 'master' into feature/apptools-undo-tools
Browse files Browse the repository at this point in the history
Conflicts:
	CHANGES.txt
  • Loading branch information
corranwebster committed Mar 16, 2015
2 parents fb92924 + 9773258 commit c299692
Show file tree
Hide file tree
Showing 19 changed files with 1,046 additions and 60 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,6 @@ kiva/agg/src/win32/plat_support_wrap.cpp
kiva/agg/src/x11/plat_support_wrap.cpp
kiva/quartz/ABCGI.c
kiva/quartz/CTFont.c

# Auto-generated version info
kiva/_version.py
1 change: 1 addition & 0 deletions CHANGES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Enhancements

* Added a base class for drag and drop tools, example and test support.
* PR #160: Basic testing for kiva backends.
* PR #168: Simple push-button and checkbox tool.
* PR #167: Added tools that support Apptools Undo/Redo.


Expand Down
2 changes: 2 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
include kiva/agg/agg.i
include chaco/tests/data/PngSuite/*.png
include chaco/tests/data/PngSuite/LICENSE.txt
71 changes: 71 additions & 0 deletions docs/source/enable_basic_tools.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
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:method:`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.
1 change: 1 addition & 0 deletions docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Enable Documentation
enable_concepts.rst
enable_constraints_layout.rst
enable_key_events.rst
enable_basic_tools.rst
enable_drag_and_drop.rst
enable_apptools_integration.rst

Expand Down
3 changes: 2 additions & 1 deletion enable/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
""" A multi-platform object drawing library.
Part of the Enable project of the Enthought Tool Suite.
"""
__version__ = '4.5.0-dev'

from kiva._version import full_version as __version__

__requires__ = [
'traitsui',
Expand Down
74 changes: 74 additions & 0 deletions enable/primitives/image.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
""" Defines the Image component class.
"""

from __future__ import absolute_import

# Enthought library imports
from traits.api import Array, Bool, Enum, Instance, Property, cached_property

# Local imports
from enable.component import Component
from kiva.image import GraphicsContext


class Image(Component):
""" Component that displays a static image
This is extremely simple right now. By default it will draw the array into
the entire region occupied by the component, stretching or shrinking as
needed. By default the bounds are set to the width and height of the data
array, and we provide the same information to constraints-based layout
with the layout_size_hint trait.
"""

#: the image data as an array
data = Array(shape=(None, None, (3,4)), dtype='uint8')

#: the format of the image data (eg. RGB vs. RGBA)
format = Property(Enum('rgb24', 'rgba32'), depends_on='data')

#: the size-hint for constraints-based layout
layout_size_hint = Property(data, depends_on='data')

#: the image as an Image GC
_image = Property(Instance(GraphicsContext), depends_on='data')

@classmethod
def from_file(cls, filename, **traits):
from PIL import Image
from numpy import asarray
data = asarray(Image.open(filename))
return cls(data=data, **traits)

def __init__(self, data, **traits):
# the default bounds are the size of the image
traits.setdefault('bounds', data.shape[1::-1])
super(Image, self).__init__(data=data, **traits)

def _draw_mainlayer(self, gc, view_bounds=None, mode="normal"):
""" Draws the image. """
with gc:
gc.draw_image(self._image, (self.x, self.y, self.width, self.height))

@cached_property
def _get_format(self):
if self.data.shape[-1] == 3:
return 'rgb24'
elif self.data.shape[-1] == 4:
return 'rgba32'
else:
raise ValueError('Data array not correct shape')

@cached_property
def _get_layout_size_hint(self):
return self.data.shape[1::-1]

@cached_property
def _get__image(self):
if not self.data.flags['C_CONTIGUOUS']:
data = self.data.copy()
else:
data = self.data
image_gc = GraphicsContext(data, pix_format=self.format)
return image_gc
48 changes: 47 additions & 1 deletion enable/testing.py
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ def mouse_up(self, interactor, x, y, button='left', window=None,

def mouse_leave(self, interactor, x, y, window=None,
alt_down=False, control_down=False, shift_down=False):
""" Send a mouse click event to the interactor.
""" Send a mouse leave event to the interactor.
Parameters
----------
Expand Down Expand Up @@ -355,6 +355,52 @@ def mouse_leave(self, interactor, x, y, window=None,
self._mouse_event_dispatch(interactor, event, 'mouse_leave')
return event

def mouse_enter(self, interactor, x, y, window=None,
alt_down=False, control_down=False, shift_down=False):
""" Send a mouse enter event to the interactor.
Parameters
----------
interactor : Interactor
The interactor (or subclass) where to dispatch the event.
x : float
The x coordinates of the mouse position
y : float
The y coordinates of the mouse position
window : AbstractWindow, optional
The enable AbstractWindow to associate with the event. Default
is to create a mock class instance. If the window has a mouse
owner then that interactor is used.
alt_down : boolean, optional
The button is pressed while `alt` is down. Default value is False.
control_down : boolean, optional
The button is pressed while `control` is down. Default value is
False.
shift_down : boolean, optional
The button is pressed while `shift` is down. Default value is
False.
Returns
-------
event : MouseEvent
The event instance after it has be processed by the `interactor`.
"""
window = self.create_mock_window() if window is None else window
event = self.create_mouse_event(x=x, y=y,
window=window,
alt_down=alt_down,
control_down=control_down,
shift_down=shift_down)
self._mouse_event_dispatch(interactor, event, 'mouse_enter')
return event

def send_key(self, interactor, key, window=None):
""" Sent a key press event to the interactor.
Expand Down
Empty file.
8 changes: 8 additions & 0 deletions enable/tests/primitives/data/PngSuite/LICENSE.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
PngSuite
--------

Permission to use, copy, modify and distribute these images for any
purpose and without fee is hereby granted.


(c) Willem van Schaik, 1996, 2011
Binary file added enable/tests/primitives/data/PngSuite/basi6a08.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added enable/tests/primitives/data/PngSuite/basn2c08.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit c299692

Please sign in to comment.