2
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?

リファレンスエディタをカスタムした話

2
Last updated at Posted at 2025-12-23

mayaで大量のリファレンスを扱うことってありませんか?

アニメーターとして働いていると多々そんな場面があります。大手の会社ならバージョンアップ、ダウンのランチャーがありとても便利なのですが、中規模だとそこまで整っていなかったりするので自分でポチポチ変換する羽目になります。

が、ついに大量にウインドウ呼び出してパス張り替えての繰り返しに飽きたので自分用に作ったものをここで公開したいと思います。

import maya.cmds as cmds
import os

# --------------------------------------------------
# Utility functions
# --------------------------------------------------

def _normalize(p):
	return p.replace("\\", "/")

def _resolve_ref_node(node):
	"""Return reference node name from a selected node, or None if not applicable."""
	if cmds.nodeType(node) == "reference":
		if node.startswith("sharedReferenceNode"):
			return None
		return node
	try:
		if cmds.referenceQuery(node, isNodeReferenced=True):
			return cmds.referenceQuery(node, referenceNode=True)
	except Exception:
		pass
	return None

def _list_reference_nodes():
	"""Return a list of valid reference nodes in the scene."""
	out = []
	for ref in cmds.ls(type="reference") or []:
		if ref.startswith("sharedReferenceNode"):
			continue
		try:
			_ = cmds.referenceQuery(ref, filename=True)
			out.append(ref)
		except Exception:
			continue
	return sorted(set(out))

# --------------------------------------------------
# Replace References
# --------------------------------------------------

def change_selected_reference_paths(new_file_path):
	"""Replace all selected references with the same file (full path)."""
	new_file_path = _normalize(new_file_path)
	sel = cmds.ls(sl=True) or []
	if not sel:
		cmds.warning("Please select reference nodes or nodes inside references.")
		return
	if not os.path.isfile(new_file_path):
		cmds.warning("File does not exist: {}".format(new_file_path))
		return

	ref_nodes = set()
	for n in sel:
		ref = _resolve_ref_node(n)
		if ref:
			ref_nodes.add(ref)

	if not ref_nodes:
		cmds.warning("No reference nodes were found from the selection.")
		return

	for ref_node in sorted(ref_nodes):
		try:
			old_path = cmds.referenceQuery(ref_node, filename=True)
		except Exception:
			old_path = "<unknown>"
		try:
			cmds.file(new_file_path, loadReference=ref_node)
			print("[Batch Replace] {}: {} → {}".format(ref_node, old_path, new_file_path))
		except Exception as e:
			cmds.warning("{} failed to reload: {}".format(ref_node, e))

def change_reference_path(ref_node, new_path):
	"""Replace a single reference with a new full path."""
	new_path = _normalize(new_path)
	if not os.path.isfile(new_path):
		cmds.warning("File does not exist: {}".format(new_path))
		return
	try:
		cmds.file(new_path, loadReference=ref_node)
		print("[Individual Replace] {} → {}".format(ref_node, new_path))
	except Exception as e:
		cmds.warning("{} failed to reload: {}".format(ref_node, e))

# --------------------------------------------------
# Delete References
# --------------------------------------------------

def delete_selected_references():
	"""Delete all references found from the current selection."""
	sel = cmds.ls(sl=True) or []
	if not sel:
		cmds.warning("Please select reference nodes or nodes inside references.")
		return

	ref_nodes = set()
	for n in sel:
		ref = _resolve_ref_node(n)
		if ref:
			ref_nodes.add(ref)

	if not ref_nodes:
		cmds.warning("No reference nodes were found from the selection.")
		return

	for ref_node in sorted(ref_nodes):
		try:
			cmds.file(removeReference=True, referenceNode=ref_node)
			print("[Deleted] Reference node: {}".format(ref_node))
		except Exception as e:
			cmds.warning("Failed to delete {}: {}".format(ref_node, e))

# --------------------------------------------------
# UI
# --------------------------------------------------

WIN_ID = "referenceToolsUI"
INDIV_COL = "referenceToolsUI_individualCol"

