devwiki:python

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Next revision
Previous revision
Last revisionBoth sides next revision
devwiki:python [2021/08/22 19:33] – created yingdevwiki:python [2024/03/21 08:53] – [Online Python run] ying
Line 1: Line 1:
-====== Intro - Maya in Python ====== 
  
-Before you read anything below, read this list first 
-  * Python can use almost all the mel command, maya.cmds is a module that simply wraps MEL commands in Python syntax, like <code python>import maya.cmds as cmds; cmds.sphere()</code> 
-  * Python can run directly in mel mode, like <code python>import maya.mel as mel; skin = mel.eval('findRelatedSkinCluster "'+geoName+'"')</code> 
-  * Python can call maya C++ API (maya api 1.0), like <code python>import maya.OpenMaya as om 
-import maya.OpenMayaAnim as oma 
-aim_dir = cmds.xform(myLoc, q=1, t=1, ws=1) 
-aim_vec = om.MVector(*aim_dir) 
-rot_quaternion = myObj_axis_vec.rotateTo(aim_vec) 
-# myObj_axis_vec is set as x axis direction 
-myObj_fnTransform.rotateBy(rot_quaternion) 
-# myObj_fnTransform is set as the transform api function object 
-</code> 
-  * Python can call maya C++ API 2.0 (partially since maya 2013, and still partially in 2015, maybe better in latest) <code python>import maya.api.OpenMaya as om2 
-aim_dir = cmds.xform(myLoc, q=1, t=1, ws=1) 
-aim_vec = om2.MVector(aim_dir) # can take python list automatically 
-# also, no need MScriptUtil for pointer to value stuff 
-# can directly return value like normal python 
-# also, sometimes faster than api 1.0 
-</code> 
-    * ref:  
-      * maya 2010 api 1.0 ref: http://download.autodesk.com/us/maya/2011help/API/main.html 
-      * maya 2013 api 2.0 doc: http://docs.autodesk.com/MAYAUL/2013/ENU/Maya-API-Documentation/python-api/index.html 
-      * maya 2017 api 2.0 doc: http://help.autodesk.com/view/MAYAUL/2017/ENU/?guid=__files_GUID_AC9B3C66_90FE_493F_A7B2_44ABBA77CED2_htm 
-  * Python can run almost every python module, as long as compatible with maya's internal python version, and add into sys.path. 
  
-  * Info on PyMel: +====== Modern Python Practice ======
-    * PyMel is created to combine above maya.cmds, maya.mel, maya.OpenMaya (api 1.0) into a seamless python module, that do the coding style translation and with extra with object-oriented features and an extra large library of useful modules; thus a python module to call those maya python modules +
-    * for me, I currently still prefer handle it directly than learning a new syntax called pymel+
  
-  * to do and merge into this +  * nowaday, now one use python 2.x anymore, use python 3 standard, use this 
-    * [[cgwiki:mel|old cgwiki:mel]] +    * use pathlib instead of os.path 
-    * [[cgwiki:python|old cgwiki:python]]+    * use f"{keyword}" instead of "{0}".format(keyword), unless it is easier
  
-===== Intro to Maya API concepts ===== 
  
-  * moved all over from my old cgwiki:maya_api+====== Online Python run ======
  
-====Directed Acyclic Graph (DAG)==== +  https://jupyter.org/try-jupyter/retro/notebooks/?path=notebooks/Intro.ipynb 
-  Simple example, the DAG describes how an instance of an object is constructed from a piece of geometry. +  * to install locally: <code>pip3 install --upgrade pip</code>
-  * *DAG* defines elements such as the position, orientation, and scale of geometry, and **DAG** is composed of two types of **DAG nodes**, transforms and shapes. +
-    Transform nodes : Maintain transformation information (position, rotation, scale, etc.) as well as parenting information.  +
-      For example, if you model a hand, you would like to apply a single transformation to rotate the palm and fingers, rather than rotating each individually-in this case the palm and fingers would share a common parent transformation node.  +
-    Shape nodes : Reference geometry and do not provide parenting or transformation information. +
-      *  For example, when you create a sphere, you create both a shape node (the sphere) and a transformation node that allows you to specify the sphere's position, scale, or rotation. The transformation node's shape node is its child.+
  
-====DAG Path==== +====== Install related ====== 
-  * DagPath is a set of nodes which uniquely identifies the location of a particular node or instance of a node in the graph.  +===== install Python3 on mac =====
-  * The path represents a graph ancestry beginning with the root node of the graph and containing, in succession, a particular child of the root node followed by a particular child of this child, etc., down to the node identified by the path.  +
-  * For instanced nodes, there are multiple paths which lead from the root node to the instanced node, one path for each instance. Paths are displayed in Maya by naming each node in the path starting with the root node and separated by the vertical line character, “|”. +
  
-====Dependency nodes==== +  Mac has its own python installvery old, only for basic usage 
-  All data within maya is contained within seperate connectable nodes (in maya terminology**dependency nodes**). the nodes that accept and output data are called dependency graph nodes. +  * to install Python3 from official python website,  
-  * It is used for animation (deform, simulation, audio) and construction history (model creation) +    - download python 3 for mac installer 
-  * All of these node typesare defined by the enumeration MFn::Type. For example, a textured polygonal mesh may be constructed from  +    - install as usual and done 
-    * an MFn::kMesh node for the polygonal surface shape +    - run python3 in cmd 
-    * an MFn::kTransform to position the mesh in the scene +    - then it pops"python3" command requires the "the commandline developer tools" to install, click install 
-    * an MFn::kLambert node for the surface material +    - if you already install XCode (the developer suite for mac)then no need; if not, you can just install the smaller "Command line tools" with your Apple account 
-    * an MFn::kFileTexture node for the surface texture +    - go Apple developer downloads pagesearch command line tools for xcode, download that and install
-  * If at anytimeyou need to find out how a set of nodes are connectedthen you can use the hypergraph within mayato show you a graphical representation of the scene data. It is worth remembering that the hypergraph only displays connections between attributes; It does not show the connections between a transform and the child objects underneath it. The outliner can do that for you.+
  
