Created
April 18, 2017 06:57
-
-
Save ryusas/626483f760c95b90622219d550c4985b to your computer and use it in GitHub Desktop.
Revisions
-
ryusas created this gist
Apr 18, 2017 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,70 @@ # coding: utf-8 u""" Maya 2017 workspaceControl の問題回避のサンプル。 workspaceControl と workspaceControlState のゴミが残らないようにする。 retain=False の場合でも何故か state のゴミが残ってしまうが、 scriptJob で workspaceControl の削除を監視して state も同時に削除するようにする。 retain=True の場合は、UI が閉じたとしても state は残って良いはずなので監視はしない。 いずれにせよ、スタートアップの UI 再生時にエラーとなった場合は (そのツールをアンインストールしたり、何らかの問題が発生している場合)、 UI が閉じられた(削除ではない)時に workspaceControl と state がともに削除されるようにする。 exec を通しているのは、何故かそうするとグローバルスコープが汚れないため。 """ import maya.cmds as cmds def create(name, code, **kwargs): u""" workspaceControl を生成する。 オプション引数の retain は False に、 loadImmediately は True に デフォルトが置き換えられている。 :param `str` name: 生成する workspaceControl の名前。 :param `str` code: 中身のUIの生成コード。 """ ret = kwargs.pop('retain', kwargs.pop('ret', False)) # デフォルト変更: True -> False li = kwargs.pop('loadImmediately', kwargs.pop('li', True)) # デフォルト変更: False -> True code = _CODE_TEMPLATE % (name, ret, code) return cmds.workspaceControl(name, ui=code, retain=ret, loadImmediately=li, **kwargs) _CODE_TEMPLATE = """ exec(''' import maya.cmds as cmds name = '%s' retain = %r def deleteWSCtl(*a): if cmds.workspaceControl(name, ex=True): cmds.deleteUI(name) if cmds.workspaceControlState(name, ex=True): cmds.workspaceControlState(name, remove=True) try: if not retain: cmds.scriptJob(uid=(name, deleteWSCtl)) %s except: from traceback import print_exc print_exc() def cleanup(): if cmds.workspaceControl(name, q=True, vis=True): cmds.workspaceControl(name, e=True, vcc=deleteWSCtl) else: deleteWSCtl() cmds.evalDeferred(cleanup) ''') """ This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,211 @@ # coding: utf-8 u""" workspaceControl.py による問題回避のテスト。 実行方法: import ws_test ws_test.TestWindow() """ import re from weakref import WeakValueDictionary import maya.cmds as cmds import maya.mel as mel from workspaceControl import create as _wctl_create MAYA_VERSION = float(re.search(r'.+/(\d+(?:\.\d+)?)', cmds.internalVar(upd=True)).group(1)) #: Mayaのバージョン float 値。 if MAYA_VERSION >= 2017.: _CREATING_WCTL = WeakValueDictionary() _INSTANCE_DICT = WeakValueDictionary() #------------------------------------------------------------------------------ class DockableWindow(object): u""" ドッキング可能ウィンドウのラッパークラス。 2017 以降の workspaceControl 向けの実装しかしていないが、 発展させれば 2017 以降とそれ以外との差の吸収も可能。 """ UI_NAME = '' #: ユニークなUI名。 WINDOW_TITLE = 'window' #: ウィンドウタイトル。 WINDOW_WH = (500, 500) #: デフォルトのウィンドウサイズ。 DOCK_AREA = 'right' #: ドッキングエリアの指定。 WCTL_OPTS = None #: workspaceControl のドッキングに関するオプションを指定する辞書。DOCK_AREA 簡易指定に優先する。 def __init__(self, root='', **kwargs): self.__name = root.split('|')[-1] if not root: root = self._createUI(**kwargs) self.__ui_root = root _INSTANCE_DICT[root] = self #------------------------ trackDestruction(self) #------------------------ def __repr__(self): return "<%s '%s'>" % (type(self).__name__, self.name()) def __str__(self): return self.__ui_root def root(self): return self.__ui_root def name(self): if not self.__name: name = self.UI_NAME if not name: raise NotImplementedError('UI_NAME') while cmds.control(name, ex=True): name = _incrementName(name) self.__name = name return self.__name if MAYA_VERSION >= 2017.: try: MAIN_WORKAREA = mel.eval('$gWorkAreaForm=$gWorkAreaForm') #: メインの workspacePanel 。 except RuntimeError: MAIN_WORKAREA = None try: MAIN_PANE = mel.eval('$gViewportWorkspaceControl=$gViewportWorkspaceControl') #: ビューポートの workspaceControl 。 except RuntimeError: MAIN_PANE = None CHAN_LAYER_EDITOR = mel.eval('getUIComponentDockControl("Channel Box / Layer Editor", false)') #: チャンネルボックスの workspaceControl 。 OUTLINER = mel.eval('getUIComponentDockControl("Outliner", false)') #: アウトライナーの workspaceControl 。 SHELF = mel.eval('getUIComponentToolBar("Shelf", false)') #: シェルフの workspaceControl 。 TIME_SLIDER = mel.eval('getUIComponentToolBar("Time Slider", false)') #: タイムスライダーの workspaceControl 。 def _createUI(self, **kwargs): # workspaceControl コマンドのオプション引数を決定。 wctl_opts = self.WCTL_OPTS if wctl_opts is None: dock_area = self.DOCK_AREA if dock_area: if dock_area == 'right': if self.CHAN_LAYER_EDITOR: wctl_opts = {'tabToControl': (self.CHAN_LAYER_EDITOR, -1)} elif dock_area == 'left': if self.OUTLINER: wctl_opts = {'dockToControl': (self.OUTLINER, 'left')} if wctl_opts is None: if self.MAIN_PANE: wctl_opts = {'dockToControl': (self.MAIN_PANE, dock_area)} elif self.MAIN_WORKAREA: wctl_opts = {'dockToPanel': (self.MAIN_WORKAREA, dock_area, True)} else: wctl_opts = {'dockToMainWindow': (dock_area, True)} else: wctl_opts = {} name = self.name() _CREATING_WCTL[name] = self cls = type(self) code = 'import %s; %s.%s._createWCContents(%s)' % ( cls.__module__, cls.__module__, cls.__name__, ', '.join([('%s=%r' % kv) for kv in kwargs.items()]), ) return _wctl_create(name, code, l=self.WINDOW_TITLE, iw=self.WINDOW_WH[0], ih=self.WINDOW_WH[1], **wctl_opts) @classmethod def _createWCContents(cls, **kwargs): u""" workspaceControl 内の UI を作成する。workspaceControl に uiScript として登録するもの。 """ # カレント親が workspaceControl 。 root = cmds.setParent(q=True) # _createUI から呼び出されたなら、__init__ 途中であるが、一時的な辞書からインスタンスを取得できる。 self = _CREATING_WCTL.pop(root, None) if self: # インスタンスが得られたなら、最初の生成中なので raise する。 cmds.evalDeferred(lambda: cmds.workspaceControl(root, e=True, r=True)) else: # インスタンスが得られないなら、Maya に再生成されているものなので、ここでインスタンスを生成する。 self = cls(root=root) # UI が削除されるまでインスタンスが解放されないようにする。 cmds.workspaceControl(root, e=True, vcc=self.onVisibilityChanged) self.createContents(**kwargs) else: # 2016 以前では dockControl コマンド等を使えば、バージョン違いを吸収できる。 raise NotImplementedError('using dockControl for MAYA_VERSION < 2017') def onVisibilityChanged(self, *args): pass def createContents(self, **kwargs): raise NotImplementedError('createContents') def delete(self): cmds.deleteUI(str(self)) @classmethod def instances(cls): return [x for x in _INSTANCE_DICT.values() if isinstance(x, cls)] @classmethod def deleteAll(cls): for x in cls.instances(): x.delete() def _incrementName(name): m = _RE_TAIL_NUMBER.search(name) if m: i = m.group(0) return name[:-len(i)] + str(int(i) + 1) return name + '1' _RE_TAIL_NUMBER = re.compile(r'\d+$') #------------------------------------------------------------------------------ from weakref import ref as _wref import traceback def trackDestruction(obj): s = repr(obj) def func(): print('# DESTRUCT: ' + s) print('# BEGIN_TRACK: ' + s) return _registerFinalizer(obj, func) def _registerFinalizer(obj, func): r = _wref(obj, _finalizer) k = id(r) _finalize_refs[k] = (r, func) return k _finalize_refs = {} def _finalizer(r): if _finalize_refs: func = _finalize_refs.pop(id(r))[1] try: func() except Exception: traceback.print_exc() #------------------------------------------------------------------------------ class TestWindow(DockableWindow): u""" テストアプリケーション。 """ UI_NAME = 'Test' WINDOW_TITLE = 'Test' def createContents(self, **kwargs): cmds.columnLayout() cmds.button() cmds.button()