def run(procedure, run_mode, image, layer, args, data): gio_file = args.index(0) bucket_size = args.index(1) sample_average = args.index(2) output_format = args.index(3) progress_bar = None config = None if run_mode == Gimp.RunMode.INTERACTIVE: config = procedure.create_config() # Set properties from arguments. These properties will be changed by the UI. #config.set_property("file", gio_file) #config.set_property("bucket_size", bucket_size) #config.set_property("sample_average", sample_average) #config.set_property("output_format", output_format) config.begin_run(image, run_mode, args) GimpUi.ui_init("histogram-export.py") use_header_bar = Gtk.Settings.get_default().get_property( "gtk-dialogs-use-header") dialog = GimpUi.Dialog(use_header_bar=use_header_bar, title=_("Histogram Export...")) dialog.add_button("_Cancel", Gtk.ResponseType.CANCEL) dialog.add_button("_OK", Gtk.ResponseType.OK) vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, homogeneous=False, spacing=10) dialog.get_content_area().add(vbox) vbox.show() # Create grid to set all the properties inside. grid = Gtk.Grid() grid.set_column_homogeneous(False) grid.set_border_width(10) grid.set_column_spacing(10) grid.set_row_spacing(10) vbox.add(grid) grid.show() # UI for the file parameter def choose_file(widget): if file_chooser_dialog.run() == Gtk.ResponseType.OK: if file_chooser_dialog.get_file() is not None: config.set_property("file", file_chooser_dialog.get_file()) file_entry.set_text( file_chooser_dialog.get_file().get_path()) file_chooser_dialog.hide() file_chooser_button = Gtk.Button.new_with_mnemonic(label=_("_File...")) grid.attach(file_chooser_button, 0, 0, 1, 1) file_chooser_button.show() file_chooser_button.connect("clicked", choose_file) file_entry = Gtk.Entry.new() grid.attach(file_entry, 1, 0, 1, 1) file_entry.set_width_chars(40) file_entry.set_placeholder_text(_("Choose export file...")) if gio_file is not None: file_entry.set_text(gio_file.get_path()) file_entry.show() file_chooser_dialog = Gtk.FileChooserDialog( use_header_bar=use_header_bar, title=_("Histogram Export file..."), action=Gtk.FileChooserAction.SAVE) file_chooser_dialog.add_button("_Cancel", Gtk.ResponseType.CANCEL) file_chooser_dialog.add_button("_OK", Gtk.ResponseType.OK) # Bucket size parameter label = Gtk.Label.new_with_mnemonic(_("_Bucket Size")) grid.attach(label, 0, 1, 1, 1) label.show() spin = GimpUi.prop_spin_button_new(config, "bucket_size", step_increment=0.001, page_increment=0.1, digits=3) grid.attach(spin, 1, 1, 1, 1) spin.show() # Sample average parameter spin = GimpUi.prop_check_button_new(config, "sample_average", _("Sample _Average")) spin.set_tooltip_text( _("If checked, the histogram is generated from merging all visible layers." " Otherwise, the histogram is only for the current layer.")) grid.attach(spin, 1, 2, 1, 1) spin.show() # Output format parameter label = Gtk.Label.new_with_mnemonic(_("_Output Format")) grid.attach(label, 0, 3, 1, 1) label.show() combo = GimpUi.prop_string_combo_box_new( config, "output_format", output_format_enum.get_tree_model(), 0, 1) grid.attach(combo, 1, 3, 1, 1) combo.show() progress_bar = Gtk.ProgressBar() vbox.add(progress_bar) progress_bar.show() dialog.show() if dialog.run() != Gtk.ResponseType.OK: return procedure.new_return_values(Gimp.PDBStatusType.CANCEL, GLib.Error()) # Extract values from UI gio_file = Gio.file_new_for_path( file_entry.get_text()) # config.get_property("file") bucket_size = config.get_property("bucket_size") sample_average = config.get_property("sample_average") output_format = config.get_property("output_format") if gio_file is None: error = 'No file given' return procedure.new_return_values(Gimp.PDBStatusType.CALLING_ERROR, GLib.Error(error)) result = histogram_export(procedure, image, layer, gio_file, bucket_size, sample_average, output_format, progress_bar) # If the execution was successful, save parameters so they will be restored next time we show dialog. if result.index(0) == Gimp.PDBStatusType.SUCCESS and config is not None: config.end_run(Gimp.PDBStatusType.SUCCESS) return result
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 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.ui_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
def run(procedure, args, data): GimpUi.ui_init ("python-console.py") namespace = {'__builtins__': __builtins__, '__name__': '__main__', '__doc__': None, 'Babl': gi.repository.Babl, 'cairo': gi.repository.cairo, 'Gdk': gi.repository.Gdk, 'Gegl': gi.repository.Gegl, 'Gimp': gi.repository.Gimp, 'Gio': gi.repository.Gio, 'Gtk': gi.repository.Gtk, 'GdkPixbuf': gi.repository.GdkPixbuf, 'GLib': gi.repository.GLib, 'GObject': gi.repository.GObject, 'Pango': gi.repository.Pango } class GimpConsole(pyconsole.Console): 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 _commit(self): pyconsole.Console._commit(self) Gimp.displays_flush() class ConsoleDialog(GimpUi.Dialog): def __init__(self): use_header_bar = Gtk.Settings.get_default().get_property("gtk-dialogs-use-header") GimpUi.Dialog.__init__(self, use_header_bar=use_header_bar) self.set_property("help-id", PROC_NAME) Gtk.Window.set_title(self, _("Python Console")) Gtk.Window.set_role(self, PROC_NAME) Gtk.Dialog.add_button(self, "_Save", Gtk.ResponseType.OK) Gtk.Dialog.add_button(self, "Cl_ear", RESPONSE_CLEAR) Gtk.Dialog.add_button(self, _("_Browse..."), RESPONSE_BROWSE) Gtk.Dialog.add_button(self, "_Close", Gtk.ResponseType.CLOSE) Gtk.Widget.set_name (self, PROC_NAME) GimpUi.Dialog.set_alternative_button_order_from_array(self, [ Gtk.ResponseType.CLOSE, RESPONSE_BROWSE, RESPONSE_CLEAR, Gtk.ResponseType.OK ]) self.cons = GimpConsole(quit_func=lambda: Gtk.main_quit()) self.style_set (None, None) self.connect('response', self.response) self.connect('style-set', self.style_set) self.browse_dlg = None self.save_dlg = None vbox = Gtk.VBox(homogeneous=False, spacing=12) vbox.set_border_width(12) contents_area = Gtk.Dialog.get_content_area(self) contents_area.pack_start(vbox, True, True, 0) scrl_win = Gtk.ScrolledWindow() scrl_win.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.ALWAYS) vbox.pack_start(scrl_win, True, True, 0) scrl_win.add(self.cons) width, height = self.cons.get_default_size() minreq, requisition = Gtk.Widget.get_preferred_size(scrl_win.get_vscrollbar()) sb_width = requisition.width sb_height = requisition.height # Account for scrollbar width and border width to ensure # the text view gets a width of 80 characters. We don't care # so much whether the height will be exactly 40 characters. Gtk.Window.set_default_size(self, width + sb_width + 2 * 12, height) def style_set(self, old_style, user_data): pass #style = Gtk.Widget.get_style (self) #self.cons.stdout_tag.set_property ("foreground", style.text[Gtk.StateType.NORMAL]) #self.cons.stderr_tag.set_property ("foreground", style.text[Gtk.StateType.INSENSITIVE]) def response(self, dialog, response_id): if response_id == RESPONSE_BROWSE: self.browse() elif response_id == RESPONSE_CLEAR: self.cons.banner = None self.cons.clear() elif response_id == Gtk.ResponseType.OK: self.save_dialog() else: Gtk.main_quit() self.cons.grab_focus() def browse_response(self, dlg, response_id): if response_id != Gtk.ResponseType.APPLY: Gtk.Widget.hide(dlg) return proc_name = dlg.get_selected() if not proc_name: return proc = pdb[proc_name] cmd = '' if len(proc.return_vals) > 0: cmd = ', '.join(x[1].replace('-', '_') for x in proc.return_vals) + ' = ' cmd = cmd + 'pdb.%s' % proc.proc_name.replace('-', '_') if len(proc.params) > 0 and proc.params[0][1] == 'run-mode': params = proc.params[1:] else: params = proc.params cmd = cmd + '(%s)' % ', '.join(x[1].replace('-', '_') for x in params) buffer = self.cons.buffer lines = buffer.get_line_count() iter = buffer.get_iter_at_line_offset(lines - 1, 4) buffer.delete(iter, buffer.get_end_iter()) buffer.place_cursor(buffer.get_end_iter()) buffer.insert_at_cursor(cmd) def browse(self): if not self.browse_dlg: use_header_bar = Gtk.Settings.get_default().get_property("gtk-dialogs-use-header") dlg = GimpUi.ProcBrowserDialog(use_header_bar=use_header_bar) Gtk.Window.set_title(dlg, _("Python Procedure Browser")) Gtk.Window.set_role(dlg, PROC_NAME) Gtk.Dialog.add_button(dlg, "Apply", Gtk.ResponseType.APPLY) Gtk.Dialog.add_button(dlg, "Close", Gtk.ResponseType.CLOSE) Gtk.Dialog.set_default_response(self, Gtk.ResponseType.OK) GimpUi.Dialog.set_alternative_button_order_from_array(dlg, [ Gtk.ResponseType.CLOSE, Gtk.ResponseType.APPLY ]) dlg.connect('response', self.browse_response) dlg.connect('row-activated', lambda dlg: dlg.response(Gtk.ResponseType.APPLY)) self.browse_dlg = dlg Gtk.Window.present(self.browse_dlg) def save_response(self, dlg, response_id): if response_id == Gtk.ResponseType.DELETE_EVENT: self.save_dlg = None return elif response_id == Gtk.ResponseType.OK: filename = dlg.get_filename() try: logfile = open(filename, 'w') except IOError as e: Gimp.message(_("Could not open '%s' for writing: %s") % (filename, e.strerror)) return buffer = self.cons.buffer start = buffer.get_start_iter() end = buffer.get_end_iter() log = buffer.get_text(start, end, False) try: logfile.write(log) logfile.close() except IOError as e: Gimp.message(_("Could not write to '%s': %s") % (filename, e.strerror)) return Gtk.Widget.hide(dlg) def save_dialog(self): if not self.save_dlg: dlg = Gtk.FileChooserDialog() Gtk.Window.set_title(dlg, _("Save Python-Fu Console Output")) Gtk.Window.set_transient_for(dlg, self) Gtk.Dialog.add_button(dlg, "_Cancel", Gtk.ResponseType.CANCEL) Gtk.Dialog.add_button(dlg, "_Save", Gtk.ResponseType.OK) Gtk.Dialog.set_default_response(self, Gtk.ResponseType.OK) GimpUi.Dialog.set_alternative_button_order_from_array(dlg, [ Gtk.ResponseType.OK, Gtk.ResponseType.CANCEL ]) dlg.connect('response', self.save_response) self.save_dlg = dlg self.save_dlg.present() def run(self): Gtk.Widget.show_all(self) Gtk.main() ConsoleDialog().run() return procedure.new_return_values(Gimp.PDBStatusType.SUCCESS, GLib.Error())