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! :)

Bye bye github

Microsoft announces the ruination of github.

Because apparently destroying skype, linkedin, hotmail, etc etc etc wasn’t enough.

I can’t fathom the rationale behind this. Apparently there’s an accounting thing that having lots of users means you’re worth lots of money. So, 7.5 billion.

BUT surely there’s nobody out there who doesn’t think that MS buying github will immediately lead to an exodus of most of its users? As far as I’m concerned it’s a given: MS buys github, github users leave en-masse. I know it’s what I’ll be doing.

So basically MS is buying a website which will no longer have any users for 7.5 billion. Good luck with that.

I’d find it funny if it wasn’t so tragic. I liked github. Just like I liked skype.

PHP: pretty print JSON as coloured HTML

Today I wanted a way to pretty-print a JSON string with colour highlighting. I went looking and found a bunch of ‘pretty print’ functions, but none with colour, so I implemented my own

Usage:

  1. Include the relevant CSS for formatting your prettified JSON. There’s example CSS in the code below. You can do:
    echo("<style>".Convert::jsonPrettyHtmlCSS()."</style>");
  2. Call Convert::json2PrettyHTML(), e.g:
    echo(Convert::json2PrettyHTML('["a",{"foo":"bar","baz":42}]'));

    ….And the code:

    
    <?php
    
    class Convert {
    	
    	/**
    	 * Helper for Convert::prettyJSON()
    	 * Returns a HTML <span> with a class matching the data type (integer,string,double,etc)
    	 * 	Add css to colour the values according to type.
    	 * 
    	 * autodetects numeric strings and treats them as numbers 
    	 * 
    	 * runs htmlentities() and wordwrap() on values (wraps at 100 chars)
    	 * 
    	 * @param mixed $val	value to beautify
    	 * @param int $indents	number of indents
    	 * @param bool $isKey	true if this is a key name
    	 * @return HTML
    	 * @see Convert::prettyJSON()
    	 * @see Convert::json2PrettyHTML() 
    	 * 
    	 */
    	private static function jsonColor($val,$indents=1,$isKey=false) {
    		//echo print_r($val,true) . ": " . gettype($val) . "\n";
    		$type = gettype($val);
    		
    		if (($type == "string") && is_numeric($val)) {
    			//try to convert it to a number
    			$val = floatval($val);
    			
    			if (intval($val) == $val)	//convert from float to int if it's a whole number: 
    				$val = intval($val);
    			
    			$type = gettype($val);
    		}
    		
    		//$type = gettype($val);
    		
    		$color = "";
    		switch($type) {
    			case 'string':
    				$val = '"' . $val . '"';
    				break;
    			case 'array':
    				$val = self::prettyJSON($val,$indents);
    				break;
    		}
    		$val = wordwrap(htmlentities($val),100,"<br />",true);
    		
    		if ($isKey) $type = $type . " key";
    		
    		return "<span class='$type'>" . //"' style='color:$color;'>" 
    			"$val</span>"; // . " (" . gettype($val) . ")";
    	}
    	
    	/**
    	 * Helper for Convert::json2PrettyHtml()
    	 * convert a value (i.e from json_decode) into a pretty colourised string
    	 * @param array|string|number $json		value to prettify
    	 * @param number $indents				indentation level (used for recursion)
    	 * @return string
    	 * @see Convert::json2PrettyHTML()
    	 */
    	private static function prettyJSON($json,$indents = 1) {
    		$ret = "";
    		$indent=str_repeat("<span class='indent'> </span>",$indents);
    		if (is_array($json) || is_object($json) ) {
    			foreach ($json as $k => $v) {
    				$k = htmlentities($k);
    				if (is_array($v) || is_object($v)) {
    					$v = self::prettyJson($v,$indents+1);
    					$ret .= ($ret ? ",<br />\n" : "") . $indent .
    						self::jsonColor($k,$indents,true) . ":\t<br />$v";
    				} else {
    					$ret .= ($ret ? ",<br />\n" : "") . $indent .
    						self::jsonColor($k,$indents,true) . ":\t" . self::jsonColor($v,$indents);
    				}
    			}
    			if (is_object($json)) {
    				$openbrace = "{";
    				$closebrace = "}";
    			} else {
    				$openbrace = "[";
    				$closebrace = "]";
    			}
    			$outdent=str_repeat("<span class='indent'> </span>",$indents-1);
    			$ret = "$outdent$openbrace<br />\n$ret<br />\n$outdent$closebrace";
    		} else
    			$ret = self::jsonColor($json,$indents);
    		
    		return $ret;
    		
    	}
    	
    		/**
    	 * Return or add some CSS for json2PrettyHTML to the requirements
    	 * @param string $return	if true, return the CSS. Otherwise insert it using Requirements::customCSS()
    	 * @return string | void
    	 * @see Convert::json2PrettyHTML()
    	 */
    	public static function jsonPrettyHtmlCSS($return = true) {
    		return 'span.json .integer, span.json .double {
    				color: #700;
    				font-family: mono;
    			}
    			
    			span.json .string {
    				color: #070;
    				font-family: mono;
    			}
    			
    			
    			span.json .key.string {
    				color: #007;
    			}
    			
    			span.json .key.integer, span.json .key.double {
    				color: #707;
    			}
    			
    			
    			span.json .indent {
    				padding-left: 40px;
    			}';
    	}
    	
    	/**
    	 * Converts a JSON string to pretty, readable HTML output which can be 
    	 * 	colourised/customised via CSS
    	 * 
    	 * Also does other nice things, like word wrapping at 100 chars, running 
    	 * 	values through htmlentities(), and treating numeric strings as numbers
    	 * 
    	 * Include CSS to style the output (set colours, indent width, etc)
    	 * Notes: 
    	 * 		- everything will be wrapped in a span.json (i.e <span> with 'json' 
    	 * 			as the class, css: span.json)
    	 * 		- keys will be spans with the'key' class  ( e.g span.key )
    	 * 		- values and keys will be spans and will have the datatype as the 
    	 * 			class ( span.integer, span.key.integer)
    	 * 		- there will be empty spans with the 'indent' class in the 
    	 * 			appropriate places. There may be more than one consecutively. 
    	 * 
    	 * Example CSS is returned by the jsonPrettyHtmlCSS() function
    			
    	 * @param string $json	the json to beautify
    	 * @return HTML
    	 * @see Convert::jsonPrettyHtmlCSS()
    	 */
    	public static function json2PrettyHTML($json) {
    		return "<span class='json'>" . self::prettyJSON(json_decode($json)) . "</span>";
    	}
    }
    
    
    
    

    I hope someone finds this useful! :)

