deutsch     english    français     Print

 

3.7 MOUSE EVENTS

 

 

INTRODUCTION

 

So far, your understanding of the computer is that it executes instruction after instruction. It can also change the course of a program due to certain conditions or run through loops. The corresponding program structures are called sequence, selection, and iteration. As early as 1966 Böhm and Jacopini proved in a famous article that any calculation procedures (algorithms) can be realized using these three programming structures.

This is, however, only true as long as you do not incorporate any external influences. For example, you can cancel a program at any moment by clicking with the mouse on "Close" (close button). Such processes need a new programming concept: event control (event handling). You have already learned the basic principles in the chapter Turtle Graphics/Event Control. It consists in procedures of the type:

"Whenever the event e occurs, the function f is executed".

The implementation is simple and known since the early days of computer technology in the fifties of the last century. We define a function f (then called interrupt routine) which is never even called by our own program. It sleeps, so to speak, until a certain event E occurs, an external influence, upon which it is then automatically called by the system. Today we call such a function callback and we say that the callback f is “fired” by the event E. Often, callbacks are called with parameter values that contain important information about the event, for example, which mouse button was pressed or where the mouse is located.

PROGRAMMING CONCEPTS: Event-driven program, callback, registering callbacks

 

 

REACTING TO A MOUSE EVENT

 

You can also use mouse events in the GPanel, just like in turtle graphics. In the first example, a green circle is drawn at the current mouse position when the left or right mouse button is pressed. Do the following:

First, in a function with an arbitrarily chosen name, define what should happen when a mouse button is pressed. Here you choose a name, for example onMousePressed(), that expresses what the function does as well as possible. When called by the system, the callback receives the current coordinates of the mouse cursor.

Next you need to tell the system that it should call your callback when the mouse button is pressed. This process is called callback registration.

 

To register your callback you will need a named parameter of makeGPanel() that is called mousePressed.

from gpanel import *

def onMousePressed(x, y):
    move(x, y) 
    fillCircle(0.02)

makeGPanel(mousePressed = onMousePressed)
setColor("green")              
Highlight program code (Ctrl+C copy, Ctrl+V paste)

 

 

MEMO

 

A callback is not called by your own program, but rather automatically when the event is triggered. The registration of the callback is performed through a named parameter.

You can detect the pressing of a mouse button with two different callbacks: a click event or a press event. The click event will not be triggered until after the key is released, but the press event triggers immediately once you press the button.

 

 

DETECTING MOUSE MOVEMENT

 

The mouse movement can also be recognized as an event, which is triggered in rapid succession when the mouse is moved. The parameter is called mouseMoved. Your program draws a red filled circle with a black outline at every call of the callback, whereby you can draw fun tubelike pictures.

 

from gpanel import *      

def onMouseMoved(x, y):
    move(x, y)
    setColor("red")
    fillCircle(.04) 
    setColor("black")
    circle(.04)  

makeGPanel(mouseMoved = onMouseMoved)
Highlight program code (Ctrl+C copy, Ctrl+V paste)

 

 

MEMO

 

The onMouseMoved(x, y) callback is registered through a named parameter mouseMoved.

 

 

FREE HAND DRAWING WITH A PRESSED MOUSE BUTTON

 

Now you are already capable of writing a simple drawing program, with which you can draw a figure free-handedly using the mouse. All you need is the drag event which is triggered in rapid succession when you move the mouse with the button pressed down. The program logic is simple: move the graphics cursor to the current location when the press event occurs and then draw a line using draw() in the drag event callback.

 
from gpanel import *

def onMousePressed(x, y):
    move(x, y)

def onMouseDragged(x, y):
    draw(x, y)

makeGPanel(mousePressed = onMousePressed, 
           mouseDragged = onMouseDragged)
Highlight program code (Ctrl+C copy, Ctrl+V paste)

 

 

MEMO

 

You can register multiple callback with named parameters simultaneously. The order of the parameters does not matter.

 

 

THE LEFT AND RIGHT MOUSE BUTTON

 

As you have probably noticed, the mouse events are triggered by both the left and right mouse buttons. If you want to differentiate the two buttons, use the functions isLeftMouseButton() and isRightMouseButton(). These return True when the left or the right button is involved, respectively.

When you press on the right mouse button the program opens a color palette. You can then select the fill color of the circle with the left mouse button.
 
from gpanel import *

def onMousePressed(x, y):
  if isLeftMouseButton():
      pixColor = getPixelColor(x, y)
      if pixColor == makeColor("white"):
          return
      clear()
      setColor(pixColor)
      move(5, 5)
      fillCircle(2)
      
  if isRightMouseButton():
      for i in range(5):
          move(9, 2 * i + 1)
          if i == 0:
              setColor("deep pink")
          if i == 1:
              setColor("green")
          if i == 2:
              setColor("yellow")
          if i == 3:
              setColor("deep sky blue")
          if i == 4:
              setColor("dark violet")
          fillRectangle(2, 2)

