Jan 042017
 

GIMP logo

In my last post, I briefly explained GIMP‘s scripting and plug-in system and the two most common ways to program custom extensions and scripts: Script-Fu and Python-Fu. Script-Fu, being the older of the two options, is well documented and the more widely used of the two. Not because I love the road less traveled, but because I love Python, I have chosen Python-Fu, a set of Python modules that serve as a wrapper for libgimp, as my platform for extending GIMP.

This post (and any I hopefully follow up with) are meant to track my progress as I learn my way around Python-Fu. Even while writing relatively simple scripts I have encountered gotchas and conflicting information about how to do things. Hopefully some of what I’ve discovered scouring both the web and Python source code will save other developers the time and frustration I’ve already paid.

Hello, GIMP World!

Although I actually do often prefer the road less traveled, I am not going to buck any trends with the first Python-Fu script I present. A simple “Hello, World” plug-in will actually demonstrate the few fundamentals common to all GIMP plug-ins written in Python.

Before I get to the code, my development environment is currently: GIMP 2.8.18 and Python 2.7.12 running on Ubuntu 16.10. I have found that the version of GIMP does make a difference. 2.8.18 is the most recent version, and some of the code I have come across written for older versions has different function signatures, etc. that break the scripts. Any modern version of Python, either Python 2.7 or Python 3, should work. And as should be my standard disclaimer, I don’t do or know Windows. Everything I present will usually work in a *nix environment, specifically Linux or OS X.


GIMP Plug-in Directories

Before creating a Python-Fu plug-in, you need to know where GIMP expects to find its plug-ins. These are the directories that it scans on start up, automatically adding any plug-ins it finds.

First, in the GIMP main menu choose Edit -> Preferences. In the left hand column of the Preferences window click on the Folders entry to expand it if it isn’t already and then click on Plug-Ins. The directories where GIMP expects to find plug-ins are listed in the window on the right.

GIMP preferences

My copy of GIMP is currently using the default settings and is looking for plug-ins in two directories: /usr/lib/gimp/2.0/plug-ins and /home/tpodlaski/.gimp-2.8/plug-ins. The first directory stores the plug-ins that came with GIMP when it was installed, and the latter directory are for plug-ins I may add, including those I write. You should save your custom plug-ins in your $USER/.gimp-2.8/plug-ins directory. It is good practice and ensures that your custom plug-ins won’t be overwritten during a GIMP upgrade.

Note that .gimp-2.8 is a hidden directory. You will likely not be able to see it from your desktop’s file browser without modifying your preferences to show all hidden files and directories. Also note that you can add other directories to GIMP’s plug-ins folder list. I haven’t had any reason to, but maybe you will.


GIMP Plug-In File Permissions

Once you’ve created your Python file and saved it in the appropriate directory, you will need to verify it’s permissions. Because this file launches the Python interpreter when the plug-in is run in GIMP, it will need to be executable. The easiest way to do this is to launch a terminal and navigate to the plugin directory and run chmod +x hello_world.py.

Terminal window


GIMP Python-Fu Code

Once you’ve verified your file is executable it’s time to get to coding. For this tutorial we are going to work top down instead of bottom up, so I’ll start with Python code in its entirety and explain it line by line.

The first line is the shebang that opens most executable files:

This tells the calling program, in this case GIMP, where to find the Python interpreter that will run the rest of the code in the file.

The next line imports the few entities we will need from the gimpfu module to run our script.

gimp is a module with interfaces to libgimp. register is the function that GIMP runs to register the plug-in with it’s plug-in system, and main is the function run by GIMP immediately after register.

In almost all of the sample code I’ve examined the gimpfu import line looked like:

This imports everything from the gimpfu module into the script’s namespace. PEP 8 tells us that it is best to avoid wildcard imports as, “… they make it unclear which names are present in the namespace, confusing both readers and many automated tools.” I’d be lying if I said that I didn’t often do it that way anyway.

Next is the heart of our plug-in, the function that is run when the plug-in is executed from within GIMP.

Keep in mind that the function consists of a single line because this is just a “Hello, World” program after all. In a regular plug-in, this function will likely be considerably longer and more complex.