kgrep

Ladies and gentlemen, presenting: kgrep – kill-grep

This is a bash function which allows you to type in a search term and kill matching processes. You will be prompted to kill each matching process for your searchterm.

You can also optionally provide a specific signal to use for the kill commands (default: 15)

Usage: kgrep [<signal>] searchterm

Signal may be -2, -9, or -HUP (this could be generalised but I CBF).

search term is anything grep recognises.

kgrep() {
    #grep for processes and prompt whether they should be killed
    if [ -z "$*" ]; then
        echo "Usage: $0 [-signal] searchterm"
        echo -e "\nSearches for processes matching  and prompts to kill them."
        echo -e "signal may be:\n\t-2\n\t-9\n\t-HUP\n to send a different signal (default: TERM)"
        return 0
    fi  
    SIG="-15"
	#yes, this could be more sophisticated
    if [ "$1" == "-9" ] ||  
        [ "$1" == "-2" ] ||
        [ "$1" == "-HUP" ]; then 
        SIG="$1"
        shift
    fi  
    #we need to unset the field separator if ^C is pressed:
    trap "unset IFS; return 0" KILL
    trap "unset IFS; return 0" QUIT
    trap "unset IFS; return 0" INT 
    trap "unset IFS; return 0" TERM

    IFS=$'\n'
	for l in `ps aux | grep "$*" | grep -v grep `; do
        echo $l
        pid=`echo $l | awk '{print $2}'`
        read -p "Kill $pid (n)? " a
        if [[ "$a" =~ [Yy]([Ee][Ss])? ]]; then
            echo kill $SIG $pid
            kill $SIG $pid
        fi
    done
    unset IFS
}

