MayaにChatGPT(openai)を追加インストールしてみる

Mayaのpip書き換えちゃうので自己責任でというかMayaの再インスト―ルもする余裕があるときにやること

とりあえずpipでインストールする前にpipのバージョンがふるいのでUpgradeしてる。

cd "C:\Program Files\Autodesk\Maya2023\bin\"
mayapy.exe -m pip install --upgrade pip

mayapy -m pip install openai
cmd /k

入った。

てかこの2フォルダをsitepackageにいれとくだけでも動くには動くはず
https://drive.google.com/file/d/12KGnpQAnkWn3wifXPsMehqZ5YFfeFhWe/view?usp=share_link

メインのPython

import os
import openai


print(os.getcwd())
#print(__file__)
MELpath=os.getcwd()
pythonPath= os.path.abspath(MELpath+"/../python/open_ai/")
pythonPath= pythonPath.replace('\\', '/')
print("pythonPath= "+pythonPath)

f = open(pythonPath+'/open_ai_api_key.txt', 'r', encoding='UTF-8')
open_ai_api_key = f.read()
f.close()

#openai.api_key = os.getenv("OPENAI_API_KEY")
openai.api_key = open_ai_api_key

# APIコールを行う関数
def completion(new_message_text:str, settings_text:str = '', past_messages:list = []):
    if len(past_messages) == 0 and len(settings_text) != 0:
        system = {"role": "system", "content": settings_text}
        past_messages.append(system)

    new_message = {"role": "user", "content": new_message_text}
    past_messages.append(new_message)

    result = openai.ChatCompletion.create(
        model="gpt-3.5-turbo",
        messages=past_messages
    )
    message_text = result.choices[0].message.content
    response_message = {"role": "assistant", "content": message_text}
    past_messages.append(response_message)
    
    return message_text, past_messages
    
    
    
import re
# 返答をPythonコードとその他の部分に分解
# reモジュールを使って```python~```で囲まれた部分とそれ以外の部分を切り出します。
def decompose_response(txt):
    pattern = r"```python([\s\S]*?)```"
    
    code_list = re.findall(pattern, txt)
    for i in range(int(len(code_list))):
        code_list[i] = re.sub('\A[\r?\n]', '', code_list[i])
        code_list[i] = re.sub('[\r?\n]\Z', '', code_list[i])
    
    comment = re.sub(pattern, '', txt)
    comment = re.sub('[\r?\n]+', '\n', comment)
    comment = re.sub('[\r?\n]\Z', '', comment)
    
    return comment, code_list
    
    
    


from maya import cmds

class ChatGPT_Maya(object):

    def __init__(self):
        self.system_settings = "質問に対して、MayaのPythonスクリプトを書いてください。スクリプト以外の文章は短めにまとめてください。"
        self.message_log = []
        self.at_first = True
        self.create_window()

    def reset_session(self, *args):
        self.message_log = []
        self.at_first = True
        cmds.scrollField(self.input_field, e=True, tx='')
        cmds.scrollField(self.ai_comment, e=True, tx='')
        cmds.cmdScrollFieldExecuter(self.script_field, e=True, t='')

    def call(self, *args):
        user_input = cmds.scrollField(self.input_field, q=True, tx=True)
        
        # APIコール
        if self.at_first:
            message_text, self.message_log = completion(user_input, self.system_settings, [])
            self.at_first = False
        else:
            message_text, self.message_log = completion(user_input, '', self.message_log)
            
        # 返答を分解
        comment, code_list = decompose_response(message_text)
        
        # Pythonコード以外の部分をai_commentに表示
        cmds.scrollField(self.ai_comment, e=True, tx=comment)

        # Pythonコードの1つ目をscript_fieldに表示。2つ目以降は無視(汗
        if code_list:
            cmds.cmdScrollFieldExecuter(self.script_field, e=True, t=code_list[0])
            # 実行
            if cmds.checkBox(self.script_exec, q=True, v=True):
                cmds.cmdScrollFieldExecuter(self.script_field, e=True, executeAll=True)
        else:
            cmds.cmdScrollFieldExecuter(self.script_field, e=True, t='')
    
    # UI作成
    def create_window(self, *args):
        cmds.window(title=u'ChatGPTがPythonスクリプトを書くよ!', width=600, sizeable=True)
        
        cmds.columnLayout(adj=True, cat=['both',5], rs=5)
        self.reset_button = cmds.button(label='Reset', c=self.reset_session) #セッションのリセット
        self.input_field = cmds.scrollField(h=50, ed=True, ww=True, tx='', ec=self.call) #テンキーのEnterで送信
        self.script_exec = cmds.checkBox(label=u'実行もする', align='left', v=True) #Checkが入っていたら返答と同時にスクリプトを実行
        cmds.separator(h=10, st='in')
        self.ai_comment = cmds.scrollField(h=100, ed=False, ww=True, tx='') #コードブロック以外の部分を表示する場所
        cmds.text(l='Script:', align='left')
        self.script_field = cmds.cmdScrollFieldExecuter(st='python', h=200) #Pythonコードを表示・実行する場所
        cmds.setParent('..')

        cmds.showWindow()
        
        
        
        