The message method of the gimp module displays a dialog box in GIMP and prints the string passed to it. The newline character (\n) at the end of our message is important in that it affects how messages are displayed. For single line messages, gimp.message displays them in the status bar at the bottom of the image window. Multi-line messages, on the other hand, are displayed in a dialog box. Since this is what we want, we tack the newline on the end.

Our hello_world function takes no arguments, but this will rarely be the case for most plug-ins. Often a plug-in will work on an image, like in the case of a filter. In this instance an object representing the image is passed to the function. In general, the variables to be passed to the function will be set in the register function.

Before looking at our register function, it may be helpful to look at the register function header in the gimpfu.py source code:

In comparison, we call the register function like so:

The first argument, “hello_world”, is the GIMP procedure name. This is the name that GIMP uses internally to refer to the plug-in. This does not need to be the same as the function name in our script. You do need to worry about name collision within GIMP so try to be cognizant of the other plug-ins that GIMP is loading.

Note that “python-fu-” will automatically be prepended to the procedure name you provide unless it starts with any of the following: python-, python_, extension-, extension_, plug-in-, plug_in, file-, or file_. If your name starts with any of those it will remain unchanged.

The next argument is a blurb, a short description of what the plug-in does. In most cases, executing a plug-in first launches a dialog box to collect parameter values to be passed to the plug-in’s main function. The blurb is displayed in this dialog box.

The next argument, help, is a more in-depth description of what the plug-in does. This is displayed in the plug-in browser along with the 3 arguments that follow: author, copyright and date.

The label argument, in our case “Hello World”, is the label that is displayed in GIMP’s menu for our plug-in.

The imagetypes argument is a comma separated list of the types of images the plug-in works on, for example: RGB, CMYK, GRAY, etc. If your plug-in works with all images, you can use the wildcard “*”.

An important fact that took me a while to figure out is that if you want your plug-in to run regardless of whether or not there is actually an image open, then you should set the imagetype argument to an empty string (“”). Since our plug-in doesn’t operate on an image, we want to be able to run it whether there is an image open or not. If the imagetype is any other value including *, then the menu item for your plug-in will only be active when an image is open.

The params argument is a list of parameters that your plug-in’s function expects to receive. Our function’s argument list is empty, so we’ll leave this as an empty list, but gimpfu provides a list of constants used to create a wide variety of input methods from sliders and radio buttons to font selectors and brush selectors to file selectors. These input elements are used in the initial dialog box launched by GIMP to collect the parameter values your plug-in needs.

The results argument is analogous to the params argument except that it describes the results of the plug-in’s actions. I’m going to be honest. I just paraphrased this from what I could glean from online searches about this argument. I have never seen it in use and don’t honestly know what it does.

The function argument is the function in your plug-in script that is to be run when the plug-in is run. In our example this is hello_world. Note that we are passing the function object and not a string representing the function’s name.

The final argument we need is menu. This is how we define where exactly in GIMP’s menu system we want our plug-in to be found. This string starts with “<Image>”. Looking through the source, “<Save>” and “<Load>” are other options. It appears as though the register function automatically added items to params list based on which of these three were present. I have never seen anything other than “<Image>”  used, and as it stands, the register function does modify the params argument when this is used.

The remainder of the menu argument is a path the represents the menu hierarchy leading to the plug-in entry. If any element (sub-menu) in the path does not already exist, it will be created when the plug-in is registered. I wanted my Hello World plug-in to appear in a submenu labeled “Samples” under the top level “Filters” menu.

Gimp filters menu

 

With the register function behind us, we have just remaining line of code:

The main function is run by GIMP immediately after the register function. It is the main execution loop of the plug-in and GIMP’s persistent hook into the Python code.

And now the moment you’ve been waiting patiently for. Once the script has been written and debugged and GIMP has launched and registered the plug-in, choosing “Hello World” from the Samples sub-menu of the Filter menu yields:

Hello, GIMP World

I’ll be the first to admit that the execution of this plug-in is hardly exciting, but before you can start exploring all of the functions exposed by GIMP’s Python Database, you need to master the register function.

In my next post on this subject I hope to cover a more practical plug-in I have written – one that I use to batch process my photographs, sharpening and resizing them for publishing on the web. Look for it soon.

 Leave a Reply

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">

(required)

(required)