makeGPanel(0, 10, 0, 10, mousePressed = onMousePressed)
move(5, 5)
fillCircle(2)
Highlight program code (Ctrl+C copy, Ctrl+V paste)

 

 

MEMO

 

The registered mouse callbacks are triggered with the left and the right mouse buttons. You can find out which button was used by calling isLeftMouseButton() or isRightMouseButton().

 

 

RUBBER BAND LINES

 

If you want to draw lines with a drawing program, you can mark the starting point by pressing the mouse button. While dragging the mouse, you make a temporary line similar to that of a rubber band that is fastened at the starting point. Only release the mouse button when you are satisfied with the position of the line, and then it will actually be drawn.

So here you need three callbacks: onMousePresssed, onMouseDragged and onMouseReleased.
 

But there is a particular problem: to move the rubber band over the drawing area it must be repeatedly erased from its old location and drawn again to the new location, without changing the already existing drawing. If you deleted the lines by overwriting them with the background color, gaps would result in the existing drawing at the intersection points.

To solve this problem, you must save the existing drawing in the press callback (one also calls this "rescue"). The deletion of the temporary rubber band then happens by restoring this “old” drawing. You can save the drawing with storeGraphics() and restore it with recallGraphics().

from gpanel import *

def onMousePressed(x, y):
    global x1, y1, x2, y2
    storeGraphics()
    x1 = x
    y1 = y
    x2 = x1
    y2 = y1
    setColor("red")
 
def onMouseDragged(x, y):
    global x2, y2
    recallGraphics()
    x2 = x
    y2 = y
    line(x1, y1, x2, y2) 

def onMouseReleased(x, y):
    setColor("white")
    if not (x1 == x2 and y1 == y2):
        line(x1, y1, x2, y2)
    else:
        recalGraphics() 

x1 = 0
y1 = 0
x2 = 0
y2 = 0

makeGPanel(mousePressed = onMousePressed, 
              mouseDragged = onMouseDragged,
              mouseReleased = onMouseReleased)
title("Press And Drag To Draw Lines")
bgColor("blue")
setColor("white")
lineWidth(2)
Highlight program code (Ctrl+C copy, Ctrl+V paste)

 

 

MEMO

 

Remember the principles of drawing rubber band lines:

In a press event the end point of the line is initialized and the graphic is saved.

In a drag event the saved/stored graphic is restored, the temporary line with the new end point is saved, and the new end point is saved.

In a release event the line is definitely drawn, but only if the mouse was really moved.

 

 

EXERCISES

 

1.


Draw a green filled circle. The fill color should change to red when you move the mouse onto the circle. It should turn back to green when the mouse moves off.

You can specify a window with
makeGPanel(-10, 10, -10, 10,
                   mouseMoved = onMouseMoved)

 



2.

Your program should draw a line segment after every mouse click.
 


3.


Upon the movement with a pressed mouse button, your program should draw a tube-like figure. While moving, the tube should swell up from an initial thickness of 0.01 to 0.1, and then return back to its original thickness.

 


4.

Write a program where you can draw green rectangles onto a black background. In this case, you should be able to place a temporary "rubber band rectangle" by pressing and dragging the mouse, before it is definitely placed upon releasing the mouse. Use the rectangle functions that are called with the coordinates of two opposite corner points of the rectangle (rectangle(x1, y1, x2, y2)).
 

 

 

   

ADDITIONAL MATERIAL

 

REGISTERING CALLBACKS WITH DECORATORS

 

Instead of using named parameters of makeGPanel() to register a callback, an arbitrary named function with two parameters x and y can be "decorated" by a preceding line, so that TigerJython automatically registers the function as callback that is called when the event happens. The additional line has to be prefixed by the "at" sign @. The following decorators are available:

@onMousePressed

mouse button is pressed

@onMouseReleased

mouse button is released

@onMouseClicked

mouse button is pressed and released

@onMouseDragged

mouse is moved while a button is pressed

@onMouseMoved

mouse is moved while no button is pressed

@onMouseEntered

mouse enters the graphics window

@onMouseExited

mouse leaves the graphics window

So the program shown above which draws a circle when the mouse button is pressed, can be written using a decorator:

from gpanel import *

@onMousePressed
def doIt(x, y):
    move(x, y) 
    fillCircle(0.02)

makeGPanel()
setColor("green")         
Highlight program code (Ctrl+C copy, Ctrl+V paste)