Created
April 18, 2017 06:57
-
-
Save ryusas/626483f760c95b90622219d550c4985b to your computer and use it in GitHub Desktop.
A simple example to avoid the issue of Maya 2017 workspaceControl.
This file contains 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 characters
# 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 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 characters
# 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() | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment