4. Un tutorial d'escriptura del connector Python

Aquesta guia d'aprenentatge us ensenyarà els conceptes bàsics sobre l'escriptura d'un connector Python per al GIMP 3.0. S'espera que ja tingueu alguns coneixements bàsics sobre l'escriptura Python en general. Si no, hi ha prou cursos de Python en línia, no el duplicarem ací.

Els connectors Python del GIMP (i també altres llenguatges) es criden des del GIMP per a realitzar certes accions. Per poder comunicar-se amb el connector i cridar-lo, el GIMP ha de saber amb quin nom l'ha de cridar i quines funcions admet.

There are certain requirements regarding a plug-in's filename and directory name, which have to be the same. For more details see Installing New Plug-Ins.

4.1. Els elements bàsics d'un connector per al GIMP

Parlarem de les parts bàsiques d'un connector que són necessàries, o almenys molt comunes per a treballar amb el GIMP.

  • No es requereix, sinó una pràctica comuna, començar amb un hashbang, una codificació i una nota de copyright. La primera línia sol ser un hashbang, que especifica com es pot executar aquest script. La següent línia especifica la codificació del fitxer Python. Recomanem utf-8. Normalment això va seguit per diverses línies que especifiquen la llicència sota la qual publiqueu l'script i una breu descripció del que fa. No aprofundirem en això, ja que és comú en el Python en general.

  • Importació de mòduls necessaris per a accedir al GIMP i opcionalment a les funcions GEGL.

  • Declara una classe amb diverses funcions predefinides que heu d'ajustar, de manera que el GIMP sàpiga quines funcions estan disponibles al vostre connector i quines funcions suporten. A continuació, entrarem en més detalls sobre aquest tema.

  • Una trucada que inicia el vostre connector o consulta les seves capacitats, depenent dels arguments que li enviï GIMP.

4.1.1. Mòduls requerits

Per a poder accedir a les funcions del GIMP, comencem amb import gi. Aquest mòdul pot esbrinar quines funcions estan disponibles en cada mòdul definit a través de la «introspecció d'objectes». El que això significa per a nosaltres és que importem tots els mòduls relacionats amb el GIMP que puguem necessitar mitjançant crides a gi.repository.

Per a la funcionalitat bàsica, només els mòduls Gimp i GimpUi poden ser suficients. Si voleu executar el connector des de la línia d'ordres, ni tan sols necessitareu el GimpUi. Comencem amb un exemple.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
#   GIMP - The GNU Image Manipulation Program
#   Copyright (C) 1995 Spencer Kimball and Peter Mattis
#
#   gimp-tutorial-plug-in.py
#   sample plug-in to illustrate the Python plug-in writing tutorial
#   Copyright (C) 2023 Jacob Boerema
#
#   This program is free software: you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation; either version 3 of the License, or
#   (at your option) any later version.
#
#   This program is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.
#
#   You should have received a copy of the GNU General Public License
#   along with this program.  If not, see <https://www.gnu.org/licenses/>.

import sys

import gi
gi.require_version('Gimp', '3.0')
from gi.repository import Gimp
gi.require_version('GimpUi', '3.0')
from gi.repository import GimpUi
from gi.repository import GLib
      

Comencem amb la importació de sys, que necessitem al final per accedir a sys.argv, després que import gi li digui a Python que ha de carregar el mòdul gi. Aquest mòdul s'utilitza per habilitar l'accés a funcions específiques del GIMP mitjançant la «introspecció d'objectes».

A la següent línia li diem a gi que necessitem que la versió de l'API del GIMP sigui la versió 3.0. (Aquest connector no funcionarà amb versions anteriors del GIMP.) La línia següent demana importar totes les funcions, classes, etc. des del mòdul del Gimp.

Les dues línies següents fan el mateix pel GimpUi. GimpUi conté tots els elements relacionats amb la interfície per al GIMP. Si teniu pensat crear un connector que només es cridarà des de la línia d'ordres, no ho necessitareu. Acabem amb la importació de GLib que necessitarem més endavant per accedir a GLib.Error.

Hi ha altres mòduls opcionals que també podeu utilitzar, com el Gegl i el Glib entre molts altres, però ací no hi entrarem.