-====DAG hierarchy nodes vs Dependency Graph (DG) nodes==== +===== Manage and handle multiple versions of python case =====
-  * These are related but different systems, used for different purposes.  +
-  * Unlike DAG nodes, dependency graph nodes can be cyclic, meaning they can pass information both directions. ([[http://knowledge.autodesk.com/support/maya/learn-explore/caas/CloudHelp/cloudhelp/2015/ENU/Maya/files/GUID-5029CF89-D420-4236-A7CF-884610828B70-htm.html|ref]]) +
-  * The DAG relates to the DG in that every DAG node is implemented as a DG node, both shapes and transforms. The only "non-DG" component of the DAG is the parenting relationship. +
-  * The dependency graph (DG) is a collection of entities connected together. Unlike the DAG, these connections can be cyclic, and do not represent a parenting relationship. Instead, the connections in the graph allow data to move from one entity in the graph to another. The entities in the graph which accept input, and output data, are called dependency graph nodes. +
-  * Most objects in Maya are dependency graph nodes, or networks of nodes (several nodes connected together). For example, DAG nodes are dependency graph nodes, and shaders are networks of nodes.+
  
-====DG node creation==== +Situation: 
-  * MPxNode: base of DG node +  * sometimesyou may have to keep both python 2.7python 3.x 
-    * virtual compute(const MPlug& plugMDataBlock& data) +  and sometimes you want to try python with AIand AI need to install tons of AI related python modules 
-      * MDataHandle inputDH=data.inputValue(inAttrName&status); float result=calFun( inputDH.asFloat() ); +  * sometimes you just to keep python module in the min form to test something
-      MDataHandle outputDH=data.outputValue(outAttrName); outputDH.set(result); +
-      * data.setClean(plug) +
-    * return MS::kSuccess; +
-    * static void* creator() +
-    * static MStatus initialize() +
-    * MTypeId yourNode::id( 0x80000); (any local id bet. 0x0000 0000 to 0x0007 ffff) +
-    * attr: readable(output?); writable(input?); connectable;storable(toFile?);settable; +
-    * attr: multi(array?); keyable; hidden +
-    * addAttribute(inAttrName); attributeAffects(whichInputoutput) +
-  * MPxLocatorNode: DG-DAG-locator node, not renderable +
-  * MPxIkSolverNode: DG-IK solver (doSolve()) +
-  * MPxDeformerNode: DG-deformer (deform()) +
-  * MPxFieldNode: DG-dynamic field (compute()) +
-  * MPxEmitterNode: DG-emitter +
-  * MPxSpringNode: DG-dynamic spring (applySpringLaw()) +
-  * MPxManipContainer: DG-manipulator +
-  * MPxSurfaceShape: DG-DAG-shape +
-  * MPxObjectSet: set +
-  * MPxHwShaderNode: hardware shader +
-  * MPxTransform: matrics (MPxTransformMatrix)+
  
-====DG node attribute==== +Solution: 
-  * simple attribute: attribute has only 1 plug +  * for those case, when install python, never add python to system path 
-    it also can hold array datalike pointArrayintArray +  just download and make python folder a portable folder (you can install and copy the python whole folder to other machine as a portable appbetter zip to copyfaster) 
-    * MFnNumericAttribute +  then your computer cmd never know where is python and won't random get your python from somewhere 
-    MFnTypedAttribute +  * thendynamically set python into python in cmd session or batch file, or use full path to python.exe to run your script 
-  * array attribute: attribute has array of plugseach element plug has own value or connection or null, all of them under the top array plug+    * session based method (win as example) <code dos>SET PATH=D:\App\Python;D:\App_Dev\PortableGit\bin;%PATH%</code> 
-    * MFnAttribute::setArray() +    * batch file method <code dos test.batch> 
-  * compound attribute: a collection of other attributes, called child attribute +@echo off 
-    * MFnCompoundAttribute +SetLocal EnableDelayedExpansion 
-      * example, color <code cpp> +set CustPython=D:\App\Python\ 
-color1R nAttr.create( “color1R”, “c1r”,MFnNumericData::kFloat); +call !CustPython!python.exe my_script.py
-color1G = nAttr.create( “color1G”, “c1g”,MFnNumericData::kFloat)+
-color1B = nAttr.create( “color1B”, “c1b”,MFnNumericData::kFloat); +
-color1 = nAttr.create( “Sides”, “c1”, color1R, color1G,color1B); +
-nAttr.setStorable(true); +
-nAttr.setUsedAsColor(true); +
-nAttr.setDefault(1.0f, 1.0f, 1.0f); +
-addAttribute(color1R); +
-addAttribute(color1G); +
-addAttribute(color1B); +
-addAttribute(color1); +
-attributeAffects(color1R, color1); +
-attributeAffects(color1G, color1); +
-attributeAffects(color1B, color1); +
-attributeAffects(color1, aOutColor);+
 </code> </code>
-  * dynamic attribute: a attribute is dynamically created when needed. 
-    * MFnDependencyNode::kLocalDynamicAttr 
  
-====DG node data==== 
-  * MDataBlock is a storage object holds DG node's data during DG node's compute() method 
-  * MDataHandle is a reference into MDataBlock's plugs, and get or set onto plug 
-  * Data creator is for creating data to be into MDataBlock, like output plug of another node,  
-    * mostly for heavy data like mesh 
-    * MFnData (MFnMeshData, DAG-MFnMesh, MFnNurbsSurfaceData, DAG-MFnNurbsSurface) 
  
-====DG shader node==== +====== Python 3 Changes and How to Make 2.7 Code Works ======
-  * static MObject color1R,color1G,color1B,color1; +
-  * static MObject aOutColorR, aOutColorG, aOutColorB,aOutColor; +
-  * static MObject aNormalCameraX, aNormalCameraY,aNormalCameraZ, aNormalCamera; +
-  * static MObject aPointCameraX, aPointCameraY,aPointCameraZ, aPointCamera; +
-  * void MStatus compute( const MPlug&, MDataBlock& );+
  
-====DG shader node type==== +  make print "" into print(""), like as a function<code> 
-  textures/2d +# in code editor, using Regular Expression to replace 
-  * textures/3d +find: (print)\s(.+) 
-  * textures/environment +replace: $1\($2\) or \1(\2) depending on your code editor 
-  * shader/surface +</code> 
-  * shader/volume +  * other stuff <code python> 
-  * shader/displacement +# python 2 reload 
-  * light +reload(myModule) 
-  * utility/general +# python 3 reload 
-  * utility/color +from imp import reload 
-  * utility/particle +reload(myModule)
-  * imageplane +
-  * postprocess/opticalFX+
  
-note: shader=material +# python 2 execfile 
- +execfile("testScript.py") 
-====DG shader node creation==== +# python 3 
- +exec(open("testScript.py").read(), globals())
-  * example, Blinn creation <code javascript>shadingNode -asShader blinn;  +
-// same as:  +
-createNode blinn; +
-connectAttr blinn1.message defaultShaderList1.shaders;</code> +
-    * if creation option checked "with shading group" <code javascript>sets -renderable true -noSurfaceShader true -empty -name blinn1SG; +
-connectAttr -f blinn1.outColor blinn1SG.surfaceShader;+
 </code> </code>
-  * example, volume creation <code javascript> 
-shadingNode -asShader lightFog; 
-sets -renderable true -noSurfaceShader true -empty -name lightFog1SG; 
-connectAttr -f lightFog1.outColor lightFog1SG.volumeShader; 
-</code> 
-  * example, displacement creation <code javascript> 
-shadingNode -asShader displacementShader; 
-sets -renderable true -noSurfaceShader true -empty -name displacementShader1SG; 
-connectAttr -f displacementShader1.displacement displacementShader1SG.displacementShader; 
-</code> 
-  * example, texture 2d <code javascript> 
-shadingNode -asTexture checker; // means: 1. createNode checker; 2. add to multilister 
- 
-// if with option checked "With New Texture Placement"  
-shadingNode -asUtility place2dTexture;  
-connectAttr place2dTexture1.outUV checker1.uv; 
- 
-// if with option checked "As Projection" 
-shadingNode -asTexture projection; 
-shadingNode -asUtility place3dTexture; 
-connectAttr place3dTexture1.wim[0] projection1.pm; 
-connectAttr checker1.outColor projection1.image; 
- 
-// else with option checked "as stencil" 
-shadingNode -asTexture stencil; 
-shadingNode -asUtility place2dTexture; 
-connectAttr place2dTexture2.outUV stencil1.uv; 
-connectAttr checker1.outColor stencil1.image; 
  
-// ------ 3d tex +  * make compatible to python 3 <code python> 
-shadingNode -asUtility place3dTexture; +if sys.version_info[:3][0]>=3: 
-connectAttr place3dTexture2.wim[0] brownian1.pm;+    import importlib 
 +    reload = importlib.reload # add reload 
 +    raw_input = input # add raw_input 
 +    xrange = range # range 
 +    long = int # int 
 +    unicode = str # str
 </code> </code>
  
-====DG shape node ====+  * integer division difference in py3 vs py2, a/b 
 +    * ref: https://blog.finxter.com/how-to-divide-two-integers-in-python/ 
 +    * for safe, use <code>35/60 0 # py2 
 +35//60  0 # py2 , always use // for calculate minutes and seconds
  
-  * MPxSurfaceShape: with component handling function and DG node function +35/60 = 0.583 #py3 
-    * MPxSurfaceShapeUI: drawing and selection functions +35//60 = 0 # py3
-    * MPxGeometryIterator: component iterator +
-    * MPxGeometryData: pass geo data +
-  * MPxNode +
- +
-  * shape component: +
-    * MFnComponent +
-      * MFnSingleIndexedComponent: vertices +
-      * MFnDoubleIndexedComponent: cvs +
-      * MFnTripleIndexedComponent: lattice points +
- +
-  * shape component access: +
-    * MFnComponent +
-      * MFn::kMeshVertComponent : vtx[] +
-    * override access +
-      * MPxSurfaceShape::componentToPlugs(MObject& componet, MSelectionList& list)  +
-    * check call process (name, index, range):  +
-      * MPxSurfaceShape::matchComponent(const MSelectionList& item, const MAttributeSpecArray& spec, MSelectionList& list) +
-    * iterate component: +
-      * MPxGeometryIterator <code cpp>virtual MPxGeometryIterator* geometryIteratorSetup(MObjectArray&, MObject&, bool ); +
-virtual bool acceptsGeometryIterator( bool writeable ); +
-virtual bool acceptsGeometryIterator( MObject&,bool, bool );+
 </code> </code>
-    * component manipulator 
-      * MPxSurfaceShape::TransformUsing (matrix, components) 
-      * to speedup of setting attribute values, use MPxNode::forceCache, to access datablock and set directly without compute, vertexOffsetDirection if normal used. 
-    * tweak storing vs no-history access 
-      * set setAttr/getAttr behavior, use MFnAttribute::setInternal 
-    * connection around geometry data 
-      * define from MPxGeomtryData based on MPxData 
-      * and iterator: virtual MPxGeometryIterator* iterator(MObjectArray& MObject&, bool); 
-    * File save data 
-      * define writeASCII, writeBinary from MPxData 
  
-====DG deformer node ====+====== Python basics ======
  
-  * custom deform attributes +  * online python code runner: https://trinket.io/ 
-    MPxGeometryData +  Python download: http://www.python.org/ 
-    localShapeInAttr +  a cool python shell: http://www.dreampie.org/
-    * localShapeOutAttr +
-    * worldShapeOutAttr +
-    * MPxGeomtryIterator +
-    * match +
-    * createFullVertexGroup +
-    * geometryData+
  
-====DG shape - polygon API ====+** My Wiki Page related** 
 +  * how to publish Python script as standalone application: [[devwiki:pyapp|Python script App]] 
 +  * My App code share: [[devwiki:pyapp:mypyapp|My Py App]] 
 +  * My Python GUI code template: [[devwiki:template|My generic Qt GUI templates]]
  
-  polygon component +** to read: ** 
-    vertices (vtx[id] ->float pos[3]) +  * https://automatetheboringstuff.com/chapter9/ 
-    edges (edge[id] -> {vtxID_s, vtexID_e}) +  * Code Like a Pythonista: Idiomatic Python http://python.net/~goodger/projects/pycon/2007/idiomatic/handout.html#other-languages-have-variables
-    faces (fe[ids], face[id] -> fe_offset) +
-    * face vertices & uv ( face[id] -> fv[id]; fe[ids] -> {v_s, v_e}) +
-  * polygon attribute: +
-    * inMesh (storable) +
-    * outMesh +
-    * cachedInMesh (storable) +
-    * pnts (tweaks) (position offset for each vertex) +
-      * = modifier -> inMesh -> tweaks -> outMesh +
-      * or = outMesh +
-      * or cachedInMesh -> tweaks -> outMesh -cp-> cachedInMesh +
-  * polygon access, modify, create +
-    * MItMeshPolygon (Face comp, f-v, f-e, adjacent comp, uv, color-per-v, smooth info) +
-    * MItMeshEdge (Edge comp, e-f, e-v, edge smooth, adjacent comp) +
-    * MItMeshVertex, MItMeshFaceVertex (pos, v-f, v-e, normal, uv, color, adj comp) (f-fv_id, normal, uv, color) +
-    * MFnMesh (mesh operate on comp) +
-      * meshFn.numPolygons(); +
-      * meshFn.numEdges(); +
-      * meshFn.numVertices(); +
-      * meshFn.polygonVertexCount(); +
-      * meshFn.numUVs(); +
-  * polygon history +
-    * original polyShape -> modifier -> modifier -> (new modifier)-> polyNode +
-  * polygon custom modifier +
-    * MPxCommand +
-      * MStatus doModifyPoly(); +
-      * MStatus redoModifyPoly(); +
-      * MStatus undoModifyPoly(); +
-  * polygon exporter +
-    * class polyExporter, polyWriter, polyRawExporter, polyRawWriter+
  
 +===== Python vs . =====
  
 +  * Python vs Swift
 +    * https://blog.michaelckennedy.net/2014/11/26/comparison-of-python-and-apples-swift-programming-language-syntax/
  
-====== Maya in command line ======+===== Python Comment Tips =====
  
-  * launch Maya GUI interface with custom path setting and startup cmd or script at launch time, windows batch example <code batch> +  * single line comment <code python># single comment</code> 
-set MAYA_MODULE_PATH=Z:/example_external_folder/maya/modules/2014;%MAYA_MODULE_PATH%  +  * multiple line comment <code python>''' 
-set MAYA_SHELF_PATH=Z:/example_external_folder/maya/shelves/2014;%MAYA_SHELF_PATH% +line 1 
-set MAYA_PRESET_PATH=Z:/example_external_folder/maya/presets/2014;%MAYA_PRESET_PATH%+line 2 
 +''' 
 +</code> 
 +  * block, section, partition <code python>
  
-set MAYA_PLUG_IN_PATH=Z:/example_external_folder/maya/plug-ins/2014;%MAYA_PLUG_IN_PATH% +######################################## 
-set MAYA_PLUG_IN_LIST=fbxmaya.mll;objExport.mll;%MAYA_PLUG_IN_LIST% +# a major block of asset (40x#) 
- +######################################## 
-set MAYA_SCRIPT_PATH=Z:/example_external_folder/maya/scripts/2014;%MAYA_SCRIPT_PATH% +     
-set MAYA_SCRIPT_PATH=Z:/example_external_folder/maya/scripts/2014/custom2;%MAYA_SCRIPT_PATH% +#------------------------------ 
-set MAYA_SCRIPT_PATH=Z:/example_external_folder/maya/scripts/2014/custom3;%MAYA_SCRIPT_PATH%+# a function collection (30x-) 
 +#------------------------------
  
-set PYTHONPATH=Z:/example_external_folder/maya/Python/2014/Lib;%PYTHONPATH% +# ---- section comment ----
-set PYTHONPATH=Z:/example_external_folder/maya/tools/2014;%PYTHONPATH%+
  
-"C:\path_to_maya\maya.exe" -c "python(\"execfile('Z:/path_to_custom_script_to_run_on_start/custom.py')\")"+# function section tip 
 +def ________Loading_Functions________(): 
 +    # 8x_ 
 +    pass
 </code> </code>
 +===== Launch Python script =====
  
-  * process file with Maya in commandline only interface with "maya" in mac/linux, with "mayabatch.exewith win+  * launch in windows command batch file next to it<code batch>@echo off 
 +call python %~dp0\\MyPythonScript.py</code> 
 +  * launch in Mac AppleScript file (wip)<code applescript> 
 +set myScriptPath to path to me 
 +set myScriptFolderPath to POSIX path of (container of myScriptPath as string) -- in "/form 
 +set myScriptName to name of myScriptPath
  
- +tell application "Terminal" 
-**maya flags** +    do script with command "python /path_to_script/myScript.py" 
- +end tell
-^ -v | version number | +
-^ -batch | start in batch mode | +
-^ -prompt | non-gui mode | +
-^ -proj [dir] | start in define project | +
-^ -command [mel] | start with defined mel command +
-^ -script [mel] | start with mel script file | +
-^ -recover | recovers last file | +
-^ -render [file] | render defined file | +
-^ -archive [file] | list required or related files to archive a scene |  +
-^ -help | display flags help | +
-^ -noAutoloadPlugins | don't load any plugin | +
- +
- +
- +
-====== Preference ====== +
- +
-  * set viewport display color <code python>cmds.displayColor('locator', 17, c=1, dormant=1) # 17, gold color +
-# active=1 if for active color</code> +
- +
-====== Viewport ====== +
- +
-  * list all maya UI element and close some <code python> +
-allWindows = cmds.lsUI(windows=1) +
-toClose_win_list = [x for x in allWindows if x not in ['CommandWindow','ConsoleWindow','MayaWindow','ColorEditor'] ] +
-for each in toClose_win_list : +
-    cmsd.deleteUI(each)+
 </code> </code>
 +===== OS system operation =====
  
-  * select tool filter <code python> +  * get user/home folder <code python> 
-cmds.selectType(allObjects=0) # query by cmds.selectType(allObjects=1, q=1+os.path.expanduser('~') # return home directory, in Maya it return document, in windows, it return user dir 
-cmds.selectType(locator=1)+os.getenv("HOME"# return home directory 
 +os.getenv("USERPROFILE"# return user profile directory
 </code> </code>
  
-  * display layer management <code python> +  * make python code able to print unicode directly without .encode('utf-8') for text <code python>import sys 
-cmds.select(objList) +import codecs 
-cmds.createDisplayLayer(name="objList_layer", number=1, nr=1# create layer from selection +sys.stdout = codecs.getwriter('utf8')(sys.stdout)
- +
-cmds.setAttr(layer + ".displayType", 2# 0 normal, 1 template, 2 reference+
 </code> </code>
-  * display defined curve smoother, (note only during current maya session, restart maya will back to default) <code python> +  * get python program path<code python>import sys 
-cmds.displaySmoothness(['guide_R_path', 'guide_L_path'],divisionsU=3, divisionsV=3, pointsWire=16, pointsShaded=4, polygonObject=3)+print(sys.executable# the python cmd or app containing the python call path
 </code> </code>
- +  * get python module path <code python>import ModuleName 
-====== Maya UI automation scripting ====== +os.path.dirname(ModuleName.__file__)
- +
-^ mel name ^ python name ^ description ^ +
-| $gChannelBoxName | mainChannelBox | channel box | +
-| $gCommandReporter | cmdScrollFieldReporter1 | script editor result panel | +
-| $gMainWindow | MayaWindow | maya main window | +
-| graph1HyperShadeEd | graph1HyperShadeEd | material hypershade | +
-| nodeEditorPanel2NodeEditorEd | nodeEditorPanel2NodeEditorEd | node editor | +
- +
-  * get panel currently in focus <code python>cmds.getPanel(wf=1)</code> +
-  * get panel currently under pointer <code python>cmds.getPanel(up=1)</code> +
- +
-  * list maya UI \\ ref: (scan through all Maya UI elements) http://ewertb.soundlinker.com/mel/mel.101.php \\ <code python> +
-cmds.lsUI(type='tabLayout') # list all maya ui tab layout object +
-cmds.lsUI(windows=1) # list all window +
- +
-# trick cmds +
-cmds.lsUI(controlLayouts=1, l=1) +
-layoutType = cmds.objectTypeUI('AErootLayout'+
-childs = cmds.layout('AErootLayout', q=1, childArray=1)+
 </code> </code>
-  * list maya UI's child and object type and parent<code python> +  * get script path <code python>os.path.dirname(__file__)</code> 
-# most maya layout has a -childArray flag to get child +  get class path but not its super class <code python> 
-cmds.tabLayout("compEdTab",q=1,childArray=1+self.location = os.path.realpath(sys.modules[self.__class__.__module__].__file__
-get UI type +self.location os.path.abspath(inspect.getfile(self.__class__))
-cmds.objectType("compEdTabChild5"+
-# then continue call with the object's related cmd +
- +
-# to get parent +
-cmds.tabLayout("compEdTab",q=1,parent=1)+
 </code> </code>
-  * toggle maya window title bar visible and edit<code python> +  * get class name <code python>self.__class__.__name__</code
-cmds.window('MayaWindow', e=1, titleBar=(not cmds.window('MayaWindow', q=1, titleBar=1))+  * get basename of folder and file <code python>os.path.basename(os.path.normpath('/folderA/folderB/folderC/folderD/')) # 'folderD'</code> 
-hide title bar (true no UI by: press ctrl+space; then run this cmd) +  * get file name without extension <code python> 
- +file_name os.path.basename(file_path).rsplit('.',1)[0] 
-newT="Autodesk Maya 2009 x64 Unlimited: untitled | cool" +import pathlib 
-cmds.window('MayaWindow', e=1, title=newT# mel name as $gMainWindow+file_name_2 pathlib.Path(file_path).stem
 </code> </code>
-  * toggle outline window <code python> +  * get expanded and un-symbolic path <code python> 
-if cmds.window('outlinerPanel1Window',ex=1): +os.path.abspath(__file__
-    cmds.deleteUI('outlinerPanel1Window'+os.path.realpath(__file__)
-else: +
-    mel.eval('OutlinerWindow')+
 </code> </code>
 +  * path comparison <code python>
 +os.path.relpath(childPath, parentPath) # get the sub path only based on parent Path
 +os.path.commonprefix(list_of_paths) # get the common parent path of all input pathes
 +</code>
 +  * python code to detect platform, python version, bit <code python>
 +from sys import platform
  
-  * ignore a set of scripting/cmd in undo history <code python> +def get_platform()
-# refhttp://cgbreakdown.com/Blog/tools-and-scripts-maya-undo +    if platform.startswith('win'): 
-import maya.mel as mel +        return 'windows' 
-import maya.cmds as cmds +    elif platform.startswith('darwin'): 
-# nextFrame ('Alt+.') +        return 'mac' 
-cmds.undoInfo(stateWithoutFlush=0) +    return 'linux'
-mel.eval('playButtonStepForward') +
-cmds.undoInfo(stateWithoutFlush=1)+
  
-# previousFrame ('Alt+,') +import sys 
-cmds.undoInfo(stateWithoutFlush=0) +pyMode = '.'.join([ str(nfor n in sys.version_info[:3] ]
-mel.eval('playButtonStepBackward'+print("Python: {0}".format(pyMode))
-cmds.undoInfo(stateWithoutFlush=1) +
- +
-# nextKey ('.') +
-cmds.undoInfo(stateWithoutFlush=0) +
-cmds.currentTime(cmds.findKeyframe(timeSlider=1,which='next'), e=1) +
-cmds.undoInfo(stateWithoutFlush=1+
- +
-# previousKey (','+
-cmds.undoInfo(stateWithoutFlush=0+
-cmds.currentTime(cmds.findKeyframe(timeSlider=1,which='previous'), e=1) +
-cmds.undoInfo(stateWithoutFlush=1) +
- +
-# firstKey +
-cmds.undoInfo(stateWithoutFlush=0) +
-cmds.currentTime(cmds.findKeyframe(which='first'), e=1) +
-cmds.undoInfo(stateWithoutFlush=1)+
  
-# lastKey +is_64bits sys.maxsize > 2**32 
-cmds.undoInfo(stateWithoutFlush=0) +print(is_64bits)
-cmds.currentTime(cmds.findKeyframe(which='last'), e=1) +
-cmds.undoInfo(stateWithoutFlush=1)+
 </code> </code>
 +  * check file and path exists<code python>os.path.exists(myPath):</code>
 +  * scan file and folder content with pattern filtering <code python>
 +result_list = [x for x in os.listdir(scanPath) if os.path.isdir(os.path.join(scanPath,x))] 
 +# isdir for folder, isfile for file
 +cur_pattern = re.compile('\d{3}_\d{4}') # match 000_0000 name pattern
 +result_list = [x for x in result_list if cur_pattern.match(x)]
  
-  * group a set of scripting in undo history <code python> +# jpg name example 
-cmds.undoInfo(openChunk=1) +jpg_list = [x.rsplit('.',1)[0] for x in os.listdir(scanPath) if os.path.isfile(os.path.join(scanPath,x)) and x.lower().endswith('.jpg')]
-# your scripting block here +
-cmds.undoInfo(closeChunk=1)+
 </code> </code>
-  * get maya global variable to python nameexample <code python> +  * rename file or folder <code python>os.rename(os.path.join(parentPath,old_name), os.path.join(parentPath,new_name))</code> 
-mel.eval('global string $gChannelBoxName; $temp=$gChannelBoxName;')+  * move file (always use full path for safe, and always check source and target exist before operation)<code python> 
 +# file move within same drive 
 +os.rename("/sourcePath/fileName.ext", "/targetPath/fileName.ext"
 +os.replace("/sourcePath/fileName.ext", "/targetPath/fileName.ext"
 +# work for folder and file and cross disk drive 
 +shutil.move("/sourcePath/fileName.ext", "/targetPath/fileName.ext")
 </code> </code>
- +  
-  * toggle channel box nice and short names <code python> +  * system cmds for each os<code python> 
-main_cb = 'mainChannelBox' +os.system('move "%s\%s" "%s\zBk.%s"'(rootDir,oldPreset,rootDir,oldPreset)
-next_state = not cmds.channelBox(main_cbq=1nn=1+os.system('mklink /D "%s\%s" "%s\%s"' % (rootDir,oldPreset,resDir,newPreset))
-cmds.channelBox(main_cbe=1ln=next_statenn=next_state)+
 </code> </code>
 +  * cross-platform delivery from source
 +    * win: frozen
 +    * mac: frozen
 +    * lin: packaged
 +    * <code python>
 +def getSetup():
 +    if hasattr(sys, 'frozen'):
 +        return 'frozen'
 +    elif is_packaged():
 +        return 'packaged'
 +    return 'source'
 +</code>
 +  * change file permission <code python>
 +# ref: http://stackoverflow.com/questions/16249440/changing-file-permission-in-python
 +import os
 +import stat
 +os.chmod("R:\\folder\\permit.txt", stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH) # user, group, other readonly
 +</code>
 +  * windows register operation <code python>
 +PS_key = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE,"SOFTWARE\\Adobe\\Photoshop\\12.0") # may need check version no.
 +PS_APP = _winreg.QueryValueEx(PS_key, 'ApplicationPath')[0] + 'Photoshop.exe'
  
-  * toggle script editor echo and clear echo <code python> +def ps_loc_win(): 
-cmdPanel = 'cmdScrollFieldReporter1' +    import _winreg 
-next_state not cmds.cmdScrollFieldReporter(cmdPanelq=1, eac=1+    regKey _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\Photoshop.exe"
-cmds.cmdScrollFieldReporter(cmdPanele=1, eac=next_state+    regPath = _winreg.QueryValueEx(regKey'Path')[0] + 'Photoshop.exe' 
- +    return regPath
-# clear echo +
-cmds.cmdScrollFieldReporter(cmdPanel, e=1, clear=1)+
 </code> </code>
  
-  * create script editor <code python> +  * windows set default playback device <code python>subprocess.call('nircmd.exe setdefaultsounddevice "Speakers"') # need download nircmd</code>
-if cmds.cmdScrollFieldExecuter(ui_name, q=1, ex=1): +
-    cmds.deleteUI(ui_name) +
-mui = cmds.cmdScrollFieldExecuter(ui_name, st='python', sth=0, sln=1)# width=200, height=100)+
  
-selected_text = cmds.cmdScrollFieldExecuter(muiq=1, selectedText=1+  * run python script itself as admin <code python> 
-# change selected or insert text +import ctypessys 
-cmds.cmdScrollFieldExecuter(mui, e=1, insertText='#==== '+selected_text+' ====')+def is_admin(): 
 +    try: 
 +        return ctypes.windll.shell32.IsUserAnAdmin() 
 +    except: 
 +        return False
  
-# set search text and search next +if is_admin(): 
-cmds.cmdScrollFieldExecuter(mui, e=1, searchDown=1, searchWraps=1, searchMatchCase=0, searchString=text+    Code of your program here 
-res = cmds.cmdScrollFieldExecuter(mui, q=1, searchAndSelect=1) 1 or 0 +else: 
- +    Re-run the program with admin rights 
-go to line number +    ctypes.windll.shell32.ShellExecuteW(None"runas"sys.executable, ""None, 1)
-cmds.cmdScrollFieldExecuter(muie=1currentLine=lineNum) +
- +
-# get script editor whole text +
-script_text = cmds.cmdScrollFieldExecuter(muiq=1t=1)+
 </code> </code>
-  * create script history logger <code python> +  * run cmd with admin (3rd party module) <code python> 
-mui = cmds.cmdScrollFieldReporter(ui_name)#, width=200, height=100) +# pywin32 : sourceforge.net/projects/pywin32/files/pywin32/ 
- +import win32com.shell.shell as shell 
-# clear history +commands = 'echo hi' 
-cmds.cmdScrollFieldReporter(muie=1clr=1)+shell.ShellExecuteEx(lpVerb='runas'lpFile='cmd.exe'lpParameters='/c '+commands)
 </code> </code>
  
-  * open component editor and show skining tab <code python> +  * control, start, stop, restart service (3rd party module method) <code python> 
-mel.eval('ComponentEditor') +import win32serviceutil 
-# active smooth skin tab +serviceName = "serviceName
-tab_name_list cmds.tabLayout("compEdTab", q=1, tabLabel=1) +win32serviceutil.RestartService(serviceName)
-for i in range(len(tab_name_list)): +
-    if tab_name_list[i] == "Smooth Skins": +
-        cmds.tabLayout("compEdTab",e=1, selectTabIndex=i+1)+
 </code> </code>
  
-  add selected node into hyperShade and related <code python> +==== List File and sub-folders - the Correct Way ==== 
-for each in cmds.ls(sl=1): + 
-    cmds.hyperGraph("graph1HyperShadeEd"e=1, addDependNode=each )+  in Python 3.5, scandir module is part of python, also inside os.walk() internal instead of os.listdir() old way. 
 +  * so compare glob, python 2.7 os.walk() and scandir (also available as external module for previous python) 
 +    * scandir is at least 10x faster than old os.walk, glob is 2x faster than old os.walk by skipping dir/file type checking 
 +      * 132s (walk) / 62s (glob) / 12s (scandir) 
 + 
 +<code python> 
 +import glob 
 +def getDirData(top, maxDepth=1): 
 +    res = [] 
 +    for in range( 1, maxDepth+2 ): 
 +        maxGlob = "/".join"*" * d ) 
 +        topGlob = os.path.join( top, maxGlob ) 
 +        allFiles = glob.glob( topGlob ) 
 +        res.extend( allFiles ) 
 +        #someFiles.extend( [ f for f in allFiles if fnmatch.fnmatch( os.path.basename( f ), fnMask ) ] ) 
 +    return res 
 + 
 +import os 
 +def getDirData( top, maxDepth=1): 
 +    # ref: https://stackoverflow.com/questions/7159607/list-directories-with-a-specified-depth-in-python 
 +    # py3 option: better with scandir,  
 +    # py2 has: https://github.com/benhoyt/scandir 
 +    top = os.path.normpath(top) 
 +    res = [] 
 +    root_len = len(top) #+len(os.path.sep) 
 +    for root,dirs,files in os.walk(top, topdown=True): 
 +        depth = root[root_len:].count(os.path.sep) 
 +        if depth < maxDepth: 
 +            res += [os.path.join(root, d) for d in dirs] 
 +            res += [os.path.join(root, f) for f in files] 
 +        elif depth == maxDepth: 
 +            res += [os.path.join(root, d) for d in dirs] 
 +            res += [os.path.join(root, f) for f in files] 
 +            dirs[:] = [] # Don't recurse any deeper 
 +    return res 
 + 
 +# detail inf: https://benhoyt.com/writings/scandir/ 
 +# can use with maya 2017 by zip out _scandir.pyd and scandir.py: (scandir-1.10.0-cp27-cp27m-win_amd64.whl) https://pypi.org/project/scandir/1.10.0/#files 
 +tmp_path = r'D:\PyLib\scandir27x64' 
 +tmp_path in sys.path or sys.path.append(tmp_path) 
 +import scandir 
 +def getDirData(topmaxDepth=1)
 +    # py3.5 has built in, only for 2.7 
 +    # https://github.com/benhoyt/scandir 
 +    top = os.path.normpath(top) 
 +    res = [] 
 +    root_len = len(top)#+len(os.path.sep) 
 +    for root,dirs,files in scandir.walk(top, topdown=True): 
 +        depth = root[root_len:].count(os.path.sep) 
 +        if depth < maxDepth: 
 +            res += [os.path.join(root, d) for d in dirs] 
 +            res += [os.path.join(root, f) for f in files] 
 +        elif depth == maxDepth: 
 +            res += [os.path.join(root, d) for d in dirs] 
 +            res += [os.path.join(root, f) for f in files] 
 +            dirs[:] = [] # Don't recurse any deeper 
 +    return res
          
-cmds.hyperGraph("graph1HyperShadeEd", e=1addBookmark=1+# test 
-newBookmark cmds.hyperGraph("graph1HyperShadeEd", q=1, bookmarkName=1# return recent created mark +import time 
-cmds.setAttr(newBookmark+".description", "my bookmark note", type="string"+start = time.time(
 +result getDirData(r'R:\ServerFolder\RootDir',5
 +end time.time() 
 +print('used: {}s'.format(end - start))
 </code> </code>
-  * add selected node in nodeEditor and related <code python> +  * other module option: https://github.com/juancarlospaco/faster-than-walk
-for each in cmds.ls(sl=1): +
-    cmds.nodeEditor('nodeEditorPanel2NodeEditorEd',e=1, addNode=each)+
  
-cmds.getPanel(scriptType="nodeEditorPanel") # add NodeEditorEd at the end to get the nodeEditor object +==== Copy File - the Correct Way ====
-cmds.nodeEditor('nodeEditorPanel2NodeEditorEd', q=1, getNodeList=1) +
-</code> +
-  * toggle channel box long short name display<code python> +
-import maya.mel as mel +
-mel.eval('setChannelLongName(!`channelBox -query -nn $gChannelBoxName`)'+
-</code> +
-  * maya mel button with python function with parameter <code python> +
-def process_func(data): +
-    print(data) +
-cmds.button( command = lambda *args: process_func('one')</code> +
-  * show GraphEditor <code python> +
-# get panel by name +
-whichPanel = cmds.getPanel(withLabel='Graph Editor'+
-cmds.scriptedPanel(whichPanel, e=1, tor=1) +
-wndName = whichPanel + "Window";the window name +
-</code> +
-====== Hotkey ======+
  
-**Note**from Maya 2017 onwards, you need to duplicate default maya hotkey to be able to make changes, it is under Windows Settings Hotkey Editor+  speed on windowsSHFileOperation (8) win32file (8.6) os System (9) > shutil (18) 
 +    * ref: http://www.cnblogs.com/lovemo1314/archive/2010/11/11/1874975.html
  
-  * reset hotkey <code python>cmds.hotkey(factorySettings=1)</code> +  * use python shutil module copy, copyfile and copy2 function <code python> 
-  * create hotkey <code python> +import shutil 
-import maya.cmds as cmds +copy: return target path, target can be file or folder 
-cmds.hotkey(factorySettings=1+shutil.copy('d:/z_tmp/aa.jpg','d:/z_tmp/bb.jpg'
-cmds.hotkey(autoSave=0)+shutil.copyfile('d:/z_tmp/aa.jpg','d:/z_tmp/dd.jpg')
  
--- toggle select mode +''' result modified date change 
-g_shiToggleMode=0 +02/06/2017  02:13 PM           193,399 aa.jpg 
-def toggleSelectMode(curMode): +12/09/2017  12:06 PM           193,399 bb.jpg 
-    global g_shiToggleMode +12/09/2017  12:06 PM           193,399 dd.jpg 
-    print([g_shiToggleModecurMode]) +'''
-    if g_shiToggleMode != curMode: +
-        if curMode == 1:  +
-            cmds.SelectVertexMask() +
-        elif curMode == 2: +
-            cmds.SelectEdgeMask() +
-        elif curMode == 3: +
-            cmds.SelectFacetMask() +
-        elif curMode == 0:  +
-            cmds.SelectToggleMode() +
-        g_shiToggleMode = curMode +
-    else: +
-        cmds.SelectToggleMode() +
-        g_shiToggleMode = 0+
  
-for i in range(1,3+1): +shutil.copy2('d:/z_tmp/aa.jpg','d:/z_tmp/bb.jpg') 
-    cmds.nameCommand('NC_shiToggleMode_'+str(i)ann='NC_shiToggleMode_'+str(i), c='python("toggleSelectMode({0})")'.format(i)) +''' # result keeps modified date 
-    cmds.hotkey(k=str(i), ctl=1, n='NC_shiToggleMode_'+str(i))+02/06/2017  02:13 PM           193,399 aa.jpg 
 +02/06/2017  02:13 PM           193,399 cc.jpg 
 +''
 +</code> 
 +  * copy folder and its content <code python> 
 +import shutil, errno
  
-# -- viewport +def copyFolder(srcdst)
-cmds.nameCommand('NC_view_tWireframe'ann='NC_view_tWireframe', c='python("cmds.modelEditor(cmds.getPanel(up=1), e=1, wireframeOnShaded=(1-cmds.modelEditor(cmds.getPanel(up=1), q=1, wireframeOnShaded=1)))")'+    try: 
-cmds.hotkey(k='f'ctl=1, alt=1, n='NC_view_tWireframe') +        shutil.copytree(srcdst) # folder structure only 
--- windows +    except OSError as exc: # python >2.5 
-cmds.nameCommand('NC_win_outliner', ann='NC_win_outliner', c="OutlinerWindow;") +        if exc.errno == errno.ENOTDIR: 
-cmds.hotkey(k='4', ctl=1, n='NC_win_outliner'+            shutil.copy2(srcdst) # file only with same dateor copy for current date 
-# -- script editor +        else: raise 
-cmds.nameCommand('NC_win_scriptEditor', ann='NC_win_scriptEditor', c="ScriptEditor") +         
-cmds.hotkey(k='x'ctl=1, alt=1, n='NC_win_scriptEditor') +or use this 
-#-- hyperShade +distutils.dir_util.copy_tree(src_foldertgt_folder)
-cmds.nameCommand('NC_win_hyperShade'ann='NC_win_hyperShade', c="HypershadeWindow") +
-cmds.hotkey(k='s', ctl=1, alt=1, n='NC_win_hyperShade') +
-#-- graph editor +
-cmds.nameCommand('NC_win_graphEditor', ann='NC_win_graphEditor', c="GraphEditor"+
-cmds.hotkey(k='g', ctl=1, alt=1, n='NC_win_graphEditor') +
-#-- custom +
-cmds.nameCommand('NC_fun01', ann='NC_fun01', c='python("fun01")'+
-cmds.hotkey(k='d'ctl=1, alt=1, n='NC_fun01')+
 </code> </code>
-  * Hotkey with Qt <code python> +  * python more complete version for copy folder and its content <code python> 
-# ref: http://bindpose.com/custom-global-hotkey-maya+# ref: http://blog.csdn.net/liyuan_669/article/details/25346645 
-qt import +folder content to target folder content. created folders seams not same date, but file same date 
-try+import os 
-    from PySide import QtGui, QtCore +import shutil 
-    import PySide.QtGui as QtWidgets +def copy_tree(src, dst, symlinks=False)
-    import shiboken +    names = os.listdir(src) 
-except ImportError:+    if not os.path.isdir(dst): 
 +        os.makedirs(dst) 
 +    errors = [] 
 +    for name in names: 
 +        srcname = os.path.join(src, name) 
 +        dstname = os.path.join(dst, name) 
 +        try: 
 +            if symlinks and os.path.islink(srcname): 
 +                linkto = os.readlink(srcname) 
 +                os.symlink(linkto, dstname) 
 +            elif os.path.isdir(srcname): 
 +                copy_tree(srcname, dstname, symlinks) 
 +            else: 
 +                # perform overrides 
 +                if os.path.isdir(dstname): 
 +                    os.rmdir(dstname) 
 +                elif os.path.isfile(dstname): 
 +                    os.remove(dstname) 
 +                shutil.copy2(srcname, dstname) 
 +            # other case like: devices, sockets etc. 
 +        except (IOError, os.error) as why: 
 +            errors.append((srcname, dstname, str(why))) 
 +        # catch the Error from the recursive copy_tree so that we can 
 +        # continue with other files 
 +        except OSError as err: 
 +            errors.extend(err.args[0])
     try:     try:
-        from PySide2 import QtCoreQtGui, QtWidgets +        shutil.copystat(srcdst) 
-        import shiboken2 as shiboken +    except WindowsError: 
-    except ImportError:+        # can't copy file access times on Windows
         pass         pass
-import maya.cmds as cmds +    except OSError as why: 
-import maya.OpenMayaUI as omui +        errors.extend((src, dst, str(why))
-hotkey = {} +    if errors: 
-mayaWindow = shiboken.wrapInstance(long(omui.MQtUtil.mainWindow()), QtWidgets.QWidget)+        raise Error(errors)
  
-def shortcut_process(keyname): +copy_tree('E:/book''E:/newbook') 
-    if keyname == 'toggleWire': +</code> 
-        if 'modelPanel' in cmds.getPanel(wf=1): +  * use system command to copy <code python> 
-            print('Done Something') +src = 'd:/z_tmp/aa.jpg
-        else+tgt = 'd:/z_tmp/sys_copy.jpg' 
-            # give key event to maya +os.system('copy {0} {1}'.format(os.path.normpath(src),os.path.normpath(tgt)) ) 
-            hotkey[keyname].setEnabled(0+# normpath will change the slash for win from unix path
-            e = QtGui.QKeyEvent(QtCore.QEvent.KeyPressQtCore.Qt.Key_F, QtCore.Qt.ControlModifier | QtCore.Qt.AltModifier) +
-            QtCore.QCoreApplication.postEvent(mayaWindow , e) +
-            cmds.evalDeferred(partial(hotkey[keyname].setEnabled, 1)) +
-    else: +
-        pass +
  
-# shortcut +os.system ("xcopy /s {0} {1}".format(src_dirtgt_dir))
-hotkey['toggleWire'] = QtWidgets.QShortcut(QtGui.QKeySequence('Ctrl+Alt+F')mayaWindow) +
-hotkey['toggleWire'].setContext(QtCore.Qt.ApplicationShortcut) +
-hotkey['toggleWire'].activated.connect(partial(shortcut_process, 'toggleWire'))+
 </code> </code>
 +  * use system file manager to copy with system progress window (windows) <code python>
 +#ref: https://stackoverflow.com/questions/16867615/copy-using-the-windows-copy-dialog
 +import pythoncom
 +from win32com.shell import shell,shellcon
  
-====== Object Level API Process ======+def win_copy_process(src_files,tgt_folder): 
 +    # win32com module 
 +    pfo pythoncom.CoCreateInstance(shell.CLSID_FileOperation,None,pythoncom.CLSCTX_ALL,shell.IID_IFileOperation) 
 +    # Respond with Yes to All for any dialog 
 +    # ref: http://msdn.microsoft.com/en-us/library/bb775799(v=vs.85).aspx 
 +    pfo.SetOperationFlags(shellcon.FOF_NOCONFIRMATION) 
 +    # Set the destionation folder 
 +    dst shell.SHCreateItemFromParsingName(tgt_folder,None,shell.IID_IShellItem) 
 +    for f in src_files: 
 +        src shell.SHCreateItemFromParsingName(f,None,shell.IID_IShellItem) 
 +        pfo.CopyItem(src,dst) # Schedule an operation to be performed 
 +    # ref: http://msdn.microsoft.com/en-us/library/bb775780(v=vs.85).aspx 
 +    success pfo.PerformOperations() 
 +    # ref: sdn.microsoft.com/en-us/library/bb775769(v=vs.85).aspx 
 +    aborted pfo.GetAnyOperationsAborted() 
 +    return success and not aborted
  
-** Python Maya API reference **+file_list = [r'D:\z_tmp\aa.jpg',r'D:\z_tmp\bb.jpg'
 +tgt_folder = r'D:\zTmp' 
 +win_copy_process(file_list,tgt_folder) 
 +</code> 
 +  use system API (windows) <code python> 
 +#ref: http://timgolden.me.uk/python/win32_how_do_i/copy-a-file.html 
 +# win32 
 +from win32com.shell import shell, shellcon 
 +win32file.CopyFile (filename1, filename2, 1)
  
-  * Python script and Python Maya API example+# win32com with windows copy dialog 
-    * http://cgkit.sourceforge.net/maya_tutorials/intro/ +shell.SHFileOperation (  (0shellcon.FO_COPY, filename1, filename2, 0, None, None) ) 
-  * Cope with Python in Maya C++ API with MScriptUtilfor passing by reference; re-solved in API 2.0 +# for the "0" parameter 
-    * http://www.chadvernon.com/blog/resources/maya-api-programming/mscriptutil/ +# shellcon.FOF_RENAMEONCOLLISION: rename on collision 
-  * Maya API 2.for pythonnew python way of the redefined C++ API from Maya 2013 +# shellcon.FOF_NOCONFIRMATION: no confirmation dialog and always override 
-    * http://docs.autodesk.com/MAYAUL/2014/JPN/Maya-API-Documentation/python-api/index.html+# shellcon.FOF_NOERRORUI: no error pop dialog 
 +# shellcon.FOF_SILENT: no copying progress dialog 
 +# shellcon.FOF_NOCONFIRMMKDIR: no ask for non exist foldercreate folder by default 
 +</code>
  
-** common api page offical reference page ** +===== string operation =====
-  * all : http://download.autodesk.com/us/maya/2009help/api/classes.html+
  
-^ MSelectionList | http://download.autodesk.com/us/maya/2009help/api/class_m_selection_list.html | +  encode and decode Qt QString <code python>str(myQStringObj# ascii output 
-^ MDagPath | http://download.autodesk.com/us/maya/2010help/api/class_m_dag_path.html | +unicode(myQStringObj# unicode output 
-^ MObject | http://download.autodesk.com/us/maya/2009help/api/class_m_object.html | +unicode(myQStringObj.toUtf8(), encoding="UTF-8") # decode based on utf8then encode in utf-8 python string as unicode output
-^ MFn | http://download.autodesk.com/us/maya/2010help/api/class_m_fn.html | +
-^ MFnDagNode | http://download.autodesk.com/us/maya/2010help/api/class_m_fn_dag_node.html | +
-^ MFnDependencyNode | http://download.autodesk.com/us/maya/2011help/api/class_m_fn_dependency_node.html | +
-^ MScriptUtil | http://download.autodesk.com/us/maya/2011help/api/class_m_script_util.html | +
-^ MFnMesh | http://download.autodesk.com/us/maya/2011help/api/class_m_fn_mesh.html | +
-^ MItDependencyGraph | http://download.autodesk.com/us/maya/2009help/api/class_m_it_dependency_graph.html | +
- +
- +
-===== DAG path, object and nodes ===== +
- +
-**om.MDagPath**:  +
-  * path info for Dag node, the normal way, python/mel refering to maya objects like Transform and Shape +
-  * it is from the world node to a particular object in the DAG  +
- +
-  * string name to MDagPath <code python> +
-selectionList = om.MSelectionList() +
-selectionList.add(mesh_name+
-mesh_dagPath = om.MDagPath() +
-selectionList.getDagPath(0mesh_dagPath) +
-print(mesh_dagPath.partialPathName()) # if done correctit will give same name as mesh_name+
 </code> </code>
-  * MObject to MDagPath <code python> +  * ref:  
-geo_obj.hasFn(om.MFn.kDagNode) # check if it is Dag node, then it can have a dag path +    * UTF-8 is an encoding used to translate numbers into binary data.  
-geo_dagNode = om.MFnDagNode(geo_obj) # now it is a DagNode, then get DagPath from DagNode +    * Unicode is a character set used to translate characters into numbers
-print( geo_dagNode.partialPathName() ) # since DagNode also has name function +  * text block to clean row list <code python> 
-geo_dagPath = om.MDagPath() +data_list [row.strip() for row in source_txt.split('\n')]
-geo_dagNode.getPath(geo_dagPath) +
-print( geo_dagPath.partialPathName() ) # proof geo_dagPath work correctly +
-</code> +
-  * MFnDagNode to MDagPath() <code python> +
-geo_dagPath om.MDagPath() +
-geo_dagNode.getPath(geo_dagPath) +
-print( geo_dagPath.partialPathName() # proof geo_dagPath work correctly+
 </code> </code>
  
-**om.MObject**: +  * to use non-ascii character in python fileyou need to put encoding in first line of code, or you will get "Non-ASCII character '\xe2' in file" error <code python> 
-  * MDagPath to MObject conversion, <code python> +# coding: utf-8 
-geo_transform_ojbect geo_transform_dagPath.node() +my_special_string u"好" 
-geo_transform_ojbect.hasFn(om.MFn.kTransform# true, then can safely cast to MFnTransform+print(u"好")
 </code> </code>
-  * string name to MObject conversion <code python> +  * remove extra space from a string, also remove end single space<code python> 
-selectionList = om.MSelectionList() +tidy_name ' '.join(old_name.split())
-selectionList.add(skinName) +
-skinClusterObj = om.MObject() +
-selectionList.getDependNode(0,skinClusterObj) +
-skinClusterObj.hasFn(om.MFn.kSkinClusterFilter) # use for testing become cast to the object function +
-</code> +
- +
-**MFnDagNode**: +
-  * take input Object and provide function to access object as DAG Node, for its attribute and modify +
-  * it has same function as MDagPath like <code python> +
-int MDagPath.childCount() = MFnDagNode.childCount() +
-MObject = MDagPath.child(index) = MFnDagNode.child(index) +
-int = MDagPath.pathCount() = MFnDagNode.pathCount() +
- +
-# node only can return itself while path can return segment +
-str = MDagPath.getPath(MDagPath, i) = MFnDagNode.getPath(MDagPath+
  
-str MDagPath.fullPathName() = MFnDagNode.fullPathName() +# just remove extra space 
-str MDagPath.partialPathName() = MFnDagNode.partialPathName()+tidy_name re.sub(' +', ' ', old_name# only space, need strip() to clear end space 
 +tidy_name re.sub('\s{2,}', ' ', old_name# space, tab, new line, need strip() to clear end space
 </code> </code>
-  * but MFnDagNode has ability to add and remove child, also duplicate, instanceCount +=====  control flow operation =====
-    * for its matric info, it has transformationMatrix() +
-  * but MDagPath has ability to get nearby or itself for transform MObject, cast into shape MDagPath +
-    * for its matric info, it has inclusiveMatrix(), exclusiveMatrix(), inclusiveMatrixInverse(), exclusiveMatrixInverse()+
  
-  * str to MFnDagNode <code python> +  * define function <code python> 
-selectionList = om.MSelectionList() +def my_fun(para1): 
-selectionList.add(mesh_name) +    print para1
-mesh_dagPath = om.MDagPath() +
-selectionList.getDagPath(0, mesh_dagPath) +
-mesh_dagNode = om.MFnDagNode(mesh_dagPath) +
-</code> +
-  * MDagPath to MFnDagNode <code python> +
-my_dagNode = om.MFnDagNode(my_dagPath) +
-</code> +
-  * MObject to MFnDagNode <code python> +
-my_obj.hasFn(om.MFn.kDagNode) +
-my_dagNode = om.MFnDagNode(my_obj)+
 </code> </code>
 +  * logic and condition and loop operation <code python>for tmpitem in tmplist:
 +    print tmpitem
  
-<code python+if tmp 0: 
-import maya.OpenMaya as om +    print 'ok' 
-selected om.MSelectionList() +elif x == 0: 
-om.MGlobal.getActiveSelectionList(selected) +    print 'zero' 
-dagPath om.MDagPath() +else: 
-selected.getDagPath(0,dagPath) +    print 'no'
-print(dagPath.fullPathName()) # current full path name +
-print(dagPath.partialPathName()) # current node name+
  
-get shape count +logic operation 
-countUtil = om.MScriptUtil() +or y 
-countUtil.createFromInt(0) +x and y 
-tmpIntPtr = countUtil.asUintPtr() # unsigned int +not x
-dagPath.numberOfShapesDirectlyBelow(tmpIntPtr) +
-print(countUtil.getUint(tmpIntPtr)) +
-print(dagPath.childCount()) # another alternative method +
- +
-# get its Shape as MDagPath +
-dagPath.extendToShape() # MDagPath: change to shape path, will error if has no shape or more than 1 shape +
-dagPath.extendToShapeDirectlyBelow(0) # same as above; but won't error when more than 1 shape +
-print(dagPath.fullPathName()) # shape, if already shape, also return itself +
- +
-# get itself as Node +
-dagObj = dagPath.node() # MObject +
-dagObj.hasFn(om.MFn.kDagNode) # check if its DAG node, not DG node +
-dagNode = om.MFnDagNode(dagObj) # use function to handle this MObject +
-print(dagNode.fullPathName()) +
- +
-# get its Transform as Node +
-dagObj = dagPath.transform() # MObject: return its transform MObject, if already transform, it return itself +
-dagNode = om.MFnDagNode(dagObj) +
-print(dagNode.fullPathName()) # transform node full dag path+
 </code> </code>
- +  try operation, note try is not blockvariable is same scope before try <code python> 
-===== DG nodes ===== +try: 
- +    val test(A
-  **MFnDependencyNode**: it takes MObject input to do creation and manipulation that dependency graph nodeas some dependency node has no DAG path. but all DAG path pointing to a DAG node which is also DG node <code python> +except: 
-my_DGNode OpenMaya.MFnDependencyNode(myObj+    pass 
-print(my_DGNode.name())+# ref: https://teamtreehouse.com/community/scope-of-a-variable  
 </code> </code>
-  * MFnDependencyNode also has function like attribute editor, editing all the plug/attributes, and its related plugin info+===== data operation =====
  
-  * Conversion from other data +  * data type check <code python> 
-    * str name to MFnDependencyNode <code python> +isinstance(Vehicle(), Vehicle returns True 
-selectionList = OpenMaya.MSelectionList(+type(Vehicle()) == Vehicle      returns True 
-selectionList.add(my_name) +isinstance(Truck(), Vehicle)    # returns True, takes inheritance 
-my_obj = OpenMaya.MObject() +type(Truck()) == Vehicle        returns Falsetake exact class type
-selectionList.getDependNode(0,my_obj) +
-my_obj.hasFn(om.MFn.kDependencyNode) # check before convert +
-my_dgNode = om.MFnDependencyNode(my_obj) +
-</code> +
-    * MDagPath to MFnDependencyNode <code python> +
-my_obj = my_dagPath.node() +
-my_obj.hasFn(om.MFn.kDependencyNode) # check before convert +
-my_dgNode = om.MFnDependencyNode(my_obj) +
-</code> +
-    * MFnDagNode to MFnDependencyNode <code python> +
-my_dagPath = my_dagNode.getPath(+
-my_obj = my_dagPath.node() +
-my_obj.hasFn(om.MFn.kDependencyNode) # check before convert +
-my_dgNode = om.MFnDependencyNode(my_obj) +
-</code> +
-  * **MDGModifier**: it act directly on adding nodes, making new connectionsand removing existing connections in DG nodes+
  
-other common class: +# check string, both python 2,3 
-  * **om.MPxNode**: the the parent class for user defined dependency nodes.+isinstance(my_data, (str, unicode) ) 
 +# check list 
 +isinstance(my_data, (list, tuple) )
  
-===== MPlugs and Attributes =====+# check not None, note, None is its own type and has only one instance 
 +my_vec !None # sometime, it may cause error when try to read certain way 
 +my_vec is not None # this way is preferred to be error free 
 +isinstance(my_vec, om.MVector) # this way even better, not only check it is defined and also check its type match
  
-ref: http://docs.autodesk.com/MAYAUL/2014/ENU/Maya-API-Documentation/index.html?url=cpp_ref/class_m_plug.html,topicNumber=cpp_ref_class_m_plug_html71ba8eb6-cc15-4382-8b6f-36cdfbc2ccc5 +# compare type 
- +if type(A) is type(B): 
-  * MPlug is just API way of calling attribute. +    print('Same Class'
-  * MPlug has network(built-inand non-networked(user-created) ones +     
-  * MPlug can be a single attribute plug, or a array/element plug. +# note : is for same object and like pointer comparisionwhile == is same value comparision
-  * MPlug can have its own MPlug, as compound plug, which can be accessed with .child(); the opposite is .parent() +
-  * Array type MPlug can access its list of plug by .elementByLogicalIndex() and .elementByPhysicalIndex() method; the opposite is .array() +
- +
-Array in MPlug+
-  * array are sparse.  +
-  * Logical Index used in MEL are sparse;  +
-  * Physical Index is not sparse, it range from 0-numElements()-1, while numElements may sometimes only reflect the amount of entry changed from default, thus sometimes, numElements is not equal to the total number of plug in array. MPlug[i] = MPlug.elementByPhysicalIndex(i) +
-  * numElement only refer to data in datablock, to get update value, use <code python>my_arraPlugy.evaluateNumElements() +
-print(my_arraPlugy.numElements())</code> +
- +
-=== Logical Index and Physical Index of Joint in skincluster === +
- +
-**Concept of Dynamic array** - a pre-defined-size list with pre-loaded empty items +
-  * as you can create array with any pre-defined size, Maya keep it in memory up to the size of last non-empty element; (refhttp://download.autodesk.com/global/docs/maya2014/en_us/index.html?url=files/Arrays_vectors_and_matrices_Get_and_change_the_size_of_an_array.htm,topicNumber=d30e790260) +
-  * in addition, Maya is using the sparse array structure for array plugs(attributes) +
-    * Maya will automatically resize and create new list of elements for those item whose value is not zero. +
-    * thus the 2 methods in MPlugwhere you can get plugs of the array by either logical index (sparse, means with zeros) or physical indexes (those with actual data connection). +
-    * ref: http://forums.cgsociety.org/archive/index.php?t-603088.html +
- +
-  * example MEL code <code javascript>string $nameArray[10]; +
-$nameArray[5]="objA"; // at place 5 in nameArray +
-print("\n-------start sparse array--------\n"); +
-print $nameArray; // null null null null null objA +
-print("-------end----------"); +
- +
-$logical_index stringArrayFind( "objA", 0, $nameArray ); // result: 5 +
-print("\nlogical index of 'objA': "+ $logical_index); +
-print("\nsize: "+size($nameArray)); // size of array currently: 6 +
-print("\n9th: "+$nameArray[8]); // null +
-print("\n11th: "+$nameArray[10]); // null +
-print("\nsize: "+size($nameArray)); // size of array currently: 6 +
- +
-string $nameArray_autoNonSparse_means_noEmpty[]; +
-for($each in $nameArray){ +
-    if($each != ""){ +
-        $cur_index = size($nameArray_autoNonSparse_means_noEmpty); +
-        $nameArray_autoNonSparse_means_noEmpty[$cur_index] = $each; +
-    } +
-+
-print("\n-------start no sparse array--------\n"); +
-print($nameArray_autoNonSparse_means_noEmpty); +
-print("-------end----------"); +
-$physical_index = stringArrayFind( "objA", 0, $nameArray_autoNonSparse_means_noEmpty ); // result: 0 +
-print("\nphysical index of 'objA': "+$physical_index+"\n"); +
- +
-// clear from memory +
-clear($nameArray);+
 </code> </code>
-    * so the logical index of a array is the number is in the [], and as like Python list and other language arrays 
-      * as shown in Maya skincluster joint list connection \\ {{graphic:python:maya:research_skincluster_connection.png|}} 
- 
-**The Physical index of joint in the influence list** 
-  * the most obvious demonstration of physical index of joint is in the component editor - skin weight tab \\ {{graphic:python:maya:research_skincluster_component_editor_weight_table.png|}} 
-    * since "joint3" is deleted, and no longer has relationship in "skinCluster1", thus there is no point put "joint3" column in the weight table; 
-    * the weight table is actual representation of the weight data; 
-      * **including all the vertex** 
-      * **including all the joints linked to skinCluster**, even it **has 0 in weight**  
-        * (make sure in component editor > option menu > Uncheck hide zero column) 
-        * (make sure in component editor > option menu > Uncheck sort alphabetically column) 
-        * {{graphic:python:maya:research_skincluster_component_editor_weightlist_display_format.png|}} 
-    * and when saving, actual weight data store in a 1 dimension array { vtx[0][jnt1], vtx[0][jnt2] ... vtx[7][jnt2] } 
-      * **with joint name as column header, and vtx index as row header** 
-      * so column index is the actual **Physical index** of joint used in referring weight data table 
-      * while the **logical index** of joint used in referring current connection flow. 
- 
-===== Math Node before Calculation with API ===== 
  
-  * **MScriptUtil** ref: http://download.autodesk.com/us/maya/2011help/api/class_m_script_util.html +  * data type convertion <code python>int('42'
-    * Input: createFrom*() Int, Double, List+len +str(42)
-    * middle: as*Ptr() # Int, Short, Float, Double, Uint, Bool, Char, Uchar, x2-4 +
-    * output: get*() # Int, Short, Float, Double, Uint, Bool, Char, Uchar, x2-4 +
-  * code workflow <code python> +
-py_var = 0 # your type, say Unsign Int +
-util = om.MScriptUtil() +
-util.createFromInt(py_var) # same as your type +
-util_ptr = util.asUintPtr() # same as your type +
-api_obj.api_function_return_a_value(util_ptr) # use pointer to hold the result value +
-py_var = util.getUint(util_ptr) # same as your type +
-print(py_var# new value from API+
 </code> </code>
 +  * get datetime date <code python>date_text = datetime.date.today().strftime("%Y.%m.%d")</code>
 +  * get processing time <code python>
 +import time
  
-===== Transform, Vector, Matrix, Quaternion, Angular Math ===== +start time.time() 
-  * other align vector related reading (mel only<code javascript> +print("hello"
-/* +end = time.time() 
-rot: rot vector around another vector by a degree +print(end - start)
-rad_to_deg, deg_to_rad +
-*/+
 </code> </code>
-  * **MVector** functions <code python> +  * wild char process <code python> 
-# ref: https://groups.google.com/forum/#!topic/python_inside_maya/-Yp8WfyTkPA +import glob 
-# MVector: rotateBy (MVector::Axis axis, const double angle)  +os.chdir(path) 
-# get a MVector result by rotate one Vector around default x,y,z axis by certain degree+for file in glob.glob("*.jpg"): 
 +    print file 
 +     
 +tmplist=glob.glob('*.[jJ][pP][gG]')
  
-MVectorrotateBy (const MQuaternion &) const  +other methodfnmatch and re
-# case 1: get a MVector result by rotate one Vector around another Vector by certain degree +
-res_vector = mainVector.rotateBy( maya.OpenMaya.MQuaternion(math.radians(rotDegree), aimVector) ) +
-# case 2: get a MVector result by rotate one Vector by a swing motion defined by start vector to end vector +
-res_vector = mainVector.rotateBy( maya.OpenMaya.MQuaternion(MotionStartVector, MotionEndVector, optionalPercentage ) )+
 </code> </code>
-  * **Align Object Axis one at a time to Vector direction** (OpenMaya method) <code python> +  * string operation <code python> 
-import maya.OpenMaya as om +name="01_Blue_Sun_DSC08897.jpg" 
-import math +print name[3:-13].replace('_',' ') #Blue Sun#
-def getLocalVecToWorldSpaceAPI(obj, vec=om.MVector.xAxis): +
-    # refhttp://forums.cgsociety.org/showthread.php?t=1124527 +
-    # take obj's local space vec to workspacedefault object x axis +
-    selList = om.MSelectionList() +
-    selList.add(obj) +
-    nodeDagPath = om.MDagPath() +
-    selList.getDagPath(0, nodeDagPath) +
-    matrix = nodeDagPath.inclusiveMatrix() +
-    vec = (vec * matrix).normal() +
-    return vec.x, vec.y, vec.z +
-     +
-define objects+
  
-a locator reference that we are aligning our pCube object to +len(str) string length
-# of course, you dont need a refLoc, you can use Vector from other data as well +
-# here locator is just a visual example +
-refLoc = 'locator_1' +
-obj = 'pCube1' # a cube we are aligning+
  
-# grab object transform function lib +"hello world".title() 
-selectionList = om.MSelectionList() +'Hello World'
-selectionList.add( obj ) +
-dagPath = om.MDagPath() +
-selectionList.getDagPath(0, dagPath) +
-fnTransform = om.MFnTransform(dagPath)+
  
-STEP 1align Y axis first +remove Whitespace on the both sides
-refLoc_normal_y getLocalVecToWorldSpaceAPI(refLoc, om.MVector.yAxis) + \t a string example\t  " 
-aim_vec_y om.MVector(*refLoc_normal_y) # break array into 3 parameters +s.strip()
-obj_normal_y = getLocalVecToWorldSpaceAPI(obj, om.MVector.yAxis) +
-obj_vec_y = om.MVector(*obj_normal_y) +
-rot_quaternion = obj_vec_y.rotateTo(aim_vec_y) +
-# absolute version +
-#fnTransform.setRotationQuaternion( rot_quaternion.x, rot_quaternion.y, rot_quaternion.z, rot_quaternion.w )  +
-# relative transform on top of existing obj transform +
-fnTransform.rotateBy(rot_quaternion)+
  
-STEP 2align Z axis +remove Whitespace on the right side
-refLoc_normal_z getLocalVecToWorldSpaceAPI(refLoc, om.MVector.zAxis) +s.rstrip() 
-aim_vec_z = om.MVector(*refLoc_normal_z+# remove Whitespace on the left side: 
-obj_normal_z = getLocalVecToWorldSpaceAPI(obj, om.MVector.zAxis) +s.lstrip()
-obj_vec_z om.MVector(*obj_normal_z) +
-rot_quaternion = obj_vec_z.rotateTo(aim_vec_z) +
-fnTransform.rotateBy(rot_quaternion)+
 </code> </code>
  
-  * get position <code python> +Data Type 
-#general +  * Lists []: a list of valuesEach one of them is numberedstarting from zero - the first one is numbered zerothe second 1, the third 2, etc. You can remove values from the list, and add new values to the end. Example: Your many cats' names. 
-pos = cmds.xform(objq=1t=1, ws=1)+  * Tuples (): are just like lists, but you can't change their values. The values that you give it first up, are the values that you are stuck with for the rest of the program. Again, each value is numbered starting from zero, for easy reference. Example: the names of the months of the year. 
 +  * Dictionaries {'':'',}are similar to what their name suggests - a dictionary. In a dictionary, you have an 'index' of words, and for each of them a definition. In python, the word is called a 'key', and the definition a 'value'
  
-# loc +  * list operation (so-called array) ([[http://effbot.org/zone/python-list.htm|ref]]<code python> 
-pos = cmds.getAttr(loc+'.worldPosition')[0# world position +[1,2,3
-pos = cmds.pointPosition(loc) # world position+len(arrayName) # size of list
  
 +# add value to all elements of list
 +my_list=[1,3,5,7,8]
 +new_list = [x+1 for x in my_list] # [2,4,6,8,9]
  
-#cv - local position from origin at creation time +list.append(obj) 
-cmds.getAttr('my_curve.cv[1]')[0] +list.extend(list
-cmds.pointPosition('my_curve.cv[1]',l=1)+list.index(obj) 
 +list.count(obj# count appearance of obj
  
-vtx - +array to string and string to array 
-cmds.getAttr('pCube1.vtx[1]')[0] # local offset value since creation +sentence = ["there", "is", "no", "spoon"] 
-cmds.getAttr("pCube1.pnts[1]")[0] local offset value since creation+'.join(sentence) #'there is no spoon'
  
-cmds.pointPosition('pCube1.vtx[0]',l=1) +list=[3.1,121.2,34.1] 
-cmds.setAttr("pCubeShape1.pnts[5].pntx"0) +', '.join(map(str,list)) #'3.1, 121.234.1'
-cmds.setAttr("pCubeShape1.pnts[5].pnty"0) +
-cmds.setAttr("pCubeShape1.pnts[5].pntz", 0)+
  
-vtx - local position relative to pivot+'3.1, 121.2, 34.1'.split(', '#['3.1','121.2','34.1'
 +# 2nd number make how many split point 
 +'3.1, 121.2, 34.1'.split(', ',1) #['3.1','121.2, 34.1'
  
-loc, vtx, cv, uv, point - global position +list sort (modify itself) 
-pos = cmds.pointPosition(pointObj) +versionList.sort(reverse=1# higher to lower 
-</code> +versionList.sort() 
-  * freeze transform  +
-    * it will make curve like re-created based on current result shape, previous local cv position is same as global position +
-    * it will make polygon pivot change back to origin, and vtx global position stay same, local position will same as current global position, and its offset value will be based on previous value +
-    * code <code python> +
-cmds.makeIdentity(cmds.ls(sl=1),apply=1,t=1,r=1,s=1,n=0) +
-</code> +
-====== Selection in API ======+
  
-  * get current selection <code python> +str([-4, -2, 0, 2, 4]# '[-4,-2,0,2,4]' 
-selected = om.MSelectionList() +bb_str ='_'.join(map(str, bb)) # -4_-2_0_2_4
-om.MGlobal.getActiveSelectionList(selected) +
-</code>+
  
-  * get current soft selection and weight<code python> +# list compare 
-def getVtxSelectionSoft_OpenMaya(): +[-4, -2, 0, 2, 4] == [-4, -2, 0, 2, 3] false
-    # ref: http://around-the-corner.typepad.com/adn/2013/05/support-soft-selection-with-custom-mpxcomponentshape.html +
-    startTime = cmds.timerX() # 0.13s > no string 0.07s +
-    soft selection +
-    softSelectionList = om.MSelectionList() +
-    softSelection = om.MRichSelection() +
-    om.MGlobal.getRichSelection(softSelection) +
-    softSelection.getSelection(selectionList)+
  
-    selectionPath = om.MDagPath() +multiple list operation
-    selectionObj = om.MObject() +
-    iter = om.MItSelectionList( selectionList, om.MFn.kMeshVertComponent) +
-    #elements = [] +
-    id_list = [] +
-    weights = [] +
-    node_list = [] +
-    while not iter.isDone(): +
-        iter.getDagPath( selectionPath, selectionObj ) +
-        selectionPath.pop() #Grab the parent of the shape node +
-        node = selectionPath.fullPathName() +
-        compFn = om.MFnSingleIndexedComponent(selectionObj)    +
-        getWeight = lambda i: compFn.weight(i).influence() if compFn.hasWeights() else 1.0 +
-        node_list.append(node) +
-        for i in range(compFn.elementCount()): +
-            #elements.append('{0}.vtx[{1}]'.format(node, compFn.element(i))) +
-            id_list.append(compFn.element(i)) +
-            weights.append(getWeight(i))  +
-        iter.next() +
-    totalTime = cmds.timerX(startTime=startTime) +
-    print("\nTotal Time: {0}".format(totalTime)) +
-    return list(set(node_list)), id_list, weights +
-</code>+
  
-  * API way to get selection vertex ids (single geo case), and set selection using vertex ids <code python> +wMap_zero_Jnt=[x for x in wMap_All_Jnt if x not in wMap_Jnt_tgt# remove list from list
-def createVtxSelection_cmds(objName, id_list): +
-    startTime = cmds.timerX() # 0.039s +
-    result_sel = ['{0}.vtx[{1}]'.format(objName, i) for i in id_list] +
-    totalTime = cmds.timerX(startTime=startTime) +
-    print("\nTotal Time: {0}".format(totalTime)) +
-    return result_sel +
-     +
-def createVtxSelection_OpenMaya(objName, id_list): +
-    # ref: http://discourse.techart.online/t/maya-api-components/3400/+
-    startTime = cmds.timerX() # 0.009s +
-    sel = om.MSelectionList() +
-    sel.add(objName) +
-    mdag = om.MDagPath() +
-    sel.getDagPath(0, mdag) +
-    util = om.MScriptUtil() +
-    util.createFromList(id_list, len(id_list)) +
-    ids_ptr = util.asIntPtr() +
-    ids = om.MIntArray(ids_ptr, len(id_list)) +
-     +
-    compFn = om.MFnSingleIndexedComponent() +
-    components = compFn.create( om.MFn.kMeshVertComponent ) +
-    compFn.addElements(ids) +
-     +
-    result_sel = om.MSelectionList() +
-    result_sel.add(mdag, components) +
-    totalTime = cmds.timerX(startTime=startTime) +
-    print("\nTotal Time: {0}".format(totalTime)) +
-    return result_sel +
-     +
-def getVtxSelection_cmd(l=0): +
-    startTime = cmds.timerX() # 0.18s +
-    selected = cmds.ls(sl=1, fl=1, l=l) # len(selected) +
-    id_list = [x.split('[',1)[-1][:-1] for x in selected] +
-    totalTime = cmds.timerX(startTime=startTime) +
-    print("\nTotal Time: {0}".format(totalTime)) +
-    return selected[0].split['.'][0], id_list +
-     +
-def getVtxSelection_OpenMaya(): +
-    startTime = cmds.timerX() # 0.11s > no comp iter: 0.039 +
-    # normal selection +
-    normalSelectionList = om.MSelectionList() +
-    om.MGlobal.getActiveSelectionList(normalSelectionList) +
-    selectionPath = om.MDagPath() +
-    selectionObj = om.MObject() +
-    indexList = om.MIntArray() +
-    iter = om.MItSelectionList( normalSelectionList, om.MFn.kMeshVertComponent ) +
-    #normalSelectionElements = [] # len(normalSelectionElements) +
-    node_list = [] +
-    id_list = [] +
-    while not iter.isDone(): +
-        iter.getDagPath( selectionPath, selectionObj ) +
-        selectionPath.pop() +
-        node = selectionPath.fullPathName() +
-        compFn = om.MFnSingleIndexedComponent(selectionObj) # flatten the component selection +
-        compFn.getElements(indexList) +
-        id_list.extend(list(indexList)) +
-        node_list.append(node) +
-        ''' +
-        for i in range(compFn.elementCount()): +
-            normalSelectionElements.append('{0}.vtx[{1}]'.format(node, compFn.element(i))) +
-            id_list.append(compFn.element(i)) +
-        ''' +
-        iter.next() +
-    totalTime = cmds.timerX(startTime=startTime) +
-    print("\nTotal Time: {0}".format(totalTime)) +
-    return list(set(node_list)), id_list +
-</code> +
-====== Modeling in Python vs Mel ======+
  
-  * list of cmds in both python and mel for modeling+a = [1, 2, 3, 4, 5] 
 +b = [9, 8, 7, 6, 5] 
 +common = set(a) & set(b) #[5], no order applied
  
-<code python> +[i for i, j in zip(a, bif i == j] # [5] for equal-sized lists, which order matters 
-# mel: DeleteHistory; +
-cmds.DeleteHistory()+
  
-# show poly vertex +version_list_prefix=["previous","current"] 
-# melsetPolygonDisplaySettings("verts"); +version_list_postfix=["A","B"
-cmds.polyOptions(r=1, activeObjects=1dv=1)+version_list_color=["rgb(255, 128, 128)","rgb(85, 170, 255)"
 +for each,post,color in zip(version_list_prefix,version_list_postfix,version_list_color): 
 +    print(each+" "+post+" is "+color
 +     
 +# list slice operation 
 +# n = len(L) 
 +# item L[index] 
 +# seq L[start:stop] 
 +# seq = L[start:stop:step] 
 +seq = L[::2] # get every other itemstarting with the first 
 +seq L[1::2] # get every other item, starting with the second
  
-# melToggleFaceNormalDisplay; +seq=L[::-1] # reverse reading list 
-cmds.ToggleFaceNormalDisplay()+list(reversed(seq)) # generate a list reversed 
 +L.reverse() # reverse list, it changed original list 
 +seq=L[::-2] # reverse reading list, and get every other item
  
-# mel: ToggleBackfaceGeometry; +list=[1,2,3,4,5,6] 
-cmds.ToggleBackfaceGeometry()+seq=list[::-2] # [6,4,2]
  
-melReversePolygonNormals; +list to pair list 
-cmds.ReversePolygonNormals()+info = ['name','John', 'age', '17', 'address', 'abc'
 +info_pair = zip(info[::2], info[1::2]) 
 +# [('name', 'John'), ('age', '17'), ('address', 'abc')
  
-hard edge +list flatten, and remove duplicates 
-# mel: SoftPolyEdgeElements(0); +nestList = [[2,3],[1,2,4],[5,6]] 
-cmds.polySoftEdge(a=0)+flatList set(sum(nestList,[])) # for small list of 2 level nest
  
-show border +delete a element from list 
-# mel: ToggleBorderEdges; +a = [1,2,3,4] 
-cmds.ToggleBorderEdges()+del(a[2]# [1,2,4]
  
-dialog to change edge width display +for large list 
-# mel: ChangeEdgeWidth; +import itertools 
-cmds.ChangeEdgeWidth()+flatList = set( list(itertools.chain.from_iterable(nestList)) ) # for same type element list, no mix of list and element like [[5],6], as it will error
  
-# show UV window +import operator 
-# mel: TextureViewWindow; +flatList = set( reduce(operator.add, map(list, nestList)) ) # like itertool, but it will not error for mix of list and element situation
-cmds.TextureViewWindow()+
-mel: CenterPivot; +
-cmds.CenterPivot()+
  
-turn on / off symmetric modeling +for any mix list 
-''' +def flatFn(mainList): 
-symmetricModelling -e -symmetry true;symmetricModelling -e -symmetry false; +    newList [] 
-symmetricModelling -e -about "world"; +    for each in mainList
-symmetricModelling -e -about "object"; +        if isinstance(each, (listtuple)): 
-symmetricModelling -e -axis "x" +            newList.extendflatFn(each) ) 
-''' +        else: 
-cmds.symmetricModelling(e=1, symmetry=1+            newList.append(each
-cmds.symmetricModelling(e=1, symmetry=0) +    return newList
- +
-# face selection to edge border selection +
-# melselect -r `polyListComponentConversion -ff -te -bo`; +
-cmds.select(cmds.polyListComponentConversion(ff=1te=1, bo=1),r=1) +
- +
-# selection to shell +
-# melConvertSelectionToShell; +
-cmds.ConvertSelectionToShell() +
- +
-# selection to create layer +
-''' +
-string $tmpname[]=`ls -sl`; +
-createDisplayLayer -name ($tmpname[0]+"_Lyr"-number 1 -nr; +
-''' +
-selected = cmds.ls(sl=1) +
-if len(selected)>0: +
-    cmds.createDisplayLayer(name=selected[0]+'_layer', number=1, nr=1)+
          
-# remove from layers +flatList = setflatFn(nestList) )
-# mel: editDisplayLayerMembers -noRecurse "defaultLayer" `ls -sl` +
-cmds.editDisplayLayerMembers("defaultLayer", cmds.ls(sl=1), noRecurse=1)+
  
  
-#### curve #####+</code> 
 +  * dictionary operation <code python> 
 +ref: http://www.dotnetperls.com/dictionary-python 
 +dict.clear() 
 +dict.get(key, default=None)  returns value or default if key not in dictionary 
 +dict.has_key(key) 
 +dict.items() a list dict's (key, value) tuple pairs 
 +dict.keys() a list of keys 
 +dict.values() a list of values 
 +len(dict) size of dict
  
-show CV +#example, obj property 
-# melToggleCVs; +dict = {'Name''Zara', 'Age': 7, 'Class': 'First'};
-cmds.ToggleCVs()+
  
-show editable point +#example, obj list 
-# melToggleEditPoints; +dict = {'Alice''2341', 'Beth': '9102', 'Cecil': '3258'}
-cmds.ToggleEditPoints()+
  
-rebuild curve dialog +delete key 
-# mel: RebuildCurveOptions; +my_dict.pop(key_name, None) 
-cmds.RebuildCurveOptions()+</code> 
 +  * set operation <code python> 
 +set( ("hello", "world", "of", "words", "of", "world") ) 
 +# set(['world', 'hello', 'words', 'of'])
  
-detach surface by edge or isopam +fib=set( [1,1,2,3,5,8,13] ) 
-mel: DetachCurve; +prime=set( [2,3,5,7,11,13] ) 
-cmds.DetachCurve()+combine=fib | prime set([1, 2, 3, 5, 7, 8, 11, 13]) 
 +common=fib & prime #set([2, 3, 5, 13]) 
 +diff = fib - prime # set([8, 1]) 
 +fib ^ prime # combine - common #set([1, 7, 8, 11])
  
-detach/attach curve by cut +<, <=, >, >=, ==, !=, in, not in works in set 
-mel: CutCurve; AttachCurve; +len(fib)=6 
-cmds.CutCurve() +max(fib)=13 
-cmds.AttachCurve()+min(fib)=1 
 +sum(fib)=32 
 +any(set([false,true])) For sets, Return True if there exists any item which is True. 
 +all(set([false,true])) # For sets, Return True if there all items which is True. 
 +set.add(new
 +set.remove(old)
 </code> </code>
  
-====== Modeling in API ======+  * list attribute of a object <code python>object.__dict__</code> 
 +===== turn a file into a module =====
  
-**Vertex check in API way** +  a file can be a module like <code python> 
- +import FileName 
-  * poly count <code python> +FileName.functionName() # call function side that file
-vtx_cnt = cmds.polyEvaluate(geoName, v=1)+
 </code> </code>
  
-  * get transform to shape to vtx <code python> 
-selected = om.MSelectionList() 
-om.MGlobal.getActiveSelectionList(selected) 
  
-dagPath om.MDagPath() +===== turn a folder into a module =====
-selected.getDagPath(0,dagPath)+
  
-print(dagPath.fullPathName()) # transform +  * you can turn a list of python file under same folder into a module by create a empty file under the directory: <code>__init__.py</code> 
-dagPath.extendToShape() # .extendToShapeDirectlyBelow() if dont want to get child of child; will error if has no shape +    * it will automatically turn the folder into a module so that you can call each file after import folder like <code python> 
-print(dagPath.fullPathName()) # shape, if shape, also return itself+import FolderName 
 +from FolderName import FileName 
 +</code> 
 +    * whenever you call import FolderName, the __init__py will be run-ed first, so you can put some code inside to do some pre-run task, or append the folder into python path <code python __init__.py> 
 +import os, sys 
 +my_path = os.path.dirname(__file__
 +my_path in sys.path or sys.path.append(my_path) 
 +</code>
  
-dagPath.transform() # transform+===== check module installed =====
  
-mesh = om.MFnMesh(dagPath.node()MObject to MeshFunction +  * python code <code python>help(moduleName) 
-print(mesh.numVertices()) # vtx count+dir(moduleName) 
 +#import moduleName 
 +#then type moduleName to show location 
 +</code> 
 +  * check module in shell command <code bash>pydoc moduleName</code> 
 +  * change current working path<code python>os.chdir('/my_path/')
 </code> </code>
  
-**Vertex Check**+===== cross python file reference=====
  
-  * test empty geo shape <code python> +  * import python function from another python file<code python> 
-# test empty geo shape +import sys 
-all_geo_list = cmds.ls(type='mesh'+sys.path.append("/home/my/python_file_folder" # you need to have python path knows your file location 
-zeroVtx_list = [] +# method 1 
-for each in all_geo_list: +import sample   
-    if cmds.polyEvaluate(each, v=1== 0: +sample.abcd() 
-        zeroVtx_list.append(each+# method 2 
-print(zeroVtx_list)+from sample import abcd   
 +abcd() 
 +#method 3 
 +from sample import * 
 +abcd()
 </code> </code>
 +  * run another python script <code python>
 +execfile("testScript.py")
 +# python 3, that one not work, use this one
 +exec(open("testScript.py").read(), globals())
 +</code>
 +  * pass parameter to a python script (ref: http://www.tutorialspoint.com/python/python_command_line_arguments.htm) <code python>
 +# method 1: python myscript.py arg1 arg2
 +import sys
  
-**Face Check and Correct**+print 'Number of arguments:', len(sys.argv), 'arguments.' 
 +print 'Argument List:', str(sys.argv)
  
-  * double side face <code python> +# method 2 
-shape_list = cmds.ls(type='mesh'    +import getoptsys
-for shape in shape_list: +
-    cmds.setAttr(shape+".doubleSided"1)+
 </code> </code>
 +  * example code for a mel file to html file process, in both module and app way
 +    * run code <code bash>python viewMel.py myMelFunc.mel</code>
 +    * run in python <code python>import viewMel
 +viewMel.viewMel_html('myMelFunc.mel','mydoc.html')
 +</code>
 +    * code <code python>
 +#view mel lib
 +#a python tool to view mel lib file functionts
 +import sys
  
-**Curve related** +def viewMel(file): 
- +    with open(file) as f: 
-  get curve construction info <code python> +        for line in f: 
-def getCurveInfo(curveObjindex=2): +            lineClean=line.strip() 
-    cmds.getAttr(curveObj+'.degree') +            if lineClean.startswith('//==') or lineClean.startswith('global proc') or lineClean.startswith('/**'): 
-    p = cmds.getAttr(curveObj+'.cv[:]') +                print lineClean.replace('{','').replace('*/','').replace('/**',' - ') 
-    p_new [ [numClean(yindexfor y in x] for x in p] +                 
-    tmpInfo = cmds.createNode( 'curveInfo' ) +def viewMel_html(melFile,htmlFile): 
-    cmds.connectAttrcurveObj + '.worldSpace', tmpInfo+'.inputCurve' ) +    open(htmlFile,'w') 
-    k = cmds.getAttrtmpInfo + '.knots[*]' ) +    with open(melFile) as f: 
-    cmds.delete(tmpInfo+        h.write('<html><head><link rel="stylesheet" type="text/css" href="s1.css"></head>\n<body>'+'\n'
-    return (d, p_new, k)+        for line in f: 
 +            lineClean=line.strip() 
 +            if lineClean.startswith('//=='): 
 +                h.write('<h1>'+lineClean.replace('//==','').replace('=','')+'</h1>'+'\n'
 +            elif lineClean.startswith('global proc'): 
 +                 h.write('<h5>'+lineClean.replace('{','').replace('global proc ','')+'</h5>'+'\n'
 +            elif lineClean.startswith('/**'): 
 +                 h.write('<p>'+lineClean.replace('*/','').replace('/**',' - ')+'</p>'+'\n') 
 +        h.write('</body></html>'+'\n'
 +    h.close()
          
-def numClean(fnum, index=2): +if __name__=="__main__": 
-    rule = "{0:."+str(index)+"f}" +    viewMel_html(sys.argv[1],sys.argv[1].replace('.mel','.html')
-    cnum = float(rule.format(fnum)) +</code>
-    if cnum.is_integer(): +
-        cnum = int(cnum) +
-    return cnum +
-     +
-def setCurveInfo(curveObj, curveInfo): +
-    # input: dict - d, p; p ; d,p,k +
-    if not isinstance(curveInfo, dict) or 'p' not in curveInfo.keys(): +
-        print('require curveInfo in dict of d,p,k or d,p or p') +
-        return +
-    if all(x in curveInfo.keys() for x in ['d','p','k']): +
-        return cmds.curve(curveObj,r=1,d=curveInfo['d'], p=curveInfo['p'], k=curveInfo['k']) +
-    elif all(x in curveInfo.keys() for x in ['d','p']): +
-        return cmds.curve(curveObj,r=1,d=curveInfo['d'], p=curveInfo['p']+
-    else: +
-        return cmds.curve(curveObj,r=1,d=1, p=curveInfo['p'])+
  
-def buildCurveInfo(curveInfo, n='tmpCurve'): +===== web link operation ===== 
-    # input: dict - d, p; p ; d,p,k + 
-    if not isinstance(curveInfo, dict) or 'p' not in curveInfo.keys(): +  * open url in brower <code python>import webbrowser 
-        print('require curveInfo in dict of d,p,k or d,p or p') +webbrowser._browsers # list registered browsers 
-        return + 
-    if all(x in curveInfo.keys() for x in ['d','p','k']): +new = 2 # open in a new tabif possible 
-        return cmds.curve(n=n,d=curveInfo['d'], p=curveInfo['p'], k=curveInfo['k']) +# open a public URLin this casethe webbrowser docs 
-    elif all(x in curveInfo.keys() for x in ['d','p']): +url = "http://docs.python.org/library/webbrowser.html" 
-        return cmds.curve(n=n,d=curveInfo['d'], p=curveInfo['p']+webbrowser.open(url,new=new) 
-    else+for using firefoxnotefirefox needs to be default or in the system environment path 
-        return cmds.curve(n=n,d=1, p=curveInfo['p'])+webbrowser.get('firefox').open(url,new=2
 +# open an HTML file on my own (Windowscomputer 
 +url = "file://X:/MiscDev/language_links.html" 
 +webbrowser.open(url,new=new)
 </code> </code>
  
 +===== file operation =====
 +  * check file exists <code python>os.path.isfile(path)</code>
 +  * access file and folder<code python>
 +# delete a file
 +os.remove(tmpitem)
  
-  * curve_mel2py cmd convertor <code python> +# check exist of a file 
-#txt = 'curve -d 1 -p 0 0 1 -p 0 0.5 0.866025 -p 0 0.866025 0.5 -p 0 1 0 -p 0 0.866025 -0.5 -p 0 0.5 -0.866025 -p 0 0 -1 -p 0 -0.5 -0.866025 -p 0 -0.866025 -0.5 -p 0 -1 0 -p 0 -0.866025 0.5 -p 0 -0.5 0.866025 -p 0 0 1 -p 0.707107 0 0.707107 -p 1 0 0 -p 0.707107 0 -0.707107 -p 0 0 -1 -p -0.707107 0 -0.707107 -p -1 0 0 -p -0.866025 0.5 0 -p -0.5 0.866025 0 -p 0 1 0 -p 0.5 0.866025 0 -p 0.866025 0.5 0 -p 1 0 0 -p 0.866025 -0.5 0 -p 0.5 -0.866025 0 -p 0 -1 0 -p -0.5 -0.866025 0 -p -0.866025 -0.5 0 -p -1 0 0 -p -0.707107 0 0.707107 -p 0 0 1 -k 0 -k 1 -k 2 -k 3 -k 4 -k 5 -k 6 -k 7 -k 8 -k 9 -k 10 -k 11 -k 12 -k 13 -k 14 -k 15 -k 16 -k 17 -k 18 -k 19 -k 20 -k 21 -k 22 -k 23 -k 24 -k 25 -k 26 -k 27 -k 28 -k 29 -k 30 -k 31 -k 32'+if not os.path.exists(tmpJPG): 
 +    print tmpitem
  
-maya float number cleaner +get file with a certain name pattern in a directory and all its sub-directories 
-def numClean(fnum, index=2)+scanDir r'D:\scanDir_start_dir' 
-    rule "{0:."+str(index)+"f}" +my_list [] 
-    cnum = float(rule.format(fnum)+for (dirpath, dirnames, filenames) in os.walk(scanDir): 
-    if cnum.is_integer()+    my_list.extend[ os.path.join(dirpath, xfor x in filenames if re.match('[a-zA-Z]+_lang_.+\.json', x)
-        cnum = int(cnum) +
-    return cnum+
  
-def curve_mel2py(txt): +# get file with a certain name pattern in a directory 
-     rest, k = txt.split('-k',1) +[x for x in os.listdir(scanDirif os.path.isfile(os.path.join(scanDir, x)and re.match('MyApp_lang_.+\.json', x) ] 
-     ks = [int(x.strip()) for x in k.split('-k')+[x for x in glob.glob(os.path.join(scanDir,'MyApp_lang_*.json')) if os.path.isfile(x]
-     rest, p = rest.split('-p',1) +
-     ps = [ x.strip().split() for x in p.split('-p') ] +
-     ps = [numClean(float(y)) for y in xfor x in ps] +
-     d = int(rest.split('-d',1)[-1].strip()+
-     result = "created = cmds.curve(d={0}, p={1}, k={2})".format(d, str(ps), str(ks)) +
-     return result+
 </code> </code>
 +  * lines to array<code python>
 +with open(fname) as f:
 +    content = f.readlines()
  
-  * create NurbsCurve passing defined point list <code python> +with open(fnameas f: 
-lineCurve = cmds.curve(p=pointList, d=1+    for line in f: 
-nurbsCurve = cmds.fitBspline(lineCurve,ch=0,tol=0.01) +        #do something with line    
-cmds.delete(lineCurve)+
 </code> </code>
 +  * remove any non-ascii words in file names <code python>
 +import os
 +import urllib
 +curdir = os.getcwd()
 +os.chdir(parentDir)
 +file_list_en = os.listdir('.') # this is ok ascii list
 +file_list_cn = os.listdir(u'.') # this u fix extreme non-ascii names
 +for i in xrange(len(file_list_en)):
 +    itemName = file_list_en[i]
 +    print(itemName)
 +    if itemName.startswith('%'):
 +        # convert %AA cn name to unicode
 +        new_fixName1 = urllib.unquote(itemName).decode('utf8').replace('+',' ')
 +        # then remove cn chars
 +        new_itemName2 = ''.join([c for c in new_fixName1 if ord(c) < 128 ]).replace('?','').replace('+',' ').strip()
 +        if itemName != new_itemName2:
 +            print([itemName,new_itemName2])
 +            os.rename(itemName,new_itemName2)
 +    else:
 +        itemName = file_list_cn[i] # for non ascii names
 +        # just remove cn chars
 +        new_itemName = ''.join([c for c in itemName if ord(c) < 128 ]).replace('?','').replace('+',' ').strip()
 +        if itemName != new_itemName:
 +            print([itemName,new_itemName])
 +            os.rename(itemName,new_itemName)
 +        
 +os.chdir(curdir)
 +</code>
 +  * read line from a huge file by multiple process at the same time, without accident write operation<code python>
 +import linecache
 +linecache.getline('/path_to_file.txt', 2) # get 2nd line of text, the first line is no. 1, unlike 0 in arrary
 +</code>
 +  * process line by line rewrite <code python>
 +import sys
 +import fileinput
  
-  * **Research on API vs Cmds in Get and Set Vertex Position based on optional selection** <code python> +for i, line in enumerate(fileinput.input('color_test.htm', inplace=1)): 
-# ref: http://www.fevrierdorian.com/blog/post/2011/09/27/Quickly-retrieve-vertex-positions-of-a-Maya-mesh-(English-Translation) +  #sys.stdout.write(line.replace('colour','color')) 
-def getVtxSelection_OpenMaya(): +  if line.find('color'> -1: 
-    startTime = cmds.timerX() 0.11s > no comp iter: 0.039 +    color=line.split(';',1)[0] 
-    # normal selection +    color=color.split('color',1)[1
-    normalSelectionList = om.MSelectionList(+    sys.stdout.write(line.replace('</span',(color+'</span'))) 
-    om.MGlobal.getActiveSelectionList(normalSelectionList) +  else: 
-    selectionPath = om.MDagPath(+    sys.stdout.write(line
-    selectionObj = om.MObject() +</code> 
-    indexList om.MIntArray() +  * make sure directory is created before create file in defined path <code python> 
-    iter om.MItSelectionListnormalSelectionListom.MFn.kMeshVertComponent ) +my_filePath = '/your_path/intermedia_folders/file.ext' 
-    #normalSelectionElements = [] # len(normalSelectionElements) +# method 1: these 2 lines only create last leaf folder, won't deal all the inbetween folders 
-    node_list = [] +if not os.path.isdir(os.path.dirname(my_filePath)): 
-    id_list = [] +    os.mkdir(os.path.dirname(my_filePath))
-    while not iter.isDone(): +
-        iter.getDagPathselectionPathselectionObj ) +
-        selectionPath.pop() +
-        node = selectionPath.fullPathName() +
-        compFn = om.MFnSingleIndexedComponent(selectionObj# flatten the component selection +
-        compFn.getElements(indexList) +
-        id_list.extend(list(indexList)) +
-        node_list.append(node+
-        ''' +
-        for i in range(compFn.elementCount()): +
-            normalSelectionElements.append('{0}.vtx[{1}]'.format(node, compFn.element(i))) +
-            id_list.append(compFn.element(i)) +
-        ''' +
-        iter.next() +
-    totalTime = cmds.timerX(startTime=startTime) +
-    print("\nTotal Time: {0}".format(totalTime)) +
-    return list(set(node_list)), id_list+
          
-def getVtxPos_cmd( shapeNode, sl=0 ): +# method 2these 2 lines create all the needed folders forming the path 
-    # full path, duplicate name proof +try:  
-    startTime = cmds.timerX() +    os.makedirs(os.path.dirname(my_filePath)) 
-    pos_list = [] +except OSError
-    index_list = [] +    pass
-    if sl==0# 0.885 +
-        #index_list = cmds.getAttr( shapeNode+".vrts", multiIndices=True ) # 0.0089 +
-        index_list = range( cmds.polyEvaluate(shapeNode,v=1) ) # 0.007 +
-    else: # 0.244 +
-        shapeNode, index_list = getVtxSelection_OpenMaya() # 0.01 +
-        shapeNode = shapeNode[0]  +
-    for i in index_list: #i=0 +
-        pos = cmds.xform'{0}.vtx[{1}]'.format(shapeNode, i), q=1, t=1, ws=1+
-        pos_list.append( pos ) +
-    totalTime = cmds.timerX(startTime=startTime) +
-    print("\nTotal Time{0}".format(totalTime)) +
-    return index_list, pos_list +
-def setVtxPos_cmd( shapeNode, pos_data, sl=0 ): +
-    startTime = cmds.timerX() +
-    cmds.undoInfo(openChunk=1) +
-    index_list, pos_list = pos_data +
-    if sl== 0: # 4.38 +
-        for i in range(len(index_list)): +
-            cmds.xform('{0}.vtx[{1}]'.format(shapeNode, index_list[i]), t=pos_list[i], ws=1) +
-    else: # 5.29 +
-        sel_index_list = getVtxSelection_OpenMaya()[1] +
-        if not set(sel_index_list).issubset(set(index_list)): +
-            print('selection is not subset of stored plmp data.'+
-            return +
-        for id in sel_index_list: +
-            i = index_list.index(id) +
-            cmds.xform('{0}.vtx[{1}]'.format(shapeNode, id), t=pos_list[i], ws=1) +
-    cmds.undoInfo(closeChunk=1) +
-    totalTime = cmds.timerX(startTime=startTime) +
-    print("\nTotal Time: {0}".format(totalTime))+
          
-def getVtxPos_OpenMaya( shapeNode, sl=0 ): +method 3all in one check and create if neededlike method but not need try and except since it will be ok for exist 
-    full path, duplicate name proof +os.makedirs(os.path.dirname(output_path), exist_ok=True)
-    startTime = cmds.timerX()  +
-    pos_list = [] +
-    index_list = [] +
-    if sl==0# 0.18 +
-        selectionList = om.MSelectionList() +
-        selectionList.add(shapeNode) +
-        dagPath = om.MDagPath() +
-        selectionList.getDagPath(0, dagPath) +
-         +
-        mesh_fn = om.MFnMesh(dagPath) +
-        meshPointArray = om.MPointArray() +
-        mesh_fn.getPoints(meshPointArray, om.MSpace.kWorld) +
-        index_list = range(meshPointArray.length()) +
-        for i in index_list: +
-            pos_list.append( [meshPointArray[i][0], meshPointArray[i][1]meshPointArray[i][2]] ) +
-    else: # 0.04 +
-        selectionList = om.MSelectionList() +
-        om.MGlobal.getActiveSelectionList(selectionList) +
-        selectionPath = om.MDagPath() +
-        selectionCompObj = om.MObject() +
-        iter_sel = om.MItSelectionList( selectionList, om.MFn.kMeshVertComponent )     +
-        while not iter_sel.isDone(): +
-            iter_sel.getDagPath( selectionPath, selectionCompObj ) +
-            iter_vtx = om.MItMeshVertex(selectionPath,selectionCompObj) +
-            while not iter_vtx.isDone(): +
-                pnt = iter_vtx.position(om.MSpace.kWorld) +
-                index_list.append( iter_vtx.index() ) +
-                pos_list.append([pnt.x, pnt.y, pnt.z]) +
-                iter_vtx.next() +
-            iter_sel.next() +
-    totalTime = cmds.timerX(startTime=startTime) +
-    print("\nTotal Time: {0}".format(totalTime)) +
-    return index_list, pos_list +
- +
-def setVtxPos_OpenMaya(shapeNode, pos_data, sl=0): +
-    startTime = cmds.timerX() +
-    cmds.undoInfo(openChunk=1) +
-    index_list, pos_list = pos_data +
-    selectionList = om.MSelectionList() +
-    selectionList.add(shapeNode) +
-    dagPath = om.MDagPath() +
-    selectionList.getDagPath(0, dagPath) +
-    mesh_fn = om.MFnMesh(dagPath) +
-    if sl==0: # 0.069 +
-        # set one by one - 4.22 +
-        #for i in range(len(index_list)): +
-        #    mesh_fn.setPoint(index_list[i], om.MPoint(*pos_list[i]) ) +
-        # get all and get all - 0.069 +
-        meshPointArray = om.MPointArray() +
-        mesh_fn.getPoints(meshPointArray, om.MSpace.kWorld) +
-        for i in range(len(index_list)): +
-            meshPointArray.set(index_list[i],*pos_list[i] ) +
-        mesh_fn.setPoints(meshPointArray, om.MSpace.kWorld) +
-    else: # 0.53 +
-        sel_index_list = getVtxSelection_OpenMaya()[1] +
-        if not set(sel_index_list).issubset(set(index_list)): +
-            print('selection is not subset of stored plmp data.'+
-            return +
-        meshPointArray = om.MPointArray() +
-        mesh_fn.getPoints(meshPointArrayom.MSpace.kWorld) +
-        for id in sel_index_list: +
-            i index_list.index(id) +
-            meshPointArray.set(id,*pos_list[i] ) +
-        mesh_fn.setPoints(meshPointArray, om.MSpace.kWorld) +
-    cmds.undoInfo(closeChunk=1) +
-    totalTime = cmds.timerX(startTime=startTime) +
-    print("\nTotal Time: {0}".format(totalTime))+
 </code> </code>
-====== UV Editing ======+  * write python data<code python> 
 +# write raw data and read raw data 
 +os.getcwd()
  
 +import json
 +def writeDataFile(data, file):
 +    with open(file, 'w') as f:
 +        json.dump(data, f)
 +        
 +def readDataFile(file):
 +    with open(file) as f:
 +        data = json.load(f)
 +    return data
 +    
 +# write dictionary data and read dictionary data
 +import csv
 +def writeDictFile(dict, file):
 +    w = csv.writer(open("output.csv", "w"))
 +    for key, val in dict.items():
 +        w.writerow([key, val])
  
-====== Material ======+def readDictFile(file):     
 +    dict {} 
 +    for key, val in csv.reader(open(file)): 
 +        dict[key] val 
 +    return dict 
 +</code> 
 +  * UTF-8 unicode file write<code python> 
 +############### UTF-8 content write ############# 
 +import codecs 
 +txt=u"<table border=1>\n" 
 +i=
 +for each in a: 
 +    i=i+1; 
 +    txt=txt+u"<tr><td><a href='{link}'>{title}</a></td><td>{code}</td><td>{idx}</td><tr>\n".format(link=each['link'], title=each['title'],code=each['code'], idx=i)
  
-  * assign material to selection or objects <code python>cmds.hyperShade(testGeo, assign='refMat')</code> +txt=txt+u'</table>'
-  * material creation <code python>cmds.shadingNode('lambert', n='refMat', asShader=1)</code> +
-  * change property <code python>cmds.setAttr('{0}.transparency'.format(testMat), 0.5, 0.5, 0.5, type="double3")</code>+
  
-====== Rigging ====== +with codecs.open('page.htm',mode='w',encoding='utf-8') as f: 
- +    f.write(txt) 
-===== delete unknown nodes ===== +</code> 
- +  * copy directory structure only <code python> 
-  * delete unknown nodes, but check "exists", "not referenced", "not locked" <code python> +for dirpathdirnamesfilenames in os.walk(srcPath): 
-nodeList = cmds.ls(type=['unknown''unknownDag''unknownTransform']) +    to_mkdir = os.path.join(tgtPathdirpath[1+len(srcPath):]) 
-#nodeToDelete  = nodeList[0] +    if not os.path.isdir(to_mkdir): 
-def deleteIfNotReferencednodeList ): +        os.mkdir(to_mkdir
-    if not isinstance(nodeList, (list, tuple)): +os.startfile(tgtPath# show result folder
-        nodeList = [nodeList+
-    for nodeToDelete in nodeList: +
-        if cmds.objExists(nodeToDelete) and not cmds.reference(nodeToDelete, q=1, isNodeReferenced=1): +
-            isLocked = cmds.lockNode(nodeToDelete, q=1, lock=1)[0] +
-            if not isLocked: +
-                cmds.deletenodeToDelete )+
 </code> </code>
  
 +===== thread operation =====
  
- +  run a process or python service/app into another thread, so it wont block current interaction <code python> 
- +def app(opt): 
-===== common node and attributes ===== +    if opt == 'Noter': 
- +        import threading 
-| obj_transform | .worldMatrix | +        th threading.Thread(target run_noter
- +        th.start() 
-===== Prefix remover ===== +def run_noter(): 
- +    import Noter 
-  * remove prefix in shape and transform and avoid same node name level collision +    Noter.main()
-  * python <code python> +
-mesh_list = cmds.ls('sw2_*',type='mesh',l=1) +
-for each_mesh in mesh_list: +
-    info = each_mesh.rsplit('|',1) +
-    if len(info )==2: +
-        cmds.rename(each_mesh, info[-1].replace('sw2_','')) +
-    else+
-        print(each_mesh) +
-     +
-obj_list cmds.ls('sw2_*',type='transform',l=1+
-for each_obj in obj_list: +
-    info = each_obj.rsplit('|',1+
-    if len(info)==2+
-        cmds.rename(each_obj, info[-1].replace('sw2_','')) +
-    else: +
-        print(each_obj)+
 </code> </code>
  
-===== alias attribute =====+====== module operation ======
  
-  * for blendshape and constraintsthere is always an alias name for the weight Attribute, code example <code python> +  * How to install extra module automatically with pip 
-alias_target_weight_list = cmds.aliasAttr( final_bsq=1 ) # ['body_BST', 'weight[0]'] +    * download pip and use bellow method to manually install itor run this line of command to get or update pip stuffs. <code python>python -m pip install --upgrade pip wheel setuptools</code
-target_list = alias_target_weight_list[::2] # all the alias target names +    * once doneyou can install rest modules with cmd <code bash>python -m pip install package_name</code> 
-weight_attr_list = alias_target_weight_list[1::2] # all the weight attribute list+  * How to install wheel package <code dos>REM pip app is at PythonAppDir/Scripts 
 +REM make sure that wheel is in 32/64bit matching your python 32/64bit version 
 +pip install C:/some-dir/some-file.whl
 </code> </code>
 +  * How to install extra module into seperate directory <code>
 +python.exe -m pip install --target=D:\my_path\plot_lib matplotlib
 +</code>
 +  * How to install extra modules manually
 +    - download the module source,
 +      * if it has setup.py in its directory, run <code python>python setup.py install</code> to ask python to automatically install it into your python site-package directory
 +      * if it has no setup.py in its directory, or just a single folder or a python .py file, then just include its path in your python path under your system variable or run this command in python cmd line before you call <code python>import sys;myPath='/YourPath/ToTheFolderOrFile/';myPath in sys.path or sys.path.append(myPath)</code>
 +      * **Ideally, if you put the Module Folder (not its parent distribution folder) under the Lib/site-package folder, it should work out of box when you type "import Module" in cmd**, of course, if you want to use specific version of the module or having the module in some other location, use the path way is more controllable to use exactly what you need.
 +      * However, if you want your Python module to be at a specific location, then add it to python path environment variable. For windows advanced system setting > environment variable <code>set PYTHONPATH=%PYTHONPATH%;D:\Dev\PyQT</code>
 +    - distribute your python file with module,
 +      * you can ask user to manually install those modules that used in your python program file
 +      * or just put those dependent file or folder next to your python program file  
  
 +  * check with module info <code python>
 +# check whether a module has that attribute
 +if hasattr(QtGui, 'QTabWidget'):
 +    print("yes")
  
-===== weight ===== +# try get a function from a module and with fallback option 
- +resultFunc getattr(MyClass, "function_A_name", default_function_if_not_found_A)
-  * [[graphic:python:maya:research_maya_weight_api|graphic:python:maya:research_maya_weight_api: The Complete Research and Study on Maya Weight in Mel Python and C++ API]]+
  
-  * apply single vtx weight to whole geo, good for unify same weight on attachment type geos on another surface <code python> +check imported module name dictionary 
-attach_vtx_id = 16 use same weight as on vtx[16] +if 'MyModulein sys.modules: 
-cur_skin = cmds.skinCluster(cur_bones, cur_geo, tsb=1, n=cur_geo+'_skinCluster')[0] +    print('yes')
-cmds.setAttr( cur_skin + ".skinningMethod", 1) +
-vtx_weight = cmds.skinPercent(cur_skin, cur_geo+'.vtx[{0}]'.format(attach_vtx_id), q=1, v=1) +
-cmds.skinPercent(cur_skin, cur_geo+'.vtx[:]', tv=zip(cur_bones,vtx_weight))+
 </code> </code>
 +  * work with module using variable names <code python>
 +# import PySide
 +__import__('PySide')
  
-  * get only bone affecting the selected vtx <code python> +# same as from PySide import QtNetwork as Net 
-cur_vtx_list cmds.ls(sl=1fl=1+qtModeList = ("PySide", "PyQt4", "PySide2""PyQt5"
-if len(cur_vtx_list)>0: +qtMode = 
-    if '.vtx[' in cur_vtx_list[0]+Net = getattr( __import__(qtModeList[qtMode], fromlist=['QtNetwork']), 'QtNetwork')
-        cur_skin weightAPI.findRelatedSkinCluster( cur_vtx_list[0].split('.')[0] ) +
-        affect_boneList = cmds.skinPercent(cur_skincur_vtx_list, q=1, ignoreBelow=0.01, t=None)+
 </code> </code>
-  * select only vtx affected the defined bone <code python> +  * import module from its parent directory <code python> 
-cmds.skinCluster(cur_skine=1, selectInfluenceVerts=bone_name)+import os,sys 
 +os.path.join(sys.path[0]'..') in sys.path or sys.path.insert(1, os.path.join(sys.path[0], '..')
 +import my_module_at_current_file_parent_dir
 </code> </code>
-===== Blendshape =====+===== useful module list =====
  
-{{graphic:python:maya:blendshape_data_structure.png?600|}}+==== useful HTML modules ====
  
-  each vertex of a polygon model holds local transform value at [0,0,0], but after some move and sculpt, the vertex local position value changed, +    * [[devwiki:python:html|python with HTML process]
-  you can optionally clear out local vertex transform with cmds.polyMoveVertex(localTranslate=(0, 0, 0)), and delete history +      <del>urllib2</del> [[https://pypi.python.org/pypi/lxml/|lxml]] and [[https://pypi.python.org/pypi/requests|requests]] 
-  however, Blendshape in maya seems not care about those local transform or global transform, it only care its position relative to origin when object is at zero x,y,z transform in channel box +      BeautifulSoup [[http://www.crummy.com/software/BeautifulSoup/|site]] [[http://www.pythonforbeginners.com/python-on-the-web/scraping-websites-with-beautifulsoup/|tut]] 
-  move at object level will not affect blendshape unless you tell blendshape to look at global level instead of vertex level +      Mechanize [[http://wwwsearch.sourceforge.net/mechanize/|site]] [[https://www.ibm.com/developerworks/linux/library/l-python-mechanize-beautiful-soup/|tut]] 
-  alsoany deform modifier added on the object will reset all the vertex local value to 0,0,0 in result object shape. +      Scrapy [[http://scrapy.org/|site]](xpath expressionslink extractorsitem loaders) 
-  * **inputTarget[0].paintTargetWeights[11]**this is just a temporary weight holder, as anytime you use blendshape paint tool to select which data to paint on, it loads the data into this attribute.+      pyquery [[https://pythonhosted.org/pyquery/api.html|api]] [[http://davedash.com/tutorial/pyquery/|tut]
 +      dokuwiki[[http://python-dokuwiki.readthedocs.io/en/latest/|dokuwiki interaction]]
  
 +  * embed code editor info:
 +    * http://eli.thegreenplace.net/2011/04/01/sample-using-qscintilla-with-pyqt
 +    * https://pypi.python.org/pypi/QScintilla#downloads
 +    * http://stackoverflow.com/questions/40002373/qscintilla-based-text-editor-in-pyqt5-with-clickable-functions-and-variables
 +    * http://pyqt.sourceforge.net/Docs/QScintilla2/
 +  * embed python console info:
 +    * code module
 +    * http://epydoc.sourceforge.net/stdlib/code.InteractiveConsole-class.html
 +    * http://code.activestate.com/recipes/355319-using-codeinteractiveconsole-to-embed-a-python-she/
 +    * https://docs.python.org/3/library/code.html
 +    * http://docs.cryengine.com/pages/viewpage.action?pageId=26217213
 +    * http://www.qtcentre.org/threads/29773-Embedding-PyQt4-into-an-Application-running-Python-via-Boost-Python
  
-  * vtx general info code<code python> +==== useful automation modules ====
-cmds.getAttr('ball.vtx[0]')[0] # those value in channelbox CV, and component editor+
  
-cmds.polyMoveVertex(localTranslate=(0, 0, 0)) clear local values +    * a online book related: https://automatetheboringstuff.com/#toc 
-</code> +    * winsys: admin automation for windows 
-  blendshape per target per vtx weight info cmds way <code python> +      ref: http://timgolden.me.uk/python/winsys/index.html 
-cmds.getAttr(cur_bs+'.it[0].itg[0].tw[0]') # cmds method extremely slow even for a ball+    * pywinauto: https://github.com/pywinauto/pywinauto/wiki/UI-Automation-tools-ratings 
 +      * tutorial: 
 +        * DevNami - Python Pyautogui Take Screenshot : https://www.youtube.com/watch?v=a6pHYIXqqSg 
 +        * SF Python - https://www.youtube.com/watch?v=dZLyfbSQPXI 
 +        * https://automatetheboringstuff.com/ 
 +      * additional ref:  
 +        * https://www.youtube.com/watch?v=mhNIHgJPP3g 
 +        * http://www.programcreek.com/python/index/322/win32gui 
 +    * selenium: web browser automation 
 +      * the doc: http://selenium-python.readthedocs.io/installation.html
  
-# target 1 : weight per vtx +==== useful Graphic modules ====
-cmds.getAttr('blendShape1.inputTarget[0].inputTargetGroup[0].targetWeights[240]'+
-cmds.getAttr('blendShape1.inputTarget[0].inputTargetGroup[0].targetWeights')[0] # get full list of weights +
-# target 2 : weight per vtx +
-cmds.getAttr('blendShape1.inputTarget[0].inputTargetGroup[1].targetWeights[240]')+
  
-# set target 2weight per vtx +    * numpy numerical python 
-cmds.setAttr('blendShape1.inputTarget[0].inputTargetGroup[1].targetWeights[241]', .8)+    * matplotlib : plot graphing tool (my note: [[devwiki:python:matplotlib|python with matplotlib]]
 +    * xlrd : excel read tool 
 +    * pandas : data structures and data analysis tools (loading datas and data operation like SQL) 
 +      * http://pandas.pydata.org/ 
 +    * PyQtGraph :  plot graphing tool 
 +    * Gnuplot : plot tool 
 +    * ipython : interactive python and visualization tool 
 +    * OpenEXR 
 +      * IlmImfa library that reads and writes OpenEXR images 
 +      * Half, a C++ class for manipulating half values  
 +      * Imath, a math library with support for matrices, 2d- and 3d-transformations, solvers for linear/quadratic/cubic equations, and more.  
 +      * exrdisplay, a sample application for viewing OpenEXR images at various exposure. 
 +    * wxPython : wx GUI 
 +    * PyQt/PySide :  Qt GUI 
 +    * pysqlite/MySQL_python : sql database 
 +    * Graph (node graph): https://www.nodebox.net/code/index.php/Graph 
 +    * NetworkX (node graph big): http://networkx.github.io/
  
-# get current loaded paint weight data +==== useful audio module ====
-cmds.getAttr(cur_bs+'.it[0].pwt[11]') #vtx 11 at current paint data+
  
-# get current base (the final mixing global per vtx weight+    * pyaudio <code python>import pyaudio 
-cmds.getAttr(cur_bs+'.it[0].bw[11]'#vtx 11 at current paint data+p = pyaudio.PyAudio() 
 +for i in range(p.get_device_count()): 
 +    print p.get_device_info_by_index(i).get('name')
 </code> </code>
 +  * useful Game building modules
 +    * quick tutorial on python openGL game: http://www.youtube.com/watch?v=G0jWMoOEFxc
  
-{{:graphic:python:maya:blendshape_data_weight.png|}} {{:graphic:python:maya:blendshape_data_structure_default.png?600|}} +==== database and file modules ==== 
-  * API: **MFnBlendShapeDeformer** <code python> + 
-import maya.OpenMaya as om +    * sqlite (built-in): 
-import maya.OpenMayaAnim as oma +      * additional desktop sqlite commandline tools: https://www.sqlite.org/download.html 
-selectionList om.MSelectionList() +    json (built-in ascii out
-selectionList.add(cur_bs) +    * cPickle (built-in binary out)
-bs_obj om.MObject() +
-selectionList.getDependNode(0,bs_obj) +
-bs_obj.hasFn(om.MFn.kBlendShape) +
-bs_fn oma.MFnBlendShapeDeformer(bs_obj) # blendshape object fn +
-# current result object +
-obj_list = om.MObjectArray() +
-bs_fn.getBaseObjects(obj_list) +
-print(obj_list.length()) # 1, only one blendshape object, even if it has more targets +
-bs_base_obj = obj_list[0] +
-</code> +
-  API: **weight slider attribute and alias target attribute name** <code python> +
-nWeights = bs_fn.numWeights() # number of blend targets +
-indexList = om.MIntArray()  +
-# get logic index of blend targets, since target can be remove from array, and become sparse +
-bs_fn.weightIndexList(indexList)+
  
-w_info_list=[] # weight attr info +  * encryption modules 
-w_plug = bs_fn.findPlug('weight'+    * hashlib (built-in<code python> 
-for index in indexList: +hashlib.md5() 
-    cur_plug w_plug.elementByLogicalIndex(index+m.update(user_input_text
-    cur_plug_alias = bs_fn.plugsAlias(cur_plug+# a sample hash code example 
-    cur_plug_w = bs_fn.weight(index+if m.hexdigest() != '88888888888888888888888888888888': 
-    w_info_list.append([cur_plug_alias, cur_plug_w]) +    return
-print(w_info_list)+
 </code> </code>
  
-  * API: **baseWeights data*(overall deform weight map): <code python> +  * data hashing, like compress data into a string, good for big data compare and record 
-inputTargetArrayPlug = bs_fn.findPlug('inputTarget'+    hash (built-in, not need import64bit, take a object and get a int for quick compare 
-inputTargetPlug inputTargetArrayPlug.elementByLogicalIndex(0)  # base_model +    * hashlib (built-in, need import128bit+ and more option like md5, sha for more secure and digital signiture 
-# base weight list +==== system info modules ====
-baseWeightsArrayPlug inputTargetPlug.child(1) # .inputTarget[0].baseWeights +
-vtxCnt cmds.polyEvaluate(base_geo,v=1) +
-base_weight_list [1]*vtxCnt # default is 1+
  
-for j in range(baseWeightsArrayPlug.numElements()): +    * getpass (built-in, user name) <code python>getpass.getuser()</code> 
-    cur_plug = baseWeightsArrayPlug.elementByPhysicalIndex(j# same as baseWeightsArrayPlug[j+    * glob (built-in, wild char file list) <code python>glob.glob(os.path.join(cur_path,prefix)+'_v???.[Mm][abAB]'
-    logicalIndex = cur_plug.logicalIndex() +# get all file named prefix variable _v001 like ma/mb file</code> 
-    base_weight_list[logicalIndex] = cur_plug.asFloat() # update from sparse array data+    * stat (built-in, file permission) <code python> 
 +# lock down file for user, group, other readonly 
 +os.chmod(filePath, stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH)</code> 
 +    * time (built-in, time related) <code python>time.strftime('%Y/%m/%d %H:%M') # 1980/12/31 23:55 format</code>
  
-# following method may fail if you start maya fresh and read the scene  +==== system command modules ====
-''' +
-base_weight_list [] +
-for j in range(baseWeightsArrayPlug.numElements()): # fresh start will cause numElements become 0 or sparse count again, since not in datablock memory yet +
-    base_weight_list.append( baseWeightsArrayPlug.elementByLogicalIndex(j).asFloat() ) +
-''' +
-# even this will not get the full index list +
-baseWeightsArrayPlug.evaluateNumElements() +
-print(baseWeightsArrayPlug.numElements()) +
-</code>+
  
-  * API: **targetWeights data for each target** <code python> +    subprocess (built-in)  
-inputTargetGroupArrayPlug = inputTargetPlug.child(0) +      subprocess.Popen don't block your main program  <code python>subprocess.Popen([r'notepad.exe', r'D:\zTmp\coder.py']);print('ok')
- +
-target_weight_data = [+
-for index in indexList: # i=0 +
-    inputTargetGroupPlug = inputTargetGroupArrayPlug.elementByLogicalIndex(index) # inputTargetGroupPlug.name() +
-    targetWeightsArrayPlug = inputTargetGroupPlug.child(1) # .targetWeights +
-    target_weight_list = [1]*vtxCnt  +
-    for j in range(targetWeightsArrayPlug.numElements())# j=0 +
-        cur_plug = targetWeightsArrayPlug.elementByPhysicalIndex(j) +
-        logicalIndex = cur_plug.logicalIndex() +
-        target_weight_list[logicalIndex= cur_plug.asFloat() +
-    target_weight_data.append(target_weight_list)+
 </code> </code>
- +      subprocess.call will block the main program till call finish, equals <code python>subprocess.call('nircmd.exe win close class "CabinetWClass"'
- * API: **Geometry result deformed vertex list position info** <code python> +subprocess.call(r'notepad.exe D:\zTmp\coder.py');print('ok'
-pos_list = [] +subprocess.Popen([r'notepad.exe'r'D:\zTmp\coder.py']).wait();print('ok'
-geo_iter = om.MItGeometry(obj_list[0]+os.system(r'notepad.exe D:\zTmp\coder.py');print('ok')
-while not geo_iter.isDone(): +
-    p = geo_iter.position() +
-    pos_list.append((p.xp.y, p.z)) +
-    geo_iter.next()+
 </code> </code>
- +      * subprocess.check_call will raise an exception 
-  * API: **Data of Target shape vertex list position info** <code python> +      both return result 
-target_data = [] +      Ask System to Open Folder <code python> 
- +if os.path.isdir(folderPath): 
-def geoObject_to_posList_OpenMaya(geo_object): +    if sys.platform == 'darwin'
-    # MObject as input +        try: 
-    geo_iter = om.MItGeometry(geo_object) +            subprocess.check_call(['open''--'folderPath]) 
-    pos_list [] +        except subprocess.CalledProcessError
-    while not geo_iter.isDone()+            pass handle errors in the called executable 
-        pnt = geo_iter.position() +        except OSError
-        pos_list.append([pnt.xpnt.ypnt.z]) +            pass # executable not found 
-        geo_iter.next() +    elif sys.platform == 'linux2'
-    return pos_list +        try
-     +            subprocess.check_call(['xdg-open'folderPath]) 
-for index in indexList# i = 0 +        except subprocess.CalledProcessError: 
-    target_object_data = [] +            pass handle errors in the called executable 
-     +        except OSError
-    target_list = om.MObjectArray() +            pass executable not found 
-    bs_fn.getTargets(bs_base_obj, index, target_list) +    elif sys.platform in ['win32','win64']
-    target_list.length() #1, ideally one blend target only have one object +        try
-    target_cnt = target_list.length() +            subprocess.check_call(['explorer', folderPath.replace("/","\\")]
-    if target_cnt > 1: +        except subprocess.CalledProcessError: 
-        # more than one object case +            pass # handle errors in the called executable 
-        for j in range(target_cnt )+        except OSError
-            target_object_data.append( geoObject_to_posList_OpenMaya(target_list[j]) ) +            pass # executable not found
-    else            +
-        # normal one object case +
-        if target_cnt == 0+
-            # no longer object link to it, get it from base +
-            # switch all shape off and only leave it on +
-            old_w_list = [] +
-            for n in indexList: +
-                old_w_list.append( [ nbs_fn.weight(n) ] ) # backup +
-                bs_fn.setWeight(n, 0) +
-            bs_fn.setWeight(n, 1) +
-            # get object data +
-            target_object_data.append( geoObject_to_posList_OpenMaya(bs_base_obj) ) +
-            # from backup +
-            for n, w in old_w_list: +
-                bs_fn.setWeight(n, w) +
-        elif target_cnt == 1+
-            target_object_data.append( geoObject_to_posList_OpenMaya(target_list[0]) ) +
-     +
-    target_data.append(target_object_data) +
-     +
-# locator creation +
-def pos_to_loc(pos_listprefix='tmp')+
-    for i,pos in enumerate(pos_list)+
-        cur_loc = cmds.spaceLocator(n='loc_{0}_{1}'.format(prefix,i))[0] +
-        cmds.xform(cur_loc, t=pos) +
-         +
-for i,targets in enumerate(target_data)+
-    pos_to_loc(targets[0], 'tgt'+str(i))+
 </code> </code>
-====== Animation ====== +      * check if a process is running <code python> 
- +appName 'googledrivesync.exe' 
-**Import and Export animation with atom plugin** +info=subprocess.Popen('tasklist.exe /FO CSV /FI "IMAGENAME eq {0}"'.format(appName),stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE) 
-  * import <code python> +out, err info.communicate() 
-# select controls to load animation, then +#out str(out) # for py3 byte convert 
-cmds.file("D:/path_to_file/pose_001.atom", i=1, type="atomImport")+out out.decode('ascii'
 +result [ x for x in out.replace('"','').split('\r\n') if x !='' ] 
 +if len(result)>and result[1].startswith(appName): 
 +    print('process found: '+appName) 
 +else: 
 +    subprocess.Popen([r'D:\path_to_app\googledrivesync.exe'])
 </code> </code>
-**Import and Export animation with Maya built-in file import export** +      ask System to open file <code python> 
-  * method 1: select the animation curve nodes, then just export those nodes for later import and manual reconnect<code python> +if sys.platform in ['win32','win64']: 
-cmds.file("D:/path_to_scene/animation_node_only_v001.ma", f=1, typ="mayaAscii", es=1+    os.startfile(filePath) 
 +elif sys.platform == 'darwin': 
 +    os.open(filePath) 
 +elif sys.platform == 'linux2': 
 +    os.xdg-open(filePath)
 </code> </code>
-    the result file will contains, animation nodes + renderGlobals stufs +      launch a subprocess to run a Bat file or python file in desktop <code python> 
-  * method 2: select animated objects, then just export animation info with auto reconnect on import<code python> +subprocess.Popen([r'D:\App\Python27\python.exe',r'D:\Dev\Pipeline\PyDesktop\CompFolder\CompFolder.py']
-cmds.file("D:/path_to_scene/animation_node_only_v002.ma", f=1typ="mayaAscii", exportSelectedAnim=1)  +subprocess.Popen([r'D:\Dev\Pipeline\PyDesktop\CompFolder\CompFolder.bat']) 
-</code> +# force it to open in new console window instead of inside parent console window 
-    * the result file will contains, animation nodes + re-connection commands, you can replace namespace on import back with -swapNamespace, but you can't change object of re-connection infoso not flexible, better with first method and do dictionary data connection+subprocess.Popen([r'D:\Dev\Pipeline\PyDesktop\CompFolder\CompFolder.bat']creationflags=subprocess.CREATE_NEW_CONSOLE) 
  
 +# if above method failed because PATH or PYTHONPATH issue, especially when call like above in a mixed python environment like Maya,
 +# you need to use a clean copy of environment, like this Example
 +env2={}
 +env2['PATH']='''C:\Windows\system32
 +D:\App\Python27
 +D:\App\Python27\Lib\site-packages\PyQt4
 +D:\App\Python27\lib\site-packages\pywin32_system32
 +'''.replace('\n',';')
 +env2['PYTHONPATH']='''D:\App\Python27\python27.zip
 +D:\App\Python27\DLLs
 +D:\App\Python27\lib
 +D:\App\Python27\lib\plat-win
 +D:\App\Python27\lib\lib-tk
 +D:\App\Python27
 +D:\App\Python27\lib\site-packages
 +D:\App\Python27\lib\site-packages\win32
 +D:\App\Python27\lib\site-packages\win32\lib
 +D:\App\Python27\lib\site-packages\Pythonwin
 +'''.replace('\n',';')
  
-**Get all Animation Curve** +subprocess.Popen([r'D:\App\Python27\python.exe',r'D:\Dev\Pipeline\PyDesktop\CompFolder\CompFolder.py'], env=env2
-  * noteit may include time value driven drive key curve, so need check<code python+subprocess.Popen([r'D:\Dev\Pipeline\PyDesktop\CompFolder\CompFolder.bat'], env=env2)
-for animCurve_type in ['animCurveTA', 'animCurveTL', 'animCurveTU']+
-    curve_list = cmds.ls( type = animCurve_type ) +
-  +
-    for each_curve in curve_list: +
-        cur_connection_out = each_curve + ".output" +
-        destinations = cmds.connectionInfo(cur_connection_outdestinationFromSource=1+
-        if len(destinations) == 0: +
-            # optional process for unconnected animation node +
-            cmds.delete(each_curve) +
-        else: +
-            cur_node_attr = destinations[0] +
-            cur_node, cur_attr = cur_node_attr.split('.'+
-            # optional get transform node +
-            cur_transform = '' +
-            if cmds.nodeType(cur_node) == 'transform': +
-                cur_transform = cur_node +
-            else: +
-                try_parent = cmds.listRelatives(cur_node, p=1) +
-                if try_parent: +
-                    cur_transform = cmds.listRelatives(cur_node, p=1)[0] +
-            # optional unlock attribute +
-            old_lock = cmds.getAttr(cur_node_attr, l=1) +
-            cmds.setAttr(cur_node_attr, l=0) +
-            # optional process here +
-            cmds.delete(cur_node_attr, staticChannels=1, unitlessAnimationCurves=0, hierarchy="", controlPoints=0, shape=1) +
-            # lock back +
-            cmds.setAttr(cur_node_attrlockold_lock)+
 </code> </code>
 +    * subprocess a console app and get return, and optionally hide the pop up console when call <code python>
 +# hide popup console for win platform only
 +startupinfo = subprocess.STARTUPINFO()
 +startupinfo.dwFlags |= 1 #subprocess.STARTF_USESHOWWINDOW
 +# startupinfo.wShowWindow = subprocess.SW_HIDE #(optional)
  
-**Clean Static Animation on object** +info=subprocess.Popen( '{0}'.format(app_path),stdout=subprocess.PIPEstdin=subprocess.PIPEstderr=subprocess.PIPE startupinfo=startupinfo) 
-  * note: that operation can't take locked channel, so unlock process may required +outerr info.communicate() 
-  * general clean static animation <code python> +out = out.decode('ascii') #str(out# for py3 byte convert
-cmds.delete(cur_node_attrstaticChannels=1unitlessAnimationCurves=0hierarchy=""controlPoints=0shape=1)+
 </code> </code>
 +==== string related ====
  
-**Bake Animation** +    re (built-inregular expression) <code python> 
-  * note: make sure all channel unlocked +# capture group and non-capture group: 
-  * bake animation curve<code python>cmds.bakeResults(animation_curve_node,t=(bake_start,bake_end))</code> +http://stackoverflow.com/questions/3512471/what-is-a-non-capturing-group-what-does-a-question-mark-followed-by-a-colon 
-  * bake object <code python>cmds.bakeResults(object_node,t=(bake_start,bake_end))</code+re.findall('([a-zA-Z0-9_]+)_atn(?:Msg)?', 'test_atn'# return ['test'] 
- +re.findall('([a-zA-Z0-9_]+)_atn(?:Msg)?', 'test_atnMsg'# return ['test'
-====== Animation Cache====== +# since we only need the prefix, the Msg group is to test exisits
- +
-  * check abc node inside maya, and its cache path <code python> +
-get all abc node +
-all_abc_nodes = cmds.ls(type='AlembicNode'+
-# check current file path +
-for each in all_abc_nodes: +
-    print('------------------') +
-    print('For node{0}'.format(each)+
-    print(cmds.getAttr(each+'.fn')+
-    print(cmds.getAttr(each+'.fns'))+
 </code> </code>
-  * to update abc cache path, (default maya only change the ".fn" attribute in attribute editor, you need also change ".fns" in code, which is a string array attribute), here I just copy first path to the string array path <code python> 
-all_abc_nodes = cmds.ls(type='AlembicNode') 
-for each in all_abc_nodes: 
-    print('------------------') 
-    print('Fixing node: {0}'.format(each)) 
-    cur_abc_path = cmds.getAttr(each+'.fn') 
-    cmds.setAttr('{0}.fns'.format(each), 1, cur_abc_path, type='stringArray') 
-</code> 
-====== Camera and Viewport calculation ====== 
  
-  * get 2D screen space from 3D position <code python> +==== compile and de-compile module ====
-import maya.OpenMayaUI as omui +
-import maya.OpenMaya as om +
-import maya.cmds as cmds +
-obj cmds.ls(sl=1, fl=1)[0] # get first vtx or object, assume you have selected sth +
-pos om.MPoint(obj) +
-</code>+
  
 +**cx_Freeze**: compile to exe, app, bin
 +  * After compile, the only files need to do partial update each time is (exe + library.zip)
  
-  get active viewport camera name <code python> +**PyInstaller**: compile py into binary, deeper than cx_Freeze, 
-dagPath = om.MDagPath() # dag path holder +
-omui.M3dView().active3dView().getCamera(dagPath) # get info +
-cam_name = om.MFnDagNode(dagPath.transform()).fullPathName() # get cam name +
-</code>+
  
-  * get viewport width and height in monitor pixel unit <code python+<code> 
-omui.M3dView().active3dView().portWidth() +# cmd install 
-omui.M3dView().active3dView().portHeight() +python -m pip install pyinstaller
-</code>+
  
-  * Make Move tool aim to cameraor parallel to camera; Good for matchmove <code python>  +# compile, cd to your py file firstyou may need copy your supporting folder to dist path after compile 
-''' +python.exe -m PyInstaller MyApp.py
-========================================== +
-2019.05.21 +
-by Shining Ying  +
-shiningdesign[with]live.com+
  
-make move tool aim to camera (with object/vtx selection) +no terminal version 
-========================================== +python.exe -m PyInstaller MyApp.py --noconsole 
-''' +set app binary icon 
-import maya.cmds as cmds +python.exe -m PyInstaller MyApp.py --icon=icons/MyApp.ico
-import maya.OpenMaya as om +
-import math +
-def getLocalVecToWorldSpaceAPI(obj, vec=om.MVector.xAxis): +
-    ref: http://forums.cgsociety.org/showthread.php?t=1124527 +
-    # take obj's local space vec to workspace, default object x axis +
-    selList = om.MSelectionList() +
-    selList.add(obj) +
-    nodeDagPath = om.MDagPath() +
-    selList.getDagPath(0, nodeDagPath) +
-    matrix = nodeDagPath.inclusiveMatrix() +
-    vec = (vec * matrix).normal() +
-    return vec.x, vec.y, vec.z +
-     +
-def alignObjectToVector(obj, axis, aim_dir, up_guide_dir = None, side_axis=None, side_guide_dir = None ): +
-    grab object transform function lib +
-    selectionList = om.MSelectionList() +
-    selectionList.add( obj ) +
-    dagPath om.MDagPath() +
-    selectionList.getDagPath(0, dagPath) +
-    fnTransform = om.MFnTransform(dagPath) +
-    # axis dict +
-    axis_dict = {'x':om.MVector.xAxis, 'y':om.MVector.yAxis, 'z':om.MVector.zAxis} +
-    obj_axis_dir = getLocalVecToWorldSpaceAPI(obj, axis_dict[axis]) +
-    obj_axis_vec = om.MVector(*obj_axis_dir) +
-     +
-    aim_vec = om.MVector(*aim_dir) +
-    rot_quaternion = obj_axis_vec.rotateTo(aim_vec) +
-    fnTransform.rotateBy(rot_quaternion) +
-    # optional 2nd axis setup +
-    if up_guide_dir != None and side_axis != None: +
-        # based on: aim_vec info +
-        up_guide_vec = om.MVector(*up_guide_dir) +
-        # make sure aim is not parallel to up_guide_vec +
-        if up_guide_vec.isParallel(aim_vec): +
-            # then align side x to provided backup side axis +
-            if side_guide_dir != None: +
-                alignObjectToVector(obj, side_axis, side_guide_dir) +
-        else: +
-            side_vec =  up_guide_vec ^ aim_vec # side dir +
-            alignObjectToVector(obj, side_axis, (side_vec.x,side_vec.y,side_vec.z ))+
  
-cur_selection cmds.ls(sl=1)+# exclude tk and tcl library 
 +python.exe -m PyInstaller MyApp.py --icon=icons/MyApp.ico --exclude-module=FixTk --exclude-module=tk --exclude-module=tcl --exclude-module=_tkinter --exclude-module=tkinter --exclude-module=Tkinter
  
-#fixCam= 'camera1' +for mac, if failed to build on pyconfig.h 
-fixCam = '' +# - fix pyconfig.h; by cmd: sudo touch /Library/Python/3.8/include/pyconfig.h 
-cmds.setToolTo('moveSuperContext') # $gMove +python.exe -m PyInstaller MyApp.py --icon=icons/MyApp.icns
-cur_cam = cmds.modelEditor(cmds.playblast(activeEditor=1)q=1, camera=1) +
-if fixCam !='': +
-    cur_cam = fixCam +
-print('Aim Camera: {0}'.format(cur_cam)) +
-camPos = cmds.xform(cur_cam, q=1, t=1, ws=1) +
-objPos = cmds.manipMoveContext('Move', q=1, position=1) # only work for 1st time selection, then it got 1/10 +
-objPos2 = cmds.xform(cur_selection[0],q=1,t=1,ws=1 ) # override with actual measure +
-if (objPos[0] objPos2[0])**2 > 0.000001: +
-    objPos_maya objPos  +
-    objPos = objPos2 +
-    print('Maya make mistake on move tool position, I correct it from {0} to {1}'.format(str(objPos_maya),str(objPos2)))+
  
-aim_dir = [a-b for a,b in zip(camPos,objPos)]  +python.exe -m PyInstaller MyApp.py --icon=icons/MyApp.icns -w --onefile
-up_ref_dir getLocalVecToWorldSpaceAPI(cur_cam, vec=om.MVector.yAxis)+
  
-loc +cmd+i to check app infoand drag icns file to app/folder icon logo to set the icon 
-tmp_loc = cmds.spaceLocator(n='tmp_move_loc')[0] +manually turn folder into appjust add .app behind folder namefolder name must be same as main binary file
-cmds.xform(tmp_loct=objPos, ws=1) +
-align +
-alignObjectToVector(tmp_loc'z', aim_dir, up_guide_dir = up_ref_dir, side_axis='x'+
-rot_val = cmds.xform(tmp_locq=1, ro=1) +
-cmds.delete(tmp_loc) +
-# back to old selection +
-cmds.select(cur_selection) +
-# set tool +
-cmds.setToolTo('moveSuperContext'+
-cmds.manipMoveContext( 'Move', e=1, activeHandle=2, m=6, orientAxes=[math.radians(a) for a in rot_val])+
  
 +# when run result mac app on other computer, if security stop it run like "app is damaged", run this command on created app or app folder
 +xattr -cr "/Applications/MyAppFileorMyAppFolder.app"
 +</code>
  
-''' +**uncompyle2**: convert pyc to py (python 2.7 code support only) 
-========================================== +
-2019.05.21 +
-by Shining Ying  +
-shiningdesign[with]live.com+
  
-make move tool paralell to camera (with object/vtx selection) +https://github.com/wibiti/uncompyle2
-========================================== +
-'''+
  
-cur_selection = cmds.ls(sl=1) +  * code example <code python>in the init file 
- +main('/pyc_file_dir/', '/pyc_file_dir_decompile_dir/', ['that_pyc_file.pyc'], [])
-#fixCam= 'camera1' +
-fixCam = '' +
-cmds.setToolTo('moveSuperContext') # $gMove +
-cur_cam = cmds.modelEditor(cmds.playblast(activeEditor=1)q=1, camera=1) +
-if fixCam !=''+
-    cur_cam = fixCam +
-print('Aim Camera: {0}'.format(cur_cam)) +
-# loc +
-tmp_loc = cmds.spaceLocator(n='tmp_move_loc')[0] +
-# align +
-cmds.parentConstraint(cur_cam,tmp_loc) +
-rot_val = cmds.xform(tmp_loc, q=1, ro=1) +
-cmds.delete(tmp_loc) +
-# back to old selection +
-cmds.select(cur_selection) +
- +
-# set tool +
-cmds.setToolTo('moveSuperContext'+
-cmds.manipMoveContext( 'Move', e=1, activeHandle=2, m=6, orientAxes=[math.radians(a) for a in rot_val])+
 </code> </code>
-====== Render Related ====== 
  
-  * the 'defaultRenderGlobals' object, it contains everything in render setting, and can be accessed through this object <code python> +====== Python Important Modules ====== 
-cmds.select('defaultRenderGlobals'+===== pip =====
- +
-# get current scene name +
-cur_scene cmds.file(q=1, sn=1, shn=1) +
-if cur_scene == '': +
-    cur_scene 'untitled' +
-    print('Not Name Yet'+
-else: +
-    cur_scene cur_scene.rsplit('.',1)[0] +
-# get render global output name, update to custom name format +
-cur_img_prefix cmds.getAttr('defaultRenderGlobals.imageFilePrefix')+
  
-if '<Scene>in cur_img_prefix: +  * pip is a quick python module get and installer module for python, it can download module for your python library. 
-    result_txt = cur_img_prefix.replace('<Scene>', cur_scene.rsplit('_',1)[0]) +  * check version <code>python -m pip --version</code> 
-    cmds.setAttr('defaultRenderGlobals.imageFilePrefix',result_txt , type='string' )+  * update it before install other module, make sure you already in pythonpip folder path <code>sudo pip install --upgrade pip 
 +# or 
 +python -m pip install --upgrade pip 
 </code> </code>
-    * it maps all the setting in Render Setting - Common Tab, <code python> +===== re =====
-image_ext 'png' +
-cmds.setAttr("defaultRenderGlobals.imageFormat", 32) # png+
  
-scene_path = os.path.dirname(cmds.file(q=True, sceneName=True)) +  regular expression pattern
-dir_path_info = [scene_path, 'images'+
-render_root_path = os.path.join(*dir_path_info ).replace('\\','/')+
  
-cmds.setAttr("defaultRenderGlobals.imageFilePrefix", render_root_path +'/%s', type='string' ) +^ all data file | <code>(?!(.*[.]db)|(^Keyboard$)|(^[.].*))</code>| 
-# set as name.####.ext format +^ all maya file | <code>[^.].+(.ma|.mb|.abc|.xml|.fbx)</code>|
-cmds.setAttr("defaultRenderGlobals.animation",1# render multi frame +
-cmds.setAttr("defaultRenderGlobals.outFormatControl",0# use ext +
-cmds.setAttr("defaultRenderGlobals.putFrameBeforeExt",1) # use frame num +
-cmds.setAttr("defaultRenderGlobals.periodInExt",1) # use .ext +
-cmds.setAttr("defaultRenderGlobals.extensionPadding",4# use padding+
  
-# set width +  * variable name case conversion 
-cmds.setAttr("defaultResolution.w",1920) # use padding +    * CamelCase to "snake_case" or "sentence case" 
-cmds.setAttr("defaultResolution.h",1080) # use padding +    * code: <code python> 
-</code> +def convert(name, gap='_'): 
-    * it also contains all the Deadline render job setting+    s1 = re.sub('(.)([A-Z][a-z]+)', r'\1'+gap+r'\2'name) # separate more generic AAAABc case to AAAA Bc 
 +    return re.sub('([a-z0-9])([A-Z])'r'\1'+gap+r'\2', s1) # main conversion 
 +#get2HTTPResponseCode get2_HTTP_Response_Code
  
-  * get absolution render output path (including dynamic changing output path<code python> +def convert2(name, gap='_'): 
-cmds.renderSettings(firstImageName=1, fullPath=1)+    return re.sub('((?<=[a-z0-9])[A-Z]|(?!^)[A-Z](?=[a-z]))', gap+r'\1', name# main conversion
 </code> </code>
-  * get workspace current information <code python> +===== requests =====
-cmds.workspace(q=1, rootDirectory=True) # root proj dir, holding the scene/image/etc folders +
-cmds.workspace(q=1, dir=1) # scene folder full-path+
  
-# scene path and scene name +  * 3rd party modulehtml get post request response operation library 
-cmds.file(q=TruesceneName=True) +  * header list: https://deviceatlas.com/blog/list-of-user-agent-strings
-cmds.file(q=True, sceneName=True, shortName=True)+
  
-# segment subpath +  * **Single Get web link content** <code python> 
-cmds.workspace(fr=1,q=1) # list of segment sub names +import requests 
-cmds.workspace(fr=['images', 'images']# set the segment subpath +result requests.get('http://my_site/my_page', timeout=5
-cmds.workspace(fr=['depth', 'renderData/depth']) # set the segment subpath +print(result.text)
-cmds.workspace(fileRuleEntry='depth'# get the segment path+
 </code> </code>
-  * mel to use python write Pre-Render-Mel, to store scene path into render image folder nearby text file (note to escape \ in one line as python("") as mel cmd) <code python> +  * **Single post web link content with login info** <code python> 
-import datetime; +result requests.post('http://my_site/my_page', data={'site_username_field_name''your_name', 'site_password_field_name': 'your_pass'}
-time_text datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'); +print(result.text)
-import maya.cmds as cmds; +
-out_path = cmds.renderSettings(firstImageName=1fullPath=1)[0];  +
-scene_path = cmds.file(q=True, sceneName=True);  +
-parent_dir = os.path.dirname(out_path); +
-info_file=parent_dir+'.info'+
-os.path.exists(os.path.dirname(info_file)) or os.mkdir(os.path.dirname(info_file)); +
-f = open(info_file,'w'); +
-f.writelines([scene_path,'\n',time_text])+
-f.close(); +
-print('Render Info written to {0}'.format(info_file))+
 </code> </code>
-  * mel version <code javascript+  * **one-time login with continue consistent header info for session operation** <code python
-python("import datetime;time_text = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S');import maya.cmds as cmds;out_path = cmds.renderSettings(firstImageName=1fullPath=1)[0]; parent_dir = os.path.dirname(out_path);info_file=parent_dir+'.info';os.path.exists(os.path.dirname(info_file)) or os.mkdir(os.path.dirname(info_file));scene_path cmds.file(q=True, sceneName=True);f = open(info_file,'w');f.writelines([scene_path,'\\n',time_text]);f.close();print('Render Info written to '.format(info_file))")+with requests.Session() as session: 
 +    # first time login, 
 +    reply = session.post('http://site/login_page', data={'site_username_field_name': 'your_name', 'site_password_field_name': 'your_pass'}) 
 +    print(reply.text) 
 +    # read rest login-required pages 
 +    reply session.get('http://site/other_info_page') 
 +    print(reply.text)
 </code> </code>
-  * use python to set mel setting, note maya sometimes create another tmp ma file to render, and make scene file not the same, thus need to bake current scene name into it. (note the r%%'''x'''%% format to auto escape \, the above code also can use r'x' to simplify the escape need) <code python> +  * check send header and received header info <code python> 
-# bake scene name into mel cmd, as render will create a another tmp scene file, which cause get scene name different in script +print(session_object.headers# user sending header info 
-scene_path = cmds.file(q=1, sceneName=True) +print(reply_object.headers) # server replying header info
-pre_mel = r'''python("import datetime;time_text = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S');import maya.cmds as cmds;out_path = cmds.renderSettings(firstImageName=1, fullPath=1)[0]; parent_dir = os.path.dirname(out_path);info_file=parent_dir+'.info';os.path.exists(os.path.dirname(info_file)) or os.mkdir(os.path.dirname(info_file));f = open(info_file,'w');f.writelines(['{0}','\\n',time_text]);f.close();print('Render Info written to {0}'.format(info_file))")'''.format(scene_path) +
-set as per render layer instead pre scene, since each folder has different name +
-cmds.setAttr("defaultRenderGlobals.preRenderLayerMel",pre_mel, type="string")+
 </code> </code>
-    preMel: before maya kick render (postMel) +  more function explain <code python> 
-    * preRenderLayerMel: for each render layer, as for each render folder, it do the per render info write (postRenderLayerMel) +result.content # is html in bytes 
-    * preRenderMel: per frame (postRenderMel) +result.text # is html in unicode text
- +
-  * defaultArnoldRenderOptions: +
-    * the arnold render setting object, contains all render setting for Arnold render +
- +
-====== Pipeline ====== +
- +
-  * cross platform maya scene directory mapping and management +
-    * dirmap command: http://help.autodesk.com/cloudhelp/2017/CHS/Maya-Tech-Docs/Commands/dirmap.html +
-    * ref: https://fredrikaverpil.github.io/2010/11/24/managing-maya-scenes-on-multiple-platforms/ +
- +
-  * get mel system level operation and maya launch directory <code python> +
-import maya.mel as mel +
-print(mel.eval('pwd'))+
 </code> </code>
 +  * disable non secure cert warning <code python>requests.packages.urllib3.disable_warnings(requests.packages.urllib3.exceptions.InsecureRequestWarning)</code>
 +===== PIL and Pillow=====
  
-  * Publish workflow +  * PILPython Imaging Library (Fork) =  Pillowinstall the lib <code>python -m pip install Pillow</code> 
-    * http://forums.pyblish.com/ +  * python code <code python> 
-  * Project Dir <code python>cmds.workspace(dir='d:/proj/current')</code> +from PIL import ImageImageDrawImageOps
-  * File New <code python> +
-cmds.file(f=1,new=1) +
-</code> +
-  * File Open <code python> +
-cmds.file(filePath, o=True) +
-cmds.file(renameToSave=1) # prevent overwrite save after open +
-</code> +
-  * Importing <code python>cmds.file(filePathi=True)</code> +
-  * Reference <code python>cmds.file(filePath.replace('\\', '/'), r=True, namespace=ns) +
-allRefNode = cmds.ls( references=1) +
-cur_node = allRefNode[0] +
-# get reference used namespace :MyNameSpace +
-cur_ns= cmds.referenceQuery(cur_node, namespace=1) +
-# get reference used file - C:/user/myfile.ma +
-cur_path = cmds.referenceQuery(cur_node, f=1) +
-# get reference used file name - myfile.ma +
-cur_name = cmds.referenceQuery(cur_node, f=1, shn=1) +
-</code> +
-  * delete unused node <code python>mel.eval('MLdeleteUnused')</code+
-  * checking <code python> +
-cmds.file(q=1sn=1) # filepath +
-cmds.file(q=1sn=1, un=1) # filepath in variable format +
-cmds.file(q=1, sn=1, shn=1) # filename +
-cmds.file(q=1, sn=1, wcn=1) # withoutCopyNumber +
-</code> +
-  * check file change and save <code python> +
-cmds.file(q=1, modified=1) +
-cmds.file(save=1) +
-</code> +
-  * saving as <code python> +
-# you need to rename first before you can save as +
-if ext.lower() == ".ma": +
-    cmds.file(rename = filePath) +
-    cmds.file(f=1, save=1, type="mayaAscii"+
-    save_done = 1 +
-elif ext.lower() == ".mb": +
-    cmds.file(rename = filePath) +
-    cmds.file(f=1, save=1, type="mayaBinary"+
-    save_done = 1 +
-</code> +
-  * export <code python> +
-if format_info == "ma": +
-    if self.uiList['publish_selected_check'].isChecked(): +
-        cmds.file(filePath, f=1, type="mayaAscii", pr=1, exportSelected=1) +
-    else: +
-        cmds.file(filePath, f=1, type="mayaAscii", pr=1, exportAll=1) +
-else: +
-    if self.uiList['publish_selected_check'].isChecked(): +
-        cmds.file(filePath, f=1, type="mayaBinary", pr=1, exportSelected=1) +
-    else: +
-        cmds.file(filePath, f=1, type="mayaBinary", pr=1, exportAll=1)+
 </code> </code>
-  * mel shelf button<code javascript> 
-global string $gShelfTopLevel; 
-string $currentShelf = `tabLayout -query -selectTab $gShelfTopLevel`; 
-setParent $currentShelf; 
-// - step 2: prepare path, name, icon, cmd 
-string $name="test"; 
-string $ver="v1.0"; 
-string $icon="test.png"; 
-string $cmd ="ls -sl"; 
-// - step 3: create shelf btn 
-shelfButton -c $cmd -ann $name -l $name -imageOverlayLabel $ver -overlayLabelColor 0 0 0 -overlayLabelBackColor 0 0 0 0.0 -image $icon -image1 $icon -sourceType "mel"; 
  
-shelfButton -c $cmd -ann $name -l $name -imageOverlayLabel $ver -overlayLabelColor 0 0 0 -overlayLabelBackColor 1 1 1 0.5 -image $icon -image1 $icon -sourceType "mel";+===== BeautifulSoup =====
  
-string $curBtn = `shelfButton -parent $currentShelf -c $cmd -mi "reload" "print ok" -ann $name -l $name -imageOverlayLabel $ver -overlayLabelColor 0 0 0 -overlayLabelBackColor 0 0 0 0.-image $icon -image1 $icon -sourceType "mel"`; +  * a html info extractor, best works with another parser module "lxmlto get partial info 
-string $curPop = `popupMenu -parent $curBtn -button 1 -ctl 1`; // shaltctl +  * ref: https://beautiful-soup-4.readthedocs.io/en/latest/#installing-a-parser 
-menuItem -p $curPop -l "A A A-c "print A"; +    * parser is a html interprateras not all html is clean format and tidysome parser can auto fix some html tag miss match. 
-menuItem -p $curPop -l "Reload" -c "print reload";+  * example code <code python> 
 +from bs4 import BeautifulSoup 
 +soup = BeautifulSoup(html, 'lxml') # html byte from requests.get 
 +# find all title link text 
 +link_list = soup.find_all("a", {'class':['title', 'threadtitle_unread']} ) # or 'title' only if no multiple 
 +name_list = [x.string for x in link_list ] # text element extract 
 +link_list[0].attrs['href'] # link url element extract
 </code> </code>
-  * shelf operation with python <code python> 
-import maya.cmds as cmds 
-import maya.mel as mel 
-shelf_holder = mel.eval('$tmpVar=$gShelfTopLevel') # get tab holder 
-shelves = cmds.tabLayout(shelf_holder, q=1, selectTab=1) # get selected tab 
-shelves = cmds.tabLayout(shelf_holder, q=1, childArray=1) # names of the layout's immediate children. 
-cmds.shelfTabLayout(e=1, selectTabIndex=3, shelf_holder) # change active tab by index 
-cmds.shelfTabLayout(shelf_holder, e=1, selectTab = shelf_name) # change active tab by name 
-cmds.shelfLayout(shelf_name, ex=True) # check shelf exists by name 
  
-shelf_name = mel.eval('addNewShelfTab %s' % shelf_name) # add shelf 
-mel.eval('deleteShelfTab %s' % shelf_name) # delete shelf 
  
-cmds.saveAllShelves(shelf_holder) # save all shelfs +===== selenium =====
-</code>+
  
-  * Maya path customizeIt looks like maya only initialize those PATHs info during startup, manually add them after restart, maya still complain can't find the path. So use following method first. +  * you need install 2 things to get it working
-    - maya.env method (It loads before UI, before userSetup.py)<code> +    - Python module:  
-USER_SCRIPT_PATH1=C:/myscripts/whatever/path1 +      - for py2.7: https://pypi.python.org/pypi/selenium/2.7.0 
-USER_SCRIPT_PATH2=C:/myscripts/whatever/path2 +      - for py3.x: https://pypi.python.org/pypi/selenium 
-MAYA_SCRIPT_PATH=$MAYA_SCRIPT_PATH;$USER_SCRIPT_PATH1;USER_SCRIPT_PATH2 +    - system platform dependent webdriver binary: http://www.seleniumhq.org/download
-MAYA_MODULE_PATH D:\MayaModule\mgear_3.5.1 +      - for chrome browser automate: go above link and find 3rd binding "Google Chrome Driver" 
-MAYA_PLUG_IN_PATH = D:\whatever\plugin +    - then you can load that drive binary into python selenium module object to start <code python> 
-PYTHONPATH = D:\whatever\scripts+from selenium import webdriver 
 +driver webdriver.Chrome('/your_path_to_that_binary_driver/chromedriver.exe') 
 +driver.get('http://google.com') 
 +driver.implicitly_wait(20) 
 +driver.get_screenshot_as_file(r'D:\testshot.png'
 +driver.quit()
 </code> </code>
-    - userSetup.py method (load after env, before UI) (~\Documents\maya\version\scripts<code python+  * get element <code python> 
-print("\nLoading Pipeline from userSetup.py\n")+# get css selector by right click browser's inspector view, right click on html element to copy css selector 
 +driver.find_element_by_css_selector('.jsb > center:nth-child(1) > input:nth-child(1)' 
 +# path method 
 +driver.find_element_by_xpath('//input[@name="btnk"]')
 </code> </code>
-    - userSetup.mel method (load after UI loaded) (~\Documents\maya\version\scripts<code javascript> +  * element operation 
-print("\nLoading Pipeline from userSetup.mel\n"); +    * element.get_attribute('href') 
-$path1 = "C:/myscripts/whatever/path1"; +    * element.get_attribute('outerHTML'
-$path2 = "C:/myscripts/whatever/path2"; +    * element.text 
-string $newScriptPath = $path1 + ";" +  $path2 + ";" +`getenv "MAYA_SCRIPT_PATH"`; +    * element.click(
-putenv "MAYA_SCRIPT_PATH" $newScriptPath;+    * element_input.send_keys(and webdriver.common.keys 
 +    * element_form.submit() 
 +    * driven_browser.back(), forward(), refresh()
  
-// or source other config script +  * additional tool
-$script1 = "C:/myscripts/whatever/script1.mel"; +    * selenium ide firefox - a all-in-one test tool for it: https://addons.mozilla.org/en-US/firefox/addon/selenium-ide
-$script2 = "C:/myscripts/whatever/script2.mel"; +      * tutorialhttp://www.seleniumeasy.com/selenium-tutorials/css-selectors-tutorial-for-selenium-with-examples 
-source $script1; +===== PyAutoGUI =====
-source $script2; +
-</code> +
-    cmd or bash shell method (run after userSetup.mel)<code bash> +
-// bash +
-alias proj1="export MAYA_SCRIPT_PATH=path/to/scripts:$MAYA_SCRIPT_PATH; maya;" +
-// dos +
-set MAYA_MODULE_PATH=R:/Pipeline/maya/modules; +
-"C:/Program Files/Autodesk/Maya2020/bin/maya.exe" -c "python(\"print('Loading Pipeline from Launch Cmd')\")" +
-</code>+
  
-===== Batch File Process Example =====+  * GUI cross-platform GUI interaction automation  
 +    * ref: https://www.youtube.com/watch?v=dZLyfbSQPXI&t=392s 
 +    * tutorial: https://www.youtube.com/results?search_query=PyAutoGUI 
 +  * full list of library required in download page: https://pypi.python.org/pypi/PyAutoGUI
  
-  * convert fbx file to mb file, and cleanup geo naming and material naming and texture node naming <code python> +  * mouse control 
-import maya.cmds as cmds +    * click(), doubleClick(), rightClick() 
-import maya.mel as mel +    * click([x,y]) 
-import os +    * moveTo(x,y [,duration=sec]) 
-root_path = r'__________fbx______folder_path' +    * moveRel(x_offsety_offset [,duration=sec]
-fbx_list = [x for x in os.listdir(root_path ) if x.endswith('.fbx') ] +    * dragTo(x,y [,duration=sec]) 
-prefix = 'rock_' # prefix for new name +    * position(), size(),  
-for each_fbx in fbx_list: +    * displayMousePosition() <code python> 
-    #each_fbx = fbx_list[0] +note, that displayMousePosition require 3rd party library 
-    name = each_fbx.split('.',1)[0] # get file name (eg. rockName_1.fbx) +# * PIL (Py2.x onlyadapted by Pillow> PIL.ImageGrab > pyscreenze (edit line 309 with "from PIL import ImageGrab"> pyautogui 
-    id = name.rsplit('_',1)[-1] # get file index number +http://www.pythonware.com/products/pil/ 
-    # new file +https://pypi.python.org/pypi/PyScreeze
-    cmds.file(f=1,new=1) +
-    cmds.file(os.path.join(root_patheach_fbx), i=1) +
-    mel.eval('MLdeleteUnused'+
-    anim_list = cmds.ls(type='animCurveTA')+cmds.ls(type='animCurveTL')+cmds.ls(type='animCurveTU'+
-    if len(anim_list)>0: +
-        cmds.delete(anim_list) +
-    # mesh +
-    mesh_list = cmds.listRelatives(x,p=1)[0for x in cmds.ls(type='mesh')] +
-    new_geo = cmds.rename(mesh_list[0], prefix+str(id)+'_geo') # assume single geo in scene +
-    cmds.scale(0.1,0.1,0.1,new_geo+
-    cmds.xform(new_geot=(0,0,0),ro=[0,0,0]) +
-    cmds.makeIdentity(new_geo, s=1,apply=1) +
-    # material +
-    mat_node = cmds.shadingNode('lambert'asShader=1, n=prefix+str(id)+'_mat'+
-    cmds.select(new_geo) +
-    cmds.hyperShade(new_geoassign=mat_node) +
-    cmds.sets(new_geo, e=1, forceElement=mat_node+'SG'+
-    texture +
-    file_node = [x for x in cmds.ls(type='file') if x.startswith('Map')][0] assume the file node named "Map***" +
-    new_file = cmds.rename(file_node, prefix+str(id)+'_map'+
-    cmds.connectAttr(new_file+'.ot'mat_node+'.it') +
-    cmds.connectAttr(new_file+'.oc', mat_node+'.c'+
-    # save +
-    mel.eval('MLdeleteUnused'+
-    cmds.file(rename = os.path.join(root_path, name+'.mb')) +
-    cmds.file(f=1, save=1, type='mayaBinary'+
-    # ask for verify as it runs +
-    res = cmds.confirmDialog(title="Confirm", message="Next?", button=["Yes", "No"], defaultButton="Yes", cancelButton="No", dismissString="No"+
-    if res == 'No': +
-        break+
 </code> </code>
-====== Scripting ====== 
  
-  * load and source mel script <code python> +  * keyboard control 
-mel_path r"D:\Dev\Pipeline\PyMaya\maya_3rd_tool\someMelScript.mel" +    * typewrite('Text goes here', [,interval=secs]) # pyautogui.KEYBOARD_KEYS 
-mel.eval('source "{0}"'.format(mel_path.replace('\\','/'))+    * press('pageup'
-</code>+    * hotkey('ctrl', 'o') 
 +    * note: best work with time.sleep(5) to give 5 sec for pc to respond
  
-  * use python to ask maya to execute a python command in its variable scope <code python> +  * image recognition 
-# this enable external python tool to ask maya to run a command with variable in maya's scope +    * pixel(x,y) # return RGB 
-cmds.python('cmds.select({0})'.format('my_variableName_in_maya'))+    * screenshot([filename]) # return PIL,Pillow Image object [and save file] 
 +    * locateOnScreen(imageFilename) # returns (x,y, w, h) or None 
 +    * linux: scrot 
 +    * example code <code python> 
 +import pyautogui as pui 
 +pos = pui.locateOnScreen('d:\\zTmp\\test_build.png'
 +pui.click(pos[0]+pos[2]*0.5 , pos[1]+pos[3]*0.5) )
 </code> </code>
-====== Utility ====== 
  
-  * open socket for external communication <code python> +===== SikuliX =====
-# open port in maya +
-import maya.cmds as cmds +
-port 7001 +
-open_already +
-try: +
-    cmds.commandPort(name=":{0}".format(port), sourceType="python"+
-except: +
-    open_already +
-if open_already == 1: +
-    print('Port already open')+
  
-# send to Maya from external python +  * SikuliX automates anything you see on the screen of your desktop computer running Windows, Mac or some Linux/UnixLink: http://sikulix.com 
-import socket +  * tutorial: https://www.youtube.com/watch?v=VdCOg1bCmGo
-host = '127.0.0.1' # localhost +
-port = 7001 +
-client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) +
-client.connect((host, port)) +
-client.send('print "cool from external"')+
  
-# close port in maya +===== pyautoit =====
-cmds.commandPort(name=":7001", close=1) +
-</code> +
-====== Plugin ======+
  
-  * check and load python plugins <code python> +  * python binding for AutoIt toola windows only automation software 
-if not cmds.pluginInfo('myPlugin.py', query=Trueloaded=True): +    * https://github.com/jacexh/pyautoit 
-    cmds.loadPlugin('myPlugin.py'+    AutoIt home page: https://www.autoitscript.com/site/autoit/ 
-</code> +    * API level interaction
-  group a portion of cmds into one undo <code python> +
-cmds.undoInfo(openChunk=True) +
-# after end of the operation and before error rise, put +
-cmds.undoInfo(closeChunk=True) +
-</code>+
  
-  * check plugin in use in a scene <code python>plugin_list = sorted(cmds.pluginInfo(q=1,listPlugins=1)) 
-plugin_list_inUse = sorted(cmds.pluginInfo(q=1,pluginsInUse=1)) 
-</code> 
-  * remove unknown plugin from Maya 2015 onwards <code python> 
-oldplugins = cmds.unknownPlugin(q=True, list=True) 
-for plugin in oldplugins: 
-    cmds.unknownPlugin(plugin, remove=True) 
-</code> 
-====== Maya Qt GUI Tool Development ====== 
  
 +===== pywinauto =====
  
-  * Qt python tool build guide tutorial links: +  * a mostly windows only GUI automation modulewith mouse, keyboard support on linux 
-    * this uses Qt designer app to create UI fileand python code to hook it +    * API level interaction
-      * http://www.attackack.com/maya-tool-pg1 +
-    * this one use Qt codes to build a UI inside code of python +
-      * http://www.chris-g.net/2011/06/24/maya-qt-interfaces-in-a-class/ +
-    * a list of guides +
-      * http://www.mattmurrayanimation.com/archives/180+
  
-  * check Python version and bit <code python>import sys;print(sys.version) +===== pywin32 - win32com =====
-import struct;print( 8 * struct.calcsize("P"))</code>+
  
-  write PyQt4 GUI python code in Maya +  tip and ref:  
-    - you need  +    * http://timgolden.me.uk/python/index.html 
-      either compile  +    http://timgolden.me.uk/pywin32-docs/html/com/win32com/HTML/QuickStartClientCom.html 
-      * or download PyQt4 binary package +  * COMbrowserwin32com\client\combrowse.py
-    - then  +
-      * either copy them into Maya's local python site package  +
-      * or add your "correct Python version and bit PyQt4" path to sys.path in Maya  <code python+
-# check Maya python version and bit +
-import sys;print(sys.version) +
-import struct;print( 8 struct.calcsize("P")) +
-# check Maya python included path +
-import sys; +
-for each in sys.path: +
-    print each +
-# maya's local addition python package place: +
-# example on windows: C:\Program Files\Autodesk\Maya2015\Python\lib\site-packages+
  
-# or add my downloaded PyQt4 install python, +  * install and restart computer to get dll reg refreshed <code> 
-# PyQt4 windows binary link (you can install/extra to any place, just link like below): +https://sourceforge.net/projects/pywin32/files/pywin32
-# http://www.riverbankcomputing.com/software/pyqt/download +(or) python -m pip install pypiwin32</code>
-sys.path.append('D:\z_sys\App_CG\Python27x64\Lib\site-packages')+
  
-from PyQt4 import QtGui,QtCore</code> +  * get windows explorer in taskbar orderreturn [(title, hwnd int, addressbar url, [top,left,w,h], vis)] <code python> 
-    * or use PySide (PySide version 1.2 is included with Maya 2015built with Python 2.7 and Maya Qt version 4.8.5.) <code python>from PySide import QtGui,QtCore</code>+import win32com 
 +from win32com import client 
 +def get_explorer_list(): 
 +    # win32com: get folder windows in taskbar order; return [(title, hwnd int, addressbar url, [top,left,w,h], vis)] 
 +    shell = client.Dispatch("Shell.Application"
 +    win_list = shell.Windows() 
 +    result_list = [] 
 +    for win in win_list: 
 +        info = [] 
 +        info.append(win.LocationName) # title name 
 +        info.append(win.hwnd) # handle value as int 
 +        info.append(win.LocationURL) # address bar urlegfile:///C:/Users/Test/Downloads 
 +        info.append([win.Top, win.Left, win.width, win.height]# win pixel position 
 +        info.append(win.visible) # visible 
 +        info.append(win) # com object 
 +        result_list.append(info) 
 +    return result_list 
 +''' full list of property 
 +['ReadyState', 'Busy', 'Container', 'Silent', 'Top',  
 +'RegisterAsDropTarget', 'LocationName', 'Application', 'Offline', 'Document',  
 +'Type', 'ToolBar', 'MenuBar', 'FullScreen', 'Parent', 'TheaterMode',  
 +'Path', 'Name', 'RegisterAsBrowser', 'StatusText', 'Left', 'TopLevelContainer',  
 +'Resizable', 'Width', 'StatusBar', 'HWND', 'Height', 'Visible', 'FullName',  
 +'LocationURL', 'AddressBar'
 +''' 
 +</code> 
 +  * get selected item in window explorer <code python> 
 +# ref:  
 +# https://win32com.goermezer.de/microsoft/windows/find-selected-files-in-windows-explorer.html 
 +# https://msdn.microsoft.com/en-us/library/windows/desktop/bb787810(v=vs.85).aspx
  
-  * explanation of abovePyQt binding vs PySide binding (the 2 common way to link Python and Qt GUI library together) ([[http://askubuntu.com/questions/140740/should-i-use-pyqt-or-pyside-for-a-new-qt-project|ref]])+def get_selection_list(win): 
 +    # win32com: get selection item detail from win COM object 
 +    item_list = [] 
 +    folderItems = win.Document.SelectedItems() 
 +    for i in range(folderItems.Count): 
 +        tmp_info = [
 +        tmp_info.append(folderItems.Item(i).path) # C:\Users\Test\Downloads\example.zip 
 +        tmp_info.append(folderItems.Item(i).name) 
 +        tmp_info.append(folderItems.Item(i).isFolder) 
 +        tmp_info.append(folderItems.Item(i).size) 
 +        tmp_info.append(folderItems.Item(i).type) 
 +        item_list.append(tmp_info) 
 +    return item_list 
 +</code> 
 +  * explorer window COM object control <code python> 
 +# control the window COM object to go to a location 
 +win.Navigate('file:///C:/Users/Test/Desktop') 
 +win.Busy # a check for still in processing
  
 +# check visibile and make full screen
 +win.FullScreen =1 # make first windows explorer full screen
 +</code>
 +  * system operation<code python>
 +# get system dir and 
 +oShell = win32com.client.Dispatch("Wscript.Shell")
 +print(oShell.SpecialFolders("Desktop"))
  
-====== My Python Tool ======+# change drive letter 
 +# ref: http://timgolden.me.uk/python/win32_how_do_i/change-a-drive-letter.html
  
-  * shelf load python code <code python> +# create windows shortcut and read it 
-import maya.cmds as cmds +shortcut = shell.CreateShortCut("t:\\test.lnk"
-from sys import platform +shortcut.Targetpath = "D:\\tmp" 
-import sys +shortcut.save()
-import os +
-  +
-def getOS()+
-    ''' get os '''     +
-    if platform.startswith('win')+
-        return 'windows' +
-    elif platform.startswith('darwin')+
-        return 'mac' +
-    return 'linux'+
  
-def getZ(): +shortcut shell.CreateShortCut("t:\\test.lnk") 
-    ''' get z_sys path '''     +print(shortcut.Targetpath)
-    z_sys=os.getenv('z_sys'+
-    z_sys = z_sys.strip() +
-    z_sys = z_sys.replace("\\","/"# fix windows path     +
-    return z_sys+
  
-sys.path.append(z_sys+"/xTool/resMaya/py") # much better than source+# get single screen resolution and total screen 
 +# ref: https://msdn.microsoft.com/en-us/library/windows/desktop/ms724385(v=vs.85).aspx 
 +import win32api 
 +w=win32api.GetSystemMetrics(0) # primary screen width 
 +h=win32api.GetSystemMetrics(1) # primary screen height 
 +vw = win32api.GetSystemMetrics(78) # total screen area width 
 +vh = win32api.GetSystemMetrics(79) # total screen area height 
 +print('screen resolution: {0}x{1}'.format(w,h)) 
 +print('virtual area resolution: {0}x{1}'.format(vw,vh))
 </code> </code>
 +===== ctypes =====
  
-====== 3rd Party Tool ====== +  ctypes is an advanced ffi (Foreign Function Interface) package, ctypes allows to call functions in dlls/shared libraries and wrap libraries in pure Python. ctypes works cross-platform with libffi support. 
-  poseDeform +    * ref: http://nullege.com/codes/search/ctypes 
-    * http://www.tokeru.com/t/bin/view/Maya/MayaRigging +    * my doc on windows API and DLL functions [[devwiki:winapi|WinAPI]] 
-  * Sculpt Inbetween Editor +  * (method 1) get all windows explorer windows in Z order, return [(title, class, [x,y,w,h], hwnd int)] <code python> 
-    * refhttp://danielslima.com/sculpt-inbetween-editor-1-0/+def get_win_order_all(): 
 +    # ctypegrab folder windows in z order, return [(title, class, [x,y,w,h], hwnd int)] 
 +    from ctypes import wintypes 
 +    order_list = [] 
 +    result_list = [] 
 +    top = ctypes.windll.user32.GetTopWindow(None) 
 +    if not top: 
 +        return order_list 
 +    length = ctypes.windll.user32.GetWindowTextLengthW(top) 
 +    buff = ctypes.create_unicode_buffer(length + 1) 
 +    ctypes.windll.user32.GetWindowTextW(top, buff, length + 1) 
 +    class_name = ctypes.create_string_buffer(200) 
 +    ctypes.windll.user32.GetClassNameA(top, ctypes.byref(class_name), 200) 
 +    r=ctypes.wintypes.RECT() 
 +    ctypes.windll.user32.GetWindowRect(top,ctypes.byref(r)) 
 +    # class_name.value in py3 is byte, while py2 is string 
 +    if isinstance(class_name.value, str): 
 +        result_list.append( [buff.value, class_name.value, [r.left,r.top,r.right-r.left,r.bottom-r.top], top ]) 
 +    else: 
 +        result_list.append( [buff.value, class_name.value.decode('ascii'), [r.left,r.top,r.right-r.left,r.bottom-r.top], top ]) 
 +    order_list.append(top) 
 +    while True: 
 +        next = ctypes.windll.user32.GetWindow(order_list[-1], 2) # win32con.GW_HWNDNEXT 
 +        if not next: 
 +            break 
 +        length = ctypes.windll.user32.GetWindowTextLengthW(next) 
 +        buff = ctypes.create_unicode_buffer(length + 1) 
 +        ctypes.windll.user32.GetWindowTextW(next, buff, length + 1) 
 +        class_name = ctypes.create_string_buffer(200) 
 +        ctypes.windll.user32.GetClassNameA(next, ctypes.byref(class_name), 200) 
 +        r=ctypes.wintypes.RECT() 
 +        ctypes.windll.user32.GetWindowRect(next,ctypes.byref(r)) 
 +        # class_name.value in py3 is byte, while py2 is string 
 +        if isinstance(class_name.value, str): 
 +            result_list.append( [buff.value, class_name.value, [r.left,r.top,r.right-r.left,r.bottom-r.top], next] ) 
 +        else: 
 +            result_list.append( [buff.value, class_name.value.decode('ascii'), [r.left,r.top,r.right-r.left,r.bottom-r.top], next] ) 
 +        order_list.append(next) 
 +    return result_list
  
-====== Python and Maya Qt interface ====== +full_list get_win_order_all() 
-===== PyQt case code =====+visible_list [x for x in full_list if ctypes.windll.user32.IsWindowVisible(x[-1])] 
 +# visible list is actually more useful, 10+ instead 100+ items
  
-  panel creation +explorer_list = [x for x in visible if x[1]=='CabinetWClass'
-    * optionQWidget (it has panel name showing in taskbar), QDialog (not showing in taskbar)+firefox_list = [x for x in visible if x[1]=='MozillaWindowClass'
 +</code> 
 +  (method 2) get all (visible) (windows explorer) windows in Z order, return [(title, class, hwnd as pointer)]<code python> 
 +def get_win_order_v2(): 
 +    # ctypes 
 +    # refhttps://sjohannes.wordpress.com/2012/03/23/win32-python-getting-all-window-titles/ 
 +    # info (title, class_name, hwnd) 
 +    window_list = [] 
 +    # propare windows list passing 
 +    def window_info_process(hwndlParam): 
 +        if ctypes.windll.user32.IsWindowVisible(hwnd): 
 +            length = ctypes.windll.user32.GetWindowTextLengthW(hwnd) 
 +            buff = ctypes.create_unicode_buffer(length + 1) 
 +            ctypes.windll.user32.GetWindowTextW(hwnd, buff, length + 1) 
 +            class_name = ctypes.create_string_buffer(200) 
 +            ctypes.windll.user32.GetClassNameA(hwnd, ctypes.byref(class_name), 200) 
 +            t_class_name = class_name.value 
 +            if not isinstance(t_class_name, str)
 +                t_class_name = class_name.value.decode('ascii') # py3 fix 
 +            if t_class_name == 'CabinetWClass': 
 +                window_list.append( [ buff.value, t_class_name, hwnd ]) 
 +        return True 
 +    procObj = ctypes.WINFUNCTYPE(ctypes.c_bool, ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_int)) 
 +    # execute it 
 +    ctypes.windll.user32.EnumWindows(procObj(window_info_process), 0) 
 +    return window_list 
 +</code> 
 +  * (method 3) Break down ctypes hwnd, pid, process_handle into functions modules <code python> 
 +''' 
 +ref: http://timgolden.me.uk/python/win32_how_do_i/find-the-window-for-my-subprocess.html 
 +    Window handle → process ID: GetWindowThreadProcessId 
 +    Process ID → process handle: OpenProcess 
 +    Process handle → executable path: QueryFullProcessImageName (compare this with your target) 
 +    Close the process handle: CloseHandle 
 +ref: http://www.mengwuji.net/forum.php?mod=viewthread&tid=1432 
 +    Windows 2000 = GetModuleFileName() 
 +    Windows XP x32 = GetProcessImageFileName() 
 +    Windows XP x64 = GetProcessImageFileName() 
 +    Windows Vista = QueryFullProcessImageName() 
 +    Windows 7 = QueryFullProcessImageName() 
 +ref: 
 +    uisoup - utils : https://pypi.python.org/pypi/UISoup 
 +    https://programtalk.com/python-examples/ctypes.windll.user32.GetWindowThreadProcessId/ 
 +    https://pypkg.com/pypi/pywinauto/f/pywinauto/win32functions.py 
 +'''
  
-  * layout alignment, QtCore.Qt.AlignTopAlignRight +def get_win_pid(hwnd): 
-    * layout.setAlignment(QtCore.Qt.AlignTop)+    cur_pid = ctypes.c_long() 
 +    ctypes.windll.user32.GetWindowThreadProcessId(hwndctypes.byref(cur_pid)) 
 +    return cur_pid 
 +def get_process_path(pid): 
 +    PROCESS_QUERY_INFORMATION = 0x0400 
 +    max_unicode_path = 32767 2 + 200 
 +    file_path_buffer = ctypes.create_unicode_buffer(max_unicode_path) 
 +    length_path = ctypes.c_ulong(max_unicode_path) 
 +    pHandle = ctypes.windll.kernel32.OpenProcess(PROCESS_QUERY_INFORMATION, False, pid) # won't get admin process if not as admin 
 +    retval = ctypes.windll.kernel32.QueryFullProcessImageNameW(pHandle, 0, file_path_buffer, ctypes.byref(length_path)) 
 +    if retval == 0: 
 +        raise Exception('QueryFullProcessImageNameW failed'
 +    #ctypes.windll.kernel32.CloseHandle(pHandle) 
 +    return file_path_buffer.value 
 +def get_win_path(hwnd): 
 +    return get_process_path(get_win_pid(hwnd))
  
-====== Environment setup ======+def get_win_rect(hwnd): 
 +    from ctypes import wintypes 
 +    r=ctypes.wintypes.RECT() 
 +    ctypes.windll.user32.GetWindowRect(hwnd,ctypes.byref(r)) 
 +    return [r.left,r.top,r.right-r.left,r.bottom-r.top] 
 +def get_win_title(hwnd): 
 +    length ctypes.windll.user32.GetWindowTextLengthW(hwnd) 
 +    title_buffer ctypes.create_unicode_buffer(length + 1) 
 +    ctypes.windll.user32.GetWindowTextW(hwnd, title_buffer, length + 1) 
 +    return title_buffer.value 
 +# AE CS6: AE_CApplication_11.0 
 +def get_win_class(hwnd): 
 +    class_name ctypes.create_string_buffer(200) 
 +    ctypes.windll.user32.GetClassNameA(hwnd, ctypes.byref(class_name), 200) 
 +    t_class_name class_name.value 
 +    if not isinstance(t_class_name, str): 
 +        t_class_name class_name.value.decode('ascii') # py3 fix 
 +    return t_class_name 
 +def is_win_top(hwnd): 
 +    top_hwnd ctypes.windll.user32.GetTopWindow(None) 
 +    return hwnd == top_hwnd
  
-Environment of Python in Maya +def get_win_list(visible=1, orderMethod=0): 
-  * PYTHONPATH +    # both method get result 
-  * Maya.env<code python> +    # orderMethod uses : ctypes.windll.user32.GetTopWindow 
-import sys +    # callBackMethod usesctypes.windll.user32.EnumWindows 
-sys.path.append("/xTool/resMaya/py"+    win_list = [] 
-much better than eval("source "+$scriptName+".mel")+    if orderMethod: 
 +        order_list = [] 
 +        top = ctypes.windll.user32.GetTopWindow(None) 
 +        if not top: 
 +            return win_list 
 +        visible_info = ctypes.windll.user32.IsWindowVisible(top) 
 +        win_list.append( [ top, visible_info ]) 
 +        order_list.append(top) 
 +        while True: 
 +            next = ctypes.windll.user32.GetWindow(order_list[-1], 2) # win32con.GW_HWNDNEXT 
 +            if not next: 
 +                break 
 +            visible_info = ctypes.windll.user32.IsWindowVisible(next) 
 +            win_list.append( [next, visible_info] ) # hwnd: as int 
 +            order_list.append(next) 
 +    else: 
 +        # propare windows list passing 
 +        def window_info_process(hwnd, lParam): 
 +            visible_info = ctypes.windll.user32.IsWindowVisible(hwnd) 
 +            win_list.append( [ hwnd, visible_info ]) #hwnd: as LP_c_long 
 +            return True 
 +        # ref: http://makble.com/the-story-of-lpclong 
 +        # function 1: this will get as LP_c_long as hwnd 
 +        #procObj = ctypes.WINFUNCTYPE(ctypes.c_bool, ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_int)
 +        function 2: this will get int as hwnd like other win32api function 
 +        procObj = ctypes.WINFUNCTYPE(ctypes.c_bool, ctypes.c_int, ctypes.POINTER(ctypes.c_int)
 +        # execute it 
 +        ctypes.windll.user32.EnumWindows(procObj(window_info_process), 0) 
 +    if visible: 
 +        return [x for x in win_list if x[1]] 
 +    return win_list
 </code> </code>
-  * userStartup.py +  * ctypes window operation by hwnd<code python> 
-  * load custom system environment variable, Python shines so much comparing to Mel in this case <code python> +# my_hwnd can be a int or a ctypes int pointer 
-import maya.cmds as cmds +# full function refhttp://www.jasinskionline.com/windowsapi/ref/funca.html
-from sys import platform +
-import os +
-  +
-def getOS(): +
-    if platform.startswith('win'): +
-        return 'windows' +
-    elif platform.startswith('darwin'): +
-        return 'mac' +
-    return 'linux'+
  
-def getZ(): +bring window to bring 
-    ''' get z_sys path ''' +ctypes.windll.user32.SetForegroundWindow(my_hwnd)
-    mel, about -os "nt", "win64", "mac", "linux" and "linux64" +
-    checkOS = getOS()  +
-     +
-    #if($checkOS == "windows") $z_sys=system("echo %z_sys%"); # windows +
-    #else $z_sys=system("printf $z_sys") # linux, osx (bash,csh) echo do the name but with new line +
-     +
-    z_sys=os.getenv('z_sys') +
-     +
-    ## remove new line, mel +
-    #$a=stringToStringArray($z_sys,"\r"); // windows echo use \r newline +
-    #$aS=stringArrayToString($a,""); +
-    #$a=stringToStringArray($aS,"\n"); // linux echo use \n newline     +
-    #$z_sys=$a[0];+
  
-    z_sys z_sys.strip()+# close window 
 +WM_CLOSE 0x0010 
 +ctypes.windll.user32.PostMessageA(my_hwnd, WM_CLOSE, 0, 0)
  
-    #$z_sys = substituteAllString($z_sys"\\""/"); // to normal directory format for windows +get window x,y,w,h 
-    z_sys z_sys.replace("\\","/"+from ctypes import wintypes 
-    # make sure system environment got defined +r=ctypes.wintypes.RECT() 
-    if(z_sys!=""): +ctypes.windll.user32.GetWindowRect(my_hwnd,ctypes.byref(r)
-        print z_sys +print( [r.left,r.top,r.right-r.left,r.bottom-r.top] )
-    return z_sys+
  
 +# move window by x,y,w,h and refresh
 +ctypes.windll.user32.MoveWindow(my_hwnd, 0, 0, 760, 500, True)
 </code> </code>
-====== Python in mel basic ====== +  mouse, keyboard operation <code python> 
-  print string and variable <code python>word="awesome" +# get mouse position 
-print("This is %s% word+class POINT(ctypes.Structure): 
-print("This is "+word)+    _fields_ [("x", ctypes.c_ulong), ("y", ctypes.c_ulong)] 
 +pt = POINT() 
 +ctypes.windll.user32.GetCursorPos(ctypes.byref(pt)
 +print('{0} {1}'.format(pt.x,pt.y)) 
 + 
 +ctypes.windll.user32.SetCursorPos(1200, 850)  # move cursor to 1200,850 (x,y) 
 +ctypes.windll.user32.mouse_event(2, 0, 0, 0,0) # left mouse button down 
 +ctypes.windll.user32.mouse_event(4, 0, 0, 0,0# left mouse button up
 </code> </code>
-  * string and interger <code python>for i in range (1,10): +  * get and set wallpaper in windows <code python> 
-    string="string"+str(i)+def getWallpaper(): 
 +    SPI_GETDESKWALLPAPER 0x0000073 
 +    #    try: 
 +    #        win32con.SPI_GETDESKWALLPAPER  
 +    #    except AttributeError, attributeError: 
 +    #        raise WinDesktopError, "getWallpaper() not supported on this OS" 
 +    wallpaper = ctypes.c_buffer("\000" * 256) 
 +    ctypes.windll.user32.SystemParametersInfoA(SPI_GETDESKWALLPAPER, len(wallpaper), ctypes.byref(wallpaper), 0) 
 +    return wallpaper.value 
 + 
 +def setWallpaper(wallpaperPath, conversionPath=None):     
 +    # Convert to bitmap if necessary 
 +    wallpaperImage = Image.open(wallpaperPath) 
 +    if wallpaperImage.format != 'BMP': 
 +        conversionPath = conversionPath or os.getenv('SystemRoot'
 +        wallpaperPath = os.path.join(conversionPath, os.path.splitext(os.path.split(wallpaperPath)[-1])[0] '.bmp'
 +        wallpaperImage.save(wallpaperPath) 
 +    # Set wallpaper 
 +    wallpaperPath = ctypes.c_buffer(wallpaperPath) 
 +    ctypes.windll.user32.SystemParametersInfoA(win32con.SPI_SETDESKWALLPAPER, 0, wallpaperPath, 0)
 </code> </code>
-  * start using mel as python <code python> +  * wallpaper set (win7) the simple version <code python> 
-import maya.cmds +import ctypes 
-maya.cmds.select("ball01",type="mesh") +SPI_SETDESKWALLPAPER=20 
-maya.cmds.ls(sl=True)+ctypes.windll.user32.SystemParametersInfoA(SPI_SETDESKWALLPAPER0, r"D:\Rec\bg2.jpg",0
 +</code> 
 +  * set id <code python>ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID('com.app.1.0')</code> 
 +  * get if user is admin <code python>print(ctypes.windll.shell32.IsUserAnAdmin())</code> 
 +  * system operation <code python> 
 +# get screen count 
 +screen_cnt ctypes.windll.user32.GetSystemMetrics(80) 
 +# get screen resolution 
 +screen_x, screen_y = ( ctypes.windll.user32.GetSystemMetrics(78), ctypes.windll.user32.GetSystemMetrics(79) )
 </code> </code>
  
-  python modules <code python> +**file and folder operation** 
-import myModule +  * show drive letter in use <code python> 
-myModule.myFunc() +drive_list_data = ctypes.cdll.msvcrt._getdrives() # binary mask in decimal format 
- +mask_list = list( '{0:b}'.format(drive_list)[::-1] 
-reload +A_code = 65 ascii code of letter A 
-reload(myModule)+drive_letter_in_use = [ chr(A_code+i) for i in range(len(mask_list)if mask_list[i]=='1' ]
 </code> </code>
 +  * show file and select file in explorer (return 42)<code python>
 +my_filePath = u'C:\\Users\\Public\\Pictures\\Sample Pictures\\Koala.jpg'
 +ctypes.windll.shell32.ShellExecuteW(None, u'open', u'explorer.exe', u'/n,/select,'+my_filePath, None, 1)</code>
 +  * open file property window <code python>
 +import time
 +import ctypes
 +import ctypes.wintypes
 +
 +SEE_MASK_NOCLOSEPROCESS = 0x00000040
 +SEE_MASK_INVOKEIDLIST = 0x0000000C
 +
 +class SHELLEXECUTEINFO(ctypes.Structure):
 +    _fields_ = (
 +        ("cbSize",ctypes.wintypes.DWORD),
 +        ("fMask",ctypes.c_ulong),
 +        ("hwnd",ctypes.wintypes.HANDLE),
 +        ("lpVerb",ctypes.c_char_p),
 +        ("lpFile",ctypes.c_char_p),
 +        ("lpParameters",ctypes.c_char_p),
 +        ("lpDirectory",ctypes.c_char_p),
 +        ("nShow",ctypes.c_int),
 +        ("hInstApp",ctypes.wintypes.HINSTANCE),
 +        ("lpIDList",ctypes.c_void_p),
 +        ("lpClass",ctypes.c_char_p),
 +        ("hKeyClass",ctypes.wintypes.HKEY),
 +        ("dwHotKey",ctypes.wintypes.DWORD),
 +        ("hIconOrMonitor",ctypes.wintypes.HANDLE),
 +        ("hProcess",ctypes.wintypes.HANDLE),
 +    )
  
-  * eval like in Python <code python> +ShellExecuteEx = ctypes.windll.shell32.ShellExecuteEx 
-import maya.mel +ShellExecuteEx.restype = ctypes.wintypes.BOOL 
-maya.mel.eval("ls -sl")+ 
 +sei = SHELLEXECUTEINFO() 
 +sei.cbSize = ctypes.sizeof(sei) 
 +sei.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_INVOKEIDLIST 
 +sei.lpVerb = "properties" 
 +sei.lpFile = 'C:\\Users\\Public\\Pictures\\Sample Pictures\\Koala.jpg' 
 +sei.nShow = 1 
 +ShellExecuteEx(ctypes.byref(sei)) 
 +time.sleep(5)
 </code> </code>
  
-  * python inside mel <code javascript+  * get clipboard data (ref: https://stackoverflow.com/questions/101128/how-do-i-read-text-from-the-windows-clipboard-from-python<code python
-string $list[]=python("['a','b','c']"); +import ctypes 
-size($list)+def get_clipboard_text(): 
 +    ctypes.windll.kernel32.GlobalLock.argtypes = [ctypes.c_void_p] 
 +    ctypes.windll.kernel32.GlobalLock.restype = ctypes.c_void_p 
 +    ctypes.windll.kernel32.GlobalUnlock.argtypes = [ctypes.c_void_p] 
 +    ctypes.windll.user32.GetClipboardData.restype = ctypes.c_void_p 
 + 
 +    ctypes.windll.user32.OpenClipboard(0
 +    try: 
 +        if ctypes.windll.user32.IsClipboardFormatAvailable(1): 
 +            data = ctypes.windll.user32.GetClipboardData(1) 
 +            data_locked = ctypes.windll.kernel32.GlobalLock(data) 
 +            text = ctypes.c_char_p(data_locked) 
 +            value = text.value 
 +            ctypes.windll.kernel32.GlobalUnlock(data_locked) 
 +            return value 
 +    finally: 
 +        ctypes.windll.user32.CloseClipboard() 
 +# test 
 +print(get_clipboard_text())
 </code> </code>
 +===== twisted =====
  
-====== Common Python function======+  * network communication: 
 +    * old version with python 2.7 32bit wheel: https://pypi.python.org/pypi/Twisted/15.0.0
  
-  * to use maya cmds, you need to run this code to get follow working <code python>import maya.cmds as cmds</code> +===== django =====
-===== selection related =====+
  
-  * select all geo from root node<code python> +  * a python web server, install by <code>python -m pip install django</code>
-# method 1, filterExpand +
-childs = cmds.listRelatives(root, ad=1, f=1 ) +
-allgeo = cmds.filterExpand(childs, selectionMask=12)+
  
-# method 2naming based +===== openpyxl===== 
-cmds.select(root+ 
-cmds.select(hierarchy=1+  * a new excel readerxlsx format, not old xls format, since now everyone use new excel version 
-tAll=cmds.ls(sl=1,type='transform'+  * read excel as dictionary (1st key, rest data<code python> 
-allgeo=[] +    def excel2Dict(filename): 
-for each in tAll+        # import openpyxl 
-    if(each.endswith('_geo')): +        # max_row sheet.max_row 
-        allgeo.append(each    +        # max_column = sheet.max_column 
-    #mel:if(`gmatch $each "*_geo"`$selected[size($selected)]=$each;+        wb = openpyxl.load_workbook(filename=filenameread_only=True
 +        records = [] 
 +        for sheet in wb.worksheets
 +            count = 0 
 +            keys = [] 
 +             
 +            for row in sheet.rows: 
 +                temp = [] 
 +                for cell in row: 
 +                    if count == 0: 
 +                        keys.append(str(cell.value)) 
 +                    else: 
 +                        temp.append(str(cell.value)) 
 +                if count > 0
 +                    records.append( dict(zip(keys, temp)) ) #  
 +                else: 
 +                    count += 1 
 +        return records 
 +</code> 
 +  * read excel as row data wo empty cell. <code python> 
 +    def excel2Data(filename): 
 +        import openpyxl 
 +        wb = openpyxl.load_workbook(filename=filename, read_only=True, data_only=True) 
 +        sheet_data = [] 
 +        for sheet in wb.worksheets: 
 +            table_data = [] 
 +            for row in sheet.values: 
 +                row_data = [] 
 +                for cell in row: 
 +                    if cell is not None: 
 +                        row_data.append(cell) 
 +                if len(row_data)>0: 
 +                    table_data.append(row_data) 
 +            sheet_data.append(table_data) 
 +        return sheet_data
 </code> </code>
 +====== Python by Reference or by Value ======
  
-===== attribute related =====+  * my experience found: "pure assignment will not change original holder's value, but other operation will change original holder's value" 
 +  * more study on Pointer vs Reference: http://www.learncpp.com/cpp-tutorial/74a-returning-values-by-value-reference-and-address/ 
 +    * Python variable are all reference, so pass reference to a function, the inside function holds copy of the reference, nothing outside affected, but if inside function reference modify, outside will be affected, since they all store value at same memory location(reference to that location): http://stackoverflow.com/questions/986006/how-do-i-pass-a-variable-by-reference 
 +      * http://stackoverflow.com/questions/986006/how-do-i-pass-a-variable-by-reference 
 +    * and another clear explanation of python reference vs c++ reference from c++ user by code: https://hbfs.wordpress.com/2011/06/14/python-references-vs-c-and-c/ 
 +  * here is my study experiment <code python>
  
-  * note attribute, which is not added by default, need to be added manually <code python> +#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
-s="""Long Text +# Example 1 
-2nd line; +dict {} 
-""" +dict['seq']={} 
-if(cmds.objExists('base_prefs_null.notes')==False): +for each in 'aa bb cc dd'.split(): 
-    cmds.addAttr('base_prefs_null',dt='string',ln='notes',sn='nts') +    dict['seq'][each]=[] 
-cmds.setAttr('base_prefs_null.notes', s, type="string"+list_aa = dict['seq']['aa'] # passing by reference!!! 
-</code>+for i in [1,2,3,4,5]: 
 +    list_aa.append(i) 
 +print dict # affect both list_aa and dict 
 +     
 +temp_list dict['seq']['aa'] 
 +temp_list.append(22# you are not get a copy of dict entry, but affect orginal dict as well 
 +print temp_list 
 +print dict # changed ------- because it is a operation
  
-===== sets related =====+# ref: http://stackoverflow.com/questions/2612802/how-to-clone-or-copy-a-list-in-python 
 +temp_list2 dict['seq']['aa'][:] # actually duplicate the value out, the fastest way 
 +temp_list2.append(55) 
 +print temp_list2 
 +print dict # stay same
  
-  * empty group <code python>bulletGeoGrp=cmds.group(n='bulletGeoGrp',em=1)</code> +#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
-  * add to a organizing set<code python>cmds.sets([c_start, c_end],add='LocatorSet')</code>+# Example 2 
 +locs = [1], [2] ] 
 +for loc in locs: # passing by values !!! 
 +    loc [] 
 +print locs # stay same
  
-===== rigging related =====+for loc in locs: 
 +    loc[0] loc[0] * 2 
 +print locs # changed ------- because it is a operation
  
-  * findRelatedSkinCluster python version (written based on original mel version)<code python> +#~~~~~~~~~~~~~~~~~~~~~~~~ 
-def findRelatedSkinCluster( skinObject ): +Example 3 
-    #ref:http://takkun.nyamuuuu.net/blog2/en/archives/592 +a=1 
-    skinShape None +b=a 
-    skinShapeWithPath None +b=2 
-    hiddenShape None +print a # stay the same
-    hiddenShapeWithPath = None +
-  +
-    cpTest = cmds.ls( skinObject, typ="controlPoint"+
-    if len( cpTest ): +
-        skinShape = skinObject +
-  +
-    else: +
-        rels = cmds.listRelatives( skinObject ) +
-        for r in rels : +
-            cpTest = cmds.ls( "%s|%s" % ( skinObject, r ), typ="controlPoint"+
-            if len( cpTest ) == 0: +
-                continue +
-  +
-            io = cmds.getAttr( "%s|%s.io" % ( skinObject, r ) ) +
-            if io: +
-                continue +
-  +
-            visible = cmds.getAttr( "%s|%s.v" % ( skinObject, r ) ) +
-            if not visible: +
-                hiddenShape = r +
-                hiddenShapeWithPath = "%s|%s" % ( skinObject, r ) +
-                continue +
-  +
-            skinShape = r +
-            skinShapeWithPath = "%s|%s" % ( skinObject, r ) +
-            break +
-  +
-    if len( skinShape ) == 0: +
-        if len( hiddenShape ) == 0: +
-            return None +
-  +
-        else: +
-            skinShape = hiddenShape +
-            skinShapeWithPath = hiddenShapeWithPath +
-  +
-    clusters = cmds.ls( typ="skinCluster"+
-    for c in clusters: +
-        geom = cmds.skinCluster( c, q=True, g=True ) +
-        for g in geom: +
-            if g == skinShape or g == skinShapeWithPath: +
-                return c +
-    return None +
-</code>+
  
-===== animation related =====+a=[1] 
 +b=
 +b=[2] 
 +print a # stay the same
  
-  * setkeyframe <code python>cmds.setKeyframe(testGeo+'.v',time=10, value=False) +a=[1] 
-cmds.setKeyframe(testGeo+'.v',time=11, value=True)+b=a 
 +b.append(2) 
 +print a # changed ------- because it is a operation
  
-just key the channel +thus: assignment will not change original one, but operation will change original one
-cmds.setKeyframe(testGeo+'.v'+
-</code>+
  
-  * delete key <code python> +a=[1,2,3] 
-# delete all keys +b=a 
-cmds.cutKey('locator1.t',cl=1) clear clear without copy to clipboard+c=[4,5,6] 
 +b=
 +print a stay the same
  
-# delete some of keys +a=[1,2, [3.1,3.2,3.3] ] 
-1. get key frame list +b=a[2] 
-key_list = cmds.keyframe('locator1.tx'q=1) +c=[4,5,6] 
-key_list cmds.keyframe('locator1'q=1at='tx')+b=
 +print a # stay the same
  
-current key value +a=[1,2, [3.1,3.2,3.3] ] 
-cur_v cmds.getAttr('locator1.tx', time=1) +b=a[2] 
-</code>+b=[4,5,6] 
 +print a stay the same 
 + 
 +a={'first':[3.1,3.2,3.3] } 
 +b=a['first'
 +b=[4,5,6] 
 +print a # stay the same 
 + 
 +a={'first':[3.1,3.2,3.3] } 
 +b=a['first'
 +b[0]=4 
 +print a # changed ------- because it is a operation 
 + 
 +a=[1,2, [3.1,3.2,3.3] ] 
 +b=a[2][:] # fast copy 
 +c=[4,5,6] 
 +b=c 
 +print a # stay the same
  
-  * run through frame range <code python> +#~~~~~~~~~~~~~~~~~~~~~~~ 
-startFrame = cmds.playbackOptions(q=1, min=1+# example 4:  
-endFrame = cmds.playbackOptions(q=1, max=1+    # ref: http://stackoverflow.com/questions/1132941/least-astonishment-in-python-the-mutable-default-argument 
-for f in range(int(startFrame), int(endFrame+1)): +# explain: http://effbot.org/zone/default-values.htm 
-    cmds.currentTime(f)+def foo(a=[]): 
 +    a.append(5
 +    return a 
 +foo() # changed every time 
 + 
 +######################################## 
 +# reference  
 +''' 
 +# ref: http://stackoverflow.com/questions/9696495/python-when-is-a-variable-passed-by-reference-and-when-by-value 
 +Everything in Python is passed and assigned by value (like Java) 
 +Every value in Python is a reference (pointerto an object.  
 +Objects cannot be values 
 +Assignment always copies the value (which is a pointer);  
 +two such pointers can thus point to the same object.  
 +Objects are never copied unless you're doing something explicit to copy them. 
 +# ref: http://python.net/~goodger/projects/pycon/2007/idiomatic/handout.html#other-languages-have-variables 
 +????Everything is passed by object. Rebinding and mutating are different operations 
 +'''
 </code> </code>
  
-===== dynamic related =====+====== Common Python syntax ======
  
-  * emitter, particle, instance, field <code python+  * python 101 - http://www.rexx.com/~dkuhlman/python_101/python_101.html
-#============================== +
-# creation particle system +
-#============================== +
-mName='bullet' +
-mE_subfix='Emiiter' +
-mP_subfix='Particle' +
-mI_subfix='Instancer' +
-mInMesh_subfix='_InMesh' +
-mFt_subfix='_turbulenceField' +
-mC_loc_subfix='_loc' +
-mC_curve_subfix='_guideCurve' +
-mC_null_subfix='_null'+
  
-node subfix +^comment || 
-mN_mdl_subfix='_mdl#multDoubleLinear+^print |print | 
 +^string |var="string"
 +^long string |%%varL='''\ whatever '''%% | 
 +^raw string |r"Any dangerous Character here" |
  
-#---------------------- +^os navigation | ^file operation | ^string operation | ^lib | ^rename | 
-# emitter and particle +^os.chdir('/path') |cd ^tfile=open('t.txt','w') | ^str.replace('o','n') |replace ^os |system ^prefix_remove_fixed | 
-#---------------------- +^os.getcwd() | ^tfile.write('hello') | ^str[2:6] |substring ^glob |wildchar list ^prefix_add_fixed | 
-mE=cmds.createNode('pointEmitter',n=mName+mE_subfix+^os.ctermid() | ^tfile.close() | ^str1+str2 |concatenate ^re |regular expression ^end_remove_fixed | 
-mP=cmds.createNode('particle',n=mName+mP_subfix)+^os.uname(| ^tfile.read() | ^str(100) |string convert ^ | ^end_add_fixed | 
 +^os.listdir('/path') | ^tfile.writeline() | ^ | ^ | ^prefix_remove_vary | 
 +^os.mkdir('/path') | ^tfile.readline() | ^[a-c] [abc] |class ^ | ^ | 
 +^os.removedirs('/path') | ^ | ^[%%^%% abc] |not in class ^ | ^ | 
 +^os.rename(src,dst| ^ | ^a* |may have ^ | ^ | 
 +^os.getenv('HOME') | ^ | ^a+ |must have one ^ | ^ | 
 +^ | ^ | ^a? |may have one ^ | ^ | 
 +^ | ^ | ^a{1,3} |May 1~3 one ^ | ^ | 
 +^ | ^ | ^r”str” |raw string ^ | ^ | 
 +^regular expression | ^ | ^ | ^ | ^ | 
 +^ | ^ | ^ | ^ | ^ | 
 +http://docs.python.org/howto/regex.html#regex-howto
  
-# connect dynamic 
-cmds.connectDynamic(mP,em=mE) 
-cmds.connectAttr('time1.outTime',mP+'.cti') 
  
-# set initial value +====== Python based tool and application ====== 
-e_conf={'emitterType':0,'rate':24,'scaleRateByObjectSize':0,'dx':0,'dz':1,'speed':150} +  * [[http://nadiana.com/animated-terminal-progress-bar-in-python|progress bar in terminal]]
-for attr, v in e_conf.items(): +
-    cmds.setAttr(mE+'.'+attr, v)+
  
-#---------------------- +  * http://sphinx.pocoo.org/ 
-# instance +    * Python documentation generator
-#---------------------- +
-mI=cmds.createNode('instancer',n=mName+mI_subfix) +
-cmds.connectAttr(mP+'.instanceData[0].instancePointData', mI+'.inputPoints')+
  
-# instance Mesh +  * XML to PDF generation using Python 
-mInMesh=cmds.polyCylinder(n=mName+mInMesh_subfix,ax=[1,0,0]) +    * http://www.blog.pythonlibrary.org/2012/07/18/parsing-xml-and-creating-a-pdf-invoice-with-python/
-mMesh=mInMesh[0] +
-mShape=mInMesh[1] +
-cmds.setAttr(mMesh+'.v',0) +
-cmds.connectAttr(mMesh+'.matrix', mI+'.inputHierarchy[0]'+
-cmds.particleInstancer(mP, e=1, name=mI, aimDirection='velocity')+
  
-#---------------------+  * Python add-on and libs 
-# field +    * Python image library (PIL): http://www.pythonware.com/products/pil/ 
-#---------------------+    * Python EXIF.py: http://sourceforge.net/projects/exif-py/ 
-mFt=cmds.createNode('turbulenceField',n=mName+mFt_subfix) +      * demo of usage: http://snippets.dzone.com/posts/show/4089 
-cmds.connectDynamic(mP,f=mFt)+    * minimal EXIF reader and writer: http://fetidcascade.com/pyexif.html#
 +    * 50 python lib list: http://blog.sina.com.cn/s/blog_4b5039210100g8ho.html
  
-ft_conf={'mag':10,'att':0.75,'maxDistance':10} +  * a Chinese blog on python usagehttp://blog.sina.com.cn/s/articlelist_1263548705_8_1.html
-for attr, v in ft_conf.items(): +
-    cmds.setAttr(mFt+'.'+attr, v)+
  
-cmds.expression(s="phy=time*4",o=mFt,ae=1,uc='all') +** Python web applications ** 
-</code> +  * https://wiki.python.org/moin/WebFrameworks/ 
-===== math related=====+  http://www.web2py.com/ 
 +  * http://webpy.org
 +====== Python GUI ====== 
 +  * Python GUI option list: http://wiki.python.org/moin/GuiProgramming 
 +    * web platform: PyJamas 
 +    * desktop platform: QT, wxWidget
  
-  * sin and cost function <code python>import math +  * GUI design: 
-#----------------------------- +    * wxGlade: http://wxglade.sourceforge.net/index.php 
-# aim vector to rotation value +    * wxFormBuilder: http://sourceforge.net/projects/wxformbuilder/files/wxformbuilder-nightly/
-#----------------------------- +
-# rotation +
-aimDir=cmds.particle(pName,q=1,at='velocity', order=i) +
-d=math.sqrt(aimDir[0]*aimDir[0]+aimDir[1]*aimDir[1]+aimDir[2]*aimDir[2])+
  
-rx=math.degrees(math.asin(aimDir[1]/d))*-1 +  IDE
-ry=0 +    * Erichttp://eric-ide.python-projects.org/index.html
-if(aimDir[2]>0)# check +z normal condition, result (-90,90) +
-    ry=math.degrees(math.atan(aimDir[0]/aimDir[2])) +
-elif(aimDir[2]==0)# check z=0 xy panel, which rotateX=+-90 condition -90 or 90 or 0 +
-    if(aimDir[0]>0)# +xy panel condition +
-        ry=90 +
-    elif(aimDir[0]<0):-xy panel condition +
-        ry=-90 +
-    elif(aimDir[0]==0): # 0=xy panel +
-        ry=0 +
-    else: # for easy reading only +
-        ry=0 +
-elif(aimDir[2]<0): # check -z +-90 condition (-90,-180) and (90,180) and 180 +
-    if(aimDir[0]>0): # +xy panel condition +
-        ry=math.degrees(math.atan(aimDir[0]/aimDir[2]))+180 +
-    elif(aimDir[0]<0):-xy panel condition +
-        ry=math.degrees(math.atan(aimDir[0]/aimDir[2]))-180 +
-    elif(aimDir[0]==0): # 0=xy panel condition +
-        ry=180 +
-    else: # for easy reading only +
-        ry=0 +
-else: # for easy reading only +
-    ry=0 +
-rz=0 +
-cmds.xform(tGeo,ro=[rx,ry,rz],a=1) +
-</code>+
  
-====== RnD List=======+====== wxPython ======
  
-  * face track:  
-    * http://facetracker.net/ 
-  * Sunday pipe and shaders: 
-    * http://www.3dg.dk/category/asset/ 
-    * http://www.3dg.dk/2011/08/13/sunday-pipeline-maya-warehouse-resources/ 
-    * http://www.3dg.dk/2011/08/12/sunday-pipeline-maya-public/ 
-    * http://www.jonasavrin.com/2011/08/23/massive_sunday_pipeline_vray_shader_library/ 
  
-  * python code +===== wxPython GUI concept quick guide =====
-    * get clipboard: http://www.jonasavrin.com/2011/03/08/python-in-maya-pywin32-clipboard-data-modules-update/+
  
-  * Blender shading: +  * main build block 
-    * https://www.youtube.com/watch?v=V3wghbZ-Vh4 +    * A Frame is a top-level window.
-    * https://www.youtube.com/watch?v=fj9hbnFPaH8+
  
-====== Angle Calculation ====== 
  
-===== Proof of cos(A+B) formula =====+===== wxWidget - wxPython =====
  
-  * original reference([[http://www.ies.co.jp/math/java/trig/kahote/kahote.html|Link 1]], [[http://clowder.net/hop/cos(a+b).html|link 2]])\\ {{graphic:mathproof_sina_b.png?400|}} \\ {{graphic:mathproof_cosa_b.png?400|}}+  * download: http://www.wxpython.org/download.php 
 +  * youtube video tutorial: http://wiki.wxpython.org/YouTubeVideoSeries
  
-===== Proof of rotation matrix ===== 
-  * 2D rotation of an angle from (x,y) to (x1,y1) \\ {{graphic:math_rotation_matrix_2d.png|}} 
-  * Proof <code> 
-let A (x,y) to A1 (x1,y1) turns degree of a in respect of origin. 
  
-in Polar coordinate,  +  * basic wxPython widget syntax and codes <code python> 
-so, A (o,R), A1 (o1,R)+# get wxWidget frame work
 +import wx
  
-from A1we have +# create classsince you are doing a App or a script
-x1=R*cos(o1) +
-y1=R*sin(o1)+
  
-as o1=o+a, resulting o1 from turn o in a degreewe have +# inheriate python window (so-called frame) 
-x1=R*cos(o1)=R*cos(o+a+class myApp(wx.Frame):  
-y1=R*sin(o1)=R*sin(o+a)+  # constructor(so-called the initialize function) 
 +  def __init__(self,parent,id): 
 +    wx.Frame.__init__(self,parent,id,'My Window', size=(300,200)
 +    panel=wx.Panel(self
 +     
 +    #bitmap button 
 +    pic=wx.Image('img.bmp', wx.BITMAP_TYPE_BMP).ConvertToBitmap() 
 +    myBtn=wx.BitmapButton(panel, -1, pic, pos=(10,100)) 
 +    self.Bind(wx.EVT_BUTTON,self.btnAct,myBtn) 
 +    myBtn.SetDefault() 
 +     
 +    #slider 
 +    slider=wx.Slider(panel,-1,50,1,100,pos=(10,30), size=(250,-1),style=wx.SL_AUTOTICKS | wx.SL_LABELS) 
 +    slider.SetTickFreq(5,1) # interval 
 +     
 +    # spinner 
 +    spinner=wx.SpinCtrl(panel,-1,'',pos=(10,1), size=(90,-1)) 
 +    spinner.SetRange(1,100) 
 +    spinner.SetValue(10) 
 +     
 +    # checkbox 
 +    wx.CheckBox(panel,-1,'Apples',pos=(100,1),size=(60,-1)) 
 +    wx.CheckBox(panel,-1,'Orange',pos=(160,1),size=(60,-1)) 
 +    wx.CheckBox(panel,-1,'Banana',pos=(220,1),size=(60,-1))
  
-based on formula of cos(A+B)=cosA*cosB-sinA*sinB; sin(A+B)=sinA*cosB+cosA*sinB; +    # combo box 
-x1=R*cos(o1)=R*cos(o+a)=R*cos(o)*cos(a)-R*sin(o)*sin(a+    mylist=['Apple','Orange','Banana'] 
-y1=R*sin(o1)=R*sin(o+a)=R*sin(o)*cos(a)+R*cos(o)*sin(a)+    cbox=wx.ListBox(panel,-1,(300,1),(80,60),mylist,wx.LB_SINGLE
 +    cbox.SetSelection(0)
  
-as in Polar coordinate, the R*cos(o) is actually xR*sin(ois actually y, so we have +# event handlers 
-x1=x*cos(a)-y*sin(a) +  def btnAct(self,event): 
-y1=y*cos(a)+x*sin(a)=x*sin(a)+y*cos(a)+    self.Destroy()
  
-in matrix formit is same is the question above, Done.+if __name__=='__main__': 
 +    app=wx.PySimpleApp() # application obj 
 +    frame=myClass(parent=None,id=-1) # frame obj 
 +    frame.Show() # show frame 
 +    app.MainLoop() # start app    
 </code> </code>
-  * thus in 3D space, we expand to have\\ {{graphic:math_rotation_matrix.png|}}+  * wxWidget binding for python tutorialhttp://zetcode.com/wxpython/introduction/
  
-===== understand Matrix multiplication =====+  * 2 Panel App Flow 
 +    * http://www.blog.pythonlibrary.org/2010/06/16/wxpython-how-to-switch-between-panels/
  
-  * ref: http://en.wikipedia.org/wiki/Matrix_multiplication+===== Common wxPython syntax =====
  
-====== Vector calculation ======+  * prompts <code python> 
 +#import wx.lib.agw.multidirdialog as MDD 
 +import multidirdialog as MDD 
 +def showMsg(msg): 
 +    wx.MessageBox(msg, 'Info',  
 +    wx.OK | wx.ICON_INFORMATION)
  
-  * in Maya +import wx.lib.dialogs 
-    * OpenMaya MVector method <code python>import maya.cmds as cmds +def showTxt(txt): 
-from maya.OpenMaya import MVector+    ask = wx.lib.dialogs.ScrolledMessageDialog(None, txt, "Info") 
 +    ask.ShowModal() 
 +    ask.Destroy()
  
-# functionmake selected points into a sphere/circle based on the defined center.+def getInput(): 
 +    tmpT="" 
 +    ask = wx.TextEntryDialog( 
 +            None, 'Your input:', 
 +            'Input', '1'
 +    if ask.ShowModal() == wx.ID_OK: 
 +        print "Input: %s\n" % ask.GetValue() 
 +        tmpT = ask.GetValue() 
 +    ask.Destroy() 
 +    return tmpT
  
-points=cmds.ls(sl=1fl=1+def getFolder(): 
-center=cmds.ls(sl=1)[0]+    tmpPath="" 
 +    dialog wx.DirDialog Nonemessage 'Select a directory.' 
 +    if dialog.ShowModal() == wx.ID_OK: 
 +        print 'Directory:', dialog.GetPath(
 +        tmpPath dialog.GetPath() 
 +    else: 
 +        print 'No directory.' 
 +    dialog.Destroy() 
 +    return tmpPath
  
-pointVectors = [] 
-for pnt in points: 
-    v = MVector(*cmds.pointPosition(pnt)) 
-    pointVectors.append(v) 
  
-v_center MVector(*cmds.pointPosition(center))+def getFolders(): 
 +    tmpPaths="" 
 +    ask = MDD.MultiDirDialog(None, title="Select folders:", defaultPath='', agwStyle=0) 
 +    if ask.ShowModal() == wx.ID_OK: 
 +        tmpPaths = ask.GetPaths() 
 +        print "You chose the following file(s):" 
 +        for path in tmpPaths: 
 +            print path 
 +    ask.Destroy() 
 +    return tmpPaths
  
-pointDistances [] +def getFile(): 
-for v in pointVectors: +    tmpPaths="" 
-    pointDistances.append((v - v_center).length())+    wildcard = "Python source (*.py)|*.py|" "All files (*.*)|*.*" 
 +    ask = wx.FileDialog(None, message="Choose a file", defaultDir="", defaultFile="", wildcard=wildcard,style=wx.OPEN | wx.MULTIPLE | wx.CHANGE_DIR) 
 +    if ask.ShowModal() == wx.ID_OK: 
 +        tmpPaths = ask.GetPaths() 
 +        print "Select file(s):" 
 +        for path in tmpPaths: 
 +            print path 
 +    ask.Destroy() 
 +    return tmpPaths
  
-minRadius min(pointDistances)+def saveFile(): 
 +    tmpPaths="" 
 +    wildcard = "Python source (*.py)|*.py|" "All files (*.*)|*.*" 
 +    ask = wx.FileDialog(None, message="Save As", defaultDir="", defaultFile="", wildcard=wildcard,style=wx.SAVE) 
 +    if ask.ShowModal() == wx.ID_OK: 
 +        tmpPath = ask.GetPath() 
 +        print "Save file:", tmpPath 
 +    ask.Destroy() 
 +    return tmpPath 
 +     
 +def getColor(): 
 +    ask = wx.ColourDialog(None) 
 +    ask.GetColourData().SetChooseFull(True) 
 +    if ask.ShowModal() == wx.ID_OK: 
 +        data = ask.GetColourData() 
 +        print 'You selected: %s\n' % str(data.GetColour().Get(includeAlpha=False)) 
 +    ask.Destroy() 
 +    return data.GetColour().Get(includeAlpha=False)
  
-for pnt,v in zip(points, pointVectors): +def getChoice(): 
-    new_v = (v - v_center).normal()*minRadius + v_center +    tmpC="" 
-    cmds.xform(pnt, ws=1t=(new_v.xnew_v.ynew_v.z))+    ask = wx.SingleChoiceDialog( 
 +        None, "Select a choice", 'The direction', 
 +        ["Up", "Down", "Left", "Right"],  
 +        wx.CHOICEDLG_STYLE 
 +        ) 
 +    if ask.ShowModal() == wx.ID_OK: 
 +        tmpC = ask.GetStringSelection() 
 +        print 'Choice: %s\n' % ask.GetStringSelection() 
 +    ask.Destroy(
 +    return tmpC 
 + 
 +def getChoices(): 
 +    tmpCs=[] 
 +    list = ["Photo""Movie", "Docs"
 +    ask wx.MultiChoiceDialogNone 
 +                               "Select the choices", 
 +                               "wx.MultiChoiceDialog"list) 
 + 
 +    if (ask.ShowModal(== wx.ID_OK): 
 +        selections = ask.GetSelections() 
 +        strings = [list[x] for x in selections] 
 +        print "You chose:" + str(strings) 
 +        tmpCs = strings 
 +    ask.Destroy() 
 +    return tmpCs 
 +     
 +application = wx.PySimpleApp()
 </code> </code>
-====== Plane Intersection calcuation ======+  * all dialog reference:  
 +    * http://www.blog.pythonlibrary.org/2010/06/26/the-dialogs-of-wxpython-part-1-of-2/ 
 +    * http://www.blog.pythonlibrary.org/2010/07/10/the-dialogs-of-wxpython-part-2-of-2/
  
-  * 2 planes (represented by 2 vectors) intersection line 
  
-====== Math tutorial links ====== 
  
-  * 3D math for CG artists: +====== PyQt ======
-  * http://programmedlessons.org/VectorLessons/vectorIndex.html+
  
-  * Shapes that tessellate +  * check [[devwiki:python_qt|Qt and Python]] 
-    * http://gwydir.demon.co.uk/jo/tess/grids.htm+====== Python Portable for Windows ======
  
-  * math fun: http://www.mathsisfun.com/geometry/index.html +  * since all mac and linux have python built-in
-====== Local space and World space and Screen space ======+
  
-  ref: http://around-the-corner.typepad.com/adn/2012/08/convert-maya-world-coordinates-to-screen-coordinates.html+**Workflow on portable python**
  
 +  - Prepare python 2.7/3.5, by download the official python exe, and install it to D drive
 +    * https://www.python.org/
 +      * for Python3.5.2: Windows x86 executable installer
 +      * for Python2.7.12: Windows x86 MSI installer
 +  - update: for PyQt and PySide, just cd to that python directory and .\python.exe -m pip install PySide or PyQt4 to install that module with pip or ./python for mac/linux
 +  - <del>Prepare PyQt4 library for py2.7/3.5 by download the official, and install to the correct PythonX.X folder</del>
 +    * <del>https://www.riverbankcomputing.com/software/pyqt/download</del>
 +      * <del>for Python3.5: PyQt4-4.11.4-gpl-Py3.4-Qt4.8.7-x32.exe</del> 
 +      * <del>for python2.7: PyQt4-4.11.4-gpl-Py2.7-Qt4.8.7-x32.exe</del>
 +  - other library:
 +    * cx_Freeze
  
-====== Fluid Calculation model ======+====== Python Portable for Mac ======
  
-  * ref: http://around-the-corner.typepad.com/adn/2012/08/maya-fluid-shader.html+  * Python 2.7 is built-in in Mac at /System/Library/Frameworks/Python.framework/Versions/2.7/ 
 +  * There are many articles on install latest python 2.7 and 3.x version with many methods, like homebrew and offical python site, while my idea is not touch any system path or system environment, be portable means, the system dont even need to know my personal python app is there, like windows portable version 
 +    * the advantage is you choose which python to run the py script 
 +    * the disadvantage is you can't default python the_script.py in your command line, so you should always use a launch bash script to dynamically use which python path and open the script with it. 
 +      * check my App_V9.bat launch for windows: https://github.com/shiningdesign/universal_tool_template.py/blob/master/App_V9.bat
  
-====== 3D Camera model ====== +**Standard Install Python 3.8** 
-  * camera model: http://around-the-corner.typepad.com/adn/2012/11/cameras-in-maya.html +  * download and install python3 installer 
-  * stereoscopic camera: http://around-the-corner.typepad.com/adn/2012/11/maya-stereoscopic.html+  * download and install command line tools for xcode latest (use normal Apple acc to login) 
 +    * https://developer.apple.com/download/ 
 +  * install PySide2 (with user flag that fixes permission issue) <code bash>pip3 install PySide2 --user</code> 
 +  * now python3 work in terminal app 
 +    * when first run, allow Access folder right
 +  * to make command extension file runable, do bash for the command extension launcher <code bash>chmod 755 App_V9.command</code> 
 +====== Python Cool Tricks and Notes ======
  
-====== VR rendering in 3D======+  * start HTML and FILE server in current directory <code dos>python -m SimpleHTTPServer 8181</code> 
 +  * decimal vs float for real life accounting usage: 
 +    * decimal wont have those float annoying issue with tons of 0s after . 
 +    * decimal deal number like human do instead of infinite computing accuracy 
 +    * https://towardsdev.com/python-tips-decimal-vs-float-72fe72fb7086 
 +    * https://www.laac.dev/blog/float-vs-decimal-python/ 
 +    * <code>from decimal import Decimal; a Decimal(0.01)</code>
  
-  * VR rendering from 3D package 
-    * http://www.andrewhazelden.com/blog/free-downloads/ 
-    * http://www.andrewhazelden.com/blog/2012/04/domemaster3d-stereoscopic-shader-for-autodesk-maya/ 
-    * https://github.com/zicher3d-org/domemaster-stereo-shader 
-    * https://www.youtube.com/watch?v=Xo5ZL0N9k2k 
-    * https://community.renderman.pixar.com/article/991/rendering-for-vr.html 
-    * https://vrwiki.wikispaces.com/3D+Rendering 
-    * http://www.irisvr.com/blog/category/how-to-create-mobile-panoramas-using-v-ray 
-    * http://forums.cgsociety.org/showthread.php?t=1278710 
  
-  * article +  * python and whatsapp msg 
-    * http://elevr.com/cg-vr-1/ +    * ref: https://iq.opengenus.org/python-script-to-send-whatsapp-message/ 
-    * http://elevr.com/cg-vr-2/+    * **pywhatkit**: use web client to automate msg sending, make sure it on main screen (multi screen case)  
 +      * because it use automation, so interaction with browser can be buggy, no quick repeat sending (since automate like human action, need wait response of other app update), send group chat may send to wrong group if you also interact with browser 
 +      * example<code python> 
 +import pywhatkit 
 +pywhatkit.sendwhatmsg_instantly("+88123456",auto_url,10,True,3)  
 +# wait 10s to ui response, close tab true after 3s,  
 +# you need to close tab as whatsapp web only allow window active 
 +</code> 
 +    * **selenium**: use selenium browser automate driver to operate, since it is linking to a dedicated browser with browser api, it is more reliable to send fast msg and maintain connection, no user interaction affection 
 +      * example setup and login manually for first time <code python> 
 +my_drive = None 
 +def drive_setup(): 
 +    from selenium import webdriver 
 +    from selenium.webdriver.common.keys import Keys 
 +    from selenium.webdriver.common.by import By 
 +     
 +    my_drive = webdriver.Chrome() 
 +    phone_no = "+881234567" # target phone or your own phone as target 
 +    my_drive.get(f'https://web.whatsapp.com/send?phone={phone_no}&text=')
  
-  * tool +def drive_msg(in_text): 
-    * http://hawksley.github.io/eleVR-Picture-Player/+    from selenium.webdriver.common.by import By 
 +    if my_drive is None: 
 +        return 
 +    else: 
 +        chat_box = my_drive.find_element(by=By.XPATH, value='/html/body/div[1]/div/div/div[4]/div/footer/div[1]/div/span[2]/div/div[2]/div[1]/div/div/p'
 +        chat_box.send_keys(in_text) 
 +        send_button = my_drive.find_element(by=By.CSS_SELECTOR, value='[data-icon="send"]'
 +        send_button.click() 
 +</code> 
 + 
 +====== Python and interaction with other API ====== 
 + 
 +Telegram 
 +  - search @BotFather on telegram 
 +    * manual: https://core.telegram.org/bots/api 
 +  in the chat, type /newbot, then type your bot "botname", then type your bot "botusername_bot" 
 +  now, your bot api key will show up, copy and save it 
 +  - now search your bot "botusername_bot" to start chat with it 
 +  - in python, use requests to get the chat from the bot, try one more time if no result <code python> 
 +import requests 
 +the_key = "api_key" 
 +url = "https://api.telegram.org/bot{0}/getUpdates".format(the_key) 
 +response = requests.get(url) 
 +result = response.json() 
 +print(result) 
 +</code> 
 +  - once you got result in json, you will find the chat id. <code python>result['result'
 +# the list of all chat spec your bot received, yours is likely the first one. 
 +my_chat_info = result['result'
 +my_msg_info = my_chat_info[0]['message'
 +# it got info of: chat, date, from, message_id, text 
 +my_chat_id = my_msg_info['chat']['id'
 +</code> 
 +  - to send a text to that chat from your bot side <code python> 
 +reply_text = 'hello world' 
 +url = "https://api.telegram.org/bot{key}/sendMessage?chat_id={id}&text={reply}".format(**{'key':the_key, 'id':my_chat_id,'reply':reply_text}) 
 +response = requests.get(url) 
 +</code> 
 +  - now, you should have got the reply from your bot <code python> 
 +result = response.json() 
 +send_state = result['ok'
 +send_result = result['result'
 +# chat (first_name, id, type); date:int; message_id; text 
 +</code>
  
-  * case study: 
-    * https://www.youtube.com/watch?v=uEeyHevzIAs 
  • devwiki/python.txt
  • Last modified: 2024/03/25 06:36
  • by ying