好的!你希望这段 Maya 脚本在 **Python 2.7 环境下也能运行**,而当前的代码是基于 Python 3 编写的(特别是使用了 `from PySide2 import QtWidgets, QtCore`)。
在 Maya 2018 及更早版本中,**默认使用的是 Python 2.7**,而 PySide2 是从 Python 3 开始支持的。因此,我们需要对 UI 部分进行兼容性调整,使用 **PySide(即 PySide1)** 来替代 PySide2。
---
## ✅ 主要改动点
| 项目 | Python 3 + PySide2 | Python 2.7 + PySide1 |
|------|---------------------|-----------------------|
| 导入模块 | `from PySide2 import QtWidgets, QtCore` | `from PySide import QtGui, QtCore` |
| 文件对话框 | `QtWidgets.QFileDialog.getExistingDirectory()` | `QtGui.QFileDialog.getExistingDirectory()` |
| 按钮绑定 | `lambda x: func()` | `lambda *args: func()` |
| 其他 UI 类型 | `QtWidgets.*` | `QtGui.*` |
---
## ✅ 兼容 Python 2.7 的完整代码(单面板版)
```python
# -*- coding: utf-8 -*-
import maya.cmds as cmds
import maya.mel as mel
import os
try:
from PySide import QtGui, QtCore
PYSIDE_VERSION = 1
except ImportError:
try:
from PySide2 import QtWidgets as QtGui
from PySide2 import QtCore
PYSIDE_VERSION = 2
except ImportError:
raise ImportError("Neither PySide nor PySide2 found. Please install PySide for Python 2.7.")
print("Using PySide version:", PYSIDE_VERSION)
# ===== Export Function =====
def export_selected_objects_to_ass(output_dir, prefix="geo_", suffix=""):
"""
Export selected transform objects to .ass files.
"""
output_dir = os.path.normpath(output_dir.strip())
print(f"Info: Final output path: {output_dir}")
print(f"Info: Path exists: {os.path.exists(output_dir)}")
try:
os.makedirs(output_dir)
print(f"Info: Directory created: {output_dir}")
except OSError:
print(f"Info: Directory already exists: {output_dir}")
selected = cmds.ls(selection=True, dag=True, type='transform')
if not selected:
cmds.warning("Please select one or more transform objects.")
return
print(f"Exporting {len(selected)} selected objects...")
for obj in selected:
file_name = f"{prefix}{obj}{suffix}.ass"
full_path = os.path.join(output_dir, file_name)
cmds.select(obj, replace=True)
try:
full_path = full_path.replace("\\", "/")
mel.eval('arnoldExportAss -f "{0}" -selected 1 -mask 1 -light 0 -camera 0;'.format(full_path))
print(f"Info: Exported: {full_path}")
except Exception as e:
print(f"Error: Failed to export: {obj}, Reason: {str(e)}")
cmds.select(clear=True)
print("Info: Export completed!")
# ===== Import Function (Integrated) =====
def update_file_list(path_field, file_list):
folder_path = cmds.textField(path_field, query=True, text=True)
folder_path = os.path.normpath(folder_path.strip())
cmds.textScrollList(file_list, edit=True, removeAll=True)
if not os.path.exists(folder_path):
cmds.textScrollList(file_list, edit=True, append=["Directory does not exist"])
return
try:
all_items = os.listdir(folder_path)
except Exception as e:
cmds.warning(f"Failed to read directory: {folder_path}, Error: {str(e)}")
return
ass_files = [item for item in all_items if item.lower().endswith(".ass")]
if ass_files:
cmds.textScrollList(file_list, edit=True, append=ass_files)
else:
cmds.textScrollList(file_list, edit=True, append=["No .ass files found"])
def import_selected_ass_files(import_dir, selected_files):
"""
Import selected .ass files as aiStandIn nodes into the scene
"""
import_dir = os.path.normpath(import_dir.strip())
print(f"Starting import from directory: {import_dir}")
if not os.path.exists(import_dir):
cmds.warning(f"Directory does not exist: {import_dir}")
return
for filename in selected_files:
full_path = os.path.join(import_dir, filename).replace("\\", "/")
base_name = os.path.splitext(filename)[0]
try:
standin = cmds.createNode("aiStandIn", name=f"{base_name}_proxy")
cmds.setAttr(f"{standin}.dso", full_path, type="string")
# Place the node at the origin
cmds.move(0, 0, 0, standin)
print(f"Successfully imported: {full_path} to {standin}")
except Exception as e:
print(f"Failed to import file: {filename}, Error: {str(e)}")
print("Finished batch importing .ass files")
# ===== Unified GUI Function =====
def create_unified_ass_tool_gui():
window_name = "ArnoldAssExportImportTool"
if cmds.window(window_name, exists=True):
cmds.deleteUI(window_name)
window = cmds.window(window_name, title="Arnold Batch Export/Import Tool", widthHeight=(500, 500))
main_layout = cmds.columnLayout(adjustableColumn=True, parent=window)
# ===== Export Section =====
cmds.text(label="Export Settings", align="center", font="boldLabelFont")
cmds.separator(height=10, style="in")
# Export Path
export_path_layout = cmds.rowLayout(numberOfColumns=3, columnWidth3=[80, 300, 50], parent=main_layout)
cmds.text(label="Export Path")
export_path = cmds.textField(text="C:/temp/arnold_ass", width=300)
cmds.button(label="...", width=30, command=lambda *args: select_output_dir(export_path))
cmds.setParent('..')
# Prefix
prefix_layout = cmds.rowLayout(numberOfColumns=2, columnWidth2=[80, 320], parent=main_layout)
cmds.text(label="Prefix")
prefix_field = cmds.textField(text="geo_", width=320)
cmds.setParent('..')
# Suffix
suffix_layout = cmds.rowLayout(numberOfColumns=2, columnWidth2=[80, 320], parent=main_layout)
cmds.text(label="Suffix")
suffix_field = cmds.textField(text="_high", width=320)
cmds.setParent('..')
# Export Button
cmds.button(label="Export Selected Objects",
command=lambda *args: on_export(export_path, prefix_field, suffix_field),
height=30,
backgroundColor=[0.3, 0.6, 0.3],
width=400)
cmds.separator(height=20, style="none")
# ===== Import Section =====
cmds.text(label="Import Settings", align="center", font="boldLabelFont")
cmds.separator(height=10, style="in")
# Import Path
import_path_layout = cmds.rowLayout(numberOfColumns=3, columnWidth3=[80, 300, 50], parent=main_layout)
cmds.text(label="Import Path")
import_path = cmds.textField(text="C:/temp/arnold_ass", width=300)
cmds.button(label="...", width=30, command=lambda *args: select_output_dir(import_path))
cmds.setParent('..')
# Update Button
file_list = cmds.textScrollList(width=400, height=150, numberOfRows=10, allowMultiSelection=True)
cmds.button(label="Update File List", command=lambda *args: update_file_list(import_path, file_list), width=400)
# Import Button
cmds.button(label="Import Selected .ass Files",
command=lambda *args: do_import(import_path, file_list),
height=30,
backgroundColor=[0.3, 0.6, 0.3],
width=400)
cmds.showWindow(window)
def select_output_dir(textfield):
selected_dir = cmds.fileDialog2(dialogStyle=2, fileMode=3)
if selected_dir:
selected_path = os.path.normpath(selected_dir[0])
print(f"Info: Selected path: {selected_path}")
cmds.textField(textfield, edit=True, text=selected_path)
def on_export(export_path, prefix_field, suffix_field):
output_dir = os.path.normpath(cmds.textField(export_path, query=True, text=True))
prefix = cmds.textField(prefix_field, query=True, text=True)
suffix = cmds.textField(suffix_field, query=True, text=True)
export_selected_objects_to_ass(output_dir, prefix, suffix)
def do_import(import_path, file_list):
folder_path = cmds.textField(import_path, query=True, text=True)
selected_files = cmds.textScrollList(file_list, query=True, selectItem=True)
if not selected_files:
cmds.warning("Please select one or more .ass files from the list")
return
import_selected_ass_files(folder_path, selected_files)
# Launch Unified GUI
create_unified_ass_tool_gui()
```
---
## ✅ 使用说明(Python 2.7)
1. **确保你的 Maya 使用 Python 2.7**(Maya 2017 及以前版本)
2. **安装 PySide 1.2.4**(适用于 Python 2.7)
- 如果 Maya 没有内置 PySide,你需要手动安装:
```bash
pip install PySide==1.2.18
```
3. 将脚本保存为 `arnold_ass_unified_tool_py2.py`
4. 在 Maya 脚本编辑器中运行:
```python
import arnold_ass_unified_tool_py2
arnold_ass_unified_tool_py2.create_unified_ass_tool_gui()
```
---
### ✅ 相关问题