Click To Print

Here’s a nifty little piece of javascript I whipped up the other day in response to a client request.

With this code (and jquery) on a web page an element on a web page with the “clicktoprint” class becomes clickable. When clicked, it is printed, but only that element. In addition, the element will be scaled to the full width of the page.

I was asked to do this so that a client could have a voucher on their website which you could click on and have it printed. Their previous solution (using a ‘print’ media query in the site css) meant that the rest of the page could never be printed. This code injects a new piece of css for the duration of the special print and removes it afterwards, allowing the rest of the page to be printed by the regular means.

<script type="text/javascript">
jQuery(document).ready(function() {
	jQuery('.clicktoprint').click(function() {
		jQuery(this).parents().each(function(idx,i) {
			jQuery(i).addClass('clicktoprint-parent');
		});
		jQuery('head').append('<style id="clicktoprint-style">@media print { * { display: none; } .clicktoprint, .clicktoprint-parent {display: block !important; width: 100% !important;} }</style>');
		window.print();
		jQuery('style#clicktoprint-style').remove();
		return false;
	});
});
</script>

Dear the entire world

You don’t need to quote database column and table names in queries unless they contain special characters like spaces.

This applies for every database engine and every dialect of SQL I’ve ever used – quoting column names is always optional.

So why the fuck do you insist on writing this in your php codez?

$query=”SELECT \”some_ordinary_column\” from \”some_table\” where \”some_table\”.\”some_column\” = \”some_value\”"

Are you a masochist who loves escaping things or what?

How much more readable is this:
$query=’SELECT some_ordinary_column from some_table where some_table.some_column = “some_value”‘

The funny thing is that the type of people who write this garbage are the same type of people who tell you that using an if statement without braces is “bad style”. lol.

Logging is necessary

Unless you’re me, you’re less awesome than you think you are.

(I’m more awesome than I think I am. This is not a paradox)

Therefore, when you write a mission-critical piece of code, you need a logging system

Your logging system needs to have different types or log message: error and debug at the bare minimum.

Your code needs to log every action it takes.

This might be expensive or difficult. Tough shit. If it’s important, it needs to be logged – you must be able to go back over a particular execution and determine what happened. This is not optional.

This is a good rule even for not-important code. It makes debugging SO much easier. There are approximately 100 billion logging systems available, use a library if you must. Or you could write your own in 10 minutes.

Let’s discuss! Give me an example of a situation where logging is undesirable for important code, and I’ll tell you why you’re wrong… ;)

Converting red-blue anaglyph to stereoscopic images

(EDIT: Updated to add black border between images – makes it easier to see the 3d, and makes the 3d image better defined)

I hate those red-blue anaglyphs. The red and blue fucks with my head – my brain refuses to interpret it properly, and the object does this wierd “flashing” between red and blue.

Plus, I’m too cheap to buy (and too reckless to keep) a pair of those red-blue 3D glasses.

So, I installed Imagemagick and wrote myself a bash function:

stereo_convert () {                                                                 
    in="$1"                                                                   
    out="$2"                                                           
    if [ -z "$in" ] || [ -z "$out" ]; then                                    
        echo -e "\nYou need to supply input and output files!\n"              
        return 42                                                             
    fi                                                                        
    convert \( $in -gravity east -background Black -splice 10x0 -gamma 1,0,0 -modulate 100,0 \) \( $in -gamma 0,1,1 -modulate 100,0 \) +append $out;                  
    echo -e "\nConverted red-blue stereo image '$in' to side-by-side image '$out'.\n"
} 