ChatGPT_Maya_ins=ChatGPT_Maya()

エラーが返ってくる

openai : error_code=None error_message=’You exceeded your current quota, please check your plan and billing details.’ error_param=None error_type=insufficient_quota message=’OpenAI API error received’ stream_error=False

エラー: RateLimitError: file C:\Program Files\Autodesk\Maya2023\Python\lib\site-packages\openai\api_requestor.py line 679: You exceeded your current quota, please check your plan and billing details.

このエラーは

https://platform.openai.com/account/billing/overview

で Payment methods と Billing preferencesPrimary business addressを設定したら動くようになった。

お世話になった記事

https://qiita.com/akasaki1211/items/34d0f89e0ae2c6efaf48

https://okumuralab.org/~okumura/python/openai_api.html

https://knowledge.autodesk.com/ja/support/maya/learn-explore/caas/CloudHelp/cloudhelp/2023/JPN/Maya-Scripting/files/GUID-72A245EC-CDB4-46AB-BEE0-4BBBF9791627-htm.html

https://qiita.com/sakasegawa/items/db2cff79bd14faf2c8e0

https://tomo-web.jp/chat-gpt-you-exceeded-your-current-quota/#index_id0

pythonの標準モジュールで windowsにクリップボードにコピーするには?

pyperclipを使わないで、pythonの標準モジュールで windowsにクリップボードにコピーするには?

import subprocess 
s="abcdefg"
print("s=__"+s+"__")
subprocess.run("clip", text=True, input=s)

参考

Python script to copy text to clipboard [duplicate]

https://stackoverflow.com/questions/11063458/python-script-to-copy-text-to-clipboard

[clip / pbcopy・pbpaste]クリップボードにコピー

https://xtech.nikkei.com/it/atcl/column/15/042000103/080400036/

[UnrealEngine] Export TGA by Python

import unreal

import codecs
import os
def my_makedirs(path):
    if not os.path.isdir(path):
        os.makedirs(path)

my_makedirs(SaveDir)
print("SaveDir= "+SaveDir)
print("FileName= "+FileName)
savePath=SaveDir+"/"+FileName+".tga"
print("savePath= "+savePath)



editorAssetLib = unreal.EditorAssetLibrary()
texture2d=editorAssetLib.load_asset(assetPath) 
#canvasRenderTarget2D=editorAssetLib.load_asset(assetPath) 
#texture2d=unreal.Texture2D(canvasRenderTarget2D)
print("texture2d= "+str(texture2d))

task = unreal.AssetExportTask()
task.set_editor_property('automated', True)
task.set_editor_property('filename', savePath)
task.set_editor_property('object', texture2d)
task.set_editor_property('prompt', False)
task.set_editor_property('exporter', unreal.TextureExporterTGA())

check = unreal.Exporter.run_asset_export_task(task)


if check==True:
    pass
else:
    print(u"tga イメージの生成に失敗しました")

[maya]「スムーズ スキン サーフェスから未使用のインフルエンスを除去する」だけで、すごくいろんな動作が軽くなる。

