|
|
@@ -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() |
|
|
|