} ## GimpPlugIn virtual methods ## def do_query_procedures(self): self.set_translation_domain( "gimp30-python", Gio.file_new_for_path(Gimp.locale_directory())) return ['gradient-save-as-css'] def do_create_procedure(self, name): procedure = None if name == 'gradient-save-as-css': procedure = Gimp.Procedure.new(self, name, Gimp.PDBProcType.PLUGIN, gradient_css_save, None) procedure.set_image_types("*") procedure.set_documentation( "Creates a new palette from a given gradient", "palette_from_gradient (gradient, number, segment_colors) -> None", name) procedure.set_menu_label("Save as CSS...") procedure.set_attribution("Joao S. O. Bueno", "(c) GPL V3.0 or later", "2011") procedure.add_menu_path('<Gradients>') procedure.add_argument_from_property(self, "run-mode") procedure.add_argument_from_property(self, "gradient") procedure.add_argument_from_property(self, "file") return procedure Gimp.main(GradientsSaveAsCSS.__gtype__, sys.argv)
Gimp.PDBStatusType.CALLING_ERROR, GLib.Error(error)) runmode = args.index(0) # Handle args here on a plug-in that takes them. # Gimp.image_list() # Make a new image: img = Gimp.image_new(400, 400, 0) # Make a new layer: layer = Gimp.layer_new(img, "newlayer", Gimp.image_width(img), Gimp.image_height(img), Gimp.ImageType.RGBA_IMAGE, 100.0, Gimp.LayerMode.NORMAL) # Add the layer to the image: Gimp.image_insert_layer(img, layer, 0, -1) # Fill the layer: Gimp.drawable_fill(layer, Gimp.FillType.PATTERN) # Finally, display the image: Gimp.display_new(img) retval = procedure.new_return_values(Gimp.PDBStatusType.SUCCESS, GLib.Error()) return retval Gimp.main(TestPlugin.__gtype__, sys.argv)
procedure.set_image_types("*") procedure.set_documentation('save an OpenRaster (.ora) file', 'save an OpenRaster (.ora) file', name) procedure.set_menu_label('OpenRaster') procedure.set_extensions("ora") elif name == 'file-openraster-load': procedure = Gimp.LoadProcedure.new(self, name, Gimp.PDBProcType.PLUGIN, load_ora, None) procedure.set_menu_label('OpenRaster') procedure.set_documentation('load an OpenRaster (.ora) file', 'load an OpenRaster (.ora) file', name) procedure.set_mime_types("image/openraster") procedure.set_extensions("ora") procedure.set_thumbnail_loader('file-openraster-load-thumb') else: # 'file-openraster-load-thumb' procedure = Gimp.ThumbnailProcedure.new(self, name, Gimp.PDBProcType.PLUGIN, thumbnail_ora, None) procedure.set_documentation( 'loads a thumbnail from an OpenRaster (.ora) file', 'loads a thumbnail from an OpenRaster (.ora) file', name) procedure.set_attribution( 'Jon Nordby', #author 'Jon Nordby', #copyright '2009') #year return procedure Gimp.main(FileOpenRaster.__gtype__, sys.argv)
gi.require_version('Gegl', '0.4') from gi.repository import Gegl from gi.repository import GObject from gi.repository import GLib from gi.repository import Gio import gettext import os import sys # Set-up localization for your plug-in with your own text domain. # This is complementary to the gimp_plug_in_set_translation_domain() # which is only useful for the menu entries inside GIMP interface, # whereas the below calls are used for localization within the plug-in. textdomain = 'gimp30-std-plug-ins' gettext.bindtextdomain(textdomain, Gimp.locale_directory()) gettext.bind_textdomain_codeset(textdomain, 'UTF-8') gettext.textdomain(textdomain) _ = gettext.gettext def N_(message): return message class Goat(Gimp.PlugIn): ## GimpPlugIn virtual methods ## def do_query_procedures(self): # Localization for the menu entries. It has to be called in the # query function only. self.set_translation_domain(
Gegl.init(None) buffer = drawable.get_buffer() shadow_buffer = drawable.get_shadow_buffer() graph = Gegl.Node() input = graph.create_child("gegl:buffer-source") input.set_property("buffer", buffer) invert = graph.create_child("gegl:invert") output = graph.create_child("gegl:write-buffer") output.set_property("buffer", shadow_buffer) input.link(invert) invert.link(output) output.process() # This is extremely important in bindings, since we don't # unref buffers. If we don't explicitly flush a buffer, we # may left hanging forever. This step is usually done # during an unref(). shadow_buffer.flush() drawable.merge_shadow(True) drawable.update(x, y, width, height) Gimp.displays_flush() return procedure.new_return_values(Gimp.PDBStatusType.SUCCESS, GLib.Error()) Gimp.main(Goat.__gtype__, sys.argv)
def make_gradient(palette, num_segments, num_colors): gradient = Gimp.gradient_new(palette) if (num_segments > 1): Gimp.gradient_segment_range_split_uniform(gradient, 0, -1, num_segments) for color_number in range(0, num_segments): if color_number == num_colors - 1: color_number_next = 0 else: color_number_next = color_number + 1 _, color_left = Gimp.palette_entry_get_color(palette, color_number) _, color_right = Gimp.palette_entry_get_color(palette, color_number_next) Gimp.gradient_segment_set_left_color(gradient, color_number, color_left, 100.0) Gimp.gradient_segment_set_right_color(gradient, color_number, color_right, 100.0) Gimp.context_set_gradient(gradient) return gradient
def do_query_procedures(self): self.set_translation_domain( textdomain, Gio.file_new_for_path(Gimp.locale_directory())) return ["plug-in-add-balloon"]
} ## GimpPlugIn virtual methods ## def do_query_procedures(self): self.set_translation_domain( "gimp30-python", Gio.file_new_for_path(Gimp.locale_directory())) return ['python-fu-foggify'] def do_create_procedure(self, name): procedure = Gimp.ImageProcedure.new(self, name, Gimp.PDBProcType.PLUGIN, foggify, None) procedure.set_image_types("RGB*, GRAY*") procedure.set_documentation(N_("Add a layer of fog"), "Adds a layer of fog to the image.", name) procedure.set_menu_label(N_("_Fog...")) procedure.set_attribution("James Henstridge", "James Henstridge", "1999,2007") procedure.add_menu_path("<Image>/Filters/Decor") procedure.add_argument_from_property(self, "name") # TODO: add support for GBoxed values. #procedure.add_argument_from_property(self, "color") procedure.add_argument_from_property(self, "turbulence") procedure.add_argument_from_property(self, "opacity") return procedure Gimp.main(Foggify.__gtype__, sys.argv)
def foggify(procedure, run_mode, image, drawable, args, data): name = args.index(0) turbulence = args.index(1) opacity = args.index(2) if run_mode == Gimp.RunMode.INTERACTIVE: # TODO: add a GUI. This first port works just with default # values. color = Gimp.RGB() color.set(240.0, 180.0, 70.0) Gimp.context_push() image.undo_group_start() if image.base_type() is Gimp.ImageBaseType.RGB: type = Gimp.ImageType.RGBA_IMAGE else: type = Gimp.ImageType.GRAYA_IMAGE fog = Gimp.Layer.new(image, name, drawable.width(), drawable.height(), type, opacity, Gimp.LayerMode.NORMAL) fog.fill(Gimp.FillType.TRANSPARENT) image.insert_layer(fog, None, 0) Gimp.context_set_background(color) fog.edit_fill(Gimp.FillType.BACKGROUND) # create a layer mask for the new layer mask = fog.create_mask(0) fog.add_mask(mask) # add some clouds to the layer Gimp.get_pdb().run_procedure('plug-in-plasma', [ GObject.Value(Gimp.RunMode, Gimp.RunMode.NONINTERACTIVE), GObject.Value(Gimp.Image, image), GObject.Value(Gimp.Drawable, mask), GObject.Value(GObject.TYPE_INT, int(time.time())), GObject.Value(GObject.TYPE_DOUBLE, turbulence), ]) # apply the clouds to the layer fog.remove_mask(Gimp.MaskApplyMode.APPLY) fog.set_visible(True) Gimp.displays_flush() image.undo_group_end() Gimp.context_pop() return procedure.new_return_values(Gimp.PDBStatusType.SUCCESS, GLib.Error())
def do_query_procedures(self): self.set_translation_domain("gimp30-python", Gio.file_new_for_path(Gimp.locale_directory())) return [ 'file-colorxhtml-save' ]
def save_colorxhtml(procedure, run_mode, image, drawable, file, args, data): source_file = args.index(0) characters = args.index(1) size = args.index(2) separate = args.index(3) if file is None: error = 'No file given' return procedure.new_return_values(Gimp.PDBStatusType.CALLING_ERROR, GLib.Error(error)) if run_mode == Gimp.RunMode.INTERACTIVE: gi.require_version('Gtk', '3.0') from gi.repository import Gtk Gimp.ui_init ("colorxhtml.py") use_header_bar = Gtk.Settings.get_default().get_property("gtk-dialogs-use-header") dialog = Gtk.Dialog(use_header_bar=use_header_bar, title=_("Save as colored HTML text...")) dialog.add_button("_Cancel", Gtk.ResponseType.CANCEL) dialog.add_button("_OK", Gtk.ResponseType.OK) choose_file_dialog = Gtk.FileChooserDialog(use_header_bar=use_header_bar, title=_("Read characters from file..."), action=Gtk.FileChooserAction.OPEN) choose_file_dialog.add_button("_Cancel", Gtk.ResponseType.CANCEL) choose_file_dialog.add_button("_OK", Gtk.ResponseType.OK) def choose_file(button, user_data=None): choose_file_dialog.show() if choose_file_dialog.run() == Gtk.ResponseType.OK: characters_entry.set_text(choose_file_dialog.get_filename()) choose_file_dialog.hide() grid = Gtk.Grid() grid.set_column_homogeneous(False) grid.set_border_width(10) grid.set_column_spacing(10) grid.set_row_spacing(10) row = 0 label = Gtk.Label(label=_("Characters")) label.set_tooltip_text(_("Characters that will be used as colored pixels. ")) grid.attach(label, 0, row, 1 , 1) label.show() characters_entry = Gtk.Entry() characters_entry.set_width_chars(20) characters_entry.set_max_width_chars(80) characters_entry.set_text(characters) characters_entry.set_placeholder_text(_("Characters or file location")) grid.attach(characters_entry, 1, row, 1, 1) characters_entry.show() row += 1 characters_checkbox = Gtk.CheckButton(label=_("Read characters from file")) characters_checkbox.set_active(source_file) characters_checkbox.set_tooltip_text( _("If set, the Characters text entry will be used as a file name, " "from which the characters will be read. Otherwise, the characters " "in the text entry will be used to render the image.")) grid.attach(characters_checkbox, 0, row, 1, 1) characters_checkbox.show() choose_file_button = Gtk.Button(label=_("Choose file")) grid.attach(choose_file_button, 1, row, 1, 1) choose_file_button.connect("clicked", choose_file) choose_file_button.show() row += 1 label = Gtk.Label(label=_("Font Size(px)")) grid.attach(label, 0, row, 1 , 1) label.show() font_size_adj = Gtk.Adjustment.new(size, 0.0, 100.0, 1.0, 0.0, 0.0) font_size_spin = Gtk.SpinButton.new(font_size_adj, climb_rate=1.0, digits=0) font_size_spin.set_numeric(True) grid.attach(font_size_spin, 1, row, 1 , 1) font_size_spin.show() row += 1 separate_checkbox = Gtk.CheckButton(label=_("Write separate CSS file")) separate_checkbox.set_active(separate) grid.attach(separate_checkbox, 0, row, 2, 1) separate_checkbox.show() dialog.get_content_area().add(grid) grid.show() dialog.show() if dialog.run() == Gtk.ResponseType.OK: separate = separate_checkbox.get_active() size = font_size_spin.get_value_as_int() source_file = characters_checkbox.get_active() characters = characters_entry.get_text() else: return procedure.new_return_values(Gimp.PDBStatusType.CANCEL, GLib.Error()) width = drawable.width() height = drawable.height() bpp = drawable.bpp() html = open(file.peek_path(), 'w') if separate: dirname, cssfile = os.path.split(file.peek_path()) cssfile = os.path.splitext(cssfile)[0] + '.css' cssname = os.path.join(dirname, cssfile) css = open(cssname, 'w') if source_file: characters_file = open(characters, 'r') chars = characters_file.read() characters_file.close() else: chars = characters # Remove unprintable characters from "chars". # TODO: This only handles ascii files. It would be nice to handle unicode # files, so this could work for any language. goodchars = string.digits + string.ascii_letters + string.punctuation badchars = ''.join(chr(i) for i in range(256) if chr(i) not in goodchars) allchars = str.maketrans('', '', badchars) chars = chars.translate(allchars) data = [escape_table.get(c, c) for c in chars] if data: data.reverse() else: data = list('X' * 80) Gimp.progress_init(_("Saving as colored XHTML")) style = style_def % size if separate: ss = '<link rel="stylesheet" type="text/css" href="%s" />' % cssfile css.write(style) else: ss = '<style type="text/css">\n%s</style>' % style html.write(preamble % ss) colors = {} chars = [] # Constants used for formatting the pixel color. We can handle image # types where each color is 8 bits, 16 bits, or 32 bit integers. fmt = fmt_from_bpp[bpp] pixel_shift = 8 * (bpp//3 - 1) for y in range(0, height): # The characters in "chars" will be used to draw the next row. # Lets fill "chars" with data so it has at least enough characters. while len(chars) < width: chars[0:0] = data for x in range(0, width): pixel_bytes = drawable.get_pixel(x, y) pixel_tuple = struct.unpack(fmt, pixel_bytes) if bpp > 3: pixel_tuple=( pixel_tuple[0] >> pixel_shift, pixel_tuple[1] >> pixel_shift, pixel_tuple[2] >> pixel_shift, ) color = '%02x%02x%02x' % pixel_tuple style = 'background-color:black; color:#%s;' % color char = chars.pop() if separate: if color not in colors: css.write('span.N%s { %s }\n' % (color, style)) colors[color] = 1 html.write('<span class="N%s">%s</span>' % (color, char)) else: html.write('<span style="%s">%s</span>' % (style, char)) html.write('\n') Gimp.progress_update(y / float(height)) html.write(postamble) html.close() if separate: css.close() retval = Gimp.ValueArray.new(1) retval.insert(0, GObject.Value(Gimp.PDBStatusType, Gimp.PDBStatusType.SUCCESS)) return retval
self.set_translation_domain("gimp30-python", Gio.file_new_for_path(Gimp.locale_directory())) return [ 'file-colorxhtml-save' ] def do_create_procedure(self, name): procedure = None if name == 'file-colorxhtml-save': procedure = Gimp.SaveProcedure.new(self, name, Gimp.PDBProcType.PLUGIN, save_colorxhtml, None) procedure.set_image_types("RGB") procedure.set_documentation ( N_("Save as colored HTML text"), "Saves the image as colored XHTML text (based on Perl version by Marc Lehmann)", name) procedure.set_menu_label(N_("Colored HTML text")) procedure.set_attribution("Manish Singh and Carol Spears", "(c) GPL V3.0 or later", "2003") procedure.set_extensions ("html,xhtml"); procedure.add_argument_from_property(self, "source-file") procedure.add_argument_from_property(self, "characters") procedure.add_argument_from_property(self, "font-size") procedure.add_argument_from_property(self, "separate") return procedure Gimp.main(ColorXhtml.__gtype__, sys.argv)
def gradient_css_save(procedure, args, data): if args.length() != 3: error = 'Wrong parameters given' return procedure.new_return_values(Gimp.PDBStatusType.CALLING_ERROR, GLib.Error(error)) runmode = args.index(0) gradient = args.index(1) file = args.index(2) if runmode == Gimp.RunMode.INTERACTIVE: # Interactive mode works on active gradient. gradient = Gimp.context_get_gradient() # Pop-up a file chooser for target file. gi.require_version('Gtk', '3.0') from gi.repository import Gtk Gimp.init("gradients-save-as-css.py") use_header_bar = Gtk.Settings.get_default().get_property( "gtk-dialogs-use-header") dialog = Gtk.FileChooserDialog(use_header_bar=use_header_bar, title=_("CSS file..."), action=Gtk.FileChooserAction.SAVE) dialog.add_button("_Cancel", Gtk.ResponseType.CANCEL) dialog.add_button("_OK", Gtk.ResponseType.OK) dialog.show() if dialog.run() == Gtk.ResponseType.OK: file = dialog.get_file() else: return procedure.new_return_values(Gimp.PDBStatusType.CANCEL, GLib.Error()) if file is None: error = 'No file given' return procedure.new_return_values(Gimp.PDBStatusType.CALLING_ERROR, GLib.Error(error)) stops = [] wk_stops = [] n_segments = Gimp.gradient_get_number_of_segments(gradient) last_stop = None for index in range(n_segments): success, lcolor, lopacity = Gimp.gradient_segment_get_left_color( gradient, index) success, rcolor, ropacity = Gimp.gradient_segment_get_right_color( gradient, index) success, lpos = Gimp.gradient_segment_get_left_pos(gradient, index) success, rpos = Gimp.gradient_segment_get_right_pos(gradient, index) lstop = color_to_html(lcolor) + " %d%%" % int(100 * lpos) wk_lstop = "color-stop(%.03f, %s)" % (lpos, color_to_html(lcolor)) if lstop != last_stop: stops.append(lstop) wk_stops.append(wk_lstop) rstop = color_to_html(rcolor) + " %d%%" % int(100 * rpos) wk_rstop = "color-stop(%.03f, %s)" % (rpos, color_to_html(rcolor)) stops.append(rstop) wk_stops.append(wk_rstop) last_stop = rstop final_text = w3c_template % ", ".join(stops) final_text += moz_template % ",".join(stops) final_text += webkit_template % ",".join(wk_stops) success, etag = file.replace_contents( bytes(format_text(final_text), encoding='utf-8'), etag=None, make_backup=False, flags=Gio.FileCreateFlags.REPLACE_DESTINATION, cancellable=None) if success: return procedure.new_return_values(Gimp.PDBStatusType.SUCCESS, GLib.Error()) else: return procedure.new_return_values( Gimp.PDBStatusType.EXECUTION_ERROR, GLib.Error('File saving failed: {}'.format(file.get_path())))
def do_query_procedures(self): self.set_translation_domain( "gimp30-python", Gio.file_new_for_path(Gimp.locale_directory())) return ['gradient-save-as-css']
def run(self, procedure, args, data): palette = None amount = 1 # Get the parameters if args.length() < 1: error = 'No parameters given' return procedure.new_return_values(Gimp.PDBStatusType.CALLING_ERROR, GLib.Error(error)) runmode = args.index(0) if args.length() > 1: palette = args.index(1) if palette == '' or palette is None: palette = Gimp.context_get_palette() (exists, num_colors) = Gimp.palette_get_info(palette) if not exists: error = 'Unknown palette: {}'.format(palette) return procedure.new_return_values(Gimp.PDBStatusType.CALLING_ERROR, GLib.Error(error)) if args.length() > 2: amount = args.index(2) if runmode == Gimp.RunMode.INTERACTIVE: gi.require_version('Gtk', '3.0') from gi.repository import Gtk GimpUi.init ("palette-offset.py") use_header_bar = Gtk.Settings.get_default().get_property("gtk-dialogs-use-header") dialog = GimpUi.Dialog(use_header_bar=use_header_bar, title=_("Offset Palette...")) dialog.add_button("_Cancel", Gtk.ResponseType.CANCEL) dialog.add_button("_OK", Gtk.ResponseType.OK) box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, homogeneous=False, spacing=12) dialog.get_content_area().add(box) box.show() label = Gtk.Label.new_with_mnemonic("Off_set") box.pack_start(label, False, False, 1) label.show() amount = self.set_property("amount", amount) spin = GimpUi.prop_spin_button_new(self, "amount", 1.0, 5.0, 0) spin.set_activates_default(True) box.pack_end(spin, False, False, 1) spin.show() dialog.show() if dialog.run() != Gtk.ResponseType.OK: return procedure.new_return_values(Gimp.PDBStatusType.CANCEL, GLib.Error()) amount = self.get_property("amount") #If palette is read only, work on a copy: editable = Gimp.palette_is_editable(palette) if not editable: palette = Gimp.palette_duplicate (palette) tmp_entry_array = [] for i in range (num_colors): tmp_entry_array.append ((Gimp.palette_entry_get_name (palette, i)[1], Gimp.palette_entry_get_color (palette, i)[1])) for i in range (num_colors): target_index = i + amount if target_index >= num_colors: target_index -= num_colors elif target_index < 0: target_index += num_colors Gimp.palette_entry_set_name (palette, target_index, tmp_entry_array[i][0]) Gimp.palette_entry_set_color (palette, target_index, tmp_entry_array[i][1]) retval = procedure.new_return_values(Gimp.PDBStatusType.SUCCESS, GLib.Error()) value = GObject.Value(GObject.TYPE_STRING, palette) retval.remove(1) retval.insert(1, value) return retval
pyslice, None) procedure.set_image_types("*"); # table snippet means a small piece of HTML code here procedure.set_documentation (N_("Cuts an image along its guides, creates images and a HTML table snippet"), """Add guides to an image. Then run this. It will cut along the guides, and give you the html to reassemble the resulting images. If you choose to generate javascript for onmouseover and clicked events, it will use the lower three visible layers on the image for normal, onmouseover and clicked states, in that order. If skip caps is enabled, table cells on the edge of the table won't become animated, and its images will be taken from the active layer.""", name) procedure.set_menu_label(_("_Slice...")) procedure.set_attribution("Manish Singh", "Manish Singh", "2003") procedure.add_menu_path ("<Image>/Filters/Web") procedure.add_argument_from_property(self, "save-path") procedure.add_argument_from_property(self, "html-filename") procedure.add_argument_from_property(self, "image-basename") procedure.add_argument_from_property(self, "image-extension") procedure.add_argument_from_property(self, "separate-image-dir") procedure.add_argument_from_property(self, "relative-image-path") procedure.add_argument_from_property(self, "cellspacing") procedure.add_argument_from_property(self, "animate") procedure.add_argument_from_property(self, "skip-caps") return procedure Gimp.main(PySlice.__gtype__, sys.argv)
procedure.set_menu_label(_("Palette to _Gradient")) procedure.set_documentation( _("Create a gradient using colors from the palette"), _("Create a new gradient using colors from the palette."), "") elif name == 'python-fu-palette-to-gradient-repeating': procedure.set_menu_label(_("Palette to _Repeating Gradient")) procedure.set_documentation( _("Create a repeating gradient using colors from the palette"), _("Create a new repeating gradient using colors from the palette." ), "") else: procedure = None if procedure is not None: procedure.set_attribution( "Carol Spears, reproduced from previous work by Adrian Likins and Jeff Trefftz", "Carol Spears", "2006") # We don't build a GParamSpec ourselves because passing it # around is apparently broken in Python. Hence this trick. # See pygobject#227 procedure.add_argument_from_property(self, "run-mode") procedure.add_argument_from_property(self, "palette") procedure.add_return_value_from_property(self, "new-gradient") procedure.add_menu_path('<Palettes>') return procedure Gimp.main(PaletteToGradient.__gtype__, sys.argv)
def pyslice(procedure, run_mode, image, drawable, args, data): save_path = args.index(0) html_filename = args.index(1) image_basename = args.index(2) image_extension = args.index(3) separate = args.index(4) image_path = args.index(5) cellspacing = args.index(6) animate = args.index(7) skip_caps = args.index(8) cellspacing = int (cellspacing) if animate: count = 0 drw = [] #image.layers is a reversed list of the layers on the image #so, count indexes from number of layers to 0. for i in xrange (len (image.layers) -1, -1, -1): if image.layers[i].visible: drw.append(image.layers[i]) count += 1 if count == 3: break vert, horz = get_guides(image) if len(vert) == 0 and len(horz) == 0: return Gimp.progress_init(_("Slice")) progress_increment = 1 / ((len(horz) + 1) * (len(vert) + 1)) progress = 0.0 def check_path(path): path = os.path.abspath(path) if not os.path.exists(path): os.mkdir(path) return path save_path = check_path(save_path) if not os.path.isdir(save_path): save_path = os.path.dirname(save_path) if separate: image_relative_path = image_path if not image_relative_path.endswith("/"): image_relative_path += "/" image_path = check_path(os.path.join(save_path, image_path)) else: image_relative_path = '' image_path = save_path tw = TableWriter(os.path.join(save_path, html_filename), cellspacing=cellspacing, animate=animate) top = 0 for i in range(0, len(horz) + 1): if i == len(horz): bottom = image.height() else: bottom = image.get_guide_position(horz[i]) tw.row_start() left = 0 for j in range(0, len(vert) + 1): if j == len(vert): right = image.width() else: right = image.get_guide_position(vert[j]) if (skip_caps and ( (len(horz) >= 2 and (i == 0 or i == len(horz) )) or (len(vert) >= 2 and (j == 0 or j == len(vert) )) ) ): skip_stub = True else: skip_stub = False if (not animate or skip_stub): src = (image_relative_path + slice (image, None, image_path, image_basename, image_extension, left, right, top, bottom, i, j, "")) else: src = [] for layer, postfix in zip (drw, ("", "hover", "clicked")): src.append (image_relative_path + slice(image, layer, image_path, image_basename, image_extension, left, right, top, bottom, i, j, postfix)) tw.cell(src, right - left, bottom - top, i, j, skip_stub) left = right + cellspacing progress += progress_increment Gimp.progress_update(progress) tw.row_end() top = bottom + cellspacing tw.close() return procedure.new_return_values(Gimp.PDBStatusType.SUCCESS, GLib.Error())
gi.require_version('Gimp', '3.0') from gi.repository import Gimp gi.require_version('GimpUi', '3.0') from gi.repository import GimpUi gi.require_version('Gegl', '0.4') from gi.repository import Gegl from gi.repository import GObject from gi.repository import GLib from gi.repository import Gio import gettext import os import sys textdomain = 'gimp30-std-plug-ins' gettext.bindtextdomain(textdomain, Gimp.locale_directory()) # gettext.bind_textdomain_codeset(textdomain, 'UTF-8') gettext.textdomain(textdomain) _ = gettext.gettext def N_(message): return message class AddBalloon(Gimp.PlugIn): def do_query_procedures(self): self.set_translation_domain( textdomain, Gio.file_new_for_path(Gimp.locale_directory())) return ["plug-in-add-balloon"]
def do_query_procedures(self): self.set_translation_domain("gimp30-python", Gio.file_new_for_path(Gimp.locale_directory())) return [ 'python-fu-slice' ]
def run(self, procedure, run_mode, image, n_drawables, drawables, args, run_data): if n_drawables != 1: msg = _( f"Procedure '{procedure.get_name()}' only works with one drawable." ) error = GLib.Error.new_literal(Gimp.PlugIn.error_quark(), msg, 0) return procedure.new_return_values( Gimp.PDBStatusType.CALLING_ERROR, error) else: drawable = drawables[0] # check if selection exist selection = image.get_selection() flag, non_empty, x1, y1, x2, y2 = selection.bounds(image) if not non_empty: msg = _( f"The selection is empty, create a selection box and precede with the use of this plugin." ) error = GLib.Error.new_literal(Gimp.PlugIn.error_quark(), msg, 0) return procedure.new_return_values( Gimp.PDBStatusType.CALLING_ERROR, error) if run_mode == Gimp.RunMode.INTERACTIVE: gi.require_version('Gtk', '3.0') from gi.repository import Gtk gi.require_version('Gdk', '3.0') from gi.repository import Gdk GimpUi.init("add_balloon.py") dialog = GimpUi.Dialog(use_header_bar=True, title=_("Add Balloon)"), role="add_balloon-Python3") dialog.add_button(_("_Cancel"), Gtk.ResponseType.CANCEL) dialog.add_button(_("_OK"), Gtk.ResponseType.OK) geometry = Gdk.Geometry() geometry.min_aspect = 0.5 geometry.max_aspect = 1.0 dialog.set_geometry_hints(None, geometry, Gdk.WindowHints.ASPECT) box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=2) dialog.get_content_area().add(box) box.show() # Label text content label = Gtk.Label(label='Text:') box.pack_start(label, False, False, 1) label.show() # scroll area for text scrolled = Gtk.ScrolledWindow() scrolled.set_vexpand(True) box.pack_start(scrolled, True, True, 1) scrolled.show() # text content box text_content = Gtk.TextView() contents = 'text' buffer = text_content.get_buffer() buffer.set_text(contents, -1) scrolled.add(text_content) text_content.show() # Improve UI font_chooser = Gtk.FontChooserWidget() box.pack_start(font_chooser, False, False, 1) font_chooser.show() # TODO add spinner for waiting while (True): response = dialog.run() if response == Gtk.ResponseType.OK: # TODO enable spinner and lock all other values # layer position position = Gimp.get_pdb().run_procedure( 'gimp-image-get-item-position', [image, drawable]).index(1) # Create group layer_group = Gimp.get_pdb().run_procedure( 'gimp-layer-group-new', [image]).index(1) image.insert_layer(layer_group, None, position) # add new trasparent layer overlay_layer = Gimp.Layer.new(image, 'hide_background', drawable.get_width(), drawable.get_height(), Gimp.ImageType.RGBA_IMAGE, 100.0, Gimp.LayerMode.NORMAL) image.insert_layer(overlay_layer, layer_group, position) overlay_layer.fill(Gimp.FillType.TRANSPARENT) # add white fill the selection Gimp.get_pdb().run_procedure( 'gimp-drawable-edit-fill', [overlay_layer, Gimp.FillType.WHITE]) # add text layer buffer = text_content.get_buffer() text = buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter(), True) font_str = font_chooser.get_font() font_size = float(font_str.split(' ')[-1]) font_name = ' '.join(font_str.split(' ')[0:-2]) text_layer = Gimp.get_pdb().run_procedure( 'gimp-text-layer-new', [image, text, font_name, font_size, 3]).index(1) image.insert_layer(text_layer, layer_group, position - 1) # center text layer Gimp.get_pdb().run_procedure( 'gimp-text-layer-set-justification', [text_layer, 2]) cx = (x1 + x2) / 2 - text_layer.get_width() / 2 cy = (y1 + y2) / 2 - text_layer.get_height() / 2 Gimp.get_pdb().run_procedure( 'gimp-item-transform-translate', [text_layer, cx, cy]) # set selected layer image.set_selected_layers([layer_group]) dialog.destroy() break else: dialog.destroy() return procedure.new_return_values( Gimp.PDBStatusType.CANCEL, GLib.Error()) return procedure.new_return_values(Gimp.PDBStatusType.SUCCESS, GLib.Error())
def run_mode(self, runmode): self.runmode = runmode ## GimpPlugIn virtual methods ## def do_query_procedures(self): # Localization self.set_translation_domain ("gimp30-python", Gio.file_new_for_path(Gimp.locale_directory())) return [ PROC_NAME ] def do_create_procedure(self, name): if name == PROC_NAME: procedure = Gimp.Procedure.new(self, name, Gimp.PDBProcType.PLUGIN, run, None) procedure.set_menu_label(N_("Python _Console")) procedure.set_documentation(N_("Interactive GIMP Python interpreter"), "Type in commands and see results", "") procedure.set_attribution("James Henstridge", "James Henstridge", "1997-1999") procedure.add_argument_from_property(self, "run-mode") procedure.add_menu_path ("<Image>/Filters/Development/Python-Fu") return procedure return None Gimp.main(PythonConsole.__gtype__, sys.argv)
def load_ora(procedure, run_mode, file, args, data): tempdir = tempfile.mkdtemp('gimp-plugin-file-openraster') orafile = zipfile.ZipFile(file.peek_path()) stack, w, h = get_image_attributes(orafile) img = Gimp.Image.new(w, h, Gimp.ImageBaseType.RGB) img.set_filename(file.peek_path()) def get_layers(root): """iterates over layers and nested stacks""" for item in root: if item.tag == 'layer': yield item elif item.tag == 'stack': yield item for subitem in get_layers(item): yield subitem yield NESTED_STACK_END parent_groups = [] layer_no = 0 for item in get_layers(stack): if item is NESTED_STACK_END: parent_groups.pop() continue if item.tag == 'stack': name, x, y, opac, visible, layer_mode = get_group_layer_attributes( item) gimp_layer = img.layer_group_new() else: path, name, x, y, opac, visible, layer_mode = get_layer_attributes( item) if not path.lower().endswith('.png'): continue if not name: # use the filename without extension as name n = os.path.basename(path) name = os.path.splitext(n)[0] # create temp file. Needed because gimp cannot load files from inside a zip file tmp = os.path.join(tempdir, 'tmp.png') with open(tmp, 'wb') as fid: try: data = orafile.read(path) except KeyError: # support for bad zip files (saved by old versions of this plugin) data = orafile.read(path.encode('utf-8')) print( 'WARNING: bad OpenRaster ZIP file. There is an utf-8 encoded filename that does not have the utf-8 flag set:', repr(path)) fid.write(data) # import layer, set attributes and add to image gimp_layer = Gimp.get_pdb().run_procedure('gimp-file-load-layer', [ GObject.Value(Gimp.RunMode, Gimp.RunMode.NONINTERACTIVE), GObject.Value(Gimp.Image, img), GObject.Value(GObject.TYPE_STRING, tmp), ]) gimp_layer = gimp_layer.index(1) gimp_layer = Gimp.Item.get_by_id(gimp_layer) os.remove(tmp) gimp_layer.set_name(name) gimp_layer.set_mode(layer_mode) gimp_layer.set_offsets(x, y) # move to correct position gimp_layer.set_opacity(opac * 100) # a float between 0 and 100 gimp_layer.set_visible(visible) img.insert_layer(gimp_layer, parent_groups[-1][0] if parent_groups else None, parent_groups[-1][1] if parent_groups else layer_no) if parent_groups: parent_groups[-1][1] += 1 else: layer_no += 1 if gimp_layer.is_group(): parent_groups.append([gimp_layer, 0]) os.rmdir(tempdir) return Gimp.ValueArray.new_from_values([ GObject.Value(Gimp.PDBStatusType, Gimp.PDBStatusType.SUCCESS), GObject.Value(Gimp.Image, img), ])
def __init__(self, quit_func=None): banner = ('GIMP %s Python Console\nPython %s\n' % (Gimp.version(), sys.version)) pyconsole.Console.__init__(self, locals=namespace, banner=banner, quit_func=quit_func)
def run(self, procedure, run_mode, image, drawable, args, run_data): if run_mode == Gimp.RunMode.INTERACTIVE: gi.require_version('Gtk', '3.0') from gi.repository import Gtk gi.require_version('Gdk', '3.0') from gi.repository import Gdk GimpUi.ui_init("palette-offset.py") dialog = GimpUi.Dialog(use_header_bar=True, title=_("Exercise a goat (Python 3)"), role="goat-exercise-Python3") dialog.add_button("_Cancel", Gtk.ResponseType.CANCEL) dialog.add_button("_Source", Gtk.ResponseType.APPLY) dialog.add_button("_OK", Gtk.ResponseType.OK) geometry = Gdk.Geometry() geometry.min_aspect = 0.5 geometry.max_aspect = 1.0 dialog.set_geometry_hints(None, geometry, Gdk.WindowHints.ASPECT) box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=2) dialog.get_content_area().add(box) box.show() # XXX We use printf-style string for sharing the localized # string. You may just use recommended Python format() or # any style you like in your plug-ins. head_text = ("This plug-in is an exercise in '%s' to " "demo plug-in creation.\nCheck out the last " "version of the source code online by clicking " "the \"Source\" button." % ("Python 3")) label = Gtk.Label(label=head_text) box.pack_start(label, False, False, 1) label.show() contents = None # Get the file contents Python-style instead of using # GLib.file_get_contents() which returns bytes result, and # when converting to string, get newlines as text contents. # Rather than wasting time to figure this out, use Python # core API! with open(os.path.realpath(__file__), 'r') as f: contents = f.read() if contents is not None: scrolled = Gtk.ScrolledWindow() scrolled.set_vexpand(True) box.pack_start(scrolled, True, True, 1) scrolled.show() view = Gtk.TextView() view.set_wrap_mode(Gtk.WrapMode.WORD) view.set_editable(False) buffer = view.get_buffer() buffer.set_text(contents, -1) scrolled.add(view) view.show() while (True): response = dialog.run() if response == Gtk.ResponseType.OK: dialog.destroy() break elif response == Gtk.ResponseType.APPLY: url = "https://gitlab.gnome.org/GNOME/gimp/-/blob/master/plug-ins/goat-exercises/goat-exercise-py3.py" Gio.app_info_launch_default_for_uri(url, None) continue else: dialog.destroy() return procedure.new_return_values( Gimp.PDBStatusType.CANCEL, GLib.Error()) intersect, x, y, width, height = drawable.mask_intersect() if intersect: Gegl.init(None) buffer = drawable.get_buffer() shadow_buffer = drawable.get_shadow_buffer() graph = Gegl.Node() input = graph.create_child("gegl:buffer-source") input.set_property("buffer", buffer) invert = graph.create_child("gegl:invert") output = graph.create_child("gegl:write-buffer") output.set_property("buffer", shadow_buffer) input.link(invert) invert.link(output) output.process() # This is extremely important in bindings, since we don't # unref buffers. If we don't explicitly flush a buffer, we # may left hanging forever. This step is usually done # during an unref(). shadow_buffer.flush() drawable.merge_shadow(True) drawable.update(x, y, width, height) Gimp.displays_flush() return procedure.new_return_values(Gimp.PDBStatusType.SUCCESS, GLib.Error())
def _commit(self): pyconsole.Console._commit(self) Gimp.displays_flush()
def do_query_procedures(self): # Localization self.set_translation_domain( "gimp30-python", Gio.file_new_for_path(Gimp.locale_directory())) return ["python-fu-gi-test"]
if dialog.run() != Gtk.ResponseType.OK: return procedure.new_return_values(Gimp.PDBStatusType.CANCEL, GLib.Error()) amount = self.get_property("amount") #If palette is read only, work on a copy: editable = Gimp.palette_is_editable(palette) if not editable: palette = Gimp.palette_duplicate (palette) tmp_entry_array = [] for i in range (num_colors): tmp_entry_array.append ((Gimp.palette_entry_get_name (palette, i)[1], Gimp.palette_entry_get_color (palette, i)[1])) for i in range (num_colors): target_index = i + amount if target_index >= num_colors: target_index -= num_colors elif target_index < 0: target_index += num_colors Gimp.palette_entry_set_name (palette, target_index, tmp_entry_array[i][0]) Gimp.palette_entry_set_color (palette, target_index, tmp_entry_array[i][1]) retval = procedure.new_return_values(Gimp.PDBStatusType.SUCCESS, GLib.Error()) value = GObject.Value(GObject.TYPE_STRING, palette) retval.remove(1) retval.insert(1, value) return retval Gimp.main(PaletteOffset.__gtype__, sys.argv)
def save_ora(procedure, run_mode, image, n_drawables, drawables, file, args, data): def write_file_str(zfile, fname, data): # work around a permission bug in the zipfile library: # http://bugs.python.org/issue3394 zi = zipfile.ZipInfo(fname) zi.external_attr = int("100644", 8) << 16 zfile.writestr(zi, data) Gimp.progress_init("Exporting openraster image") tempdir = tempfile.mkdtemp('gimp-plugin-file-openraster') # use .tmpsave extension, so we don't overwrite a valid file if # there is an exception orafile = zipfile.ZipFile(file.peek_path() + '.tmpsave', 'w', compression=zipfile.ZIP_STORED) write_file_str(orafile, 'mimetype', 'image/openraster') # must be the first file written # build image attributes xml_image = ET.Element('image') stack = ET.SubElement(xml_image, 'stack') a = xml_image.attrib a['w'] = str(image.get_width()) a['h'] = str(image.get_height()) def store_layer(image, drawable, path): tmp = os.path.join(tempdir, 'tmp.png') interlace, compression = 0, 2 Gimp.get_pdb().run_procedure( 'file-png-save', [ GObject.Value(Gimp.RunMode, Gimp.RunMode.NONINTERACTIVE), GObject.Value(Gimp.Image, image), GObject.Value(GObject.TYPE_INT, 1), GObject.Value( Gimp.ObjectArray, Gimp.ObjectArray.new(Gimp.Drawable, [drawable], False)), GObject.Value(Gio.File, Gio.File.new_for_path(tmp)), GObject.Value(GObject.TYPE_BOOLEAN, interlace), GObject.Value(GObject.TYPE_INT, compression), # write all PNG chunks except oFFs(ets) GObject.Value(GObject.TYPE_BOOLEAN, True), # Save background color (bKGD chunk) GObject.Value(GObject.TYPE_BOOLEAN, False), # Save layer offset (oFFs chunk) GObject.Value(GObject.TYPE_BOOLEAN, True), # Save resolution (pHYs chunk) GObject.Value(GObject.TYPE_BOOLEAN, True), # Save creation time (tIME chunk) # Other settings GObject.Value( GObject.TYPE_BOOLEAN, True), # Save color values from transparent pixels ]) if (os.path.exists(tmp)): orafile.write(tmp, path) os.remove(tmp) else: print("Error removing ", tmp) def add_layer(parent, x, y, opac, gimp_layer, path, visible=True): store_layer(image, gimp_layer, path) # create layer attributes layer = ET.Element('layer') parent.append(layer) a = layer.attrib a['src'] = path a['name'] = gimp_layer.get_name() a['x'] = str(x) a['y'] = str(y) a['opacity'] = str(opac) a['visibility'] = 'visible' if visible else 'hidden' a['composite-op'] = gimp_layermodes_map.get(gimp_layer.get_mode(), 'svg:src-over') return layer def add_group_layer(parent, opac, gimp_layer, visible=True): # create layer attributes group_layer = ET.Element('stack') parent.append(group_layer) a = group_layer.attrib a['name'] = gimp_layer.get_name() a['opacity'] = str(opac) a['visibility'] = 'visible' if visible else 'hidden' a['composite-op'] = gimp_layermodes_map.get(gimp_layer.get_mode(), 'svg:src-over') return group_layer def enumerate_layers(layers): for layer in layers: if not layer.is_group(): yield layer else: yield layer for sublayer in enumerate_layers(layer.get_children()): yield sublayer yield NESTED_STACK_END # save layers parent_groups = [] i = 0 layer_stack = image.get_layers() # Number of top level layers for tracking progress lay_cnt = len(layer_stack) for lay in enumerate_layers(layer_stack): prev_lay = i if lay is NESTED_STACK_END: parent_groups.pop() continue _, x, y = lay.get_offsets() opac = lay.get_opacity() / 100.0 # needs to be between 0.0 and 1.0 if not parent_groups: path_name = 'data/{:03d}.png'.format(i) i += 1 else: path_name = 'data/{}-{:03d}.png'.format(parent_groups[-1][1], parent_groups[-1][2]) parent_groups[-1][2] += 1 parent = stack if not parent_groups else parent_groups[-1][0] if lay.is_group(): group = add_group_layer(parent, opac, lay, lay.get_visible()) group_path = ("{:03d}".format(i) if not parent_groups else parent_groups[-1][1] + "-{:03d}".format(parent_groups[-1][2])) parent_groups.append([group, group_path, 0]) else: add_layer(parent, x, y, opac, lay, path_name, lay.get_visible()) if (i > prev_lay): Gimp.progress_update(i / lay_cnt) # save mergedimage thumb = image.duplicate() thumb_layer = thumb.merge_visible_layers(Gimp.MergeType.CLIP_TO_IMAGE) store_layer(thumb, thumb_layer, 'mergedimage.png') # save thumbnail w, h = image.get_width(), image.get_height() if max(w, h) > 256: # should be at most 256x256, without changing aspect ratio if w > h: w, h = 256, max(h * 256 / w, 1) else: w, h = max(w * 256 / h, 1), 256 thumb_layer.scale(w, h, False) if thumb.get_precision() != Gimp.Precision.U8_GAMMA: thumb.convert_precision(Gimp.Precision.U8_GAMMA) store_layer(thumb, thumb_layer, 'Thumbnails/thumbnail.png') thumb.delete() # write stack.xml xml = ET.tostring(xml_image, encoding='UTF-8') write_file_str(orafile, 'stack.xml', xml) # finish up orafile.close() os.rmdir(tempdir) if os.path.exists(file.peek_path()): os.remove(file.peek_path()) # win32 needs that os.rename(file.peek_path() + '.tmpsave', file.peek_path()) Gimp.progress_end() return Gimp.ValueArray.new_from_values( [GObject.Value(Gimp.PDBStatusType, Gimp.PDBStatusType.SUCCESS)])
def foggify(procedure, run_mode, image, n_drawables, drawables, args, data): config = procedure.create_config() config.begin_run(image, run_mode, args) if run_mode == Gimp.RunMode.INTERACTIVE: GimpUi.init('python-fu-foggify') dialog = GimpUi.ProcedureDialog(procedure=procedure, config=config) dialog.get_color_widget('color', True, GimpUi.ColorAreaType.FLAT) dialog.fill(None) if not dialog.run(): dialog.destroy() config.end_run(Gimp.PDBStatusType.CANCEL) return procedure.new_return_values(Gimp.PDBStatusType.CANCEL, GLib.Error()) else: dialog.destroy() color = config.get_property('color') name = config.get_property('name') turbulence = config.get_property('turbulence') opacity = config.get_property('opacity') Gimp.context_push() image.undo_group_start() if image.get_base_type() is Gimp.ImageBaseType.RGB: type = Gimp.ImageType.RGBA_IMAGE else: type = Gimp.ImageType.GRAYA_IMAGE for drawable in drawables: fog = Gimp.Layer.new(image, name, drawable.get_width(), drawable.get_height(), type, opacity, Gimp.LayerMode.NORMAL) fog.fill(Gimp.FillType.TRANSPARENT) image.insert_layer(fog, drawable.get_parent(), image.get_item_position(drawable)) Gimp.context_set_background(color) fog.edit_fill(Gimp.FillType.BACKGROUND) # create a layer mask for the new layer mask = fog.create_mask(0) fog.add_mask(mask) # add some clouds to the layer Gimp.get_pdb().run_procedure('plug-in-plasma', [ GObject.Value(Gimp.RunMode, Gimp.RunMode.NONINTERACTIVE), GObject.Value(Gimp.Image, image), GObject.Value(Gimp.Drawable, mask), GObject.Value(GObject.TYPE_INT, int(time.time())), GObject.Value(GObject.TYPE_DOUBLE, turbulence), ]) # apply the clouds to the layer fog.remove_mask(Gimp.MaskApplyMode.APPLY) fog.set_visible(True) Gimp.displays_flush() image.undo_group_end() Gimp.context_pop() config.end_run(Gimp.PDBStatusType.SUCCESS) return procedure.new_return_values(Gimp.PDBStatusType.SUCCESS, GLib.Error())