Maya の処理スピードを向上させスキン ウェイト ペイント ツール(Paint Skin Weights Tool)を使いやすくするために、全てのスキン ウェイトが 0 であるスムーズ スキンから、ジョイントとインフルエンス オブジェクトを接続解除することができます。これにより未使用のインフルエンスがスキンのインフルエンス(Influence)リストに表示されなくなり、スキン ウェイト ペイント ツール(Paint Skin Weights Tool)が使いやすくなります。

たとえば、既定のオプションを使って、人間のスケルトンにストッキングをはいた左足をスムーズ スキニングするとします。スケルトン内のすべてのジョイントがポテンシャル インフルエンスとしてストッキングに接続されますが、ウェイトが 0 以外の値になるのは左足のストッキングに最も近いジョイントだけです。

肘を選択して回転させると、Maya はストッキングのスキンを計算して 0 以外の値をもつウェイトがないかどうかを調べます。ストッキングを未使用のインフルエンスから接続解除すると、肘とウェイトが 0 のその他のジョイントの回転を処理する必要がなくなり、パフォーマンスが向上します。

未使用のインフルエンスを除去するには

  1. スキンを選択します。
  2. スキン > 影響を編集 > 使われていないインフルエンスの除去(Skin > Edit Influences > Remove Unused Influences)を選択します。

[maya] pymel.tools.mel2py の python3版 変換できない問題がある20220926現在

簡単な解決策はmaya2022 以前の2020,2019バージョンを起動して そこでmel2pyを使うことだ。

A simple solution is to launch a maya2020 or maya2019 version prior to maya2022 and use mel2py there.

githubで解決していると思いダウンロードして試したが無理だった

https://github.com/LumaPictures/pymel/blob/master/pymel/tools/mel2py/melparse.py

import pymel_master.tools.mel2py as mel2py
mel_command = 'setDrivenKeyframe "-currentDriver pCube1.translateY pCube2.translateX";setDrivenKeyframe "-currentDriver pCube1.translateY pCube2.translateY";setDrivenKeyframe "-currentDriver pCube1.translateY pCube2.translateZ";'
pythonCode = mel2py.mel2pyStr(mel_command, pymelNamespace='pm')
print(pythonCode)



import pymel_master.tools.mel2py as mel2py
mel_command = '$currentNodes = `ls -sl -l`;'
pythonCode = mel2py.mel2pyStr(mel_command, pymelNamespace='pm')
print(pythonCode)

python3 Error: AttributeError: file C:\Program Files\Autodesk\Maya2023\Python\lib\site-packages\pymel\tools\mel2py\melparse.py line 438: ‘str’ object has no attribute ‘lineno

StackOverFlowのように

https://stackoverflow.com/questions/73656852/maya-2023-pymel-i-cant-access-mel2py-with-python3/73839888#73839888

上記エラーがでて無理だった。

1行ならどうにかなるが、関数などの複数行だとこのlinenoが取得できないようでバグるものしか作れなかった

        # cycle through our kwargs and format them
        for flag, value in kwargs.items():
            print("melparse.py  - format_command() - "+" flag= "+str(flag)+" ,value= "+str(value))
            if value is None:
                value = '1'

            # multi-use flag
            #    mel:     ls -type "transform" -type "camera"
            #    python:    ls( type=["transform", "camera"] )
            if isinstance(value, list):
                #sep = ', '
                # if len(value) > t.lexer.format_options['kwargs_newline_threshhold']:
                #    sep = ',\n\t'
                #pargs.append( '%s=[%s]' % ( flag, sep.join(value) )  )
                value = assemble(t, 'multiuse_flag', ', ', value, matchFormatting=True)
                #pargs.append(Token('%s=[%s]' % (flag, value), None, flag.lineno))
                pargs.append(Token('%s=[%s]' % (flag, value), None, 0))#mycode
            else:
                print("melparse.py  - format_command() -  flag= "+str(flag))
                #pargs.append(Token('%s=%s' % (flag, value), None, flag.lineno))
                pargs.append(Token('%s=%s' % (flag, value), None, 0))#mycode

本体の更新に期待する。

[maya] python prefixHierarchy remove suffix