4.1.2. Definiu la vostra classe de connector

El GIMP ha de saber quines funcions estan disponibles, quina funcionalitat admeten i quina ubicació del menú utilitzar. Per això definim una classe que es deriva de la classe Gimp.PlugIn.

Un connector mínim necessita almenys les funcions següents definides en aquesta classe:

  • Un mètode do_query_procedure, que el GIMP crida per esbrinar els noms dels procediments que es poden cridar en aquest connector.

  • Un mètode do_set_i18n que el GIMP crida per esbrinar si el vostre connector admet traduccions.

  • Un mètode do_create_procedure que el GIMP crida per iniciar una de les funcions dels connectors. Quan es cridi, hauríeu d'inicialitzar certa informació per al GIMP. Comenceu creant un procediment que indiqui al GIMP el nom de la funció Python a cridar per a engegar el connector. A continuació, proporcioneu informació addicional, com ara quins tipus d'imatge admet el connector, on s'ha de trobar el connector en el menú i altres paràmetres opcionals.

  • La funció real (anomenada procediment pel GIMP) que heu especificat anteriorment. Sovint l'anomenem run, però pot tenir qualsevol nom permès per Python. Aquesta funció és on afegireu el vostre propi codi per aplicar els efectes desitjats.

Ara entrarem una mica més en detall. No s'inclou a sota la primera part del codi Python que es mostrava a dalt. Això només mostra el disseny bàsic de la classe.

class MyFirstPlugin (Gimp.PlugIn):
    def do_query_procedures(self):
        return [ "jb-plug-in-first-try" ]

    def do_set_i18n (self, name):
        return False

    def do_create_procedure(self, name):
        procedure = Gimp.ImageProcedure.new(self, name,
                                            Gimp.PDBProcType.PLUGIN,
                                            self.run, None)

        procedure.set_image_types("*")

        procedure.set_menu_label("My first Python plug-in")
        procedure.add_menu_path('<Image>/Filters/Tutorial/')

        procedure.set_documentation("My first Python plug-in tryout",
                                    "My first Python 3 plug-in for GIMP 3",
                                    name)
        procedure.set_attribution("Your name", "Your name", "2023")

        return procedure

    def run(self, procedure, run_mode, image, n_drawables, drawables, config, run_data):
        Gimp.message("Hello world!")
        # do what you want to do, then, in case of success, return:
        return procedure.new_return_values(Gimp.PDBStatusType.SUCCESS, GLib.Error())
      

Fem una ullada més de prop a do_create_procedures. A la línia retorn [ "jb-plug-in-first-try" ] li diem al GIMP el nom del nostre procediment: l'anomenem "jb-plug-in-first-try". Aquest és el nom que es mostrarà al Navegador de procediments del GIMP.

Podeu tenir més d'un procediment definit en un connector. En aquest cas llistareu tots els noms separats per una coma.

És una bona pràctica iniciar tots els vostres tràmits amb les vostres inicials o alguna altra etiqueta reconeixible i única. D'aquesta manera és menys probable que el vostre nom sigui el mateix que el connector d'algú altre, cosa que podria confondre el GIMP. A més d'això, podeu anomenar-lo com vulgueu.

A continuació, li diem al GIMP que no admetem traduccions retornant False en la crida a do_set_i18n. Què fer quan voleu que el vostre connector es tradueixi i està fora de l'abast d'aquest tutorial.

El mètode do_create_procedure és on es fa la major part de la inicialització del GIMP.