def _rebuild_individual_list():
	"""Rebuild the list of references for individual replacement."""
	if not cmds.layout(INDIV_COL, exists=True):
		return
	for child in cmds.layout(INDIV_COL, q=True, ca=True) or []:
		cmds.deleteUI(child)

	for ref in _list_reference_nodes():
		try:
			path = cmds.referenceQuery(ref, filename=True)
		except Exception:
			path = ""
		row = cmds.rowLayout(numberOfColumns=3, columnWidth3=[220, 420, 80],
							 adjustableColumn=2, parent=INDIV_COL)
		cmds.text(label=ref, align="left")
		field = cmds.textField(text=path, parent=row)
		cmds.button(label="Replace",
					parent=row,
					c=lambda _, r=ref, f=field: change_reference_path(
						r, cmds.textField(f, q=True, text=True)))

def show_reference_tools_ui():
	"""Show the Reference Tools UI (Replace & Delete)."""
	if cmds.window(WIN_ID, exists=True):
		cmds.deleteUI(WIN_ID)

	cmds.window(WIN_ID, title="Reference Tools", widthHeight=(760, 600))
	cmds.scrollLayout(horizontalScrollBarThickness=16, verticalScrollBarThickness=16)
	cmds.columnLayout(adjustableColumn=True, rowSpacing=12)

	# === Batch Replace ===
	cmds.frameLayout(label="Batch Replace (replace all selected references with the same file)",
					 collapsable=True, marginHeight=6)
	cmds.columnLayout(adjustableColumn=True, rowSpacing=6)
	cmds.text(label="Enter the new file full path:")
	bulk_path_field = cmds.textField(text="")
	cmds.button(label="Batch Replace Selected References", bgc=(0.3, 0.6, 0.3),
				c=lambda *args: change_selected_reference_paths(
					cmds.textField(bulk_path_field, q=True, text=True)))
	cmds.setParent(".."); cmds.setParent("..")

	# === Individual Replace ===
	cmds.frameLayout(label="Individual Replace (set file path per reference)",
					 collapsable=True, marginHeight=6)
	cmds.columnLayout(adjustableColumn=True, rowSpacing=6)
	cmds.rowLayout(numberOfColumns=2, columnWidth2=[200, 200])
	cmds.button(label="Refresh List", c=lambda *_: _rebuild_individual_list())
	cmds.setParent("..")
	cmds.columnLayout(INDIV_COL, adjustableColumn=True, rowSpacing=4)
	cmds.setParent(".."); cmds.setParent("..")

	# === Delete References ===
	cmds.frameLayout(label="Delete References",
					 collapsable=True, marginHeight=6)
	cmds.columnLayout(adjustableColumn=True, rowSpacing=6)
	cmds.text(label="Deletes all references found in the current selection.")
	cmds.button(label="Delete Selected References", bgc=(0.8, 0.3, 0.3),
				c=lambda *args: delete_selected_references())
	cmds.setParent(".."); cmds.setParent("..")

	cmds.showWindow(WIN_ID)
	_rebuild_individual_list()

上のスクリプトをscriptフォルダに入れて

import reference_tools; reference_tools.show_reference_tools_ui()

で実行すればUIが立ち上がります

CleanShot 2025-11-14 at 18.25.00.png

このUIは3つのセクションから構成されています。

section A
アウトライナーで選択したリファレンスを一括リプレイスする。
テキストボックスに置き換えたいファイルのパスを挿入して、batch replace selected referenceのボタンを押しせば、全て入れ替えます。

section B
シーン内のリファレンスを個別でリプレイスする。
Refresh List のボタンを押すとシーン内のリファレンスをリストします。
テキストボックには現在使用しているリファレンスへのリンクが出ているので、入れ替えたいファイルへのリンクを入れ、Replaceボタンで個別に入れ替えることができます。

section C
アウトライナーで選択しているリファレンスを一括消去する。
シーンから削除したいリファレンスを選択し、Delete Selected Referencesボタンを押すと、一括消去されます。

以上になります! Merry X'mas !!!

2
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
2
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?