Here’s a demo image from NASA’s Pathfinder mission.

Input:

Anaglyph image of Pathfinder

Output:

Stereoscopic Pathfinder

Notes:

  • This process removes all colour information, giving you greyscale output. Unfortunately there’s no way to restore full colour to anaglyphs, as the full colour information isn’t there. IMHO greyscale is better than red/blue.
  • The images may not be exactly perfect due to:
    • Red and cyan do not have the same intensity to the human eye – cyan seems brighter, so the right eye may appear slightly lighter. I’ve done my best to eliminate this, but I CBF reading into the science of colour wavelengths etc. right now.
    • Some images may be reversed – it appears that there’s no “hard” convention as to which eye should be red and which should be blue. But it appears that “most” are red==left.

creating a self-extracting bash script

You always see things like vmware and unreal tournament being installed via a self-extracting bash script – It would seem that this is the best way to provide an installer which will work on the widest selection of Linux distributions.

After some googlage, I came up with the following. Given a tarball and an installer script named ‘installer’, it will create a self-extracting bash script:

#!/bin/bash
#############################################################
# Self-extracting bash script creator
# By Dale Maggee
# Public Domain
#############################################################
#
# This script creates a self-extracting bash script
# containing a compressed payload.
# Optionally, it can also have the self-extractor run a
# script after extraction.
#
#############################################################
VERSION='0.1'

output_extract_script() {
#echoes the extraction script which goes at the top of our self-extractor
#arguments:
# $target - suggested destination directory (default: somewhere in /tmp)
# $installer - name of installer script to run after extract
# (if specified, $target is ignored and /tmp is used)

#NOTE: odd things in this function due to heredoc:
# - no indenting
# - things like $ and backticks need to be escaped to get into the destination script

cat <<EndOfHeader
#!/bin/bash
echo "Self-extracting bash script. By Dale Maggee."
target=\`mktemp -d /tmp/XXXXXXXXX\`
echo -n "Extracting to \$target..."

EndOfHeader

#here we put our conditional stuff for the extractor script.
#note: try to keep it minimal (use vars) so as to make it nice and clean.
if [ "$installer" != "" ]; then
#installer specified
echo 'INSTALLER="'$installer'"'
else
if [ "$target" != "" ]; then
echo '(temp dir: '$target')'
fi
fi

cat <<EndOfFooter

#do the extraction...
ARCHIVE=\`awk '/^---BEGIN TGZ DATA---/ {print NR + 1; exit 0; }' \$0\`

tail -n+\$ARCHIVE \$0 | tar xz -C \$target

echo -en ", Done.\nRunning Installer..."

CDIR=\`pwd\`
cd \$target
./installer

echo -en ", Done.\nRemoving temp dir..."
cd \$CDIR
rm -rf \$target
echo -e ", Done!\n\nAll Done!\n"

exit 0
---BEGIN TGZ DATA---
EndOfFooter
}

make_self_extractor() {

echo "Building Self Extractor: $2 from $1."

if [ -f "$3" ]; then
installer="$3"
echo " - Installer script: $installer"
fi

if [ "$4" != "" ]; then
target="$4"
echo " - Default target is: $target"
fi

src="$1"
dest="$2"
#check input...
if [ ! -f "$src" ]; then
echo "source: '$src' does not exist!"
exit 1
fi
if [ -f "$dest" ]; then
echo "'$dest' will be overwritten!"
fi

#ext=`echo $src|awk -F . '{print $NF}'`

#create the extraction script...
output_extract_script > $dest
cat $src >> $dest

chmod a+x $dest

echo "Done! Self-extracting script is: '$dest'"
}

show_usage() {
echo "Usage:"
echo -e "\t$0 src dest installer"

echo -en "\n\n"
}


############
# Main
############

if [ -z "$1" ] || [ -z "$2" ]; then
show_usage
exit 1
else
make_self_extractor $1 $2 $3
fi