Mouse Events in Graphics Programs

A more highly interactive graphics program tracks the position of the mouse cursor in a canvas and responds to events such as mouse button presses, cursor movement, and dragging (a combination of the two).  For example, to draw an oval, the user might press the mouse button at the ovalÕs upper left corner, drag the cursor down to ovalÕs lower right corner, and release the mouse button.

To develop an application that handles mouse events in a canvas, the programmer must use a different design pattern from the one just discussed.  In this new pattern, the programmer defines a new subclass of EasyCanvas.  The new subclass includes definitions of the methods that will respond to relevant mouse events in the canvas.  An instance of this class is then added to the window, as the optional canvas argument to the addCanvas method.  When a mouse event occurs in the canvas, the corresponding handler method is triggered.  Default handler methods that do nothing are also defined in EasyCanvas, but if the programmer overrides the relevant method in her subclass, the application can then respond to the event appropriately.  Here are the types of mouse events and their handler methods.

 

EasyCanvas Method

What it does

mousePressed(event)

Triggered when the left mouse button is pressed.  event.x and event.y hold the mouse cursorÕs coordinates.

mouseReleased(event)

Triggered when the left mouse button is released.  event.x and event.y hold the mouse cursorÕs coordinates.

mouseMoved(event)

Triggered when the mouse is moved.  event.x and event.y hold the mouse cursorÕs coordinates.

mouseDragged(event)

Triggered when the mouse is moved with left mouse button pressed.  event.x and event.y hold the mouse cursorÕs coordinates.

mouseClicked(event)

Triggered when the left mouse button is clicked.  event.x and event.y hold the mouse cursorÕs coordinates.

 

The application window shown in Figure 25 contains two colored canvases.  The user can draw ovals by dragging the mouse in the left canvas, and can show the coordinates of a mouse press in the right canvas.  The command button clears the ovals in the left canvas.

 

 

The code for this application includes the definition of two new classes called LeftCanvas and RightCanvas.    Both are subclasses of EasyCanvas.  The __init__ method adds an instance of each canvas class to the window, as shown in the next code segment.  Note that the constructor for each new canvas class expects self, the parent window, as an argument.  Also, the application window is no longer responsible for maintaining a list of items drawn in a canvas.  ThatÕs the responsibility of the new canvas subclass.

 

def __init__(self):

    """Sets up the window and widgets."""

    EasyFrame.__init__(self, title = "Canvas Demo")

 

    # Add the labels

    self.addLabel(text = "Canvas 1", row = 0, column = 0)

    self.addLabel(text = "Canvas 2", row = 0, column = 1)

 

    # Add the canvases

    self.leftCanvas = self.addCanvas(canvas = LeftCanvas(self),

                                     row = 1, column = 0)

    rightCanvas = self.addCanvas(canvas = RightCanvas(self),

                                 row = 1, column = 1)

       

    # Add the command button

    self.button = self.addButton(text = "Clear Shapes",

                                 row = 2, column = 0,

                                 columnspan = 2,

                                 command = self.clearAll)

# Command button event handler

def clearAll(self):

    """Deletes all shapes from left canvas."""

    self.leftCanvas.clearAll()

 

The RightCanvas class, which draws the coordinates of each mouse press event, is the simpler of the two new canvas classes.  Its __init__ method sets its background color.  When a mouse is pressed in this canvas, the mousePressed method is triggered and receives the mouse event object as an argument.  As the next segment shows, this method uses the coordinates contained in this event to draw the text of the coordinates at that point on the canvas. 

 

class RightCanvas(EasyCanvas):

    """Displays the coordinates of the point where

    the mouse is pressed."""

 

    def __init__(self, parent):

        """Background is medium blue."""

        EasyCanvas.__init__(self, parent, background = "#0000CC")

 

    def mousePressed(self, event):

        """Draws the coordinates of the mouse press in white."""

        self.drawText("(" + str(event.x) + "," + str(event.y) + ")",

                      event.x, event.y, fill = "white")

 

The LeftCanvas class must respond to two types of mouse events, a press and a release.  When a press occurs, the canvas saves the current mouse cursor coordinates (method mousePressed).  When a release occurs, if the current mouse cursor coordinates are different from the earlier ones, the canvas uses these two pairs of coordinates to draw an oval (method mouseReleased).  The canvas also maintains a list of items drawn in it for eventual deletion (method clearAll).  The code for LeftCanvas is shown next.

     

class LeftCanvas(EasyCanvas):

    """This canvas supports the drawing of ovals. 

    The user presses the mouse button at one corner and drags to

    the other corner before releasing."""

 

    def __init__(self, parent):

        """Background is medium red.  Items drawn are tracked

        for later erasing."""

        EasyCanvas.__init__(self, parent, background = "#CC0000")

        self._items = list()

 

    def mousePressed(self, event):

        """Sets the first corner of the oval's bounding rectangle."""

        self._x0 = event.x

        self._y0 = event.y

 

    def mouseReleased(self, event):

        """Sets the second corner of the oval's bounding rectangle.

        Draws an oval filled in blue and saves its id."""

        if self._x0 != event.x and self._y0 != event.y:

            item = self.drawOval(self._x0, self._y0,

                                 event.x, event.y, fill = "#0000CC")

            self._items.append(item)

 

    def clearAll(self):

        """Deletes all ovals from left canvas."""

        for item in self._items:

            self.deleteItem(item)

        self._items = list()

 

Although the second design pattern for the use of canvases is more complicated than the first one, it actually simplifies the structure of the application, by separating the responsibilities for managing each canvas from the responsibilities for managing the window.