遍历嵌套的 GIMP 组以检索图层数组

Iterate through nested GIMP groups to retrieve an array of layers

我是 python 和编写 GIMP 插件的新手。我正在尝试编写一个简单的插件来对图层名称执行 search/replace。我已经设法让它与 'flat' gimp 图像一起工作,但无法弄清楚如何让它通过组迭代工作。这是 'flat' 结构的代码:

import math
import string
import re
from gimpfu import *
from array import array

def python_search_replace_layer_names(image, layer, searchterm, replaceterm, applyvisible, casesensative) :
    pdb.gimp_image_undo_group_start(image)
    pdb.gimp_context_push() 

    applylayers = []
    for layer in image.layers[::-1]:
        if (applyvisible == 0) or (layer.visible != 0): #user didn't check Only apply visible or if layer is visible.
            applylayers.append([layer,counter])

    for applylayer in applylayers:
        if casesensative == 0:
            searchtermRegEx = re.compile(re.escape(searchterm),re.IGNORECASE)
        else:
            searchtermRegEx = re.compile(re.escape(searchterm))     
        layername = searchtermRegEx.sub(replaceterm, str(applylayer[0].name))
        applylayer[0].name = layername  

    pdb.gimp_context_pop()
    pdb.gimp_image_undo_group_end(image)
    pdb.gimp_displays_flush()

register(
    "python_fu_search_replace_layer_names",
    "Search/Replace in Layer Names",
    "Search/Replace in Layer Names...",
    "stigzler",
    "stigzler",
    "2020",
    "<Image>/Edit/Search and Replace/Replace Layer Names...",
    "RGB*, GRAY*", 
    [
    (PF_STRING, "searchterm", "Search for Term:", "SearchTerm"),
    (PF_STRING, "replaceterm", "Replace With:", "ReplaceWith"),
    (PF_TOGGLE, "applyvisible",   "Only Apply to visible layers:", 0),
    (PF_TOGGLE, "casesensative",   "Case sensative:", 0)
    ],
    [],
    python_search_replace_layer_names)

main()

我猜这将涉及一些迭代的、自引用的函数,但我掌握了三点知识:

  1. 相关插件结构操作
  2. 不知道 python 的 syntax/structure(通常是 .net,虽然我确定有很多相似之处)
  3. 不知道 gimp api 来访问组结构。

希望有人能帮忙!


更新:

感谢xenoid的回答,解决了这个问题。如果你在这里寻找图层重命名插件,你可以找到完成的here

其实很简单。我写的一些代码来转储图像的图层层次结构:

def dump(parent,level,dumpList):
    prefix='     '*level
    for pos,layerOrGroup in enumerate(parent.layers):
        dumpList.append(prefix+layerOrGroup.name)
        if isinstance(layerOrGroup,gimp.GroupLayer):
            dump(layerOrGroup,level+1,dumpList)

def dumpLayerHierarchy(image):
    dumpList=[]
    dump(image,0,dumpList)
    gimp.message('\n'.join(dumpList))

要知道的两件事:

  • 层组是 gimp.GroupLayer 的实例(常规层是 gimp.Layer
  • duck-typing 最好,gimp.Imagegimp.GroupLayerlayers 属性中都有它们的 children,所以你甚至不需要special-case 从图像本身开始的第一级。

PS: 更好的注册:

register(
    "search-replace-layer-names",
    "Search/Replace in Layer Names",
    "Search/Replace in Layer Names...",
    "stigzler",
    "stigzler",
    "2020",
    "Replace Layer Names...",
    "*", 
    [
    (PF_IMAGE,"image", "Image:", None),
    (PF_DRAWABLE, "drawable", "Drawable:", None),
    (PF_STRING, "searchterm", "Search for Term:", "SearchTerm"),
    (PF_STRING, "replaceterm", "Replace With:", "ReplaceWith"),
    (PF_TOGGLE, "applyvisible",   "Only Apply to visible layers:", 0),
    (PF_TOGGLE, "casesensative",   "Case sensative:", 0)
    ],
    [],
    python_search_replace_layer_names
    menu="<Image>/Edit/Search and Replace/"
)

变化:

  • 外观:使 "atom" 更标准。 "python-fu" 无论如何由 Gimp 添加[​​=66=]
  • 将菜单声明更改为 non-deprecated 类型,具有单独的菜单项和菜单位置
  • 使图像类型 "*" 支持所有图像类型(您的代码不适用于索引图像)
  • 使 Image 和 Drawable 参数显式化。这有时很有用。由于您可能不使用 layer/drawable,因此您可以将其从参数列表(以及您的函数原型)中删除。

其他:

  • 你不需要上下文 push/pop 因为你不改变上下文
  • 您不需要复制图像层 (image.layers[::-1]),甚至不需要反转它们,因为您只是更新属性(如果您要删除层,那就另当别论了)
  • Undo-group 很好,但是您可以将您的代码放在 try/except 中,以便在出现问题时撤消组正确关闭并且您仍然可以轻松撤消部分更改:
import traceback # To still get source code location

def the_plugin(image,layer):
    image.undo_group_start()
    try:
       # Your code goes here
    except Exception as e:
        trace(e.args[0])
        gimp.message(e.args[0]+':'+traceback.format_exc())
    image.undo_group_end()

您的代码,已修复:

import re,traceback
from gimpfu import *

def python_search_replace_layer_names(image,searchterm, replaceterm, applyvisible, casesensitive) :

    image.undo_group_start()

    try:
        searchtermRegEx = re.compile(re.escape(searchterm),0 if casesensitive else re.IGNORECASE)
        renameLayers(image,searchtermRegEx,replaceterm,applyvisible)  

    except Exception as e:
        gimp.message(e.args[0]+':'+traceback.format_exc())  

    image.undo_group_end()

def renameLayers(parent,searchtermRegEx,replaceterm,applyvisible):

    for pos,layerOrGroup in enumerate(parent.layers):
        if not applyvisible or layerOrGroup.visible:
            layerName = searchtermRegEx.sub(replaceterm, layerOrGroup.name)
            layerOrGroup.name = layerName

        if isinstance(layerOrGroup,gimp.GroupLayer):
            renameLayers(layerOrGroup,searchtermRegEx,replaceterm,applyvisible)

register(
    "search-replace-layer-names",
    "Search/Replace in Layer Names",
    "Search/Replace in Layer Names...",
    "stigzler",
    "stigzler",
    "2020",
    "Replace Layer Names...",
    "*", 
    [
        (PF_IMAGE,"image", "Image:", None),
        (PF_STRING, "searchterm", "Search for Term:", "SearchTerm"),
        (PF_STRING, "replaceterm", "Replace With:", "ReplaceWith"),
        (PF_TOGGLE, "applyvisible",   "Only Apply to visible layers:", 0),
        (PF_TOGGLE, "casesensitive",   "Case sensative:", 0)
    ],
    [],
    python_search_replace_layer_names,
    menu="<Image>/Edit/Search and Replace/")

main()

修复:

  • 在注册python_search_replace_layer_names 末尾添加缺少的逗号
  • 已添加 traceback 导入
  • 删除了对 trace 函数的调用(仅在我自己的代码中)
  • 删除了许多不必要的导入
  • 删除了插件函数的 layer 参数
  • 移除了递归函数的level参数
  • 一些代码使更多pythonic

此外,组中的图层可以不可见,因为它本身不可见(layer.visible 为假)或者因为组不可见。您当前的代码不考虑 group-induced 隐形。

最后,可以找到调试 Gimp python 脚本(重点放在 Windows 上)的一些提示 here