Porting a GIMP2 python script to GIMP3

In slow-mo:

  • You want to add a layer so see if you can find a “constructor” method for the Gimp.Layer, probably Gimp.Layer.new. It is described as:
GimpLayer*
gimp_layer_new (
  GimpImage* image,
  const gchar* name,
  gint width,
  gint height,
  GimpImageType type,
  gdouble opacity,
  GimpLayerMode mode
)
  • The doc says it takes
    • a Gimp.image (you already have one)
    • name, width, height
    • a Gimp.ImageType
    • an opacity (which is a floating point “double” so in the range 0…1)
    • a Gimp.LayerMode
  • For enumerations (ImageType, LayerMode), you eventually guess how to convert from the C name to the Python name, but if you don’t want to guess, in Gimp Python console you just use dir(Gimp.ImageType) to list all the names available.
  • So the code is:
layer=Gimp.Layer.new(image,
                     "The new shiny layer",100,100,
                     Gimp.ImageType.RGBA_IMAGE,
                     100., # Opacity is in percent
                     Gimp.LayerMode.NORMAL)
gboolean
gimp_image_insert_layer (
  GimpImage* image,
  GimpLayer* layer,
  GimpLayer* parent,
  gint position
)
  • that takes:
    • A Gimp.image and a Gimp.layer that you already have. Note that except for constructors, the first argument of the method in the description is an object of the class (so insert_layer, being a method of Gimp.image, takes a Gimp.image as a first argument). When used with Python object syntax, this first argument becomes the object on which the method is called (with of course, one less argument). So for instance the method described in the doc as do_something(SomeClass, arg1, arg2) is called in Python as instance_of_SomeClass.do_something(arg1,arg2)
    • A parent that can be NULL if there is no parent group. The Python equivalent is None
    • A position
  • So the code is:
image.insert_layer(layer,None,-1)

Some more hints here: Converting the API doc to Python

Hello Ofnuts, thank you very much for your explanations, I think my object oriented skills are somehow get rusty. I now found most of my errors/problems, and the port of my scripts from Gimp2 to 3 is going on.
But I have got the next question:
How could I set in Gimp3 the paths where it is looking for external .py files.
(I am running Gimp3 on Windows 10.)

A plugin has to be placed in the plug-ins folder where the file has to have the same name than the folder (as I understood).
Now I would like to place some of the functions in an extra file in a separate folder, and import this file then to different plug-in skripts. But how could I tell Gimp where to search for this external stuff?

And how to use in Python
GList* gimp_list_images (void)?

As

imageList = Gimp.List.images()

or

imageList = Gimp.list_images()

or?
The both above do not work.

Kind regards Christoph

You can play with sys.path and add things to it before doing the import. But IMHO its is not worth it. I have a similar need for my scripts and

  • My users don’t download all the collection so setting up an extra directory for one of two plugins is overkill. So I keep the external .py in the same directory as the plugin (so I can do a plain import, because that directory is always in sys.path), and even if sometimes it means duplicate files, the files are not big (a few KB) and I don’t need to upgrade and re-test all my scripts in lockstep because I changed something in the library. Each script works with the library version I distributed and tested it with.
  • since I’m on Linux, each of my development directories contains a soft link to the common and latest version of the library (so an upgrade to a script with always keep it in in tune with the latest version of the library)

The doc for Gimp.list_images says:

This function is not directly available to language bindings.

so you can’t use it. What you can use instead is Gimp.get_images that does the same thing (don’t ask me why the duplicate) but is available in the bindings.

Thanks a lot.

