Source code for enable.drawing.drawing_canvas

# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX
# All rights reserved.
# This software is provided without warranty under the terms of the BSD
# license included in LICENSE.txt and may be redistributed only under
# the conditions described in the aforementioned license. The license
# is also available online at
# Thanks for using Enthought open source!
from enable.api import Container, Component, ColorTrait
from kiva.api import FILL, FILL_STROKE
from enable.trait_defs.kiva_font_trait import KivaFont
from traits.api import Any, Bool, Delegate, Enum, Instance, Int, List, Str

[docs]class Button(Component): color = ColorTrait("lightblue") down_color = ColorTrait("darkblue") border_color = ColorTrait("blue") label = Str label_font = KivaFont("modern 12") label_color = ColorTrait("white") down_label_color = ColorTrait("white") button_state = Enum("up", "down") # A reference to the radio group that this button belongs to radio_group = Any # Default size of the button if no label is present bounds = [32, 32] # Generally, buttons are not resizable resizable = "" _got_mousedown = Bool(False)
[docs] def perform(self, event): """ Called when the button is depressed. 'event' is the Enable mouse event that triggered this call. """ pass
def _draw_mainlayer(self, gc, view_bounds, mode="default"): if self.button_state == "up": self.draw_up(gc, view_bounds) else: self.draw_down(gc, view_bounds)
[docs] def draw_up(self, gc, view_bounds): with gc: gc.set_fill_color(self.color_) gc.set_stroke_color(self.border_color_) gc.draw_rect( ( int(self.x), int(self.y), int(self.width) - 1, int(self.height) - 1, ), FILL_STROKE, ) self._draw_label(gc)
[docs] def draw_down(self, gc, view_bounds): with gc: gc.set_fill_color(self.down_color_) gc.set_stroke_color(self.border_color_) gc.draw_rect( ( int(self.x), int(self.y), int(self.width) - 1, int(self.height) - 1, ), FILL_STROKE, ) self._draw_label(gc, color=self.down_label_color_)
def _draw_label(self, gc, color=None): if self.label != "": gc.set_font(self.label_font) x, y, w, h = gc.get_text_extent(self.label) if color is None: color = self.label_color_ gc.set_fill_color(color) gc.set_stroke_color(color) gc.show_text( self.label, ( self.x + (self.width - w - x) / 2, self.y + (self.height - h - y) / 2, ), )
[docs] def normal_left_down(self, event): self.button_state = "down" self._got_mousedown = True self.request_redraw() event.handled = True
[docs] def normal_left_up(self, event): self.button_state = "up" self._got_mousedown = False self.request_redraw() self.perform(event) event.handled = True
[docs]class ToolbarButton(Button): toolbar = Any canvas = Delegate("toolbar") def __init__(self, *args, **kw): toolbar = kw.pop("toolbar", None) super().__init__(*args, **kw) if toolbar: self.toolbar = toolbar toolbar.add(self)
[docs]class DrawingCanvasToolbar(Container): """ The tool bar hosts Buttons and also consumes other mouse events, so that tools on the underlying canvas don't get them. FIXME: Right now this toolbar only supports the addition of buttons, and not button removal. (Why would you ever want to remove a useful button?) """ canvas = Instance("DrawingCanvas") button_spacing = Int(5) auto_size = False _last_button_position = Int(0)
[docs] def add_button(self, *buttons): for button in buttons: self.add(button) button.toolbar = self # Compute the new position for the button button.x = self.button_spacing + self._last_button_position self._last_button_position += ( button.width + self.button_spacing * 2 ) button.y = int((self.height - button.height) / 2)
def _canvas_changed(self, old, new): if old: old.observe( self._canvas_bounds_updated, "bounds.items", remove=True ) if new: new.observe(self._canvas_bounds_updated, "bounds.items") def _canvas_bounds_updated(self, event): self.width = self.canvas.width self.y = self.canvas.height - self.height def _dispatch_stateful_event(self, event, suffix): super()._dispatch_stateful_event( event, suffix ) event.handled = True
[docs]class DrawingCanvas(Container): """ A DrawingCanvas has some buttons which toggle what kind of drawing tools are active on the canvas, then allow arbitrary painting on the canvas. """ # The active tool is the primary interactor on the canvas. It gets # a chance to handle events before they are passed on to other components # and listener tools. active_tool = Any # Listening tools are always enabled and get all events (unless the active # tool has vetoed it), but they cannot prevent other tools from getting # events. listening_tools = List # The background color of the canvas bgcolor = ColorTrait("white") toolbar = Instance(DrawingCanvasToolbar, args=()) fit_window = True
[docs] def dispatch(self, event, suffix): # See if the event happened on the toolbar: event.offset_xy(*self.position) if self.toolbar.is_in(event.x, event.y): self.toolbar.dispatch(event, suffix) event.pop() if event.handled: return if self.active_tool is not None: self.active_tool.dispatch(event, suffix) if event.handled: return for tool in self.listening_tools: tool.dispatch(event, suffix) super().dispatch(event, suffix)
[docs] def activate(self, tool): """ Makes the indicated tool the active tool on the canvas and moves the current active tool back into the list of tools. """ self.active_tool = tool
def _draw_container_mainlayer(self, gc, view_bounds=None, mode="default"): active_tool = self.active_tool if active_tool and active_tool.draw_mode == "exclusive": active_tool.draw(gc, view_bounds, mode) else: # super()._draw(gc, view_bounds, mode) for tool in self.listening_tools: tool.draw(gc, view_bounds, mode) if active_tool: active_tool.draw(gc, view_bounds, mode) self.toolbar.draw(gc, view_bounds, mode) def _draw_container_background(self, gc, view_bounds=None, mode="default"): if self.bgcolor not in ("clear", "transparent", "none"): with gc: gc.set_antialias(False) gc.set_fill_color(self.bgcolor_) gc.draw_rect( ( int(self.x), int(self.y), int(self.width) - 1, int(self.height) - 1, ), FILL, ) # ------------------------------------------------------------------------ # Event listeners # ------------------------------------------------------------------------ def _tools_items_changed(self): self.request_redraw()