Tips and tricks for registering python plug-ins with gimp – Number 4 will SHOCK you!

This is a write-up of some of the quirks and behaviours I’ve discovered writing Python plugins for Gimp, along with some quick reference material.

I did a bit of searching and didn’t find a good write-up or documentation of the register() method you need to use to register your Python plug-in with Gimp. I figured some stuff out and thought I’d write it down.

Normally, you don’t need much of a reference for gimp’s python library, because it has such wonderful built-in documentation: In Gimp, choose Filters -> Python-Fu -> Console. The Python console will open. Press the “Browse” button and you have a searchable library of gimp functions. If you select a function and press the Apply button, gimp will give you the python incantation on the console command-line, ready to be copy-pasted into your plug-in. This is super helpful and alleviates the need for a (seemingly-nonexistent? I can’t find it online) API reference, but it does have one drawback: It doesn’t give you any examples or tell you a whole lot about the available options. In the case of the register method used to register plug-ins with Gimp, I couldn’t find it in the browser at all.

So, here’s what I’ve learned about registering python plug-ins with Gimp:

  1. There is some documentation If you look around
    It’s not particularly easy, but you can find some documentation out there. Mostly, it’s tutorials on how to write gimp plugins with python. A web search for ‘gimp python plugin’ will give you a bunch.
    I pieced this info together by looking at multiple “how to write a gimp plugin with python” tutorials and examining the difference between their calls to register(), and by trying things out.

    • In the gimp python console, you can use:
      import gimpfu 
      help(gimpfu.register)

      to get a very basic description of the Register method. This gives you back something super useful:

      register(proc_name, blurb, help, author, copyright, date, label, imagetypes, params, results, function, menu=None, domain=None, on_query=None, on_run=None)
          This is called to register a new plug-in.
      
    • A couple of places have a list of available options for the register method:The best documentation I’ve been able to find is now a 404, but is still available thanks to the Internet Archive here. This includes things like helpful list of available parameter types, and lots of useful little notes on behaviour.The Gimp’s Developer wiki has a “Hacking Plugins” page, which doesn’t mention python but which has a few useful links.
    • Here is a table of parameters for the register method, shamelessly copied from this tutorial:
      Parameter Example Description
      proc_name “your_plugin_name” The name of the command that you can call from the command line or from scripting
      blurb “Some Text” Information about the plug-in that displays in the procedure browser
      help “Some Text” Help for the plug-in
      author “Some Person” The plug-in’s author
      copyright “Some Person” The copyright holder for the plug-in (usually the same as the author)
      date “2097″ The copyright date
      label “<Image>/Image/_Do A Thing…” The label that the plug-in uses in the menu. Put an underscore before a letter to set the accelerator key. Use <Image>/ for a plug-in which operates on an open image, or <Toolbox>/ for a plug-in which opens or creates an image.
      imagetypes “RGB*, GRAY*” (see below) The types of images the plug-in is made to handle.
      params [] (See below) The parameters for the plug-in’s method
      results [] The results of the plug-in’s method
      function myplugin The method gimp should call to run your plugin. Not a string.
  2. Making sense of register()’s parameters
    I found myself having trouble with the imagetypes and label parameters. The first few plugins I wrote simply batched up a few gimp operations into one thing, working on an image that I had open.Then, I found myself wanting to write plugins that would perform batch operations, or generate a new image. These worked just fine, but there was one snag: I found that the menu items for my plugins were disabled if I didn’t have an image open. I decided to investigate.I discovered that imagetypes and label work together to control when your menu item is available, and whether your method needs to accept parameters for the currently open image and drawable.

    imagetypes takes a string argument telling gimp what types of images your plugin operates on. The acceptable arguments I’ve found so far are:

    • “RGB*” – if your plugin works on an image and requires colour.
    • “RGB*, GRAY*” – if your plugin also works on grayscale images.
    • “*” seems to be an easier synonym for the above.
    • None – This one is important, and it’s the one I couldn’t find anywhere and found by experimentation. You need to specify None (that is the python NoneType, not the string ‘None’) to have your plugin enabled when you have no image open in gimp, i.e if you’re doing a batch operation on a directory of images, or generating a new image.
    • Maybe “GRAY*” – I haven’t tried this. Does it make sense? RGB has all the grays, too.

    label takes a string argument telling gimp where in the menu your plug-in should go. This has a couple of behaviours and implications that I had to figure out.

    • If your plugin will modify an open image, you should prefix your label with “<Image>/“. So your label might be “<Image>/Filters/Artistic/My _Plugin…”.Importantly, this is what determines whether your method will be passed timg and tdrawable parameters with the currently open image and drawable. So if your label does start with “<Image>/”, your method definition should look like this:
      def myplugin(timg, tdrawable, myfirstparam, myotherparams...):

      If your plugin will open or create image(s) itself (e.g a batch operation or a plugin which creates a new image), you should prefix your label with “<Toolbox>/“. So your label might be “<Toolbox>/File/_Batch/_My Batch Operation…
      If you use “<Toolbox>“, your method definition should not have the timg and tdrawable parameters:

      def myplugin(myfirstparam, myotherparams...):
    • Note the underscores in my examples. These specify the accelerator key gimp will use in the menu. You should set accelerators, they make your stuff easier to use.
    • You can easily create submenus or even new menus “on-the-fly” just by specifying them with a slash. They can also have accelerators. So that label might actually be “<Image>/Filters/My _Menu/My _Plugin” or “<Image>/My _Menu/My _Plugin” to create a “My Menu” menu if you want to.
  3. Here’s the list of data types you can use for plug-in parameters. Gimp will show nice, helpful selectors for them all. Use them!One which I will note is PF_LAYER, which is useful if you want the user to select a specific layer to operate on or work with.
    • PF_INT8
    • PF_INT16
    • PF_INT32
    • PF_INT
    • PF_FLOAT
    • PF_STRING
    • PF_VALUE
    • PF_COLOR
    • PF_COLOUR
    • PF_REGION
    • PF_IMAGE
    • PF_LAYER
    • PF_CHANNEL
    • PF_DRAWABLE
    • PF_TOGGLE
    • PF_BOOL
    • PF_RADIO
    • PF_SLIDER
    • PF_SPINNER
    • PF_ADJUSTMENT
    • PF_FONT
    • PF_FILE
    • PF_BRUSH
    • PF_PATTERN
    • PF_GRADIENT
    • PF_PALETTE

     

  4. Prepare to be shocked: This tip isn’t about registering plugins at all! Gasp. But since we’re talking about batch operations, it’s useful to note that you can easily have your plugin show and update progress bar by using a couple of calls in your loop. There’s also another good practice that you should be aware of if you’re writing a plug-in that’s going to take a while to run: knowing when to update the display.
    • Use gimp.progress_init(“Some Text…”) to set up a progress bar. Do this at the start of your method, duh.
    • Use gimp.progress_update(floatval) in your loop to set progress on the progress bar. floatval should be a float between 0 and 1. You can also call gimp.progress_init(“Your message”) again in your loop to update the text.
    • By default, gimp won’t update its display while your plug-in is running unless you tell it to. So you may want to call gimp.displays_flush() periodically so that the user sees what is going on.
    • But be wary of calling these too often, updating the display is expensive and may slow you down! use something like ‘if count % 5 == 0: gimp.displays_flush()
    • While we’re talking about long-running plugins, it’s not advisable to operate on images on a pixel-by-pixel basis, i.e looping through each pixel in the image, getting an RBG value, doing an operation, and changing a pixel. This is verrrry sloooooow. I assume there’s a faster way, probably retrieving the image as a multidimensional array, working with that, and then writing it back. But I haven’t managed to do that yet. I’ll update this if I do. Mail me if you figure it out!
  5. There Are Still Mysteries!
    Shocking as it is, I’m not omniscient, so I don’t have it all figured out. I haven’t had need of all the available options. I’ve discussed some unknowns already.
    For instance, I don’t know what gimp would do with your return value if you gave it results. That might make for an interesting experiment, and I don’t know what parameter you’d use for filetypes to work on indexed images. I don’t think this presents much of a problem as it’s easy to switch to and from indexed to rgb modes. I would expect that you probably only really want indexed when you’re about to export, unless you’re doing pixel art, in which case I’d recommend checking out something like Aseprite.

So, there’s my wisdoms on that subject. I mostly just wanted to document what I’d learned about writing plugins to generate a new image vs working with an open image, but I find myself searching for gimp-python docs every now and then, so I figured this would be a good thing to write and come back to. I expect I’ll come back and edit it as i learn more. Hopefully somebody else might find it useful too! :)

Leave a Reply