The second point “not directly available” i overlooked :-(. Thank you for pointing to it.
The first point, after playing around with import, now it works:

sys.path.insert(0, '.\\plugin_helper')
from plugin_helper import *

My error was to use just “import plugin_helper” and then calling its functions without prefix. But now with “from … import *” it works.

Now I will go on with my development.

Do you have a god description of “What is a drawable” in Gimp. I understand every layer, mask and so on are drawables.
An image consists of one or more drawables. But how to find the correct one if trying to modify it by a script.
I think there I will have to do some explorations and tests to understand it in more detail.

Kind regards
Christoph

Hi, now I want to export an image as JPG. This I found again in the GEGL operations/filters.
My understanding is

draw=Gimp.Image.get_selected_drawables(image)
f = Gimp.DrawableFilter.new(draw[0], "gegl:jpg-save", "")
filter_config = f.get_config()
filter_config.set_property('path', "C:\temp") 
filter_config.set_property('quality', 100) 
filter_config.set_property('smoothing', 0)
filter_config.set_property('optimize', True)
filter_config.set_property('progressive', True)
filter_config.set_property('grayscale', False)
#filter_config.set_property('metadata', 0)
image.merge_filter(f) #???

But there I got, if i use the GIMP Python Console

image = Gimp.get_images()[0]
draw=Gimp.Image.get_selected_drawables(image)
f = Gimp.DrawableFilter.new(draw[0], "gegl:jpg-save", "")
Traceback (most recent call last):
  File "<input>", line 1, in <module>
TypeError: constructor returned NULL

for the constructor.

And the message window of GIMP shows

Fehler beim Aufruf der Prozedur »gimp-drawable-filter-new«:
drawable_filter_new_invoker: the filter "gegl:jpg-save" is unsafe.

What is wrong?

A drawable is something on which you can paint… initially at least: layer, mask or channel. Then they added GroupLayers, that are a subclass of layer, but on which you can’t paint. You can apply NDE to them, but you can’t merge the filters.

To save a JPEG you use Gimp.file_save() or you use the 'file-jpeg-export' procedure from the PDB. If you use GEGL you short -circuit things in Gimp.

Again thank you, I searched today for hours, but with your help I was able to implement saving the image as JPG in both ways.

How should someone find this info without your help?
Where do I find an overview about the lookup_procedures with there properties, the DrawableFilters with there properties, and is there anything else.
I am really very happy that you answered my questions so fast, so that I am able to write my scripts. Thank you for your support.

How should someone find this info without your help?

How did I find out? :grin:

For the procedures, you open the Python console, and hit the “Browse” button, and search.
However, this lists plenty of procedures that duplicate more direct API calls. But you will quickly recognize these from their name and can figure out the equivalent call in the API. So these procedures are best used to call plugins.

The GEGL things are documented there: GEGL operations

Hi,

thanks again for your explanation, but I am faced with the next problem, maybe you could help?

In the UI of my script I would like to use something like RadioButtons. Sure with a Choise-Element I got the same functionality, but there you not see all possibilities at one view. So I looked for other argument types and I could imagine that the add_enum_argument would be what I am searching for.
But I can not find how to define the parameter GType enum_type?

void
gimp_procedure_add_enum_argument (
  GimpProcedure* procedure,
  const gchar* name,
  const gchar* nick,
  const gchar* blurb,
  GType enum_type,
  gint value,
  GParamFlags flags
)

Any hint from you?

@Christoph_Raber Hi! Create your GimpChoice, and then when you make the GUI, instantiate that parameter with .get_int_radio (): GimpUi.ProcedureDialog.get_int_radio

Hi, sorry but could you explain this for me with more details.

I instantiate the dialog with

    # Dieser Abschnitt, damit ein Menü angezeigt wird
    if run_mode == Gimp.RunMode.INTERACTIVE:
    
        gi.require_version('Gtk', '3.0')
        from gi.repository import Gtk
        GimpUi.init('python-fu-layer-scale-h540')
        dialog = GimpUi.ProcedureDialog(procedure=procedure, config=config)        
    
        dialog.fill(None)
        if not dialog.run():
            dialog.destroy()
            return procedure.new_return_values(Gimp.PDBStatusType.CANCEL, GLib.Error())
        else:
            dialog.destroy()

and I define my choise with

      layerOrientation = Gimp.Choice.new()  
      layerOrientation.add("horizontal",     0, "Horizontal: \t\t3:1", "")
      layerOrientation.add("vertical  ",     0, "Vertical  : \t\t3:4", "")
      procedure.add_choice_argument ("layerOrientation", 
                                       "Orientation of the layers", 
                                       "layerOrientationblurb",  #name, nick, blurb, 
                                       layerOrientation, "horizontal",  # choice, default-value, 
                                       GObject.ParamFlags.READWRITE)  # flags                

Where and how do I use the metioned call?
Kind regards, Christoph

Right before:

In fact, .get_widget () with the GimpUi.IntRadioFrame type would be even simpler.
dialog.get_widget("layerOrientation", GimpUi.IntRadioFrame)

With

if run_mode == Gimp.RunMode.INTERACTIVE:
    
         gi.require_version('Gtk', '3.0')
         from gi.repository import Gtk
    
         GimpUi.init('python-fu-layer-scale-h540')
         dialog = GimpUi.ProcedureDialog(procedure=procedure, config=config)
    
    
         #dialog.fill(None)
         dialog.get_widget("layerOrientation", GimpUi.IntRadioFrame)
         if not dialog.run():
             dialog.destroy()
             return procedure.new_return_values(Gimp.PDBStatusType.CANCEL, GLib.Error())
         else:
             dialog.destroy()

I got an empty menu?

Some time ago I fiddled around with those widgets.
You can check my variants here:

hope it helps

Ah, you still need dialog.fill(None)! I meant for you to put the new command right before it, so that it converts the GimpChoice to radio buttons before you fill it. :slight_smile:

Thank you for support, now it works. But again, i ask me how and where to find such stuff.
Kind regards Christoph

The API site I linked is helpful, and I’d also recommend looking at the official Python plug-ins: plug-ins/python · master · GNOME / GIMP · GitLab

We want to add more documentation as well like GIMP Developer - Python Plug-Ins, but it takes time (especially when we have relatively few developers and other work to do)

Thank you nelo, that helps to see in more detail what is possible in the UI. I also tested there some examples/datatypes, but it is really interresting that the style of the elemets is so flexible.
Kind regards
Christoph

Hi CmykStudent,

again, thank you for the additional information. I am very happy with every pease of supporting docs and stuff
AND: Please do not understand me in the wrong way. I think the developer (if I interpret you right, also you) did and do a great job, and I am shure, a lot of work. I use it for my hobbies and I like playing with all the stuff to find out what is possible. It is great that you also support in the way above (answering my questions) which helped me by implementing my plug-ins.
Kind regards Christoph

This topic was automatically closed 45 days after the last reply. New replies are no longer allowed.