Python skriptování GIMPu
Navazuji na tutorial Pavla Tišnovského o skriptování v GIMPu (1.část, 2.část). Přišel zrovna v době, kdy jsem potřeboval něco malého naskriptovat. V Pavlově textech pluginy v pythonu vždy vytvoří nový obrázek, a něco s ním dělají. Já jsem potřeboval pracovat s obrázkem existujícím, a protože mi chvíli trvalo, než jsem zjistil jak na to, tak se zde podělím o své postřehy.
Aktivní obrázek
Když píšete plugin, Vaše funkce dostane parametry, o které si řeknete, např. image a drawable. Jenže dokud si hrajete a API, potřebujete je ten obrázek v interaktivní konzoli. Nic jako právě aktivní obrázek v API není, což se dá celkem pochopit, protože v GIMPu můžete mít desítky otevřených obrázků, a konzole je jen jedna. Takže si musíme pomoci takto:
>>> gimp.image_list()
[<gimp.Image 'thumb_up.xcf'>]
>>> img = gimp.image_list()[0]
>>> type(img)
<type 'gimp.Image'>
A z obrázku můžeme získat gimp.Image.active_drawable
. Existuje taky gimp.Image.active_layer
.
>>> img.active_drawable
<gimp.Layer 'Layer'>
>>> img.active_layer
<gimp.Layer 'Layer'>
>>> img.active_layer == img.active_drawable
True
Obrázek je typu gimp.image
, takže Procedure Browseru zkusíme napsat gimp.image
.
Nabídne se nám řada funkcí. Pokračujeme
hledáním gimp.image.active_drawable
, a zjistíme, že je deprecated. Přitom jsme zjistili, že
active_layer
tam ani není, ale budeme teď používat funkce get_active_drawable
a get_active_layer
.
Do akce
Co vlastně budeme dělat? Mám několik obrázků, každý má 3 vrstvy. Potřebuju je vyrenderovat do png, v šířce 300px, jednu verzi s viditelnou pouze jednou konkrétní vrstvou a druhou verzi s ostatníma vrstvama. Jak na to?
Budu pracovat na kopii, to znamená ai = img.duplicate()
. Co se stane, když mu změníme velikost?
ai.gimp_image_scale(300, 300*ai.width/ai.height)
>>> ai.scale(300, 300*ai.height/ai.width)
>>> ai.width, ai.height
(300, 318)
>>> [(i.width, i.height) for i in ai.layers]
[(300, 318), (300, 318), (300, 318)]
Jednotlivé vrstvy taky vypadají vpořádku. Teď ta viditelnost. Moje vrstvy mají jména. Nejprve chci vidět všechny, kromě belt.
>>> for i in ai.layers:
... i.visible = i.name != "belt"
Vypadá to, že správná cesta je spojit vrstvy do jedné, a potom obrázek uložit. A protože budu s obrázkem ještě pracovat, znamená to další duplikát.
>>> dup = ai.duplicate()
>>> merged = dup.merge_visible_layers(0)
>>> pdb.file_png_save(dup, merged,
"/home/petr/temp_img_0.png", "temp_img_0.png",
0, 9, 1, 1, 1, 1, 1)
Celý plugin
Funkce register
je trošku problém. Nejlepší dokumentace, kterou jsem našel je zde:
https://www.gimp.org/docs/python/.
Jenže popis parametrů tam úplně neodpovídá tomu, co použil Pavel Tišnovský ve druhé části svého článku.
Přiznám se, že jsem nehledal úplně zevrubně, a spokojil jsem se s verzí, která mi fungovala.
Skript patří, jak víte, do ~/.gimp-2.8/plug-ins/
nebo tak nějak, a musí být
spustitelný.
#!/usr/bin/env python
import os.path
from gimpfu import (
register, pdb, main,
PF_IMAGE,
)
BACKGROUND_LAYER_NAME = "belt"
def save_300xYYY_l(img):
"""
Scales the current image to 300 x YYY (proportionaly). Then exports 2 png
files. The first one with all layers but the one named ``belt`` visible.
The second one just with the layer named ``belt`` visible.
The files are saved into the original's location with the same name with
the _fg.png and _bg.png suffix.
All operations are performed on the copy, the original image is not touched.
"""
ai = img.duplicate()
ai.scale(300, 300*ai.height/ai.width)
path = os.path.dirname(img.filename)
base_fn = os.path.basename(img.filename)
if not base_fn.endswith(".xcf"):
raise Exception("The base filename does not end with .xcf!")
base_fn = base_fn[:-4]
dup = ai.duplicate()
for i in dup.layers:
i.visible = BACKGROUND_LAYER_NAME != i.name
merged = dup.merge_visible_layers(0)
fn = base_fn + "_fg.png"
pdb.file_png_save(dup, merged,
os.path.join(path, fn), fn,
0, 9, 1, 1, 1, 1, 1)
dup = ai.duplicate()
for i in dup.layers:
i.visible = BACKGROUND_LAYER_NAME == i.name
merged = dup.merge_visible_layers(0)
fn = base_fn + "_bg.png"
pdb.file_png_save(dup, merged,
os.path.join(path, fn), fn,
0, 9, 1, 1, 1, 1, 1)
register(
"save_300xYYY_l", # name
"Save 300x??? fg and bg image", # short description
save_300xYYY_l.__doc__, # help
"Petr Blahos", # author
"Public Domain", # copyright
"2017-02-22", # date
"Save 300x??? pngs", # menu entry name
"*", # image types
[
(PF_IMAGE, "img", "Image", None),
], # parameters
[], # result
save_300xYYY_l, # function
menu="<Image>/Filters/Petr/", # menu path
)
main()
Závěr
Výhoda pythonu je, že si v něm poměrně snadno poradíte. API nepracuje s integery (jak by naznačoval
Procedure Browser), ale přímo s objetky, takže třeba gimp.image_list
Vám nevrátí list integerů, ale přímo
list obrázků. Nevoláte pak gimp-image-resize
, ale image.resize
, tak, jak byste v pythonu čekali.
Pamatujte na svého dobrého kamaráda dir
, např. [i for i in dir(gimp.Image) if "layer" in i]
.
Celkem by se mi líbilo, kdyby to šlo celé vzít obráceně. Tedy, nepsat plugin do GIMPu, ale napsat skript v pythonu, který bude volat GIMP přes API.
Komentáře byly zrušeny
V EU teď máme složitou situaci s Cookies. Na komentáře jsem používal jistou službu třetí strany. Ta však používá Cookies poměrně, ehm, benevolentně. Tak jsem se rozhodl komentáře zrušit. Pokud chcete, můžete mi napsat přímo