Gimp-Forum.net
My first python plugin review - Printable Version

+- Gimp-Forum.net (https://www.gimp-forum.net)
+-- Forum: GIMP (https://www.gimp-forum.net/Forum-GIMP)
+--- Forum: Extending the GIMP (https://www.gimp-forum.net/Forum-Extending-the-GIMP)
+---- Forum: Scripting questions (https://www.gimp-forum.net/Forum-Scripting-questions)
+---- Thread: My first python plugin review (/Thread-My-first-python-plugin-review)

Pages: 1 2


My first python plugin review - origamifreak2 - 12-28-2024

Hello Everyone,

I have just finished creating my first gimp plugin and I wanted to see if there was anything I should do differently, like best practices to follow, etc. The plugin works as is, but is there any way to improve it?

Here's a link to the plugin code https://github.com/origamifreak2/Gimp-Plugins/blob/main/sixteen_by_nine.py

The plugin converts an image to a 16:9 aspect ratio and ads a blurred background as padding.

[Image: origami_crane_square.jpg]

[Image: origami_crane_16x9.jpg]


RE: My first python plugin review - Ofnuts - 12-28-2024

Quite decent. To nitpick a bit:
  • Starting names with underscores in Python has a special meaning, so no point in using _image and _drawable instead of image and drawable
  • forground is spelled foreground
  • speaking of foreground, it would be more proper IMHO to make a copy to use it as the image background
  • the  drawable that you get as an argument could be a group, a layer mask, or a channel, you should probably bail out with a message for the last two. The group would be doable (the copy() makes a complete duplicate of the group, and you can flatten one of the two groups to blur the result).
  • you can get the width and height of the image using the attributes of the image object: image.width, image.height, which is more readable than calling the PDB.
  • In Python v2, an operation between int and float produces a float, so you can drop several uses of float() by using float constants: float(canvas_height) / 9 is also canvas_height/9.
  • your if/elif has no else... which happens when the image is already 16/9 (*).
  • lots of  common code between the two if/elif blocks. Some of it could be taken out. Personally, I would just set the new canvas/width or height. Then outside of the conditions the code would take care of offsets and such. Yes, the computations would yield  0 for one of the values...
  • And when you do the above, you can remove the if/elif, because you can write: canvas_width = max(width_16_9,canvas_width) and same for height. And your code miraculously behaves if the image is already 16/9.
  • I don't see the purpose of computing bg_scale, since anyway the background will be sized to the new  canvas dimensions that you already have.
  • your blur radius could be dependent on the image size: a 90 blur isn't the same on a 200x200 image and a 2000x2000 image.
  • you should bracket all your code except initial checks between image.undo_group_start()/image.undo_group_end() so that the whole effect of the script can be undone with a single Ctrl-Z.
15/20.

"Perfection isn't when there is nothing to add, but when there is nothing left to remove."
Antoine de Saint-Exupery 

(*) if statements are useful but dangerous, because they are a major source of bugs. Trying to avoid them is the best way to understand what is going on in your code.



RE: My first python plugin review - origamifreak2 - 12-28-2024

Thanks Ofnuts Smile  .

I've implemented most of your suggestions. The only ones I didn't are listed below with reasons why
  • lots of  common code between the two if/elif blocks. Some of it could be taken out. Personally, I would just set the new canvas/width or height. Then outside of the conditions the code would take care of offsets and such. Yes, the computations would yield  0 for one of the values...
The code is very similar, but the values inside the if/elif blocks need to be calculated based on the canvas_width or canvas_height respectively.
  • I don't see the purpose of computing bg_scale, since anyway the background will be sized to the new  canvas dimensions that you already have.
The background will actually overflow the new canvas size (see attached image). If I just resized the image to the new canvas size, it wouldn't have the same aspect ratio and would be squished horizontally or vertically

Let me know if there's anything else that can be improved.


RE: My first python plugin review - gasMask - 12-29-2024

at line 55
Code:
# blur background
blur_radius = bg_height * bg_width / 35000
pdb.plug_in_gauss(image, background, blur_radius, blur_radius, 0)

The plug_in_gauss will crash if the 'blur_radius' exceeds 500, so I would check for overflow.
Code:
# blur background
blur_radius = min(500., bg_height * bg_width / 35000)
pdb.plug_in_gauss(image, background, blur_radius, blur_radius, 0)



RE: My first python plugin review - origamifreak2 - 12-29-2024

Thanks gasMask Smile . I've added your code in.


RE: My first python plugin review - Ofnuts - 12-29-2024

(12-28-2024, 10:49 PM)origamifreak2 Wrote: Thanks Ofnuts Smile  .

I've implemented most of your suggestions. The only ones I didn't are listed below with reasons why
  • lots of  common code between the two if/elif blocks. Some of it could be taken out. Personally, I would just set the new canvas/width or height. Then outside of the conditions the code would take care of offsets and such. Yes, the computations would yield  0 for one of the values...
The code is very similar, but the values inside the if/elif blocks need to be calculated based on the canvas_width or canvas_height respectively.
  • I don't see the purpose of computing bg_scale, since anyway the background will be sized to the new  canvas dimensions that you already have.
The background will actually overflow the new canvas size (see attached image). If I just resized the image to the new canvas size, it wouldn't have the same aspect ratio and would be squished horizontally or vertically

Let me know if there's anything else that can be improved.

Now you have a problem, if your image is already 16/9 you exit without closing the undo group (and with a useless layer copy). You can delay the layer copy until after you have done all your size computations and tests, so that when everything is OK, you start the undo group, do the copy/resize/blur, and then close the undo group. This has the added benefit of not marking the image "dirty" is nothing happened.


RE: My first python plugin review - origamifreak2 - 12-31-2024

Thanks again Ofnuts Smile .

I moved the check for 16:9 to the top of the code and the creation of the background layer inside the undo/redo group.


RE: My first python plugin review - origamifreak2 - 12-31-2024

I have a question. The gimp_drawable_type() function on line 9 is returning 0 for jpegs, 1 for pngs, and 1 for layer groups. How would I tell the difference between a single layer in a png file and a layer group? I'm probably missing something very simple here.

Code:
   # make sure drawable is a layer
   drawable_type = pdb.gimp_drawable_type(drawable)
   if drawable_type != 0 and drawable_type != 1:
       pdb.gimp_message(drawable_type)
       pdb.gimp_message("Please select a layer")
       return



RE: My first python plugin review - Ofnuts - 12-31-2024

(12-31-2024, 09:40 PM)origamifreak2 Wrote: I have a question. The gimp_drawable_type() function on line 9 is returning 0 for jpegs, 1 for pngs, and 1 for layer groups. How would I tell the difference between a single layer in a png file and a layer group? I'm probably missing something very simple here.

Code:
   # make sure drawable is a layer
   drawable_type = pdb.gimp_drawable_type(drawable)
   if drawable_type != 0 and drawable_type != 1:
       pdb.gimp_message(drawable_type)
       pdb.gimp_message("Please select a layer")
       return

gimp_drawable_type() isn't about Layer/Channel/Group but about color and alpha support, O being RGB and 1 beibg RGB+alpha.

To test the drawable "genre" you use Python classes with isinstance() with the caveat that a layer group is gimp.GroupLayer but since GroupLayer is a subclass of  Layer, it is also a group. So typically:

Code:
if isinstance(drawable,gimp.Channel): # handles both channels and masks
    complain_and_exit()
elif isinstance(drawable,gimp.GroupLayer): # is a group
    process_group(drawable)
else: # should be a layer
   process_layer(drawable)



RE: My first python plugin review - origamifreak2 - 01-01-2025

Thanks Ofnuts  Smile  . I updated my code. I also added some logic to convert the image to RGB mode if it's indexed.