Here's a new version that displays a dialog to prompt you for the descriptions of the individual layers.
@Admins. Can you please enable attaching files with a .py extension?
#!/usr/bin/env python
# Author: Kevin Payne
# Copyright 2011 Kevin Payne
# License: GPL v3
# GIMP plugin to export the layers of an .xcf as png images
# each png file has the name of the layer
# For PhotoComix:- http://gimpchat.com/viewtopic.php?f=9&t=985
# The saving layer name in a file was requested here:
# http://gimpchat.com/viewtopic.php?f=9&t=978&sid=36599f7fc54901155c684afdfc03a5c2&p=10910#p10872
# Version 2.0 19.01.2011 - Use PyGTK to create a dialog box to prompt for the user descriptions
from gimpfu import *
import os.path
import pygtk
import gtk
gettext.install("gimp20-python", gimp.locale_directory, unicode=True)
# something to help with debugging
def debugMessage(Message):
dialog = gtk.MessageDialog(None, 0, gtk.MESSAGE_INFO, gtk.BUTTONS_OK, Message)
dialog.run()
dialog.hide()
# unique_filename from http://stackoverflow.com/questions/183480/is-this-the-best-way-to-get-unique-version-of-filename-w-python
def unique_filename(file_name):
counter = 1
file_name_parts = os.path.splitext(file_name) # returns ('/path/file', '.ext')
while os.path.isfile(file_name):
file_name = file_name_parts[0] + '_' + str(counter) + file_name_parts[1]
counter += 1
return file_name
def format_layer_name_for_saving(layer_name):
#[G'MIC] Graphic Novel FX : -gimp_photocomix_booost2 1,4.36364,10.4727,5,20,0.12,14,0,0.5,0.54,2.90909,9,0.936364,1,0.5,0.54,0.78
# into {USER_INPUT_HERE}{Graphic Novel FX}{gimp_photocomix_booost2}{gimp_photocomix_booost2}{1}{4.36364}{10.4727}{5}{20}{0.12}{14}{0}{0.54}{2.9}{9}{0}{0.93}{1}{0.5}{0.54}{0.78}
# Throw away the [G'MIC]
new_layer_details = layer_name.replace("[G'MIC] ", "")
temp = new_layer_details.find(":")
label = new_layer_details[0:temp-1]
new_layer_details = new_layer_details[temp+2:]
# extract the function name
temp = new_layer_details.find(" ")
function = new_layer_details[0:temp-1]
function = function.lstrip("-")
new_layer_details = new_layer_details[temp+1:]
# replace the commas with curly brackets
new_layer_details =new_layer_details.replace("," , "}{")
return "{" + label + "}{" + function + "}{" + function + "}{" + new_layer_details + "}"
def photocomix_safe_file_name(file_name):
# replace any illegal characters to be on the safe side
file_name = file_name.replace(":", ";")
file_name = file_name.replace("/", ";")
file_name = file_name.replace("\\", ";")
file_name = file_name.replace("?", ";")
file_name = file_name.replace("*", ";")
file_name = file_name.replace("\"", ";")
return file_name
# this is the bit that saves the layers
def export_layers_as_png(img):
layer_ids = img.layers # get the layers in the image
directory_name = os.path.dirname(img.filename) # where is the source image
gimp.progress_init()
num_layers = len(layer_ids)
for layer_num in range (0, num_layers): # work through layers (change to "num_layers-1" if you don't want the bottom layer)
layer_name = pdb.gimp_drawable_get_name(layer_ids[layer_num])
layer_name = photocomix_safe_file_name(layer_name)
# build the new file path - puts the saved layers in the same place as the source image
png_file = os.path.join(directory_name, layer_name + ".png")
png_file = unique_filename(png_file)
pdb.file_png_save2(img, layer_ids[layer_num], png_file, png_file, 1, 9, 1, 1, 0, 1, 1, 1, 1)
gimp.progress_update(layer_num/num_layers*100)
# The End of the main routine
# menu registration
register(
"python-fu-export-layers-as-png",
"Export Layers as PNGs",
"Export Layers as PNGs",
"paynekj",
"GimpChat",
"12.01.2011",
"Export Layers as separate PNGs",
"*",
[
(PF_IMAGE, "image", "Input image", None),
],
[],
export_layers_as_png,
menu="<Image>/contributed/"
)
# This is the old one is to format the layer names and save them in a text files
#def photocomix_save_layer_names(img, layer_descriptions):
# layer_ids = img.layers # get the layers in the image
# directory_name = os.path.dirname(img.filename) # where is the source image
#
# layer_description_list = layer_descriptions.split(",") # make a list out of the user input string
#
# num_layers = len(layer_ids) # how many layers are there in total
# text_file_name = "" # place to store the name of the text file
# text_file_contents = "" # place to store the contents of the text file
# description_num = 0 # index for the user supplied list
# for layer_num in range (0, num_layers): # work through layers
# layer_name = pdb.gimp_drawable_get_name(layer_ids[layer_num])
# if (layer_name[0] == "["): # only work on layers that have a name starting with [
# this_layer_description = layer_description_list[description_num] # get the user supplied description for this layer
# description_num += 1
# formatted_line = "{" + this_layer_description + "}" + format_layer_name_for_saving(layer_name) # build the line to be written in the file
# text_file_contents = text_file_contents + formatted_line + "\r\n"
#
# # if this is the first layer to be saved then get the user description to use as the file name
# if (len(text_file_name) == 0):
# parts = formatted_line.split("{")
# text_file_name = parts[1]
# text_file_name = text_file_name.rstrip("}") + "_preset.txt"
#
# text_file_name = photocomix_safe_file_name(text_file_name) # make sure the filename is safe to use
#
# textFile = open(os.path.join(directory_name,text_file_name) , "w")
# textFile.write(text_file_contents)
# textFile.close
# debugMessage(text_file_name + " Saved")
#
#register(
# "python-fu-photocomix-save-layer-names",
# "Save Layer Names Old",
# "Save Layer Names",
# "paynekj",
# "GimpChat",
# "13.01.2011",
# "Save layer names in a file",
# "*",
# [
# (PF_IMAGE, "image", "Input image", None),
# (PF_STRING, "layer_descriptions", "Layer Descriptions:", "Layer1,Layer2"),
# ],
# [],
# photocomix_save_layer_names,
# menu="<Image>/contributed/"
# )
# This class was created to pop up a dialog to prompt the user to enter the layer descriptions each in their own
# entry box - just for ease of use.
class getUserDescriptionsDialog:
# Things to do with the image reference
imageRef = 0
def set_imageRef(self,Image):
self.imageRef = Image
def get_imageRef(self):
return self.imageRef
# handling the ids of the various entry boxes
entryBoxRef = []
def add_entryBoxRef(self,entryRef):
self.entryBoxRef = self.entryBoxRef + [entryRef]
# debugMessage("hello boxref")
def get_entryBoxRefList(self):
return self.entryBoxRef
# handling the id of the wanted layers
wantedLayerIds = []
def add_wantedLayerId(self, layer_id):
self.wantedLayerIds = self.wantedLayerIds + [layer_id]
def get_wantedLayerIdsList(self):
return self.wantedLayerIds
# Routine to save the layer names in a text file in a very particular format
def save_layer_names(self, widget, Data):
img = self.get_imageRef()
wanted_layers = self.get_wantedLayerIdsList()
num_wanted_layers = len(wanted_layers)
entryBoxes = self.get_entryBoxRefList()
text_file_contents = ""
for layer_num in range (0, num_wanted_layers): # work through layers
layer_name = pdb.gimp_drawable_get_name(wanted_layers[layer_num])
this_layer_description = entryBoxes[layer_num].get_text() # get the user supplied description for this layer
# debugMessage(this_layer_description)
formatted_line = "{" + this_layer_description + "}" + format_layer_name_for_saving(layer_name) # build the line to be written in the file
text_file_contents = text_file_contents + formatted_line + "\r\n"
text_file_name = entryBoxes[0].get_text() + "_preset.txt" # first user description used as file name
text_file_name = photocomix_safe_file_name(text_file_name) # make sure the filename is safe to use
directory_name = os.path.dirname(img.filename) # where is the source image
# wriet out the text file
textFile = open(os.path.join(directory_name,text_file_name) , "w")
textFile.write(text_file_contents)
textFile.close
self.destroy(widget) # goodbye dialog window
debugMessage(text_file_name + " Saved") # all done
# This is a callback function. The data arguments are ignored
# in this example. More on callbacks below.
def hello(self, widget, data=None):
print "Finished"
def delete_event(self, widget, event, data=None):
# If you return FALSE in the "delete_event" signal handler,
# GTK will emit the "destroy" signal. Returning TRUE means
# you don't want the window to be destroyed.
# This is useful for popping up 'are you sure you want to quit?'
# type dialogs.
print "delete event occurred"
# Change FALSE to TRUE and the main window will not be destroyed
# with a "delete_event".
return False
# Another callback
def destroy(self, widget, data=None):
Image = self.get_imageRef()
# Image.undo_group_end()
gtk.main_quit()
# things to do after the instance has been created but before doing any work
def setup(self, widget, Data):
img = Data[0]
self.set_imageRef(img) # set the image reference
layer_ids = img.layers # get the layers in the image
num_layers = len(layer_ids) # how many layers are there in total
for layer_num in range (0, num_layers): # work through layers
layer_name = pdb.gimp_drawable_get_name(layer_ids[layer_num])
if (layer_name[0] == "["): # only work on layers that have a name starting with [
thisEntry_box = gtk.VBox(False, 0) # box to put the label/entry box pair into
thisLabel = gtk.Label(layer_name[0:40]) # label
thisEntry = gtk.Entry(max=0) # entry box
self.add_entryBoxRef(thisEntry) # keep the ref of the entry box
self.add_wantedLayerId(layer_ids[layer_num]) # keep the id of this layer
thisEntry.set_text("Layer" +str(layer_num))
# Pack everything in and make it visible
thisEntry_box.pack_start(thisLabel, True, True, 1)
thisEntry_box.pack_start(thisEntry, True, True, 1)
thisLabel.show()
thisEntry.show()
thisEntry_box.show()
self.entry_box.pack_start(thisEntry_box, True, True, 10)
# Image.undo_group_start()
def __init__(self):
# create a new window
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
self.window.set_title("Get User Descriptions")
# When the window is given the "delete_event" signal (this is given
# by the window manager, usually by the "close" option, or on the
# titlebar), we ask it to call the delete_event () function
# as defined above. The data passed to the callback
# function is NULL and is ignored in the callback function.
self.window.connect("delete_event", self.delete_event)
# Here we connect the "destroy" event to a signal handler.
# This event occurs when we call gtk_widget_destroy() on the window,
# or if we return FALSE in the "delete_event" callback.
self.window.connect("destroy", self.destroy)
# Sets the border width of the window.
self.window.set_border_width(10)
# We create a box to pack widgets into. This is described in detail
# in the "packing" section. The box is not really visible, it
# is just used as a tool to arrange widgets.
self.window_box = gtk.VBox(False, 0)
self.window.add(self.window_box)
self.title_box = gtk.HBox(False, 0) # For a dialog title/explanation
self.entry_box = gtk.VBox(False, 0) # For the entry boxes
self.button_box = gtk.HBox(False, 0) # For the buttons
self.window_box.pack_start(self.title_box, True, True, 5)
self.window_box.pack_start(self.entry_box, True, True, 5)
self.window_box.pack_start(self.button_box, True, True, 10)
titleText = gtk.Label("Enter descriptions for each layer:")
self.title_box.pack_start(titleText, True, True, 10)
titleText.show()
# Creates a new button with the label "OK".
self.ok_button = gtk.Button("OK")
self.ok_button.connect("clicked", self.save_layer_names, None)
# Creates a new button with the label "Cancel".
self.cancel_button = gtk.Button("Cancel")
# When the button receives the "clicked" signal, it will call the
# function hello() passing it None as its argument. The hello()
# function is defined above.
self.cancel_button.connect("clicked", self.hello, None)
# This will cause the window to be destroyed by calling
# gtk_widget_destroy(window) when "clicked". Again, the destroy
# signal could come from here, or the window manager.
self.cancel_button.connect_object("clicked", gtk.Widget.destroy, self.window)
# This packs the buttons into their box (a GTK container).
self.button_box.pack_start(self.ok_button, True, True, 10)
self.button_box.pack_start(self.cancel_button, True, True, 10)
# The final step is to display this newly created widgets.
self.ok_button.show()
self.cancel_button.show()
# Show everything
self.title_box.show()
self.entry_box.show()
self.button_box.show()
self.window_box.show()
self.window.show()
def main(self):
# All PyGTK applications must have a gtk.main(). Control ends here
# and waits for an event to occur (like a key press or mouse event).
gtk.main()
def pygtk_get_user_descriptions(img,drw):
thisDialog = getUserDescriptionsDialog()
thisDialog.setup(None,[img,drw])
thisDialog.main()
register(
"python-fu-pygtk_get_user_descriptions",
"Save Layer Names",
"(Version 1.0)",
"GimpChat",
"GPL License",
"2011",
"<Image>/contributed/Save Layer Names...",
"*",
[],
[],
pygtk_get_user_descriptions)
main()