Home

   Overview of Python & VPython

  "Welcome to VPython" tutorial

  Introductory Videos

  Pictures of 3D objects

  

  

  

  VPython 7 web site
  VPython license

Mouse Events

For basic examples of mouse handling, see Click example or Drag example.

Introduction to events

Here is the simplest mouse interaction, which displays an icon and waits for a mouse click (the left mouse button going down and up)::

scene.pause() An icon is displayed in the canvas to indicate a pause, waiting for a mouse click.

You can show a message to the user on the canvas: scene.pause('Click to proceed'). You can also obtain detailed information about the event:

ev = scene.pause()

You can use the package of information contained in the variable "ev":

sphere(pos=ev.pos, radius=0.1)

The package of information about the event includes information of what kind of event it was (see further event details below):

box()
while True:
    ev = scene.waitfor('mousedown mouseup')
    if ev.event == 'mousedown':
        print('You pressed the mouse button')
    else:
        print('You released the mouse button')
    print(ev.pos) # the position of the mouse

Here are additional options:

scene.waitfor('click') # wait for mouse button click
scene.waitfor('mousedown') # wait for mouse button press
scene.waitfor('mouseup') # wait for mouse button release
scene.waitfor('mousemove') # wait for mouse to be moved
scene.waitfor('mouseenter') # when move into canvas
scene.waitfor('mouseleave') # when leave canvas
scene.waitfor('mousedown mousemove') # either event
scene.waitfor('keydown') # wait for keyboard key press
scene.waitfor('keyup')   # wait for keyboard key release
scene.waitfor('click keydown') # click or keyboard

You can set up a function to be called when a mouse or keyboard event occurs. The following will display the type of event (mouse click or keydown) and, in the case of a keydown event, which key is involved (see further details below):

def process(ev):
    print(ev.event, ev.which)

scene.bind('click keydown', process)

The quantity ev.event will be 'keydown' if a key was pressed or 'mousedown' if the left mouse button was pressed. The quantity ev.which is the numerical key code or mouse button indicator (mouse button is always 1 for now). For example, ev.which is 65 for the 'a' key. The quantity ev.key is the corresponding character string, such as 'a' or 'delete'.

The quantity ev.canvas is the canvas associated with the event. You can bind events on different canvases to the same function and be able to tell in which canvas the event occurred.

Note that scene.mouse.shift is true if the shift key is down at the time of the keyboard event; similarly for scene.mouse.ctrl and scene.mouse.alt.

It is possible to use "anonymous" (unnamed) functions in this situation. For examples, see the mouse drag discussion. However, anonymous functions cannot be used in VPython 7.

The package of information about the event that caused the end of the wait includes the information of whether it was a mouse or keyboard event:

box()
ev = scene.waitfor('mousedown keydown')
if ev.event == 'mousedown':
    print('You pressed the mouse button at', ev.pos)
else:
    print('You pressed the key', ev.key)

The object scene.mouse contains lots of information about the current state of the mouse, which you can interrogate at any time:

pos The current 3D position of the mouse cursor; scene.mouse.pos. VPython always chooses a point in the plane parallel to the screen and passing through scene.center. (See Projecting mouse information onto a given plane for other options.)

pick Execute obj = scene.mouse.pick to obtain the object pointed to by the mouse. If you have a box named B, you can determine whether the picked object is that box by asking if (B == obj). If there is no object pointed to by the mouse, obj is None. Also, obj will be None if the object has the pickable attribute set to False. For example, the curves, spheres, and arrows created by make_trail, attach_trail, and attach_arrow are not pickable, and you may wish to specify that some of your own objects are not pickable. At present label and helix cannot be picked. For curve objects, obj.segment is the number of the picked segment along the curve, starting with 1 (representing the segment from point number 0 to point number 1). You can test for a curve object with if instanceof(obj, curve):. See the Web VPython example MousePicking.

ray A unit vector pointing from the camera in the direction of the mouse cursor.

project() Projects position onto a plane. See Projecting mouse position onto a given plane.

alt = True if the ALT key is down, otherwise False

ctrl = True if the CTRL key is down, otherwise False

shift = True if the SHIFT key is down, otherwise False

Different kinds of mouse

The mouse routines in Web VPython currently handle only the left (or only) mouse button.

Projecting mouse position onto a given plane

Here is a way to get the mouse position relative to a particular plane in space:

temp = scene.mouse.project(
             normal=vec(0,1,0),
             point=vec(0,3,0) )
# None if no intersection with plane:
if temp != None) ball.pos = temp

