3

我正在尝试在 Maya 中编写一个用户界面,并且它与多层次的父母和没有缩进变得令人难以置信的混乱。基本代码(没有任何功能)目前大约 400 行,需要一段时间才能找到我需要的位。

例如,下面的代码没有注释:

#Earlier user interface

py.rowColumnLayout( numberOfColumns = 5 )
py.text( label="", width = 1 )
py.text( label="Column 1", enable = False, width = 250 )
py.text( label="", width = 1 )
py.text( label="Column 2" enable = False, width = 250 )
py.text( label="", width = 1 )

py.text( label="" )
py.rowColumnLayout( numberOfColumns = 4 )
py.text( label="   Input data:", align="left" )
py.text( label="" )
py.text( label="" )
py.text( label="" )
py.textField( text = "Text here" )
py.text( label="" )
py.text( label="" )
py.text( label="" )
py.setParent( ".." )

py.text( label="" )
py.rowColumnLayout( numberOfColumns = 4 )
py.rowColumnLayout( numberOfColumns = 5 )
py.radioButton( label = "Read file from path", width = 100 )
py.text( label="" )
py.button( label = "Browse" )
py.text( label="" )
py.button( label = "Validate" )
py.setParent( ".." )
py.text( label="" )
py.text( label="" )
py.text( label="" )
py.setParent( ".." )
py.setParent( ".." )

然而,这就是缩进的样子

py.rowColumnLayout( numberOfColumns = 5 )
    py.text( label="", width = 1 )
    py.text( label="Column 1", enable = False, width = 250 )
    py.text( label="", width = 1 )
    py.text( label="Column 2" enable = False, width = 250 )
    py.text( label="", width = 1 )

    py.text( label="" )
    py.rowColumnLayout( numberOfColumns = 4 )
        py.text( label="   Input data:", align="left" )
        py.text( label="" )
        py.text( label="" )
        py.text( label="" )
        py.textField( text = "Text here" )
        py.text( label="" )
        py.text( label="" )
        py.text( label="" )
        py.setParent( ".." )

    py.text( label="" )
    py.rowColumnLayout( numberOfColumns = 4 )
        py.rowColumnLayout( numberOfColumns = 5 )
            py.radioButton( label = "Read file from path", width = 100 )
            py.text( label="" )
            py.button( label = "Browse" )
            py.text( label="" )
            py.button( label = "Validate" )
            py.setParent( ".." )
        py.text( label="" )
        py.text( label="" )
        py.text( label="" )
        py.setParent( ".." )
    py.setParent( ".." )

有什么办法我可以用缩进写它,但让它在执行时忽略它们?我看到了一个问题,询问您是否可以在没有缩进的情况下编写 python,但我有点需要相反的情况。

注意:一些py.*函数的输出值也需要分配给变量,只是还没有分配,因为布局需要首先排序。

4

7 回答 7

7

这是像我们这样的技术艺术家在 Maya 中构建 UI 时每天都面临的一个极好的用例。

对于基于 PyMEL 的用户界面:

这内置于 PyMEL 中。您不必创建上下文管理器。布局命令本身就是上下文管理器。您只需with在每个布局命令调用之前添加一个关键字,如下所示:

# Do this when using PyMEL for your UI code
import pymel.core as pm

# ...

with pm.rowColumnLayout( numberOfColumns = 5 ):
    pm.text( label="", width = 1 )
    pm.text( label="Column 1", enable = False, width = 250 )
    pm.text( label="", width = 1 )
    pm.text( label="Column 2", enable = False, width = 250 )
    pm.text( label="", width = 1 )

    pm.text( label="" )

    with pm.rowColumnLayout( numberOfColumns = 4 ):
        pm.text( label="   Input data:", align="left" )
        pm.text( label="" )
        pm.text( label="" )
        pm.text( label="" )
        pm.textField( text = "Text here" )
        pm.text( label="" )
        pm.text( label="" )
        pm.text( label="" )        

    pm.text( label="" )
    with pm.rowColumnLayout( numberOfColumns = 4 ):
        with pm.rowColumnLayout( numberOfColumns = 5 ):
            pm.radioButton( label = "Read file from path", width = 100 )
            pm.text( label="" )
            pm.button( label = "Browse" )
            pm.text( label="" )
            pm.button( label = "Validate" )

        pm.text( label="" )
        pm.text( label="" )
        pm.text( label="" )

对于基于 maya.cmds 的 UI:

一种快速的解决方案是制作一个虚拟上下文管理器。你可以做这样的事情

# Do this when using Maya's cmds for your UI code
import maya.cmds as cmds

# ...

from contextlib import contextmanager
@contextmanager
def neat_indent():
    # OPTIONAL: This is also an opportunity to do something before the block of code runs!
    try:
        # During this is where your indented block will execute
        # Leave it empty
        yield
    finally:
        # OPTIONAL: This is where you can write code that executes AFTER your indented block executes.
        pass