Python

    def delete_suffixHierarchy_remove_suffix(self,suffix):
        print("-----------prefixHierarchy_remove_suffix-----------start")
        #suffix="fat:"
        # Get the prefix the user entered
        #
        # Get a list of all descendents (The nodes are ordered from
        # leaf to root
        #    
        currentNodes=pm.mel.eval("listRelatives -pa -ad `ls -sl -l`")
        print("currentNodes 1= "+str(currentNodes))
        # add the prefix to each descendent node
        #
        if len(currentNodes)>0:
            for i in range(0,len(currentNodes)):
                #pm.mel.prefixNode(prefix, currentNodes[i])
                newName=currentNodes[i].replace(suffix,"")
                print("newName= "+newName)
                cmds.rename(currentNodes[i], newName)

            
        currentNodes=cmds.ls(l=1, sl=1)
        print("currentNodes 2= "+str(currentNodes))
        # get a list of nodes on the list
        # add the prefix to each node on the active list
        #
        if len(currentNodes)>0:
            for i in range(0,len(currentNodes)):
                #pm.mel.prefixNode(prefix, currentNodes[i])
                newName=currentNodes[i].replace(suffix,"")
                print("newName= "+newName)
                cmds.rename(currentNodes[i], newName)
        print("-----------prefixHierarchy_remove_suffix-----------end")

mel

global proc prefixHierarchy( )
{
	string $ok		= (uiRes("m_prefixHierarchy.kOK"));
	string $cancel	= (uiRes("m_prefixHierarchy.kCancel"));
	string $result = `promptDialog
		-title (uiRes("m_prefixHierarchy.kPrefixHierarchy")) 
		-message (uiRes("m_prefixHierarchy.kEnterPrefix")) 
		-text "prefix_"
		-button $ok  
		-button $cancel 
		-defaultButton $ok  
		-cancelButton $cancel 
		-dismissString $cancel `;

	// If the result was "OK", then proceed
	//
	if ( $result == $ok ) {

		// Get the prefix the user entered
		//
		string $prefix = `promptDialog -q`;

		// Get a list of all descendents (The nodes are ordered from
		// leaf to root
		//	
		string $currentNodes[] = eval("listRelatives -pa -ad `ls -sl -l`");
	
		// add the prefix to each descendent node
		//
		if ( size( $currentNodes ) > 0 ) {
			for( $i=0; $i < size( $currentNodes ); $i++ ) {
				prefixNode( $prefix, $currentNodes[$i] );
			}
		}

		// get a list of nodes on the list
		$currentNodes = `ls -sl -l`;
	
		// add the prefix to each node on the active list
		//
		if ( size( $currentNodes ) > 0 ) {
			for( $i=0; $i < size( $currentNodes ); $i++ ) {
				prefixNode( $prefix, $currentNodes[$i] );
			}
		}
	}
}

[UnrealEngine] UE Python で TGAを書き出す方法

task = unreal.AssetExportTask()
task.set_editor_property('automated', True)
task.set_editor_property('filename', outfilepath)
task.set_editor_property('object', texture2d)
task.set_editor_property('prompt', False)
task.set_editor_property('exporter', unreal.TextureExporterTGA())

check = unreal.Exporter.run_asset_export_task(task)
      
if check==True:
    pass
else:
    alert(u"tga イメージの生成に失敗しました")

参考

https://zhuanlan.zhihu.com/p/240826602

[maya] scriptJob は実行中には中断できません。のエラーを回避する方法

scriptJob でMayaの状態を監視することができるが、その監視を終わらせる際にエラーが出る
日本語のエラー:scriptJob は実行中には中断できません。 #
英語のエラー:scriptJob cannot be killed while it is running. #

実行中のそれを回避するために、evalDeferredでコマンドを囲って遅延させて実行させる。

import maya.cmds as cmds

