""" Shows how to implement a simple drag operation with transitory visual side-effects that get cleaned up when the drag completes. There is a filled container that contains two filled circles, each of which has a different kind of "shadow" object that it dynamically adds to the container when the user clicks and drags the circle around. Because the shadow objects are "first-class" components in the filled container, and because containers default to auto-sizing around their components, the container stretches to the minimum bounding box of its components as the user drags the circles around. """ from __future__ import with_statement from numpy import array from enthought.enable.example_support import DemoFrame, demo_main from enthought.enable.api import Container, Component, Container, Pointer, str_to_font, Window from enthought.traits.api import Any, Array, Enum, Float, Instance, Trait, Tuple class MyFilledContainer(Container): fit_window = False border_width = 2 resizable = "" _font = Any def _draw_container_mainlayer(self, gc, view_bounds, mode="default"): 'Draws a filled container with the word "Container" in the center' if not self._font: self._font = str_to_font(None, None, "modern 10") with gc: gc.set_fill_color(self.bgcolor_) gc.rect(self.x, self.y, self.width, self.height) gc.draw_path() self._draw_border(gc) gc.set_font(self._font) gc.set_fill_color((1.0, 1.0, 1.0, 1.0)) tx, ty, tw, th = gc.get_text_extent("Container") tx = self.x + self.width/2.0 - tw/2.0 ty = self.y + self.height/2.0 - th/2.0 gc.show_text_at_point("Container", tx, ty) return class Circle(Component): """ The circle moves with the mouse cursor but leaves a translucent version of itself in its original position until the mouse button is released. """ color = (0.0, 0.0, 1.0, 1.0) bgcolor = "none" normal_pointer = Pointer("arrow") moving_pointer = Pointer("hand") prev_x = Float prev_y = Float shadow_type = Enum("light", "dashed") shadow = Instance(Component) resizable = "" def __init__(self, **traits): Component.__init__(self, **traits) self.pointer = self.normal_pointer return def _draw_mainlayer(self, gc, view_bounds=None, mode="default"): with gc: gc.set_fill_color(self.color) dx, dy = self.bounds x, y = self.position radius = min(dx/2.0, dy/2.0) gc.arc(x+dx/2.0, y+dy/2.0, radius, 0.0, 2*3.14159) gc.fill_path() return def normal_left_down(self, event): self.event_state = "moving" self.pointer = self.moving_pointer event.window.set_mouse_owner(self, event.net_transform()) # Create our shadow if self.shadow_type == "light": klass = LightCircle else: klass = DashedCircle self.shadow = klass(bounds=self.bounds, position=self.position, color=(1.0, 1.0, 1.0, 1.0)) self.container.insert(0, self.shadow) x, y = self.position self.prev_x = event.x self.prev_y = event.y return def moving_mouse_move(self, event): self.position = [self.x + (event.x - self.prev_x), self.y + (event.y - self.prev_y)] self.prev_x = event.x self.prev_y = event.y self.request_redraw() return def moving_left_up(self, event): self.event_state = "normal" self.pointer = self.normal_pointer event.window.set_mouse_owner(None) event.window.redraw() # Remove our shadow self.container.remove(self.shadow) return def moving_mouse_leave(self, event): self.moving_left_up(event) return class LightCircle(Component): color = Tuple bgcolor = "none" radius = Float(1.0) resizable = "" def _draw_mainlayer(self, gc, view_bounds=None, mode="default"): with gc: gc.set_fill_color(self.color[0:3] + (self.color[3]*0.3,)) dx, dy = self.bounds x, y = self.position radius = min(dx/2.0, dy/2.0) gc.arc(x+dx/2.0, y+dy/2.0, radius, 0.0, 2*3.14159) gc.fill_path() return class DashedCircle(Component): color = Tuple bgcolor = "none" radius = Float(1.0) line_dash = array([2.0, 2.0]) resizable = "" def _draw_mainlayer(self, gc, view_bounds=None, mode="default"): with gc: gc.set_fill_color(self.color) dx, dy = self.bounds x, y = self.position radius = min(dx/2.0, dy/2.0) gc.arc(x+dx/2.0, y+dy/2.0, radius, 0.0, 2*3.14159) gc.set_stroke_color(self.color[0:3] + (self.color[3]*0.8,)) gc.set_line_dash(self.line_dash) gc.stroke_path() return class MyFrame(DemoFrame): def _create_window(self): circle1 = Circle(bounds=[75,75], position=[50, 50], shadow_type="dashed") circle2 = Circle(bounds=[75,75], position=[200, 50], shadow_type="light") container = MyFilledContainer(bounds=[500,500], bgcolor=(0.2, 0.4, 0.3, 1.0)) container.auto_size = True container.add(circle1) container.add(circle2) return Window(self, -1, component=container) if __name__ == "__main__": demo_main(MyFrame, title="Click and drag to move the circles") # EOF