class DragBox(Box): """A drag drawn box """ ########################################################################## # Traits ########################################################################## ### 'DragBox' interface ############################################ # Event fired when complete: complete = Event # Constraints on size: x_bounds = Trait(None, None, Tuple(Float, Float)) y_bounds = Trait(None, None, Tuple(Float, Float)) #### Pointers. #### # Pointer for the complete state: complete_pointer = Pointer('cross') # Pointer for the drawing state: drawing_pointer = Pointer('cross') # Pointer for the normal state: normal_pointer = Pointer('cross') #### Private traits # Position of the left down: start_x = Float start_y = Float ########################################################################## # 'object' interface ########################################################################## ########################################################################## # 'Component' interface ########################################################################## #### 'normal' state ###################################################### def normal_left_down(self, event): """ Handle the left button down in the 'normal' state. """ self.event_state = 'drawing' self.pointer = self.drawing_pointer self.start_x = event.x self.start_y = event.y self._set_bounds(event) return def normal_mouse_move(self, event): """ Handle the mouse moving in the 'normal' state. """ self.pointer = self.normal_pointer return #### 'drawing' state ##################################################### def drawing_mouse_move(self, event): """ Handle the mouse moving in the 'drawing' state. """ self._set_bounds(event) def drawing_left_up(self, event): """ Handle the left mouse button coming up in the 'drawing' state. """ self.event_state = 'complete' self.pointer = self.complete_pointer self.complete = True self._set_bounds(event) return ########################################################################## # Private interface ########################################################################## def _set_bounds(self, event): """ Sets the bounds based on start_x, start_y, the event position and any constrants. """ if self.x_bounds is not None: x, dx = self.x_bounds else: x = min(self.start_x, event.x) dx = abs(event.x - self.start_x) if self.y_bounds is not None: y, dy = self.y_bounds else: y = min(self.start_y, event.y) dy = abs(event.y - self.start_y) self.bounds = (x, y, dx, dy)
# http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # =============================================================================== # ============= enthought library imports ======================= from enable.enable_traits import Pointer from traits.api import Enum, CArray from chaco.tools.api import DragTool # ============= standard library imports ======================== # ============= local library imports ========================== normal_pointer = Pointer('normal') hand_pointer = Pointer('hand') class PointMoveTool(DragTool): event_state = Enum("normal", "dragging") _prev_pt = CArray constrain = Enum(None, 'x', 'y') def is_draggable(self, x, y): return self.component.hittest((x, y)) def drag_start(self, event): data_pt = self.component.map_data((event.x, event.y), all_values=True) self._prev_pt = data_pt event.handled = True
class Shape(Component): """ The base class for moveable shapes. """ #### 'Component' interface ################################################ # The background color of this component. bgcolor = 'transparent' #### 'Shape' interface #################################################### # The coordinates of the center of the shape. center = Property(Tuple) # The fill color. fill_color = ColorTrait # The pointer for the 'normal' event state. normal_pointer = Pointer('arrow') # The pointer for the 'moving' event state. moving_pointer = Pointer('hand') # The text color. text_color = ColorTrait # The text displayed in the shape. text = Str #### 'Private' interface ################################################## # The difference between the location of a mouse-click and the component's # origin. _offset_x = Float _offset_y = Float ########################################################################### # 'Interactor' interface ########################################################################### def normal_key_pressed(self, event): """ Event handler. """ print('normal_key_pressed', event.character) return def normal_left_down(self, event): """ Event handler. """ if self.is_in(event.x, event.y): self.event_state = 'moving' event.window.mouse_owner = self event.window.set_pointer(self.moving_pointer) self._offset_x = event.x - self.x self._offset_y = event.y - self.y # move this shape to the top of the z order. The components are # drawn in order, so the last one will be drawn on top siblings = self.container.components if len(siblings) > 1: siblings.remove(self) siblings.append(self) return def moving_mouse_move(self, event): """ Event handler. """ top = event.y + self._offset_y bottom = event.y - self._offset_y left = event.x - self._offset_x right = event.x + self._offset_x # Keep the shape fully in the container if bottom < 0: bottom = 0 elif top > self.container.height: bottom = self.container.height - self.height if left < 0: left = 0 elif right > self.container.width: left = self.container.width - self.width self.position = [left, bottom] self.request_redraw() return def moving_left_up(self, event): """ Event handler. """ self.event_state = 'normal' event.window.set_pointer(self.normal_pointer) event.window.mouse_owner = None self.request_redraw() return def moving_mouse_leave(self, event): """ Event handler. """ self.moving_left_up(event) return ########################################################################### # 'Shape' interface ########################################################################### def _get_center(self): """ Property getter. """ dx, dy = self.bounds ox, oy = self.position cx = ox + dx / 2 cy = oy + dy / 2 return cx, cy def _text_default(self): """ Trait initializer. """ return type(self).__name__ ########################################################################### # Protected 'Shape' interface ########################################################################### def _distance_between(self, xxx_todo_changeme, xxx_todo_changeme1): """ Return the distance between two points. """ (x1, y1) = xxx_todo_changeme (x2, y2) = xxx_todo_changeme1 return math.sqrt(pow(x1 - x2, 2) + pow(y1 - y2, 2)) def _draw_text(self, gc): """ Draw the shape's text. """ if len(self.text) > 0: gc.set_fill_color(self._get_text_color(self.event_state)) gc.set_font(Font(family=MODERN, size=16)) tx, ty, tw, th = gc.get_text_extent(self.text) dx, dy = self.bounds x, y = self.position gc.set_text_position(x + (dx - tw) / 2, y + (dy - th) / 2) gc.show_text(self.text) return def _get_fill_color(self, event_state): """ Return the fill color based on the event state. """ if event_state == 'normal': fill_color = self.fill_color_ else: r, g, b, a = self.fill_color_ fill_color = (r, g, b, 0.5) return fill_color def _get_text_color(self, event_state): """ Return the text color based on the event state. """ if event_state == 'normal': text_color = self.text_color_ else: r, g, b, a = self.text_color_ text_color = (r, g, b, 0.5) return text_color
class DesignerExtractionLineCanvas2D(ExtractionLineCanvas2D): drag_pointer = Pointer('bullseye') snap_to_grid = True grid_interval = 0.5 _constrain = False _px = None _py = None selected_item = None def drag_mouse_move(self, event): si = self.selected_item x, y = event.x, event.y dx, dy = self.map_data((x, y)) w, h = si.width, si.height dx -= w / 2. dy -= h / 2. if self.snap_to_grid: dx, dy = snap_to_grid(dx, dy, interval=self.grid_interval) if event.shift_down: if self._px is not None and not self._constrain: xx = abs(x - self._px) yy = abs(y - self._py) self._constrain = 'v' if yy > xx else 'h' else: self._px = x self._py = y else: self._constrain = False self._px, self._py = None, None if self._constrain == 'h': si.x = dx elif self._constrain == 'v': si.y = dy else: si.x, si.y = dx, dy si.request_layout() self.invalidate_and_redraw() def drag_left_up(self, event): self._set_normal_state(event) def drag_mouse_leave(self, event): self._set_normal_state(event) def _set_normal_state(self, event): self.event_state = 'normal' event.window.set_pointer(self.normal_pointer) def select_left_down(self, event): self.event_state = 'drag' event.window.set_pointer(self.drag_pointer) def _over_item(self, event): x, y = event.x, event.y return next((item for item in self.scene.iteritems(klass=RoundedRectangle) if hasattr(item, 'is_in') and \ item.is_in(x, y)), None) def normal_mouse_move(self, event): item = self._over_item(event) if item is not None: event.window.set_pointer(self.select_pointer) self.selected_item = item self.event_state = 'select' else: event.window.set_pointer(self.normal_pointer) self.selected_item = None
class ViewportPanTool(DragTool): """ A tool that enables the user to pan around a viewport by clicking a mouse button and dragging. """ # The cursor to use when panning. drag_pointer = Pointer("hand") # Scaling factor on the panning "speed". speed = Float(1.0) # The modifier key that, if depressed when the drag is initiated, constrains # the panning to happen in the only direction of largest initial motion. # It is possible to permanently restrict this tool to always drag along one # direction. To do so, set constrain=True, constrain_key=None, and # constrain_direction to the desired direction. constrain_key = Enum(None, "shift", "control", "alt") # Constrain the panning to one direction? constrain = Bool(False) # The direction of constrained draw. A value of None means that the user # has initiated the drag and pressed the constrain_key, but hasn't moved # the mouse yet; the magnitude of the components of the next mouse_move # event will determine the constrain_direction. constrain_direction = Enum(None, "x", "y") # (x,y) of the point where the mouse button was pressed. _original_xy = Tuple # Data coordinates of **_original_xy**. This may be either (index,value) # or (value,index) depending on the component's orientation. _original_data = Tuple # Was constrain=True triggered by the **contrain_key**? If False, it was # set programmatically. _auto_constrain = Bool(False) #------------------------------------------------------------------------ # Inherited BaseTool traits #------------------------------------------------------------------------ # The tool is not visible (overrides BaseTool). visible = False def drag_start(self, event): self._original_xy = (event.x, event.y) if self.constrain_key is not None: if getattr(event, self.constrain_key + "_down"): self.constrain = True self._auto_constrain = True self.constrain_direction = None event.window.set_pointer(self.drag_pointer) event.window.set_mouse_owner(self, event.net_transform()) event.handled = True return def dragging(self, event): """ Handles the mouse being moved when the tool is in the 'panning' state. """ if self._auto_constrain and self.constrain_direction is None: # Determine the constraint direction if abs(event.x - self._original_xy[0]) > abs(event.y - self._original_xy[1]): self.constrain_direction = "x" else: self.constrain_direction = "y" new_position = self.component.view_position[:] for direction, ndx in [("x", 0), ("y", 1)]: if self.constrain and self.constrain_direction != direction: continue origpos = self._original_xy[ndx] eventpos = getattr(event, direction) delta = self.speed * (eventpos - origpos) if self.component.enable_zoom: delta /= self.component.zoom new_position[ndx] -= delta if self.constrain: self.component.view_position[self.constrain_direction] = \ new_position[self.constrain_direction] else: self.component.view_position = new_position event.handled = True self._original_xy = (event.x, event.y) self.component.request_redraw() return def drag_end(self, event): if self._auto_constrain: self.constrain = False self.constrain_direction = None event.window.set_pointer("arrow") if event.window.mouse_owner == self: event.window.set_mouse_owner(None) event.handled = True return