这样您的代码就不必更改太多。只需with在每个预期缩进的开头添加带有关键字的上下文管理器功能!

cmds.rowColumnLayout( numberOfColumns = 5 )
with neat_indent():
    cmds.text( label="", width = 1 )
    cmds.text( label="Column 1", enable = False, width = 250 )
    cmds.text( label="", width = 1 )
    cmds.text( label="Column 2", enable = False, width = 250 )
    cmds.text( label="", width = 1 )

    cmds.text( label="" )

    cmds.rowColumnLayout( numberOfColumns = 4 )
    with neat_indent():
        cmds.text( label="   Input data:", align="left" )
        cmds.text( label="" )
        cmds.text( label="" )
        cmds.text( label="" )
        cmds.textField( text = "Text here" )
        cmds.text( label="" )
        cmds.text( label="" )
        cmds.text( label="" )
        cmds.setParent( ".." )

    cmds.text( label="" )
    cmds.rowColumnLayout( numberOfColumns = 4 )
    with neat_indent():
        cmds.rowColumnLayout( numberOfColumns = 5 )
        with neat_indent():
            cmds.radioButton( label = "Read file from path", width = 100 )
            cmds.text( label="" )
            cmds.button( label = "Browse" )
            cmds.text( label="" )
            cmds.button( label = "Validate" )
            cmds.setParent( ".." )
        cmds.text( label="" )
        cmds.text( label="" )
        cmds.text( label="" )
        cmds.setParent( ".." )
    cmds.setParent( ".." )

我们创建的上下文管理器neat_indent(),还让您有机会编写包装缩进块的代码。这里的一个实际例子是,在每个缩进的末尾,你发现自己在写py.setParent("..")。您可以将其放入finally上下文管理器的部分:

from contextlib import contextmanager
@contextmanager
def neat_indent(parent=None):
    # OPTIONAL: This is also an opportunity to do something before the block of code runs!
    try:
        # During this is where your indented block will execute
        # Leave it empty
        yield
    finally:
        # OPTIONAL: This is where you can write code that executes AFTER your indented block executes.
        if parent:
            cmds.setParent(parent)

您的代码现在将更有意义:

cmds.rowColumnLayout( numberOfColumns = 5 )
with neat_indent(".."):
    cmds.text( label="", width = 1 )
    cmds.text( label="Column 1", enable = False, width = 250 )
    cmds.text( label="", width = 1 )
    cmds.text( label="Column 2", enable = False, width = 250 )
    cmds.text( label="", width = 1 )

    cmds.text( label="" )

    cmds.rowColumnLayout( numberOfColumns = 4 )
    with neat_indent(".."):
        cmds.text( label="   Input data:", align="left" )
        cmds.text( label="" )
        cmds.text( label="" )
        cmds.text( label="" )
        cmds.textField( text = "Text here" )
        cmds.text( label="" )
        cmds.text( label="" )
        cmds.text( label="" )        

    cmds.text( label="" )
    cmds.rowColumnLayout( numberOfColumns = 4 )
    with neat_indent(".."):
        cmds.rowColumnLayout( numberOfColumns = 5 )
        with neat_indent(".."):
            cmds.radioButton( label = "Read file from path", width = 100 )
            cmds.text( label="" )
            cmds.button( label = "Browse" )
            cmds.text( label="" )
            cmds.button( label = "Validate" )

        cmds.text( label="" )
        cmds.text( label="" )
        cmds.text( label="" )

上下文管理器很强大。在这篇文章中,我使用了标准库模块中的contextmanager 装饰器。您可以在此处contextlib阅读有关此技术的信息。大概在这里with

此外,出于这个目的(目的之一),在 Maya 中进行 UI 开发更清洁、更 Pythonic,@theodox 创建了mGui模块。一探究竟。

于 2015-01-19T21:56:00.917 回答
1

@Kartik 的回答很好地涵盖了基础。我要指出的是,您可以通过允许上下文管理器内联声明布局(rowLayout、columnLayout 等)来进一步清理布局代码,这使得它更加容易:

class uiCtx(object):
   '''
   quickie layouthelper: automatically setParents after a layout is finished
   '''
   def __init__(self, uiClass, *args, **kwargs):
        self.Control = uiClass(*args, **kwargs)

    def __enter__(self):
        return self

    def __exit__(self, tb, val, traceback):
        cmds.setParent("..")

    def __repr__(self):
        return self.Control

