Python API 2.0 Reference
python/api2/py2MarqueeTool.py
1 ########################################################################
2 #
3 # DESCRIPTION:
4 #
5 # Produces the MEL command "marqueeToolContext".
6 #
7 # This plug-in demonstrates "marquee selection" in a user defined context.
8 # It is supported in Viewport 2.0 and OpenGL. Selection is done through
9 # the API (MGlobal).
10 #
11 ########################################################################
12 # First make sure that marqueeTool.py is in your MAYA_PLUG_IN_PATH. Then,
13 # to use the tool, execute the following in the script editor:
14 #
15 # from maya import cmds
16 # cmds.loadPlugin("marqueeTool.py")
17 # ctx = cmds.marqueeToolContext()
18 # cmds.setToolTo(ctx)
19 #
20 # Once the tool is active, you can select objects in the 3D windows in the
21 # same way that you do with the selection tool (it can be used for either
22 # click selection or drag selection). Both will also work with the shift
23 # key held down in the same manner as the selection tool.
24 #
25 ########################################################################
26 
27 import logging
28 import math
29 import sys
30 from maya.api import OpenMaya, OpenMayaRender, OpenMayaUI
31 from maya import OpenMayaRender as OpenMayaRenderV1
32 
33 logger = logging.getLogger('marqueeTool')
34 
35 # tell Maya that we want to use Python API 2.0
36 maya_useNewAPI = True
37 
38 # command
39 class MarqueeContextCmd (OpenMayaUI.MPxContextCommand):
40  """
41  This context command class creates instances of the MarqueeContext.
42  """
43  kPluginCmdName = "py2MarqueeToolContext"
44 
45  def __init__(self):
47 
48  @classmethod
49  def creator(cls):
50  """
51  This factory method creates an instance of the MarqueeContextCmd class.
52  """
53  return cls()
54 
55  def makeObj(self):
56  """
57  This factory method creates an instance of the MarqueeContext class.
58  """
59  return MarqueeContext()
60 
61 class MarqueeContext(OpenMayaUI.MPxContext):
62  """
63  This context class extends a bounding box as the user drags the cursor during a selection
64  opeartion.
65  """
66 
67  help_string = "Click with left button or drag with middle button to select"
68 
69  @classmethod
70  def creator(cls):
71  """
72  Create and return an instance of the MarqueeContext class.
73  """
74  return MarqueeContext(cls)
75 
76  def __init__(self):
77  """
78  Initialize the context member variables.
79  """
81  self.fs_drawn = False
82  self.list_adjustment = 0
83  self.view = None
84  self.setTitleString('Marquee Tool')
85  self.setImage('marqueeTool.xpm', OpenMayaUI.MPxContext.kImage1)
86 
87  def stringClassName(self):
88  """
89  Return the class name string.
90  """
91  return 'Marquee Tool'
92 
93  def toolOnSetup( self, event ):
94  """
95  Perform any setup operations when the tool is created. In this case,
96  set the help string.
97  """
98  self.setHelpString( MarqueeContext.helpString )
99 
100  def check_event( self, event ):
101  """
102  Print out some information for the given event, such as its position which button
103  was pressed, and if any modifiers were also pressed.
104  """
105  logger.info("Check event:")
106  logger.info(" position: (%d, %d)" % event.position)
107  logger.info(" windowPosition: (%d, %d)" % event.getWindowPosition())
108  mouse_btn = event.mouseButton()
109  if (mouse_btn == OpenMayaUI.MEvent.kLeftMouse):
110  logger.info(" mouseButton: kLeftMouse")
111  elif (mouse_btn == OpenMayaUI.MEvent.kMiddleMouse):
112  logger.info(" mouseButton: kMiddleMouse")
113  else:
114  logger.info(" mouseButton: unknown!")
115  if event.isModifierShift():
116  logger.info(" Shift key was pressed")
117  if event.isModifierControl():
118  logger.info(" Control key was pressed")
119  if event.isModifierNone():
120  logger.info(" No modifiers were pressed")
121 
122  def do_press_common( self, event ):
123  """
124  Perfom the press operations common to both VP2.0 and the Legacy Viewport.
125  """
126  try:
127  if event.isModifierShift() or event.isModifierControl():
128  if event.isModifierShift():
129  if event.isModifierControl():
130  # both shift and control pressed, merge new selections
131  self.list_adjustment = OpenMaya.MGlobal.kAddToList
132  else:
133  # shift only, xor new selections with previous ones
134  self.list_adjustment = OpenMaya.MGlobal.kXORWithList
135 
136  elif event.isModifierControl():
137  # control only, remove new selections from the previous list
138  self.list_adjustment = OpenMaya.MGlobal.kRemoveFromList
139  else:
140  self.list_adjustment = OpenMaya.MGlobal.kReplaceList
141 
142  # Extract the event information
143  #
144  self.start = event.position
145  except:
146  logger.info("EXCEPTION IN do_press_common!")
147 
148  def do_release_common( self, event ):
149  """
150  Perfom the release operations common to both VP2.0 and the Legacy Viewport.
151  """
152  # Get the end position of the marquee
153  self.last = event.position
154 
155  # Save the state of the current selections. The "selectFromSceen"
156  # below will alter the active list, and we have to be able to put
157  # it back.
159 
160  # If we have a zero dimension box, just do a point pick
161  #
162  if ( math.fabs(self.start[0] - self.last[0]) < 2 and math.fabs(self.start[1] - self.last[1]) < 2 ):
163  # This will check to see if the active view is in wireframe or not.
164  selection_method = OpenMaya.MGlobal.selectionMethod()
165  OpenMaya.MGlobal.selectFromScreen(self.start[0], self.start[1],
166  listAdjustment = OpenMaya.MGlobal.kReplaceList,
167  selectMethod = selection_method )
168  else:
169  # The Maya select tool goes to wireframe select when doing a marquee, so
170  # we will copy that behaviour.
171  # Select all the objects or components within the marquee.
172  OpenMaya.MGlobal.selectFromScreen(self.start[0], self.start[1],
173  self.last[0], self.last[1],
174  listAdjustment = OpenMaya.MGlobal.kReplaceList,
175  selectMethod = OpenMaya.MGlobal.kWireframeSelectMethod )
176 
177  # Get the list of selected items
179 
180  # Restore the active selection list to what it was before the "selectFromScreen"
181  OpenMaya.MGlobal.setActiveSelectionList(incoming_list, OpenMaya.MGlobal.kReplaceList)
182 
183  # Update the selection list as indicated by the modifier keys.
184  OpenMaya.MGlobal.selectCommand(marquee_list, self.list_adjustment)
185 
186  # Test the functionality of several of the Mevent methods.
187  logger.info("Mouse button release state:")
188  self.check_event(event)
189 
190  def draw_marquee_gl( self ):
191  """
192  Draw the Marquee bounding box using OpenGL. This method is used by the Legacy Viewport.
193  """
194  self.view.beginGL()
195 
196  ## drawing in VP1 views will be done using V1 Python APIs:
197  gl_renderer = OpenMayaRenderV1.MHardwareRenderer.theRenderer()
198  gl_ft = gl_renderer.glFunctionTable()
199  gl_ft.glBegin( OpenMayaRenderV1.MGL_LINE_LOOP )
200  gl_ft.glVertex2i( self.start[0], self.start[1] )
201  gl_ft.glVertex2i( self.last[0], self.start[1] )
202  gl_ft.glVertex2i( self.last[0], self.last[1] )
203  gl_ft.glVertex2i( self.start[0], self.last[1] )
204  gl_ft.glEnd()
205 
206  self.view.endGL()
207 
208  def doPressLegacy( self, event ):
209  """
210  Handle the mouse press event in the Legacy Viewport.
211  """
212  self.do_press_common(event)
213  self.view = OpenMayaUI.M3dView.active3dView()
214  self.fs_drawn = False
215 
216  def doDragLegacy( self, event ):
217  """
218  Handle the mouse drag event in the Legacy Viewport.
219  """
220  self.view.beginXorDrawing()
221  if self.fs_drawn:
222  # Redraw the marquee at its old position to erase it.
223  self.draw_marquee_gl()
224 
225  self.fs_drawn = True
226 
227  # Get the marquee's new end position.
228  self.last = event.position
229 
230  # Draw the marquee at its new position.
231  self.draw_marquee_gl()
232  self.view.endXorDrawing()
233 
234  def doHoldLegacy( self, event ):
235  """
236  Handle the mouse hold event in the Legacy Viewport.
237  """
238  self.do_hold_common(event)
239 
240  def doReleaseLegacy( self, event ):
241  """
242  Handle the mouse release event in the Legacy Viewport.
243  """
244  # Selects objects within the marquee box.
245  if self.fs_drawn:
246  self.view.beginXorDrawing();
247 
248  # Redraw the marquee at its old position to erase it.
249  self.draw_marquee_gl()
250  self.view.endXorDrawing()
251 
252  self.do_release_common( event );
253 
254  def doPress( self, event, drawMgr, context ):
255  """
256  Handle the mouse press event in VP2.0.
257  """
258  self.do_press_common(event)
259 
260  def doRelease( self, event, drawMgr, context ):
261  """
262  Handle the release press event in VP2.0.
263  """
264  self.do_release_common(event)
265 
266  def doDrag( self, event, draw_mgr, context ):
267  """
268  Handle the mouse drag event in VP2.0.
269  """
270  # Get the marquee's new end position.
271  self.last = event.position
272 
273  # Draw the marquee at its new position.
274  draw_mgr.beginDrawable()
275  draw_mgr.setColor( OpenMaya.MColor((1.0, 1.0, 0.0)) )
276  draw_mgr.line2d( OpenMaya.MPoint( (self.start[0], self.start[1])), OpenMaya.MPoint((self.last[0], self.start[1])) )
277  draw_mgr.line2d( OpenMaya.MPoint( (self.last[0], self.start[1])), OpenMaya.MPoint((self.last[0], self.last[1])) )
278  draw_mgr.line2d( OpenMaya.MPoint( (self.last[0], self.last[1])), OpenMaya.MPoint((self.start[0], self.last[1])) )
279  draw_mgr.line2d( OpenMaya.MPoint( (self.start[0], self.last[1])), OpenMaya.MPoint((self.start[0], self.start[1])) )
280  draw_mgr.endDrawable()
281 
282  def doHold( self, event, drawMgr, context ):
283  """
284  Handle the mouse hold event in VP2.0.
285  """
286  self.do_hold_common(event)
287 
288  def doEnterRegion( self, event ):
289  """
290  Handle the enter region event. This method is called from both VP2.0 and the Legacy Viewport.
291  """
292  self.setHelpString( MarqueeContext.help_string )
293 
294 # Initialize the script plug-in
295 def initializePlugin(plugin):
296  pluginFn = OpenMaya.MFnPlugin(plugin)
297 
298  try:
299  pluginFn.registerContextCommand( MarqueeContextCmd.kPluginCmdName, MarqueeContextCmd.creator)
300  except:
301  logger.error("Failed to register context command: %s\n" % MarqueeContextCmd.kPluginCmdName)
302  raise
303 
304 
305 # Uninitialize the script plug-in
306 def uninitializePlugin(plugin):
307  pluginFn = OpenMaya.MFnPlugin(plugin)
308  try:
309  pluginFn.deregisterContextCommand(MarqueeContextCmd.kPluginCmdName)
310  except:
311  logger.error("Failed to unregister command: %s\n" % MarqueeContextCmd.kPluginCmdName)
312  raise
313 
314 #-
315 # ==========================================================================
316 # Copyright (C) 2016 Autodesk, Inc. and/or its licensors. All
317 # rights reserved.
318 #
319 # The coded instructions, statements, computer programs, and/or related
320 # material (collectively the "Data") in these files contain unpublished
321 # information proprietary to Autodesk, Inc. ("Autodesk") and/or its
322 # licensors, which is protected by U.S. and Canadian federal copyright
323 # law and by international treaties.
324 #
325 # The Data is provided for use exclusively by You. You have the right
326 # to use, modify, and incorporate this Data into other products for
327 # purposes authorized by the Autodesk software license agreement,
328 # without fee.
329 #
330 # The copyright notices in the Software and this entire statement,
331 # including the above license grant, this restriction and the
332 # following disclaimer, must be included in all copies of the
333 # Software, in whole or in part, and all derivative works of
334 # the Software, unless such copies or derivative works are solely
335 # in the form of machine-executable object code generated by a
336 # source language processor.
337 #
338 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND.
339 # AUTODESK DOES NOT MAKE AND HEREBY DISCLAIMS ANY EXPRESS OR IMPLIED
340 # WARRANTIES INCLUDING, BUT NOT LIMITED TO, THE WARRANTIES OF
341 # NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR
342 # PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE, OR
343 # TRADE PRACTICE. IN NO EVENT WILL AUTODESK AND/OR ITS LICENSORS
344 # BE LIABLE FOR ANY LOST REVENUES, DATA, OR PROFITS, OR SPECIAL,
345 # DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES, EVEN IF AUTODESK
346 # AND/OR ITS LICENSORS HAS BEEN ADVISED OF THE POSSIBILITY
347 # OR PROBABILITY OF SUCH DAMAGES.
348 #
349 # ==========================================================================
350 #+