4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

MayaAdvent Calendar 2020

Day 18

existsメソッドってなんなんですか?

Last updated at Posted at 2020-12-17

本記事はMaya Advent Calendar 2020 18日目の記事です。
今年も(?)完走したいので今年遭遇した現象を書きます。
※完全にデバッグしきれている記事ではありません。推測止まりになっていることをご了承ください。

事の発端

pymelで何かを処理する前に、処理対象があるかどうかを確認するときにこんな書き方をすることはありませんか?

if node.exists():
    # なにかしらの処理

まさにこんな書き方でノードが存在するかどうかを確認したときに事件は起きました。

maya2017 up5の場合

  • maya2017 up5
  • PyMEL Version 1.0.9
import pymel.core as pm

hoge = pm.createNode('transform', n='hoge')
print hoge.exists()
# Result: True

作ったノードが存在するので結果はTrueが帰ってきますよね。今度は作ったノードを削除してから確認してみましょう。

import pymel.core as pm

hoge = pm.createNode('transform', n='hoge')
pm.delete(hoge)
print hoge.exists()
# Result: False

うんうん、それはそう。

maya2019 up3の場合

問題は同じコードをmaya2019のup3で実行したときです。

  • maya2019 up3
  • PyMEL Version 1.0.10
import pymel.core as pm

hoge = pm.createNode('transform', n='hoge')
pm.delete(hoge)
print hoge.exists()
# Result: True


True ってどういうことだ???

存在しないはずのノードに対してexists()メソッドがTrueを返してきていることが判明しました。
今回はこれに関してちょっと調べてみようと思います。

とりあえず解決したい

ノードの存在を確認して、あったら処理する。みたいな記述はよくやると思います。
この書き方をしていたスクリプトがmaya2019 up3で正常に動作しないのは仕事的にもヤバいので、とりあえずの解決方法を探しました。

import pymel.core as pm

hoge = pm.createNode('transform', n='hoge')
pm.delete(hoge)
print hoge.exists()
# Result: True

print pm.objExists(hoge)
# Result: False

なるほど、pm.objExists() を使えばとりあえず正しい状態を検出できそうです。

exists()メソッドの挙動を探ってみる

exists()メソッドが何を判断してブーリアンを返すかを pymel/general.py から順に探っていきます。

general.py
def exists(self, **kwargs):
    "objExists"
    try:
        # use __apimobject__, not __apiobject__, because that's the one
        # that calls _api.isValidMObjectHandle (ie, we don't want to get
        # an MDagPath, which won't do that validation)
        if self.__apimobject__():
            return True
    except MayaObjectError:
        pass
    return False

self.__apimobject__()があるかどうかで判断しているようです。では__apimobject__()は何をもって判断しているのか?

general.py
import pymel.api as _api

def __apimobject__(self):
    "Return the MObject for this attribute, if it is valid"
    try:
        handle = self.__apiobjects__['MObjectHandle']
    except:
        handle = _api.MObjectHandle(self.__apimplug__().attribute())
        self.__apiobjects__['MObjectHandle'] = handle
    if _api.isValidMObjectHandle(handle):
        return handle.object()

    raise MayaAttributeError

MObjectHandleを取得して_api.isValidMObjectHandle(handle)の結果で判断しているみたいですね。これも見てみましょう。
pymel/api/allapi.py

allapi.py
# fast convenience tests on API objects
def isValidMObjectHandle(obj):
    if isinstance(obj, MObjectHandle):
        return obj.isValid() and obj.isAlive()
    else:
        return False

MObjectHndleクラスから作られたインスタンス(オブジェクト)であることをチェックしているようです。
2017, 2019の両方の環境でこのapiを実行するとともに同じFalseが帰ってきました。

検証コード

exists()メソッドの挙動から以下のコードのどこかでmaya2017, maya2019に差がでるはず、と推測しました。
では実行してみましょう。

import pymel.api as _api
import pymel.core as pm

hoge = pm.createNode('transform', n='hoge')
pm.delete(hoge)

handle = hoge.__apiobjects__['MObjectHandle']

print handle.isValidMObjectHandle()

maya2017では False が表示されます。
maya2019ではhandle = hoge.__apiobjects__['MObjectHandle']の部分でhandleが取得できませんでした。
このことからmaya2017ではgeneral.pyのtryが成功し、maya2019では失敗しているのでexceptが実行されているっぽいですね。

general.py
import pymel.api as _api

def __apimobject__(self):
    "Return the MObject for this attribute, if it is valid"
    try:
        handle = self.__apiobjects__['MObjectHandle'] # maya2017はこっちのhandle?
    except:
        handle = _api.MObjectHandle(self.__apimplug__().attribute()) # maya2019はこっちのhandle?
        self.__apiobjects__['MObjectHandle'] = handle
    if _api.isValidMObjectHandle(handle):
        return handle.object()

    raise MayaAttributeError

exceptでhandle = _api.MObjectHandle(self.__apimplug__().attribute())が実行されています。
このhandleがMObjectHndleインスタンス(オブジェクト)なので、isValidMObjectHandle(obj)が、exists()メソッドで True になると推測できます。

もう少し調べたいこと

__apimobject__()_api.MObjectHandle(self.__apimplug__().attribute())あたりをもう少し詳しく調べたかったのですが、アドベントカレンダーのタイムリミットがきてしまいました。。。
時間ができたらステップ実行などでもう少し詳しくデバッグして追記します!:pray:

4
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?