0

我正在尝试添加一个程序以在插件弹出模式对话框。其目的是在插件控制流中的指定步骤查询响应(不仅仅是在其开始时获取参数)。

我尝试过使用 gtk - 我得到一个对话框,但它是异步的 - 插件继续执行。它需要作为同步函数运行。

我已经尝试注册一个插件,以便利用 gimpfu 启动对话。它本身就可以工作;它在查询时显示在程序数据库中。但我似乎永远无法从另一个插件中实际调用它——无论我尝试了多少排列,它要么是执行错误,要么是参数数量错误。

[所有这些废话背后的原因:我已经为 PaintShopPro 编写了很多扩展 Python 脚本。我已经编写了一个应用程序包(使用 App.Do、App.Constants、Environment 等,让我开始将这些脚本移植到 GIMP ——是的,它是不正当的,是的,有时代码只需要重写,但是为了我在 PSP.API 中实际使用的很多东西就足够了。

但是,调试和编写模块与 witch 押韵。所以。我正在尝试添加对 psp 的“SetExecutionMode”(即交互式)的仿真。如果设置,预期的行为是 App.Do() 方法将通过弹出一个简单的消息对话框在运行适用的 psp 仿真代码之后/之前“暂停”。]

4

1 回答 1

0

可以通过 gtk 的 Dialog 接口,特别是 gtk.MessageDialog 来实现 gimp python-fu 插件中的简单模态对话。可以通过创建通用对话框

queryDialogue = gtk.MessageDialog(None, gtk.DIALOG_DESTROY_WITH_PARENT \ gtk.MESSAGE_QUESTION, \ gtk.BUTTONS_OK_CANCEL, "")

显示对话框后,可以从中获得同步响应

queryDialogue.show() response = queryDialogue.run() queryDialogue.hide()

以上假设对话框在每次使用后都没有创建并因此被销毁。

在模态对话框的用例(在问题中提到)通过 App 模拟器包管理 gimp 中的 pspScript 单步执行时,需要为每次使用自定义对话消息内容。[因此,构造函数中消息参数的“”。[更多下文]]

此外,模拟器必须能够接受对“退出 Dodge”的 [取消] 响应 - 即(优雅地)退出整个插件。我找不到后者的 gimpfu 接口,(并且不想完全通过 gimp.exit() 杀死该应用程序)。因此,这是通过在 App pkg 中引发自定义异常类 [appTerminate] 并在插件的最外层范围内捕获异常来完成的。然后,当被捕获时,插件返回(退出)。[App.Do() 不能返回值来指示继续/退出/等,因为要逐字包含 pspScripts。]

以下是解决方案的缩写骨架 -

  • 包含(部分)pspScript 的插件
  • 提供环境的 App.py pkg 和 App.Do() 以支持 pspScript
  • 一个 Map.py pkg 支持 pspScripts 如何使用点符号作为参数

App.py 演示了模态对话框的创建、自定义和使用 - App.doContinue() 显示的对话框说明了如何在每次使用时对其进行自定义。App._parse() 解析 pspScript(摘录显示它如何通过对话确定开始/停止单步) App._exec() 实现 pspScript 命令(摘录显示它如何创建对话,识别消息小部件以供以后自定义,并开始/停止使用)

# App.py   (abbreviated)
#
import gimp
import gtk
import Map         # see https://stackoverflow.com/questions/2352181/how-to-   use-a-dot-to-access-members-of-dictionary
from Map import *
pdb = gimp.pdb

isDialogueAvailable = False
queryDialogue = None
queryMessage  = None

Environment = Map({'executionMode' : 1 })

_AutoActionMode   = Map({'Match' : 0})
_ExecutionMode    = Map({'Default' : 0}, Silent=1, Interactive=2)
Constants = Map({'AutoActionMode' : _AutoActionMode},       ExecutionMode=_ExecutionMode ) # etc... 

class appTerminate(Exception): pass

def Do(eNvironment, procedureName, options = {}):
    global appTerminate
    img = gimp.image_list()[0]
    lyr = pdb.gimp_image_get_active_layer(img)

    parsed = _parse(img, lyr, procedureName, options)
    if eNvironment.executionMode == Constants.ExecutionMode.Interactive:
        resp = doContinue(procedureName, parsed.detail)
        if resp == -5:          # OK
            print procedureName # log to stdout
            if parsed.valid:
                if parsed.isvalid:
                    _exec(img, lyr, procedureName, options, parsed, eNvironment)
                else:
                    print "invalid args"
            else:
                print "invalid procedure"
        elif resp == -6:        # CANCEL
            raise appTerminate, "script cancelled"
            pass  # terminate plugin
        else:
            print procedureName + " skipped"
            pass  # skip execution, continue
    else:
        _exec(img, lyr, procedureName, options, parsed, eNvironment)
    return

def doContinue(procedureName, details):
    global queryMessage, querySkip, queryDialogue
    # - customize the dialog -
    if details == "":
        msg  = "About to execute procedure \n    "+procedureName+ "\n\nContinue?"
    else:
        msg  = "About to execute procedure \n    "+procedureName+ "\n\nDetails - \n" + details +"\n\nContinue?"
    queryMessage.set_text(msg)
    queryDialogue.show()
    resp = queryDialogue.run()       # get modal response
    queryDialogue.hide()
    return resp

def _parse(img, lyr, procedureName, options):
    # validate and interpret App.Do options' semantics vz gimp
    if procedureName == "Selection":
        isValid=True
        # ...
        # parsed = Map({'valid' : True}, isvalid=True, start=Start, width=Width, height=Height, channelOP=ChannelOP ...
        # /Selection
    # ...
    elif procedureName == "SetExecutionMode":
        generalOptions = options['GeneralSettings']
        newMode = generalOptions['ExecutionMode']
        if newMode == Constants.ExecutionMode.Interactive:
            msg = "set mode interactive/single-step"
        else:
            msg = "set mode silent/run"
        parsed = Map({'valid' : True}, isvalid=True, detail=msg, mode=newMode)
        # /SetExecutionMode
    else:
        parsed = Map({'valid' : False})

    return parsed

def _exec(img, lyr, procedureName, options, o, eNvironment):
    global isDialogueAvailable, queryMessage, queryDialogue
    #
    try:
        # -------------------------------------------------------------------------------------------------------------------     
        if procedureName == "Selection":
            # pdb.gimp_rect_select(img, o.start[0], o.start[1], o.width, o.height, o.channelOP, ...
            # /Selection
        # ...
        elif procedureName == "SetExecutionMode":
            generalOptions = options['GeneralSettings']
            eNvironment.executionMode = generalOptions['ExecutionMode']
            if eNvironment.executionMode == Constants.ExecutionMode.Interactive:
                if isDialogueAvailable:
                    queryDialogue.destroy()   # then clean-up and refresh

                isDialogueAvailable = True
                queryDialogue = gtk.MessageDialog(None, gtk.DIALOG_DESTROY_WITH_PARENT, gtk.MESSAGE_QUESTION, gtk.BUTTONS_OK_CANCEL, "")
                queryDialogue.set_title("psp/APP.Do Emulator")
                queryDialogue.set_size_request(450, 180)

                aqdContent = queryDialogue.children()[0]
                aqdHeader  = aqdContent.children()[0]
                aqdMsgBox  = aqdHeader.children()[1]
                aqdMessage = aqdMsgBox.children()[0]
                queryMessage = aqdMessage

            else:
                if isDialogueAvailable:
                    queryDialogue.destroy()
                    isDialogueAvailable = False
            # /SetExecutionMode

        else:   # should not get here (should have been screened by parse)
            raise AssertionError,  "unimplemented PSP procedure: " + procedureName
    except:
        raise AssertionError, "App.Do("+procedureName+") generated an exception:\n" + sys.exc_info()
    return 

插件本身的骨架。这说明了合并一个 pspScript,其中包括对单步/交互式执行模式的请求,因此包括对话。它捕获通过对话引发的终止异常,然后终止。

def generateWebImageSet(dasImage, dasLayer, title, mode):
    try:
        img = dasImage.duplicate()
        # ...
        bkg   = img.layers[-1]
        frameWidth = 52
        start = bkg.offsets
        end   = (start[0]+bkg.width, start[1]+frameWidth)

        # pspScript: (snippet included verbatim)

        # SetExecutionMode / begin interactive single-step through pspScript
        App.Do( Environment, 'SetExecutionMode', {
                            'GeneralSettings': {
                                'ExecutionMode': App.Constants.ExecutionMode.Interactive
                                }
                            })
        # Selection
        App.Do( Environment, 'Selection', {
                    'General' : {
                        'Mode' : 'Replace',
                        'Antialias' : False,
                        'Feather'   : 0
                        },
                    'Start': start,
                    'End':   end
                    })      
        # Promote           
        App.Do( Environment, 'SelectPromote' )
        # und_so_weiter  ...

    except App.appTerminate:
        raise AssertionError, "script cancelled"
    # /generateWebImageSet

# _generateFloatingCanvasSetWeb.register -----------------------------------------
#               
def generateFloatingCanvasSetWeb(dasImage, dasLayer, title):
    mode="FCSW" 
    generateWebImageSet(dasImage, dasLayer, title, mode)

register(
        "generateFloatingCanvasSetWeb",
        "Generate Floating- Frame GW Canvas Image Set for Web Page",
        "Generate Floating- Frame GW Canvas Image Set for Web Page",
        "C G",
        "C G",
        "2019",
        "<Image>/Image/Generate Web Imagesets/Floating-Frame Gallery-Wrapped Canvas Imageset...",
        "*",
        [
          ( PF_STRING, "title", "title", "")
        ],
        [],
        generateFloatingCanvasSetWeb)

main()

我意识到这可能看起来像很多工作,只是为了能够在 gimp 插件中包含一些 pspScripts,并且能够单步执行仿真。但我们谈论的可能是 10K 行脚本(和多个脚本)。但是,如果其中任何一个可以帮助其他人在插件等中进行对话,那就更好了。

于 2019-09-30T18:58:45.917 回答