File: drawing_canvas.py

package info (click to toggle)
python-enable 4.3.0-2
  • links: PTS, VCS
  • area: main
  • in suites: jessie, jessie-kfreebsd
  • size: 7,280 kB
  • ctags: 13,899
  • sloc: cpp: 48,447; python: 28,502; ansic: 9,004; makefile: 315; sh: 44
file content (245 lines) | stat: -rw-r--r-- 7,491 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245

from __future__ import with_statement

from enable.api import Container, Component, ColorTrait
from kiva.constants import FILL, FILL_STROKE
from kiva.trait_defs.kiva_font_trait import KivaFont
from traits.api import Any, Bool, Delegate, Enum, Instance, Int, List, Str


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)

    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)
        return

    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)
        return

    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_)
        return

    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))
        return

    def normal_left_down(self, event):
        self.button_state = "down"
        self._got_mousedown = True
        self.request_redraw()
        event.handled = True
        return

    def normal_left_up(self, event):
        self.button_state = "up"
        self._got_mousedown = False
        self.request_redraw()
        self.perform(event)
        event.handled = True
        return


class ToolbarButton(Button):

    toolbar = Any

    canvas = Delegate("toolbar")

    def __init__(self, *args, **kw):
        toolbar = kw.pop("toolbar", None)
        super(ToolbarButton, self).__init__(*args, **kw)
        if toolbar:
            self.toolbar = toolbar
            toolbar.add(self)
        return


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)

    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)
        return

    def _canvas_changed(self, old, new):
        if old:
            old.on_trait_change(self._canvas_bounds_changed, "bounds", remove=True)
            old.on_trait_change(self._canvas_bounds_changed, "bounds_items", remove=True)

        if new:
            new.on_trait_change(self._canvas_bounds_changed, "bounds")
            new.on_trait_change(self._canvas_bounds_changed, "bounds_items")
        return

    def _canvas_bounds_changed(self):
        self.width = self.canvas.width
        self.y = self.canvas.height - self.height
        return

    def _dispatch_stateful_event(self, event, suffix):
        super(DrawingCanvasToolbar, self)._dispatch_stateful_event(event, suffix)
        event.handled = True
        return



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

    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(DrawingCanvas, self).dispatch(event, suffix)
        return

    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
        return

    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(DrawingCanvas, self)._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)
        return

    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)
        return

    #------------------------------------------------------------------------
    # Event listeners
    #------------------------------------------------------------------------

    def _tools_items_changed(self):
        self.request_redraw()
        return



# EOF