2. The simplest mouse interaction is to wait for the user to click
before proceeding in the program. Suppose the 3D display is in
scene, the default window created by VPython. Here is a way to
wait for a mouse click, which is defined as the mouse button
being pressed and released without moving the mouse (the
event occurs when the mouse button is released)
MOUSE INTERACTIONS
3. The mouse routines can handle a three-button mouse, with
"left", "right", and "middle" buttons. For systems with a two-
button mouse, the "middle" button consists of the left and right
buttons pressed together. For the Macintosh one-button mouse,
the right button is invoked by holding down the Command key
(normally used for rotating the camera view), and the middle
button is invoked by holding down the Option key (normally used
for zooming the camera view).
DIFFERENT KINDS OF MOUSE
4. VPython continues to provide the basic mouse event
functionality for handling events from right and middle buttons
when userspin or userzoom is disabled, out of concern for
supporting old programs. However, it has become evident that
there are limitations to this approach which could preclude
some kinds of mouse handling that people might want to do in
the future. For example, you might want to allow userspin with
right drags yet also pick up right clicks. For that reason it is
conceivable that future developments in this area might break
existing programs, and therefore for maximum forward
compatibility it is prudent to use only left-button interactions in
new programs.
DESIGN FOR LEFT-BUTTON EVENTS IF
POSSIBLE
5. 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.
If you use 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, and we will discuss it first. Programs that
use polling will continue to work, but you cannot mix polling and
callback approaches: you must use one or the other in a
program.
POLLING AND CALLBACK
6. Here is a simple example of how to use callbacks to process
click events:
from visual import *
s = sphere(color=color.cyan)
def change():
if s.color == 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 display named
"scene". Whenever VPython detects that a click event has
HANDLING EVENTS WITH CALLBACKS
7. 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, evt.button)
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 the
information contained in scene.mouse, plus 'event', which is the
name of the event that triggered the callback, such as
'mousedown' or 'click'. The name of the argument need not be
'evt'; use whatever name you like. In addition to evt.event and
evt.button, there is further event information in the form of
evt.press, evt.click, evt.drag, evt.drop, and evt.release (see details
in the section on polling), but this information is more relevant
when using polling rather than callbacks to get events.
DETAILS OF THE EVENT
8. Normally, only the left mouse button will trigger an event, but if
you specify scene.userspin = False, so the right button is no
longer bound to camera rotation, clicking with the right mouse
button will cause a callback. Similarly, if you specify
scene.userzoom = False, you can click with the middle button
(or left+right buttons).
RIGHT OR MIDDLE BUTTON MOUSE EVENTS
9. 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)
You can also leave a function bound but start and stop having
events sent to it:
D = scene.bind('mousemove', Drag)
...
D.stop() # temporarily stop events going to Drag
...
D.start() # start sending events to Drag again
You can check whether the callback is in start or stop mode with
D.enabled, which is True if the callback has been started and False
if it has been stopped.
UNBINDING
10. It is possible to create your own event type, and trigger a callback function to do
something. Consider the following example, where the 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(newball):
newball.color=color.cyan
scene.bind('click', clickFunc)
scene.bind('color_the_ball', ballFunc)
box(pos=(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 the 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.)
CUSTOM EVENTS: TRIGGERS
11. The following information on how to handle events using polling
is still valid, but you are encouraged to consider using the more
powerful callback approach when writing new programs.
Remember that you cannot mix the two schemes. You can use
either callback or polling in a program, but not both.
The simplest polling mouse interaction is to wait for a mouse
click:
scene.mouse.getclick() Wait for a mouse click. If you say m =
scene.mouse.getclick(), the variable m gives information about
the event. For example, m.pos is the location of the mouse at
the time of the click event.
HANDLING EVENTS WITH POLLING
12. ere is a way to get the mouse position relative to a particular plane in
space:
temp = scene.mouse.project(normal=(0,1,0),point=(0,3,0))
if temp: # temp is None if no intersection with plane
ball.pos = temp
Here a plane is specified by its normal and a point in the plane, and if
point is not specified, the plane passes through the origin. You obtain the
3D location in the plane where the user sees the mouse cursor. If the
projection of the mouse misses the plane, as would happen if the plane is
seen edge-on, the result is the special Python value None.
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 d from the origin to the
plane that is perpendicular to the specified normal. The example above is
equivalent to
temp = scene.mouse.project(normal=(0,1,0), d=3)
PROJECTING MOUSE POSITION ONTO A GIVEN
PLANE
13. Often you want to pause for either mouse or keyboard input. You
can copy the following function into your program, and then insert
pause() wherever you want to pause.
def pause():
while True:
rate(30)
if scene.mouse.events:
m = scene.mouse.getevent()
if m.click == 'left': return
elif scene.kb.keys:
k = scene.kb.getkey()
return
As of VPython 6, an alternative to this function is simply to write
scene.waitfor('click keydown').
PAUSING FOR MOUSE OR KEYBOARD INPUT