在 gimp python-fu 插件中,可以创建/调用模态对话框(和/或注册一个仅作为临时过程添加的过程?



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

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

我尝试注册一个插件,以便利用 gimpfu 启动对话框。 就其本身而言,它有效;查询时,它显示在过程数据库中。但是我似乎永远无法从另一个插件中实际调用它 - 无论我尝试多少排列,它要么是执行错误,要么是错误的参数数量。

[所有这些废话背后的原因:我已经为PaintShopPro编写了很多扩展Python脚本。我已经编写了一个应用程序包(带有 App.Do,App.Constants,环境等,让我开始将这些脚本移植到GIMP-是的,这是反常的,是的,有时代码必须重写,但是对于我在PSP中实际使用的很多内容。API 它就足够了。

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

gimp python-fu 插件中的简单模态对话可以通过 gtk 的对话框接口实现,特别是 gtk。消息对话框。 可以通过以下方式创建通用对话框

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

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

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

上述假设对话框不会创建,并在每次使用后销毁。

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

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

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

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

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+ "nnContinue?"
else:
msg  = "About to execute procedure n    "+procedureName+ "nnDetails - n" + details +"nnContinue?"
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 行脚本(和多个脚本(。 但是,如果这些可以帮助其他人在插件等中进行对话,那就更好了。

相关内容

最新更新