This projects the mouse cursor onto a plane that is perpendicular to the specified normal. If the second parameter is not specified, the plane passes through the origin. It returns a 3D position, or None if the projection of the mouse misses the plane (scene.mouse.ray is parallel to the plane).

In the example shown above, the user of your program will be able to use the mouse to place balls in a plane parallel to the xy plane, a height of 3 above the xy plane, no matter how the user has rotated the point of view.

You can instead specify a perpendicular distance from the origin to the plane that is perpendicular to the specified normal. The example above is equivalent to

temp=scene.mouse.project(
              normal=vec(0,1,0), d=3 )

Polling and callback

There are two different ways to get a mouse event, "polling" and "callback". In polling, you continually check scene.mouse.events to see whether any events are waiting to be processed, and you use scene.mouse.getevent() to get the next event to process. Prior to VPython 6, this was the only way you could handle mouse or keyboard events, but this scheme is not available in Web VPython

In the callback method, you specify a function to be executed when a specific type of event occurs, and the function is sent the event information when the specified type of event occurs. For many purposes this is a better way to handle mouse and keyboard events.

 

Callbacks

Here is a simple example of how to use callbacks to process click events:

s = sphere(color=color.cyan)

def change():
    if s.color.equals(color.cyan):
        s.color = color.red
    else:
        s.color = color.cyan

scene.bind('click', change)

We define a "function" named "change". Then we "bind" this function to click events occurring in the canvas named "scene". Whenever VPython detects that a click event has occurred, VPython calls the bound function, which in this case toggles the sphere's color between cyan and red.

This operation is called a "callback" because with scene.bind you register that you want to be called back any time there is a click event. Here are the built-in events that you can specify in a bind operation:

Mouse:    click, mousedown, mousemove, mouseup
Keyboard: keydown, keyup
Other:    redraw, draw_complete

The event 'mousedown' or 'mouseup' occurs when you press or release the left button on the mouse, and the 'mousemove' event occurs whenever the mouse moves, whether or not a button is depressed. A 'redraw' event occurs just before the 3D scene is redrawn on the screen, and a 'draw_complete' event occurs just after the redrawing (these event have rather technical uses such as timing how often redrawings occur, or how much time they take).

You can bind more than one event to a function. The following will cause the callback function to be executed whether you press or release the mouse button:

scene.bind('mouseup mousedown', change)

Another use of callbacks is to drive a function periodically. See the example program Bounce-Callbacks-VPython. Also see the related documentation for the functions get_library() and read_local_file().

Details of the event

You can get detailed information about the event by writing the callback function like this (note the variable 'evt' in parentheses):

def info(evt):
    print(evt.event, evt.pos)

Here we specify an argument in the definition of the callback function ('evt' in this case). When the function is called due to a specified event happening, VPython sends the function information about the event. The name of the argument need not be 'evt'; use whatever name you like. In addition to evt.event, evt.pos, and evt.pick, there is further event information in the form of evt.press and evt.release which are 'left' for press or release events.

The quantity evt.event will be 'keydown' if a key was pressed. The quantity evt.which is the numerical key code or mouse button indicator (mouse button is always 1 for now). For example, evt.which is 65 for the 'a' key. Note that scene.mouse.shift is true if the shift key is down at the time of the keyboard event; similarly for scene.mouse.ctrl and scene.mouse.alt.

In classic VPython you can optionally have VPython send the callback function an additional argument, but this is not currently possible in Web VPython.

Right or middle button mouse events

There is currently no way in Web VPython to handle right button or middle button events.

Unbinding

Suppose you executed scene.bind('mousedown mousemove', Drag), but now you no longer want to send mousemove events to that function. Do this:

scene.unbind('mousemove', Drag)

Custom events: triggers (works in Web VPython but currently not with installed Python)

It is possible to create your own event type, and trigger a function to do something. Consider the following example, where the custom event type is 'color_the_ball':

def clickFunc():
    s = sphere(pos=scene.mouse.pos, radius=0.1)
    scene.trigger('color_the_ball', s)

def ballFunc(ev):
    # ev.type is "color_the_ball"; ev.event is the new sphere
    ev.event.color=color.cyan

scene.bind('click', clickFunc)
scene.bind('color_the_ball', ballFunc)

box(pos=vector(1,0,0))

We bind click events to the function clickFunc, and we bind our own special event type 'color_the_ball' to the function ballFunc. The function clickFunc is executed when the user clicks the mouse. This function creates a small sphere at the location of the mouse click, then triggers an event 'color_the_ball', with the effect of passing to the function ballFunc information that includes the new sphere object. Finally ballFunc applies a color to the sphere. (Obviously one could color the sphere in clickFunc; the example is just for illustration of the basic concept.)