from Sherwood: http://vpython.org/contents/docs/mouse_drag.html GF: On limac (yosemite, macbook pro) the oldway polling example works better that the callback example. They don't do exactly the same thing, so the comparison my be inappropriate. Bu the latter was jumpy, and then contaminated the mouse after using the middle and right maneuvering keys of VPython. The polling example didn't. Drag Example There are two ways to handle dragging with the mouse, "polling" and "callbacks". Polling is the older way of handling mouse events, and it still works. Starting with VPython 6, there is another scheme involving callbacks. For more information about these two methods, see general mouse documentation. We show here two programs that achieve the same result by the two different methods. You can use either method, but you cannot mix polling and callback methods in the same program. Both the polling and callback versions of the program achieve the same goal: dragging a sphere across the screen. Handling dragging with callbacks Here is the sequence of mouse events involved in dragging something: 1) On a 'mousedown' event, see what object (if any) has been "picked" (lies under the mouse). 2) On a 'mousemove' event, update the position of the object based on the new mouse position. 3) On a 'mouseup' event, stop dragging the object. Here is a callback version of a program in which you can drag a sphere across the screen. Copy this into an edit window and try it! from visual import * scene.range = 5 # fixed size, no autoscaling ball = sphere(pos=(-3,0,0), color=color.cyan) cube = box(pos=(+3,0,0), size=(2,2,2), color=color.red) drag_pos = None # no object picked yet def grab(evt): global drag_pos if evt.pick == ball: drag_pos = evt.pickpos scene.bind('mousemove', move, ball) scene.bind('mouseup', drop) def move(evt, obj): global drag_pos # project onto xy plane, even if scene rotated: new_pos = scene.mouse.project(normal=(0,0,1)) if new_pos != drag_pos: # if mouse has moved # offset for where the ball was touched: obj.pos += new_pos - drag_pos drag_pos = new_pos # update drag position def drop(evt): scene.unbind('mousemove', move) scene.unbind('mouseup', drop) scene.bind('mousedown', grab) First, we bind 'mousedown' events to the function named grab, which checks to see whether the sphere has been touched. If so, it binds future 'mousemove' and 'mouseup' events to the move and drop functions. If you do a lot of processing of each mouse movement, or you are leaving a trail behind the moving object, you may need to check whether the "new" mouse position is in fact different from the previous position before processing the "move", as is done in the example above. For example, a trail drawn with a curve object that contains a huge number of points all at the same location may not display properly. Most VPython objects can be "picked" by touching them. Here is a more general routine which lets you drag either the tail or the tip of an arrow. Copy this into an edit window and try it! from visual import * scene.range = 8 # fixed size, no autoscaling arr = arrow(pos=(2,0,0),axis=(0,5,0)) by = 1 # touch this close to tail or tip drag_pos = None def grab(evt): global drag_pos drag = None if mag(arr.pos-evt.pos) <= by: drag = 'tail' # near tail of arrow elif mag((arr.pos+arr.axis)-evt.pos) <= by: drag = 'tip' # near tip of arrow if drag is not None: drag_pos = evt.pos # save mousedown location scene.bind('mousemove', move, drag) scene.bind('mouseup', drop) def move(evt, drag): global drag_pos new_pos = evt.pos if new_pos != drag_pos: # if mouse has moved displace = new_pos - drag_pos # how far drag_pos = new_pos # update drag position if drag == 'tail': arr.pos += displace # displace the tail else: arr.axis += displace # displace the tip def drop(evt): scene.unbind('mousemove', move) scene.unbind('mouseup', drop) scene.bind('mousedown', grab) Handling dragging with polling Here is the sequence of mouse events involved in dragging something using polling, assuming that m1 = scene.mouse.getevent(): 1) m1.press is true when you depress the mouse button (it is 'left' if left button; any quantity that is nonzero is considered true in Python). 2) m1.drag is true when the mouse coordinates change from what they were at the time of m1.press. At the time of the drag event, the mouse position is reported to be what it was at the time of the press event, so that the dragging can start at the place where the user first pressed the mouse button. If the mouse is in motion at the time of the press event, it is quite possible that the next position seen by the computer, at the time of the drag event, could be quite far from the initial mouse down position. This is why the position of the drag event is reported as though it occurred at the press location. 3) No events occur while dragging; you continually use scene.mouse.pos to update what you're dragging. 4) m1.drop is true when you release the mouse button. You can program dragging with the mouse simply by continually reading the current value of scene.mouse.pos. Here is a complete polling routine for dragging a sphere with the left button down. Copy this into an edit window and try it! from visual import * scene.range = 5 # fixed size, no autoscaling ball = sphere(pos=(-3,0,0), color=color.cyan) cube = box(pos=(+3,0,0), size=(2,2,2), color=color.red) pick = None # no object picked out of the scene yet while True: rate(30) if scene.mouse.events: m1 = scene.mouse.getevent() # get event if m1.drag and m1.pick == ball: # if touched drag_pos = m1.pickpos # where on the ball pick = m1.pick # pick now true (not None) elif m1.drop: # released at end of drag pick = None # end dragging (None is false) if pick: # project onto xy plane, even if scene rotated: new_pos = scene.mouse.project(normal=(0,0,1)) if new_pos != drag_pos: # if mouse has moved # offset for where the ball was touched: pick.pos += new_pos - drag_pos drag_pos = new_pos # update drag position If you do a lot of processing of each mouse movement, or you are leaving a trail behind the moving object, you may need to check whether the "new" mouse position is in fact different from the previous position before processing the "move", as is done in the example above. For example, a trail drawn with a curve object that contains a huge number of points all at the same location may not display properly. Most VPython objects can be "picked" by touching them. Here is a more general routine which lets you drag either the tail or the tip of an arrow. Copy this into an edit window and try it! from visual import * scene.range = 8 # fixed size, no autoscaling arr = arrow(pos=(2,0,0),axis=(0,5,0)) by = 1 # touch this close to tail or tip drag = None # have not selected tail or tip of arrow while True: rate(30) if scene.mouse.events: m1 = scene.mouse.getevent() # obtain event if m1.press: if mag(arr.pos-m1.pos) <= by: drag = 'tail' # near tail of arrow elif mag((arr.pos+arr.axis)-m1.pos) <= by: drag = 'tip' # near tip of arrow drag_pos = m1.pos # save press location elif m1.drop: # released at end of drag drag = None # end dragging (None is False) if drag: new_pos = scene.mouse.pos if new_pos != drag_pos: # if mouse has moved displace = new_pos - drag_pos # how far drag_pos = new_pos # update drag position if drag == 'tail': arr.pos += displace # displace the tail else: arr.axis += displace # displace the tip Here is general mouse documentation.