class ScriptJobber():
    def starter(self):
    
        #scriptJoblistEvents=cmds.scriptJob(listEvents=True)
        #print(scriptJoblistEvents)
    
        #for EventName in scriptJoblistEvents:
        #    print(EventName)
        
        
        
        #self.jobNum = cmds.scriptJob(event=("idle", 'Class_ins.idleTimeFunc()'))
        self.jobNum = cmds.scriptJob(event=("SceneOpened", 'Class_ins.SceneOpenedTimeFunc()'))
        #self.jobNum = cmds.scriptJob(event=("SceneOpened", 'Class_ins.SceneOpenedTimeFunc()'),runOnce = True, killWithScene = True)
        print("self.jobNum= "+str(self.jobNum))
    
    
    def SceneOpenedTimeFunc(self):
        print("SceneOpenedTimeFunc")
        print("SceneOpenedTimeFunc self.jobNum= "+str(self.jobNum))
        #cmds.scriptJob( kill=Class_ins.jobNum, force=True)
        cmds.evalDeferred("cmds.scriptJob( kill=Class_ins.jobNum, force=True)")
    
    def idleTimeFunc(self):
        print("idleTimeFunc")
        print("idleTimeFunc self.jobNum= "+str(self.jobNum))
        #cmds.scriptJob( kill=self.jobNum, force=True)
        cmds.evalDeferred("cmds.scriptJob( kill=Class_ins.jobNum, force=True)")
        
Class_ins=ScriptJobber()
Class_ins.starter()


"""
linearUnitChanged
timeUnitChanged
angularUnitChanged
Undo
undoSupressed
Redo
customEvaluatorChanged
serialExecutorFallback
timeChanged
currentContainerChange
quitApplication
idleHigh
idle
idleVeryLow
RecentCommandChanged
ToolChanged
PostToolChanged
ToolDirtyChanged
ToolSettingsChanged
tabletModeChanged
DisplayRGBColorChanged
animLayerRebuild
animLayerRefresh
animLayerAnimationChanged
animLayerLockChanged
animLayerBaseLockChanged
animLayerGhostChanged
cteEventKeyingTargetForClipChanged
cteEventKeyingTargetForLayerChanged
cteEventKeyingTargetForInvalidChanged
teClipAdded
teClipModified
teClipRemoved
teCompositionAdded
teCompositionRemoved
teCompositionActiveChanged
teCompositionNameChanged
teMuteChanged
cameraChange
cameraDisplayAttributesChange
SelectionChanged
PreSelectionChangedTriggered
LiveListChanged
ActiveViewChanged
SelectModeChanged
SelectTypeChanged
SelectPreferenceChanged
DisplayPreferenceChanged
DagObjectCreated
transformLockChange
renderLayerManagerChange
renderLayerChange
displayLayerManagerChange
displayLayerAdded
displayLayerDeleted
displayLayerVisibilityChanged
displayLayerChange
renderPassChange
renderPassSetChange
renderPassSetMembershipChange
passContributionMapChange
DisplayColorChanged
lightLinkingChanged
lightLinkingChangedNonSG
UvTileProxyDirtyChangeTrigger
preferredRendererChanged
polyTopoSymmetryValidChanged
SceneSegmentChanged
PostSceneSegmentChanged
SequencerActiveShotChanged
ColorIndexChanged
deleteAll
NameChanged
symmetricModellingOptionsChanged
softSelectOptionsChanged
SetModified
xformConstraintOptionsChanged
metadataVisualStatusChanged
undoXformCmd
redoXformCmd
freezeOptionsChanged
linearToleranceChanged
angularToleranceChanged
nurbsToPolygonsPrefsChanged
nurbsCurveRebuildPrefsChanged
constructionHistoryChanged
threadCountChanged
SceneSaved
NewSceneOpened
SceneOpened
SceneImported
PreFileNewOrOpened
PreFileNew
PreFileOpened
PostSceneRead
workspaceChanged
nurbsToSubdivPrefsChanged
PolyUVSetChanged
PolyUVSetDeleted
selectionConstraintsChanged
startColorPerVertexTool
stopColorPerVertexTool
start3dPaintTool
stop3dPaintTool
DragRelease
ModelPanelSetFocus
modelEditorChanged
MenuModeChanged
gridDisplayChanged
interactionStyleChanged
axisAtOriginChanged
CurveRGBColorChanged
SelectPriorityChanged
snapModeChanged
activeHandleChanged
ChannelBoxLabelSelected
texWindowEditorImageBaseColorChanged
texWindowEditorCheckerDensityChanged
texWindowEditorCheckerDisplayChanged
texWindowEditorDisplaySolidMapChanged
texWindowEditorShowup
texWindowEditorClose
colorMgtOCIORulesEnabledChanged
colorMgtUserPrefsChanged
RenderSetupSelectionChanged
colorMgtEnabledChanged
colorMgtConfigFileEnableChanged
colorMgtConfigFilePathChanged
colorMgtConfigChanged
colorMgtWorkingSpaceChanged
colorMgtPrefsViewTransformChanged
colorMgtPrefsReloaded
colorMgtOutputChanged
colorMgtPlayblastOutputChanged
colorMgtRefreshed
selectionPipelineChanged
playbackRangeChanged
playbackSpeedChanged
playbackModeChanged
playbackRangeSliderChanged
glFrameTrigger
currentSoundNodeChanged
graphEditorChanged
graphEditorParamCurveSelected
graphEditorOutlinerHighlightChanged
graphEditorOutlinerListChanged
EditModeChanged
RenderViewCameraChanged
texScaleContextOptionsChanged
texRotateContextOptionsChanged
texMoveContextOptionsChanged
polyCutUVSteadyStrokeChanged
polyCutUVEventTexEditorCheckerDisplayChanged
polyCutUVShowTextureBordersChanged
polyCutUVShowUVShellColoringChanged
shapeEditorTreeviewSelectionChanged
poseEditorTreeviewSelectionChanged
sculptMeshCacheBlendShapeListChanged
sculptMeshCacheCloneSourceChanged
RebuildUIValues
cteEventClipEditModeChanged
teEditorPrefsChanged

"""

