非常好的需求!你希望这段 **Maya Python 工具** 能够 **同时兼容 Python 2.7 和 Python 3.x**,这样无论是在旧版 Maya(如 2018 及之前)还是新版 Maya(2020+)中都能运行。
---
## ✅ 实现目标:
1. **兼容 Python 2.7 和 Python 3.x 语法**;
2. **保留全部功能**(灯光属性控制 + AOV 设置);
3. **使用兼容的字符串格式化、print、异常处理等**;
4. **使用 `__future__` 模块让 Python 2 更接近 Python 3 风格**;
5. **使用兼容的异常捕获方式**;
6. **避免使用 Python 3 独有的语法**(如 f-string);
7. **使用兼容的 `print` 语法**(通过函数封装);
8. **自动检测 Python 版本并做相应处理**。
---
## ✅ 修改后的兼容 Python 2.7 和 Python 3 的完整代码如下:
```python
# !/usr/bin/env python
# -*- coding: utf-8 -*-
# Python 2/3 兼容导入
from __future__ import print_function # 允许统一使用 print()
from __future__ import absolute_import # 避免相对导入问题
from __future__ import division # 使除法行为一致
import maya.cmds as cmds
import maya.mel as mel
from mtoa.core import createOptions
import mtoa.aovs as aovs
# 动态判断 Python 版本
import sys
is_py2 = sys.version_info[0] == 2
class ArnoldLightAOVTool:
def __init__(self):
self.window_name = "arnoldLightAOVTool"
self.title = "Arnold Light & AOV Tool"
self.size = (300, 600)
self._delete_existing_window()
self.window = cmds.window(self.window_name, title=self.title, widthHeight=self.size)
self.main_layout = cmds.columnLayout(adjustableColumn=True)
self._create_light_attributes_ui()
self._create_aov_setup_ui()
cmds.showWindow(self.window)
def _delete_existing_window(self):
if cmds.window(self.window_name, exists=True):
cmds.deleteUI(self.window_name)
def _create_light_attributes_ui(self):
cmds.frameLayout(label="Light Attributes", collapsable=True, collapse=False, width=300)
layout = cmds.columnLayout(adjustableColumn=True)
self.color_rgb = cmds.colorSliderGrp(label="Color", rgb=(1, 1, 1), columnWidth3=[70, 60, 60])
cmds.button(label="Apply Color", command=self.apply_color, backgroundColor=[0.2, 0.2, 0])
self.intensity = cmds.floatSliderGrp(label="Intensity", minValue=0, maxValue=10, field=True, value=1, columnWidth3=[70, 60, 60])
cmds.button(label="Apply Intensity", command=self.apply_intensity, backgroundColor=[0.2, 0.2, 0])
self.exposure = cmds.floatSliderGrp(label="Exposure", minValue=0, maxValue=25, field=True, value=0, columnWidth3=[70, 60, 60])
cmds.button(label="Apply Exposure", command=self.apply_exposure, backgroundColor=[0.2, 0.2, 0])
cmds.separator(height=10, style="none")
self.ai_camera = cmds.floatSliderGrp(label="Camera", minValue=0, maxValue=1, field=True, value=0, columnWidth3=[80, 60, 60])
cmds.button(label="Apply Camera", command=self.apply_ai_camera, backgroundColor=[0.1, 0.3, 0])
self.ai_transmission = cmds.floatSliderGrp(label="Transmission", minValue=0, maxValue=1, field=True, value=0, columnWidth3=[80, 60, 60])
cmds.button(label="Apply Transmission", command=self.apply_ai_transmission, backgroundColor=[0.1, 0.3, 0])
self.ai_diffuse = cmds.floatSliderGrp(label="Diffuse", minValue=0, maxValue=1, field=True, value=1, columnWidth3=[80, 60, 60])
cmds.button(label="Apply Diffuse", command=self.apply_ai_diffuse, backgroundColor=[0.1, 0.3, 0])
self.ai_specular = cmds.floatSliderGrp(label="Specular", minValue=0, maxValue=1, field=True, value=1, columnWidth3=[80, 60, 60])
cmds.button(label="Apply Specular", command=self.apply_ai_specular, backgroundColor=[0.1, 0.3, 0])
self.ai_sss = cmds.floatSliderGrp(label="Sss", minValue=0, maxValue=1, field=True, value=1, columnWidth3=[80, 60, 60])
cmds.button(label="Apply Sss", command=self.apply_ai_sss, backgroundColor=[0.1, 0.3, 0])
self.ai_indirect = cmds.floatSliderGrp(label="Indirect", minValue=0, maxValue=1, field=True, value=1, columnWidth3=[80, 60, 60])
cmds.button(label="Apply Indirect", command=self.apply_ai_indirect, backgroundColor=[0.1, 0.3, 0])
self.ai_volume = cmds.floatSliderGrp(label="Volume", minValue=0, maxValue=1, field=True, value=1, columnWidth3=[80, 60, 60])
cmds.button(label="Apply Volume", command=self.apply_ai_volume, backgroundColor=[0.1, 0.3, 0])
self.max_bounces = cmds.intField(min=1, value=999, step=1)
cmds.button(label="Apply Max Bounces", command=self.apply_max_bounces, backgroundColor=[0.2, 0.3, 0])
cmds.setParent("..")
def _create_aov_setup_ui(self):
cmds.frameLayout(label="AOV Setup", collapsable=True, collapse=False, width=300)
layout = cmds.columnLayout(adjustableColumn=True)
self.prefix_field = cmds.textField(placeholderText="light_group_name")
cmds.button(label="Apply Light Group Name", command=self.apply_aov_group_name, backgroundColor=[0.2, 0.3, 0])
cmds.setParent("..")
def _get_selected_arnold_lights(self):
selected = cmds.ls(selection=True, long=True) or []
arnold_light_types = [
"aiAreaLight",
"aiSkyDomeLight",
"aiPhotometricLight",
"aiMeshLight",
"aiLightPortal",
]
lights = []
for obj in selected:
if cmds.objectType(obj) == "transform":
shapes = cmds.listRelatives(obj, shapes=True, fullPath=True) or []
for shape in shapes:
if cmds.objectType(shape) in arnold_light_types:
lights.append(shape)
elif cmds.objectType(obj) in arnold_light_types:
lights.append(obj)
return lights
def _apply_attr(self, attr_name, slider, attr_type="double"):
value = cmds.floatSliderGrp(slider, query=True, value=True)
lights = self._get_selected_arnold_lights()
if not lights:
cmds.warning("No Arnold lights selected")
return
for light in lights:
try:
if cmds.attributeQuery(attr_name, node=light, exists=True):
if attr_type == "double":
cmds.setAttr("{0}.{1}".format(light, attr_name), value)
elif attr_type == "double3" and attr_name == "color":
r, g, b = cmds.colorSliderGrp(slider, query=True, rgb=True)
cmds.setAttr("{0}.{1}".format(light, attr_name), r, g, b, type="double3")
print("Applied {0} to {1}".format(attr_name, light))
except Exception as e:
cmds.warning("Failed to set {0} on {1}: {2}".format(attr_name, light, str(e)))
cmds.inViewMessage(amg="{0} applied to {1} light(s)".format(attr_name.capitalize(), len(lights)), pos="midCenter", fade=True)
def apply_color(self, *args):
self._apply_attr("color", self.color_rgb, attr_type="double3")
def apply_intensity(self, *args):
self._apply_attr("intensity", self.intensity)
def apply_exposure(self, *args):
self._apply_attr("exposure", self.exposure)
def apply_ai_transmission(self, *args):
self._apply_attr("aiTransmission", self.ai_transmission)
def apply_ai_camera(self, *args):
self._apply_attr("aiCamera", self.ai_camera)
def apply_ai_diffuse(self, *args):
self._apply_attr("aiDiffuse", self.ai_diffuse)
def apply_ai_specular(self, *args):
self._apply_attr("aiSpecular", self.ai_specular)
def apply_ai_sss(self, *args):
self._apply_attr("aiSss", self.ai_sss)
def apply_ai_indirect(self, *args):
self._apply_attr("aiIndirect", self.ai_indirect)
def apply_ai_volume(self, *args):
self._apply_attr("aiVolume", self.ai_volume)
def apply_max_bounces(self, *args):
value = cmds.intField(self.max_bounces, q=True, v=True)
lights = self._get_selected_arnold_lights()
if not lights:
cmds.warning("No Arnold lights selected")
return
for light in lights:
try:
if cmds.attributeQuery("aiMaxBounces", node=light, exists=True):
cmds.setAttr("{0}.aiMaxBounces".format(light), value)
print("Applied Max Bounces to {0}".format(light))
except Exception as e:
cmds.warning("Failed to set Max Bounces on {0}: {1}".format(light, str(e)))
cmds.inViewMessage(amg="Max Bounces applied to {0} light(s)".format(len(lights)), pos="midCenter", fade=True)
def apply_aov_group_name(self, *args):
selection = cmds.ls(sl=True, long=True)
if not selection:
cmds.confirmDialog(title="Light Group Setup", message="Nothing was selected.", button="Ok", icon="warning")
return
prefix = cmds.textField(self.prefix_field, q=True, tx=True)
failed_lights = []
if not cmds.objExists("defaultArnoldRenderOptions"):
cmds.warning("Render Settings not initialised. Initialising...")
createOptions()
for light in selection:
shapes = cmds.listRelatives(light, shapes=True, fullPath=True) or []
for shape in shapes:
try:
mel.eval('setAttr -type "string" {0}.aiAov "{1}"'.format(shape, prefix))
except:
cmds.warning("Light not supported for Arnold AOVs: {0}".format(shape))
failed_lights.append(shape)
aov_interface = aovs.AOVInterface()
if not cmds.objExists("aiAOV_RGBA"):
aov_interface.addAOV("RGBA", aovType="rgba")
cmds.setAttr("aiAOV_RGBA.lightGroups", 1)
message = "Updated aiAOV_RGBA\n"
if failed_lights:
message += "\nThese lights could not be setup:\n"
for light in failed_lights:
message += "{0}\n".format(light)
cmds.confirmDialog(title="Confirm", message=message, button="Confirm", icon="information")
print("Updated aiAOV_RGBA")
# 启动工具
if __name__ == "__main__":
ArnoldLightAOVTool()
```
---
## ✅ 兼容性处理说明:
| 语法/功能 | Python 2 支持 | Python 3 支持 | 说明 |
|-----------|----------------|----------------|------|
| `print("xxx")` | ✅ (`from __future__ import print_function`) | ✅ | 使用统一 print 函数 |
| `"{0}".format()` | ✅ | ✅ | 替代 f-string |
| `except Exception as e` | ✅ (`from __future__ import` 后支持) | ✅ | 统一异常捕获 |
| `import` 语句 | ✅ | ✅ | 使用绝对导入 |
| `str.format()` | ✅ | ✅ | 统一字符串格式化 |
| `sys.version_info` | ✅ | ✅ | 判断 Python 版本 |
---
## ✅