Procediment 13.1. Configurant do_create_procedure

  1. Si definiu més d'un procediment al vostre connector, primer heu de comprovar el paràmetre «nom» per a veure quin procediment està cridat pel GIMP. No entrarem en això.

    Per a inicialitzar el vostre procediment de connector, primer hem de crear-lo i emplenar en el nom de la nostra funció Python que farà el treball real. Ho fem cridant a Gimp.ImageProcedure.new.

        procedure = Gimp.ImageProcedure.new(self, name,
                                            Gimp.PDBProcType.PLUGIN,
                                            self.run, None)
              

    En aquest cas, definim el nom del nostre connector com a self.run. Quan qualifiquem la nostra funció amb «self.», vol dir que és un mètode dins de la nostra classe. Si ho prefereixes, també pots definir-la com una funció normal fora de la teva classe, en aquest cas ometríeu «self.». No cal posar-li el nom «run», podeu donar-li qualsevol nom que accepti Python.

  2. A continuació li direm al GIMP amb quins tipus d'imatges pot treballar aquest connector cridant al procedure.set_image_types. En cas que el tipus d'imatge no importi, utilitzarem "*", que significa tots els tipus suportats pel GIMP. Altres exemples:

    1. "RGB*,GRAY*", on el "*" ací significa que admetem les dues versions amb i sense canal A(lpha).

    2. El connector «INDEXED» només funciona en imatges indexades, sense canal alfa.

    3. El connector «RGBA», només funciona en una imatge RGB amb canal alfa.

  3. Ser capaç d'iniciar el connector des del menú del GIMP sol ser una bona idea. Comencem definint una etiqueta descriptiva per a l'entrada del menú: procedure.set_menu_label.

  4. A continuació, especifiqueu on hauria d'aparèixer en el menú: procedure.add_menu_path. En aquest cas li demanem que afegeixi el nostre connector al menú Filtres, a la categoria Tutorial (submenú).

  5. Si voleu, també podeu afegir un consell d'ajuda addicional mitjançant procedure.set_documentation, i podeu establir el vostre nom com a autor del connector utilitzant procedure.set_attribution .

  6. L'última línia del procediment de creació és procediment de retorn, que torna a enviar la informació afegida anteriorment al GIMP. Després d'això, el GIMP cridarà el vostre procediment d'execució.

4.1.3. Afegiu el punt d'entrada principal al vostre connector

Cada connector comença amb una crida a Gimp.main.

        Gimp.main(MyFirstPlugin.__gtype__, sys.argv)
      

L'única cosa que heu de canviar en aquesta línia per al vostre connector és el nom de la vostra classe de connector, ací anomenada «MyFirstPlugin».

4.1.4. El connector Python complet

A continuació presentem l'script Python sencer que s'hauria d'executar sempre que se li doni el nom correcte en un directori amb el mateix nom, i en una ubicació que conegui el GIMP. Mostrarà el missatge «Hola món!» a la consola d'errors o en un diàleg emergent.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
#   GIMP - The GNU Image Manipulation Program
#   Copyright (C) 1995 Spencer Kimball and Peter Mattis
#
#   gimp-tutorial-plug-in.py
#   sample plug-in to illustrate the Python plug-in writing tutorial
#   Copyright (C) 2023 Jacob Boerema
#
#   This program is free software: you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation; either version 3 of the License, or
#   (at your option) any later version.
#
#   This program is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.
#
#   You should have received a copy of the GNU General Public License
#   along with this program.  If not, see <https://www.gnu.org/licenses/>.

import sys

import gi
gi.require_version('Gimp', '3.0')
from gi.repository import Gimp
gi.require_version('GimpUi', '3.0')
from gi.repository import GimpUi

from gi.repository import GLib

class MyFirstPlugin (Gimp.PlugIn):
    def do_query_procedures(self):
        return [ "jb-plug-in-first-try" ]

    def do_set_i18n (self, name):
        return False

    def do_create_procedure(self, name):
        procedure = Gimp.ImageProcedure.new(self, name,
                                            Gimp.PDBProcType.PLUGIN,
                                            self.run, None)

        procedure.set_image_types("*")

        procedure.set_menu_label("My first Python plug-in")
        procedure.add_menu_path('<Image>/Filters/Tutorial/')

        procedure.set_documentation("My first Python plug-in tryout",
                                    "My first Python 3 plug-in for GIMP 3.0",
                                    name)
        procedure.set_attribution("Your name", "Your name", "2023")

        return procedure

    def run(self, procedure, run_mode, image, n_drawables, drawables, config, run_data):
        Gimp.message("Hello world!")
        # do what you want to do, then, in case of success, return:
        return procedure.new_return_values(Gimp.PDBStatusType.SUCCESS, GLib.Error())

Gimp.main(MyFirstPlugin.__gtype__, sys.argv)