Maya/Python:在循环生成中创建唯一命令 UI
Maya/Python: Creating Unique Commands in Loop-Generated UI
在 Autodesk Maya 中,我根据用户想要建造的楼层数动态创建 UI(从 intField 获取变量 numFloors)。对于每一层,我想制作一个按钮来改变相机的高度以查看地板(这是针对建筑物)。我遇到的问题是我无法让我的 viewFloor() 函数知道按下了哪个按钮来激活它。每次,按钮都会经过最近的楼层而不是特定的楼层。
我知道为什么会这样,创建按钮时命令没有动态存储,所以当单击按钮时,命令使用变量的最新实例,它应该是最后一层,它是相等的到变量 numFloors。
for i in range(0, numFloors):
floor = i
btnName = 'floor'+str(floor)+'ViewBtn'
btnLabel = "view Floor " + str(floor)
btnCmdVar = str(floor)
cmds.button(btnName, label= btnLabel, w=20, h=20, command=lambda
arg:self.viewFloor(btnCmdVar))
def viewFloor(self, arg):
print "arg = " + str(arg)
因此,当 numFloors = 4 时,viewFloor 将始终传递给它 4,而不管按下哪个楼层按钮。就像我说的,我知道为什么会发生这种情况,这个代码片段只是我创建代码片段的最佳机会,可以传达我正在尝试做的事情。不幸的是,我不知道如何真正做到这一点。
我的一些想法是:
一些复杂的 setattr 和 getattr 设置(但我不知道它是如何工作的)
或检测按钮何时被按下并将该特定按钮的名称传递给 viewFloor 函数的 scriptJob(然后我可以从字符串中提取数字以获得发言权)。但是,我在 Maya Python 文档中找不到任何关于按下按钮时发生的事件或条件的指示。因此,我无法创建 scriptJob。
如果有人能给我指出正确的方向(例如我可能需要的 scriptJob 标志或 setattr getattr 设置的示例)或任何其他解决方案,我太没有受过教育,甚至无法构思它,我们将不胜感激。在此先感谢您的帮助!
这是 lambda 工作方式的一个众所周知的问题:已记录在案 here。
您可以通过多种方式解决此问题:
使用 functools.partial
而不是 lambda
functools.partial
是来自 functools 模块的对象,它将可调用对象和参数绑定在一起。您几乎可以像使用 lambda 一样使用它:
from functools import partial
for i in range(0, numFloors):
floor = i
btnName = 'floor'+str(floor)+'ViewBtn'
btnLabel = "view Floor " + str(floor)
cmd = partial(self.viewFloor, i)
cmds.button(btnName, label= btnLabel, w=20, h=20, command=cmd)
遵循函数
def make_button(n):
def callback (_):
self.showFloor(n)
return cmds.button( label = 'floor '+ str(n), command = callback)
for i in range(6):
make_button(i)
此处嵌套函数 def 将以与您希望 lambda 相同的方式捕获值——但嵌套可防止出现您所看到的问题
顺便说一下,在任一策略中 self.showFloor
都需要一个额外的参数来容纳按钮总是触发的额外布尔值。
OP 在这里,我想我会 post 我是如何解决这个问题的,以防它对其他人有帮助。
Partial 不是一个可行的选项,因为变量 cmd(如示例中所见)仍会随循环更新,因此每个按钮仍会返回最后一层。
在 Maya 中创建动态生成的 UI 的问题是它的 UI 命令是函数而不是 类,这意味着您实际上不能在命令中存储任何数据,只能传递它。幸运的是,有人先于我将 Maya 的整个 UI 命令重写为 类。您可以在此处找到该库:mGui
本库编写时的功能代码如下:
for i in range(0, numFloors):
floor = i
BTN= mGui.Button()
BTN.data.[`floor`] = floor
BTN.command = self.viewFloor
def viewFloor(self, defaultArg, **kw):
floor = str(kw[`floor`])
在 Autodesk Maya 中,我根据用户想要建造的楼层数动态创建 UI(从 intField 获取变量 numFloors)。对于每一层,我想制作一个按钮来改变相机的高度以查看地板(这是针对建筑物)。我遇到的问题是我无法让我的 viewFloor() 函数知道按下了哪个按钮来激活它。每次,按钮都会经过最近的楼层而不是特定的楼层。
我知道为什么会这样,创建按钮时命令没有动态存储,所以当单击按钮时,命令使用变量的最新实例,它应该是最后一层,它是相等的到变量 numFloors。
for i in range(0, numFloors):
floor = i
btnName = 'floor'+str(floor)+'ViewBtn'
btnLabel = "view Floor " + str(floor)
btnCmdVar = str(floor)
cmds.button(btnName, label= btnLabel, w=20, h=20, command=lambda
arg:self.viewFloor(btnCmdVar))
def viewFloor(self, arg):
print "arg = " + str(arg)
因此,当 numFloors = 4 时,viewFloor 将始终传递给它 4,而不管按下哪个楼层按钮。就像我说的,我知道为什么会发生这种情况,这个代码片段只是我创建代码片段的最佳机会,可以传达我正在尝试做的事情。不幸的是,我不知道如何真正做到这一点。
我的一些想法是:
一些复杂的 setattr 和 getattr 设置(但我不知道它是如何工作的)
或检测按钮何时被按下并将该特定按钮的名称传递给 viewFloor 函数的 scriptJob(然后我可以从字符串中提取数字以获得发言权)。但是,我在 Maya Python 文档中找不到任何关于按下按钮时发生的事件或条件的指示。因此,我无法创建 scriptJob。
如果有人能给我指出正确的方向(例如我可能需要的 scriptJob 标志或 setattr getattr 设置的示例)或任何其他解决方案,我太没有受过教育,甚至无法构思它,我们将不胜感激。在此先感谢您的帮助!
这是 lambda 工作方式的一个众所周知的问题:已记录在案 here。
您可以通过多种方式解决此问题:
使用 functools.partial
而不是 lambda
functools.partial
是来自 functools 模块的对象,它将可调用对象和参数绑定在一起。您几乎可以像使用 lambda 一样使用它:
from functools import partial
for i in range(0, numFloors):
floor = i
btnName = 'floor'+str(floor)+'ViewBtn'
btnLabel = "view Floor " + str(floor)
cmd = partial(self.viewFloor, i)
cmds.button(btnName, label= btnLabel, w=20, h=20, command=cmd)
遵循函数
def make_button(n):
def callback (_):
self.showFloor(n)
return cmds.button( label = 'floor '+ str(n), command = callback)
for i in range(6):
make_button(i)
此处嵌套函数 def 将以与您希望 lambda 相同的方式捕获值——但嵌套可防止出现您所看到的问题
顺便说一下,在任一策略中 self.showFloor
都需要一个额外的参数来容纳按钮总是触发的额外布尔值。
OP 在这里,我想我会 post 我是如何解决这个问题的,以防它对其他人有帮助。 Partial 不是一个可行的选项,因为变量 cmd(如示例中所见)仍会随循环更新,因此每个按钮仍会返回最后一层。 在 Maya 中创建动态生成的 UI 的问题是它的 UI 命令是函数而不是 类,这意味着您实际上不能在命令中存储任何数据,只能传递它。幸运的是,有人先于我将 Maya 的整个 UI 命令重写为 类。您可以在此处找到该库:mGui
本库编写时的功能代码如下:
for i in range(0, numFloors):
floor = i
BTN= mGui.Button()
BTN.data.[`floor`] = floor
BTN.command = self.viewFloor
def viewFloor(self, defaultArg, **kw):
floor = str(kw[`floor`])