В плагине gimp python-fu можно создать / вызвать модальное диалоговое окно (и / или зарегистрировать процедуру, которая ТОЛЬКО будет добавлена ​​как временная процедура?) - PullRequest
0 голосов
/ 27 сентября 2019

Я пытаюсь добавить процедуру для всплывающего модального диалога внутри плагина.Его цель - запросить ответ на указанных шагах в пределах потока управления плагина (а не просто получить параметры при его запуске).

Я пытался использовать gtk - я получаюдиалог, но он асинхронный - плагин продолжает выполнение.Он должен работать как синхронная функция.

Я попытался зарегистрировать плагин, чтобы воспользоваться преимуществами диалогового окна запуска gimpfu.Само по себе это работает;при запросе он отображается в процедурной базе данных.Но мне кажется, что я никогда не смогу вызвать его из другого плагина - это либо ошибка выполнения, либо неверное количество аргументов, независимо от того, сколько попыток я пытаюсь использовать.

[Причина всей этой чепухи:Я написал много расширенных скриптов Python для PaintShopPro.Я написал пакет приложений (с App.Do, App.Constants, Environment и тому подобным, который позволяет мне begin портировать эти сценарии в GIMP - да, это неверно, и да, иногда код просто имеетбыть переписанным, но для того, что я на самом деле использую в PSP.API, этого достаточно.

Однако отладка и написание модуля рифмуется с ведьмой. Итак, я пытаюсь добавить эмуляцию psp "SetExecutionMode "(то есть интерактивный). Если установлено, предполагаемое поведение заключается в том, что метод App.Do () будет" приостанавливать "после / до того, как он выполнит соответствующий код эмуляции psp, открыв простое диалоговое окно сообщения.]

1 Ответ

0 голосов
/ 30 сентября 2019

Простой модальный диалог в плагине gimp python-fu может быть реализован через интерфейс Dialog gtk, в частности, gtk.MessageDialog.Общий диалог может быть создан с помощью

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

. Как только диалоговое окно было показано, из него можно получить синхронный ответ

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

Выше предполагается, что диалоговое окно не создается, а затем уничтожается после каждого использования.

В случае использования (упомянутом в вопросе) модального диалогового окна для управления одним пошаговым переходом через pspScript в gimp через пакет эмулятора приложения содержимое диалогового сообщения необходимо настраивать для каждого использования.[Следовательно, "" для аргумента сообщения в конструкторе.[подробнее ниже]]

Кроме того, эмулятор должен быть в состоянии принять [отменить] ответ «выйти из Dodge» - то есть выйти из всего плагина (изящно).Я не смог найти интерфейс gimpfu для последнего (и не хочу полностью убивать приложение через gimp.exit ()).Следовательно, это достигается путем поднятия пользовательского класса Exception [appTerminate] внутри pkg приложения и перехвата исключения в самой внешней области действия плагина.После этого подключаемый модуль возвращает (завершает работу). [App.Do () не может возвращать значение для указания продолжения / выхода / и т. Д., Потому что pspScripts должен быть включен дословно.]

ниже приведен сокращенный каркас решения -

  • плагин, включающий (частично) pspScript
  • pkg App.py, обеспечивающий среду, и App.Do () для поддержки pspScript
  • pkg Map.py, поддерживающий, как pspScripts использует точечную нотацию для параметров

App.py демонстрирует создание, настройку и использование модального диалога - App.doContinue () отображает диалог, иллюстрирующий, как это может бытьнастроены на каждое использование.App._parse () анализирует pspScript (фрагмент, показывающий, как он определяет запуск / остановку одного шага с помощью диалога). App._exec () реализует команды pspScript (фрагмент, показывающий, как он создает диалог, идентифицирует виджет сообщения для последующей настройкии запускает / останавливает его использование)

# App.py   (abbreviated)
#
import gimp
import gtk
import Map         # see /1874765/kak-ispolzovat-tochku-poluchit-dostup-k-chlenam-slovarya   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()

Я понимаю, что это может показаться большой работой, просто чтобы включить некоторые pspScripts в плагин gimp, ичтобы можно было пошагово пройти эмуляцию.Но мы говорим о, возможно, 10K строк сценариев (и нескольких сценариев).Однако, если что-то из этого поможет кому-либо еще с диалогами внутри плагинов и т. Д., Тем лучше.

...