遇到它时将调用 maya.cmds 布局函数,然后在缩进块的末尾关闭父级,因此您可以在此代码段中随时进行布局调用

    with uiCtx(cmds.rowLayout, **layout_options) as widget:
        self.Toggle = cmds.checkBox('', v = self.Module.enabled, cc = self._state_changed)
        with uiCtx(cmds.columnLayout, w=self.COLUMNS[1], cal='left') as details:
            cmds.text(l = self.ModuleKey, fn = "boldLabelFont")
            cmds.text(l = self.Module.path, fn = "smallObliqueLabelFont")
        cmds.button("edit",  c=self._edit)
        cmds.button("show", c=self._show)
    return widget

向 中添加__repr__可以uiCtx让您将其视为像普通 Maya 布局命令那样返回字符串,因此在该示例中可以以通常的方式查询“小部件”。

整个事情都在 GitHub 上,以便在上下文中查看它。正如 Kartik 还指出的那样,有一个更精细的声明性 UI 选项,形式为mGui,在实践中看起来像这样:

with gui.Window('window', title = 'fred') as example_window:
    with BindingContext() as bind_ctx:
        with VerticalForm('main') as main:
            Text(None, label = "The following items don't have vertex colors")
            lists.VerticalList('lister' ).Collection < bind() < bound  
            with HorizontalStretchForm('buttons'):
                Button('refresh', l='Refresh')
                Button('close', l='Close')

# show the window
example_window.show()

这里有更多与 Maya 布局相关的信息

于 2015-01-20T20:28:31.313 回答
1

您可以在执行代码行之前对其进行预处理,如图所示:

mycode = """\
    print "something"
        print "something else"
      print 42
    """

exec('\n'.join(line.lstrip() for line in mycode.splitlines()))

输出:

something
something else
42

它甚至可以做成“单线”:

exec('\n'.join(line.lstrip() for line in """\
    print "something"
        print "something else"
      print 42
    """.splitlines()))

您可以通过以下方式将代码保存在一个单独的文件中(这将使您的编辑器能够对其进行语法照明):

文件mycode.py

print "something"
    print "something else"
  var = 42

单独文件版本:

with open('mycode.py') as code:
    exec(''.join(line.lstrip() for line in code))
print 'var:', var

输出:

something
something else
var: 42

警告:我应该指出,这些都删除了每一行的所有缩进,这会弄乱if/else遇到的任何多行 Python 代码(如 )——这可能会限制它的有用性,具体取决于你在做什么。

于 2015-01-19T18:48:34.283 回答
0

创建另一个程序来解析和执行您的script.py

parse.py

text = open('script.py').readlines()
text = [i.strip() for i in text]

edit = open('new.py', 'w')
for line in text:
    edit.write(line + '\n')
edit.close()

execfile('new.py')

该文件创建一个new.py将被执行的修改。

于 2015-01-19T18:41:01.897 回答
0

我可以想到两种可能性,允许您直接编写代码而不必从文本中解析它(语法着色完整,没有临时文件)。

第一个是在每一行的开头加上一个不言而喻的前缀,就像if True:上面建议的那样,或者等效地,可能是这样的:

# ...

if 'level 1':  a = py.text( label="" )
if 'level 2':      b = py.rowColumnLayout( numberOfColumns = 4 )
if 'level 3':          c = py.rowColumnLayout( numberOfColumns = 5 )
if 'level 4':              d = py.radioButton( label = "Read file from path", width = 100 )

# ...

如果您不使用计算结果为False(like '') 的字符串,您可以将这些字符串用作注释,以帮助您记住您在任何给定行上构建的 GUI 的哪个部分(用适当数量的空格填充以确保事情当然,仍然排队)。

第二个想法是创建一个包含可调用对象及其参数的容器列表,按照您喜欢的方式格式化该列表,并在最后执行每个:

objects = {}
commands = [

    # ...

    dict( name = 'spam', func = py.text, label="" ),
    dict( name = 'ham', func = py.rowColumnLayout, numberOfColumns = 4 ),
        dict( name = 'eggs', func = py.rowColumnLayout, numberOfColumns = 5 ),
            dict( name = 'beans', func = py.radioButton, label = "Read file from path", width = 100 ),

    # ...
]

for d in commands:
    objects[ d.pop( 'name' ) ] = d.pop( 'func' )( **d )
于 2015-01-19T18:50:07.407 回答
0

使用声明性方法:

interface = [
  [py.rowColumnLayout, [], dict(numberOfColumns=5)],
     [py.text, [], dict(label="", width=1)],
     [py.text, [], dict(label="Column 1", enable=False, width=250)],
     ...
     [py.setParent, [".."], {}],
]    

for callable, args, kwargs in interface:
    callable(*args, **kwargs)

内部()[]缩进无关紧要,因此您可以随意组织线条。

于 2015-01-19T20:33:33.290 回答
0

我已经使用了很多年了,效果很好:

if True:
    # Indented code.
    print("This entire code block is indented.")
于 2021-08-18T10:30:31.843 回答