URL
https://forums.cgsociety.org/t/scriptjob-problems/942387

[Maya] PysideでMayaにDockingするWindowの作り方

MayaにDockingするWindowを作ってみた。


# -*- coding: utf-8 -*-
import os
from functools import partial
import time
import imp
import random

"""
PySide2モジュールを探し、ある場合はそちらをインポートします。
"""
try:
    imp.find_module('PySide2')
    from PySide2.QtWidgets import *
    from PySide2.QtGui import *
    from PySide2.QtCore import *

except ImportError:
    from PySide.QtGui import *
    from PySide.QtCore import *


LOGO_IMAGE = r"画像のパスをここに入れてください。"


def get_maya_pointer():
    """
    Mayaのメインウィンドウを取得する関数
    """
    try:
        import maya.cmds as cmds
        from maya import OpenMayaUI

    except ImportError:
        return None

    """
    実は2017ではshibokenも2になっているので、あればshiboken2をインポートします。
    """
    try:
        imp.find_module("shiboken2")
        import shiboken2
        return shiboken2.wrapInstance(long(OpenMayaUI.MQtUtil.mainWindow()), QWidget)

    except ImportError:
        import shiboken
        return shiboken.wrapInstance(long(OpenMayaUI.MQtUtil.mainWindow()), QWidget)

from maya.app.general.mayaMixin import MayaQWidgetBaseMixin, MayaQWidgetDockableMixin

#class Example_connectAttr(MayaQWidgetDockableMixin, QScrollArea):
class Example_connectAttr(MayaQWidgetDockableMixin, QMainWindow):
    def __init__(self, node=None, *args, **kwargs):
        super(Example_connectAttr, self).__init__(*args, **kwargs)
        # Member Variables
        self.nodeName = node               # Node name for the UI
        self.attrUI = None                 # Container widget for the attr UI widgets
        self.attrWidgets = {}              # Dict key=attrName, value=widget
        
        randomInt=random.randint(0,9999)
        self.setObjectName("MyDock_Window"+str(randomInt))
        self.setWindowTitle("MyDock Window")
        self._initUI()
        
        
    def _initUI(self):
        wrapper = QWidget()
        self.setCentralWidget(wrapper)

        mainLayout = QVBoxLayout()
        wrapper.setLayout(mainLayout)
        

def start():
    maya_win = get_maya_pointer()
    ui = Example_connectAttr(node = maya_win)
    ui.show(dockable=True, floating=True)
    return ui

print("__name__ = "+__name__)
if __name__ == '__main__' or __name__ == "NppMaya" or __name__ == "main":

    app = QApplication.instance()
    if app is None:
        app = QApplication([])
    ui = start()
    app.exec_()