diff options
Diffstat (limited to '')
-rw-r--r-- | plug-ins/pygimp/gimpfu.py | 873 |
1 files changed, 873 insertions, 0 deletions
diff --git a/plug-ins/pygimp/gimpfu.py b/plug-ins/pygimp/gimpfu.py new file mode 100644 index 0000000..54d7fc1 --- /dev/null +++ b/plug-ins/pygimp/gimpfu.py @@ -0,0 +1,873 @@ +# Gimp-Python - allows the writing of GIMP plug-ins in Python. +# Copyright (C) 1997 James Henstridge <james@daa.com.au> +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +"""Simple interface for writing GIMP plug-ins in Python. + +Instead of worrying about all the user interaction, saving last used +values and everything, the gimpfu module can take care of it for you. +It provides a simple register() function that will register your +plug-in if needed, and cause your plug-in function to be called when +needed. + +Gimpfu will also handle showing a user interface for editing plug-in +parameters if the plug-in is called interactively, and will also save +the last used parameters, so the RUN_WITH_LAST_VALUES run_type will +work correctly. It will also make sure that the displays are flushed +on completion if the plug-in was run interactively. + +When registering the plug-in, you do not need to worry about +specifying the run_type parameter. + +A typical gimpfu plug-in would look like this: + from gimpfu import * + + def plugin_func(image, drawable, args): + # do what plugins do best + register( + "plugin_func", + "blurb", + "help message", + "author", + "copyright", + "year", + "My plug-in", + "*", + [ + (PF_IMAGE, "image", "Input image", None), + (PF_DRAWABLE, "drawable", "Input drawable", None), + (PF_STRING, "arg", "The argument", "default-value") + ], + [], + plugin_func, menu="<Image>/Somewhere") + main() + +The call to "from gimpfu import *" will import all the gimp constants +into the plug-in namespace, and also import the symbols gimp, pdb, +register and main. This should be just about all any plug-in needs. + +You can use any of the PF_* constants below as parameter types, and an +appropriate user interface element will be displayed when the plug-in +is run in interactive mode. Note that the the PF_SPINNER and +PF_SLIDER types expect a fifth element in their description tuple -- a +3-tuple of the form (lower,upper,step), which defines the limits for +the slider or spinner. + +If want to localize your plug-in, add an optional domain parameter to +the register call. It can be the name of the translation domain or a +tuple that consists of the translation domain and the directory where +the translations are installed. +""" + +import string as _string +import math +import gimp +import gimpcolor +from gimpenums import * +pdb = gimp.pdb + +import gettext +t = gettext.translation("gimp20-python", gimp.locale_directory, fallback=True) +_ = t.ugettext + +class error(RuntimeError): pass +class CancelError(RuntimeError): pass + +PF_INT8 = PDB_INT8 +PF_INT16 = PDB_INT16 +PF_INT32 = PDB_INT32 +PF_INT = PF_INT32 +PF_FLOAT = PDB_FLOAT +PF_STRING = PDB_STRING +PF_VALUE = PF_STRING +#PF_INT8ARRAY = PDB_INT8ARRAY +#PF_INT16ARRAY = PDB_INT16ARRAY +#PF_INT32ARRAY = PDB_INT32ARRAY +#PF_INTARRAY = PF_INT32ARRAY +#PF_FLOATARRAY = PDB_FLOATARRAY +#PF_STRINGARRAY = PDB_STRINGARRAY +PF_COLOR = PDB_COLOR +PF_COLOUR = PF_COLOR +PF_ITEM = PDB_ITEM +PF_DISPLAY = PDB_DISPLAY +PF_IMAGE = PDB_IMAGE +PF_LAYER = PDB_LAYER +PF_CHANNEL = PDB_CHANNEL +PF_DRAWABLE = PDB_DRAWABLE +PF_VECTORS = PDB_VECTORS +#PF_SELECTION = PDB_SELECTION +#PF_BOUNDARY = PDB_BOUNDARY +#PF_PATH = PDB_PATH +#PF_STATUS = PDB_STATUS + +PF_TOGGLE = 1000 +PF_BOOL = PF_TOGGLE +PF_SLIDER = 1001 +PF_SPINNER = 1002 +PF_ADJUSTMENT = PF_SPINNER + +PF_FONT = 1003 +PF_FILE = 1004 +PF_BRUSH = 1005 +PF_PATTERN = 1006 +PF_GRADIENT = 1007 +PF_RADIO = 1008 +PF_TEXT = 1009 +PF_PALETTE = 1010 +PF_FILENAME = 1011 +PF_DIRNAME = 1012 +PF_OPTION = 1013 + +_type_mapping = { + PF_INT8 : PDB_INT8, + PF_INT16 : PDB_INT16, + PF_INT32 : PDB_INT32, + PF_FLOAT : PDB_FLOAT, + PF_STRING : PDB_STRING, + #PF_INT8ARRAY : PDB_INT8ARRAY, + #PF_INT16ARRAY : PDB_INT16ARRAY, + #PF_INT32ARRAY : PDB_INT32ARRAY, + #PF_FLOATARRAY : PDB_FLOATARRAY, + #PF_STRINGARRAY : PDB_STRINGARRAY, + PF_COLOR : PDB_COLOR, + PF_ITEM : PDB_ITEM, + PF_DISPLAY : PDB_DISPLAY, + PF_IMAGE : PDB_IMAGE, + PF_LAYER : PDB_LAYER, + PF_CHANNEL : PDB_CHANNEL, + PF_DRAWABLE : PDB_DRAWABLE, + PF_VECTORS : PDB_VECTORS, + + PF_TOGGLE : PDB_INT32, + PF_SLIDER : PDB_FLOAT, + PF_SPINNER : PDB_INT32, + + PF_FONT : PDB_STRING, + PF_FILE : PDB_STRING, + PF_BRUSH : PDB_STRING, + PF_PATTERN : PDB_STRING, + PF_GRADIENT : PDB_STRING, + PF_RADIO : PDB_STRING, + PF_TEXT : PDB_STRING, + PF_PALETTE : PDB_STRING, + PF_FILENAME : PDB_STRING, + PF_DIRNAME : PDB_STRING, + PF_OPTION : PDB_INT32, +} + +_obj_mapping = { + PF_INT8 : int, + PF_INT16 : int, + PF_INT32 : int, + PF_FLOAT : float, + PF_STRING : str, + #PF_INT8ARRAY : list, + #PF_INT16ARRAY : list, + #PF_INT32ARRAY : list, + #PF_FLOATARRAY : list, + #PF_STRINGARRAY : list, + PF_COLOR : gimpcolor.RGB, + PF_ITEM : int, + PF_DISPLAY : gimp.Display, + PF_IMAGE : gimp.Image, + PF_LAYER : gimp.Layer, + PF_CHANNEL : gimp.Channel, + PF_DRAWABLE : gimp.Drawable, + PF_VECTORS : gimp.Vectors, + + PF_TOGGLE : bool, + PF_SLIDER : float, + PF_SPINNER : int, + + PF_FONT : str, + PF_FILE : str, + PF_BRUSH : str, + PF_PATTERN : str, + PF_GRADIENT : str, + PF_RADIO : str, + PF_TEXT : str, + PF_PALETTE : str, + PF_FILENAME : str, + PF_DIRNAME : str, + PF_OPTION : int, +} + +_registered_plugins_ = {} + +def register(proc_name, blurb, help, author, copyright, date, label, + imagetypes, params, results, function, + menu=None, domain=None, on_query=None, on_run=None): + """This is called to register a new plug-in.""" + + # First perform some sanity checks on the data + def letterCheck(str): + allowed = _string.letters + _string.digits + "_" + "-" + for ch in str: + if not ch in allowed: + return 0 + else: + return 1 + + if not letterCheck(proc_name): + raise error, "procedure name contains illegal characters" + + for ent in params: + if len(ent) < 4: + raise error, ("parameter definition must contain at least 4 " + "elements (%s given: %s)" % (len(ent), ent)) + + if type(ent[0]) != int: + raise error, "parameter types must be integers" + + if not letterCheck(ent[1]): + raise error, "parameter name contains illegal characters" + + for ent in results: + if len(ent) < 3: + raise error, ("result definition must contain at least 3 elements " + "(%s given: %s)" % (len(ent), ent)) + + if type(ent[0]) != type(42): + raise error, "result types must be integers" + + if not letterCheck(ent[1]): + raise error, "result name contains illegal characters" + + plugin_type = PLUGIN + + if (not proc_name.startswith("python-") and + not proc_name.startswith("python_") and + not proc_name.startswith("extension-") and + not proc_name.startswith("extension_") and + not proc_name.startswith("plug-in-") and + not proc_name.startswith("plug_in_") and + not proc_name.startswith("file-") and + not proc_name.startswith("file_")): + proc_name = "python-fu-" + proc_name + + # if menu is not given, derive it from label + need_compat_params = False + if menu is None and label: + fields = label.split("/") + if fields: + label = fields.pop() + menu = "/".join(fields) + need_compat_params = True + + import warnings + message = ("%s: passing the full menu path for the menu label is " + "deprecated, use the 'menu' parameter instead" + % (proc_name)) + warnings.warn(message, DeprecationWarning, 3) + + if need_compat_params and plugin_type == PLUGIN: + file_params = [(PDB_STRING, "filename", "The name of the file", ""), + (PDB_STRING, "raw-filename", "The name of the file", "")] + + if menu is None: + pass + elif menu.startswith("<Load>"): + params[0:0] = file_params + elif menu.startswith("<Image>") or menu.startswith("<Save>"): + params.insert(0, (PDB_IMAGE, "image", "Input image", None)) + params.insert(1, (PDB_DRAWABLE, "drawable", "Input drawable", None)) + if menu.startswith("<Save>"): + params[2:2] = file_params + + _registered_plugins_[proc_name] = (blurb, help, author, copyright, + date, label, imagetypes, + plugin_type, params, results, + function, menu, domain, + on_query, on_run) + +def _query(): + for plugin in _registered_plugins_.keys(): + (blurb, help, author, copyright, date, + label, imagetypes, plugin_type, + params, results, function, menu, domain, + on_query, on_run) = _registered_plugins_[plugin] + + def make_params(params): + return [(_type_mapping[x[0]], + x[1], + _string.replace(x[2], "_", "")) for x in params] + + params = make_params(params) + # add the run mode argument ... + params.insert(0, (PDB_INT32, "run-mode", + "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }")) + + results = make_params(results) + + if domain: + try: + (domain, locale_dir) = domain + gimp.domain_register(domain, locale_dir) + except ValueError: + gimp.domain_register(domain) + + gimp.install_procedure(plugin, blurb, help, author, copyright, + date, label, imagetypes, plugin_type, + params, results) + + if menu: + gimp.menu_register(plugin, menu) + if on_query: + on_query() + +def _get_defaults(proc_name): + import gimpshelf + + (blurb, help, author, copyright, date, + label, imagetypes, plugin_type, + params, results, function, menu, domain, + on_query, on_run) = _registered_plugins_[proc_name] + + key = "python-fu-save--" + proc_name + + if gimpshelf.shelf.has_key(key): + return gimpshelf.shelf[key] + else: + # return the default values + return [x[3] for x in params] + +def _set_defaults(proc_name, defaults): + import gimpshelf + + key = "python-fu-save--" + proc_name + gimpshelf.shelf[key] = defaults + +def _interact(proc_name, start_params): + (blurb, help, author, copyright, date, + label, imagetypes, plugin_type, + params, results, function, menu, domain, + on_query, on_run) = _registered_plugins_[proc_name] + + def run_script(run_params): + params = start_params + tuple(run_params) + _set_defaults(proc_name, params) + return apply(function, params) + + params = params[len(start_params):] + + # short circuit for no parameters ... + if len(params) == 0: + return run_script([]) + + import pygtk + pygtk.require('2.0') + + import gimpui + import gtk +# import pango + gimpui.gimp_ui_init () + + defaults = _get_defaults(proc_name) + defaults = defaults[len(start_params):] + + class EntryValueError(Exception): + pass + + def warning_dialog(parent, primary, secondary=None): + dlg = gtk.MessageDialog(parent, gtk.DIALOG_DESTROY_WITH_PARENT, + gtk.MESSAGE_WARNING, gtk.BUTTONS_CLOSE, + primary) + if secondary: + dlg.format_secondary_text(secondary) + dlg.run() + dlg.destroy() + + def error_dialog(parent, proc_name): + import sys, traceback + + exc_str = exc_only_str = _("Missing exception information") + + try: + etype, value, tb = sys.exc_info() + exc_str = "".join(traceback.format_exception(etype, value, tb)) + exc_only_str = "".join(traceback.format_exception_only(etype, value)) + finally: + etype = value = tb = None + + title = _("An error occurred running %s") % proc_name + dlg = gtk.MessageDialog(parent, gtk.DIALOG_DESTROY_WITH_PARENT, + gtk.MESSAGE_ERROR, gtk.BUTTONS_CLOSE, + title) + dlg.format_secondary_text(exc_only_str) + + alignment = gtk.Alignment(0.0, 0.0, 1.0, 1.0) + alignment.set_padding(0, 0, 12, 12) + dlg.vbox.pack_start(alignment) + alignment.show() + + expander = gtk.Expander(_("_More Information")); + expander.set_use_underline(True) + expander.set_spacing(6) + alignment.add(expander) + expander.show() + + scrolled = gtk.ScrolledWindow() + scrolled.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) + scrolled.set_size_request(-1, 200) + expander.add(scrolled) + scrolled.show() + + + label = gtk.Label(exc_str) + label.set_alignment(0.0, 0.0) + label.set_padding(6, 6) + label.set_selectable(True) + scrolled.add_with_viewport(label) + label.show() + + def response(widget, id): + widget.destroy() + + dlg.connect("response", response) + dlg.set_resizable(True) + dlg.show() + + # define a mapping of param types to edit objects ... + class StringEntry(gtk.Entry): + def __init__(self, default=""): + gtk.Entry.__init__(self) + self.set_text(str(default)) + self.set_activates_default(True) + + def get_value(self): + return self.get_text() + + class TextEntry(gtk.ScrolledWindow): + def __init__ (self, default=""): + gtk.ScrolledWindow.__init__(self) + self.set_shadow_type(gtk.SHADOW_IN) + + self.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) + self.set_size_request(100, -1) + + self.view = gtk.TextView() + self.add(self.view) + self.view.show() + + self.buffer = self.view.get_buffer() + + self.set_value(str(default)) + + def set_value(self, text): + self.buffer.set_text(text) + + def get_value(self): + return self.buffer.get_text(self.buffer.get_start_iter(), + self.buffer.get_end_iter()) + + class IntEntry(StringEntry): + def get_value(self): + try: + return int(self.get_text()) + except ValueError, e: + raise EntryValueError, e.args + + class FloatEntry(StringEntry): + def get_value(self): + try: + return float(self.get_text()) + except ValueError, e: + raise EntryValueError, e.args + +# class ArrayEntry(StringEntry): +# def get_value(self): +# return eval(self.get_text(), {}, {}) + + + def precision(step): + # calculate a reasonable precision from a given step size + if math.fabs(step) >= 1.0 or step == 0.0: + digits = 0 + else: + digits = abs(math.floor(math.log10(math.fabs(step)))); + if digits > 20: + digits = 20 + return int(digits) + + class SliderEntry(gtk.HScale): + # bounds is (upper, lower, step) + def __init__(self, default=0, bounds=(0, 100, 5)): + step = bounds[2] + self.adj = gtk.Adjustment(default, bounds[0], bounds[1], + step, 10 * step, 0) + gtk.HScale.__init__(self, self.adj) + self.set_digits(precision(step)) + + def get_value(self): + return self.adj.value + + class SpinnerEntry(gtk.SpinButton): + # bounds is (upper, lower, step) + def __init__(self, default=0, bounds=(0, 100, 5)): + step = bounds[2] + self.adj = gtk.Adjustment(default, bounds[0], bounds[1], + step, 10 * step, 0) + gtk.SpinButton.__init__(self, self.adj, step, precision(step)) + + class ToggleEntry(gtk.ToggleButton): + def __init__(self, default=0): + gtk.ToggleButton.__init__(self) + + self.label = gtk.Label(_("No")) + self.add(self.label) + self.label.show() + + self.connect("toggled", self.changed) + + self.set_active(default) + + def changed(self, tog): + if tog.get_active(): + self.label.set_text(_("Yes")) + else: + self.label.set_text(_("No")) + + def get_value(self): + return self.get_active() + + class RadioEntry(gtk.VBox): + def __init__(self, default=0, items=((_("Yes"), 1), (_("No"), 0))): + gtk.VBox.__init__(self, homogeneous=False, spacing=2) + + button = None + + for (label, value) in items: + button = gtk.RadioButton(button, label) + self.pack_start(button) + button.show() + + button.connect("toggled", self.changed, value) + + if value == default: + button.set_active(True) + self.active_value = value + + def changed(self, radio, value): + if radio.get_active(): + self.active_value = value + + def get_value(self): + return self.active_value + + class ComboEntry(gtk.ComboBox): + def __init__(self, default=0, items=()): + store = gtk.ListStore(str) + for item in items: + store.append([item]) + + gtk.ComboBox.__init__(self, model=store) + + cell = gtk.CellRendererText() + self.pack_start(cell) + self.set_attributes(cell, text=0) + + self.set_active(default) + + def get_value(self): + return self.get_active() + + def FileSelector(default="", title=None): + # FIXME: should this be os.path.separator? If not, perhaps explain why? + if default and default.endswith("/"): + if default == "/": default = "" + return DirnameSelector(default) + else: + return FilenameSelector(default, title=title, save_mode=False) + + class FilenameSelector(gtk.HBox): + #gimpfu.FileChooserButton + def __init__(self, default, save_mode=True, title=None): + super(FilenameSelector, self).__init__() + if not title: + self.title = _("Python-Fu File Selection") + else: + self.title = title + self.save_mode = save_mode + box = self + self.entry = gtk.Entry() + image = gtk.Image() + image.set_from_stock(gtk.STOCK_FILE, gtk.ICON_SIZE_BUTTON) + self.button = gtk.Button() + self.button.set_image(image) + box.pack_start(self.entry) + box.pack_start(self.button, expand=False) + self.button.connect("clicked", self.pick_file) + if default: + self.entry.set_text(default) + + def show(self): + super(FilenameSelector, self).show() + self.button.show() + self.entry.show() + + def pick_file(self, widget): + entry = self.entry + dialog = gtk.FileChooserDialog( + title=self.title, + action=(gtk.FILE_CHOOSER_ACTION_SAVE + if self.save_mode else + gtk.FILE_CHOOSER_ACTION_OPEN), + buttons=(gtk.STOCK_CANCEL, + gtk.RESPONSE_CANCEL, + gtk.STOCK_SAVE + if self.save_mode else + gtk.STOCK_OPEN, + gtk.RESPONSE_OK) + ) + dialog.set_alternative_button_order ((gtk.RESPONSE_OK, gtk.RESPONSE_CANCEL)) + dialog.show_all() + response = dialog.run() + if response == gtk.RESPONSE_OK: + entry.set_text(dialog.get_filename()) + dialog.destroy() + + def get_value(self): + return self.entry.get_text() + + + class DirnameSelector(gtk.FileChooserButton): + def __init__(self, default=""): + gtk.FileChooserButton.__init__(self, + _("Python-Fu Folder Selection")) + self.set_action(gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER) + if default: + self.set_filename(default) + + def get_value(self): + return self.get_filename() + + _edit_mapping = { + PF_INT8 : IntEntry, + PF_INT16 : IntEntry, + PF_INT32 : IntEntry, + PF_FLOAT : FloatEntry, + PF_STRING : StringEntry, + #PF_INT8ARRAY : ArrayEntry, + #PF_INT16ARRAY : ArrayEntry, + #PF_INT32ARRAY : ArrayEntry, + #PF_FLOATARRAY : ArrayEntry, + #PF_STRINGARRAY : ArrayEntry, + PF_COLOR : gimpui.ColorSelector, + PF_ITEM : IntEntry, # should handle differently ... + PF_IMAGE : gimpui.ImageSelector, + PF_LAYER : gimpui.LayerSelector, + PF_CHANNEL : gimpui.ChannelSelector, + PF_DRAWABLE : gimpui.DrawableSelector, + PF_VECTORS : gimpui.VectorsSelector, + + PF_TOGGLE : ToggleEntry, + PF_SLIDER : SliderEntry, + PF_SPINNER : SpinnerEntry, + PF_RADIO : RadioEntry, + PF_OPTION : ComboEntry, + + PF_FONT : gimpui.FontSelector, + PF_FILE : FileSelector, + PF_FILENAME : FilenameSelector, + PF_DIRNAME : DirnameSelector, + PF_BRUSH : gimpui.BrushSelector, + PF_PATTERN : gimpui.PatternSelector, + PF_GRADIENT : gimpui.GradientSelector, + PF_PALETTE : gimpui.PaletteSelector, + PF_TEXT : TextEntry + } + + if on_run: + on_run() + + dialog = gimpui.Dialog(proc_name, "python-fu", None, 0, None, proc_name, + (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, + gtk.STOCK_OK, gtk.RESPONSE_OK)) + + dialog.set_alternative_button_order((gtk.RESPONSE_OK, gtk.RESPONSE_CANCEL)) + + dialog.set_transient() + + vbox = gtk.VBox(False, 12) + vbox.set_border_width(12) + dialog.vbox.pack_start(vbox) + vbox.show() + + if blurb: + if domain: + try: + (domain, locale_dir) = domain + trans = gettext.translation(domain, locale_dir, fallback=True) + except ValueError: + trans = gettext.translation(domain, fallback=True) + blurb = trans.ugettext(blurb) + box = gimpui.HintBox(blurb) + vbox.pack_start(box, expand=False) + box.show() + + table = gtk.Table(len(params), 2, False) + table.set_row_spacings(6) + table.set_col_spacings(6) + vbox.pack_start(table, expand=False) + table.show() + + def response(dlg, id): + if id == gtk.RESPONSE_OK: + dlg.set_response_sensitive(gtk.RESPONSE_OK, False) + dlg.set_response_sensitive(gtk.RESPONSE_CANCEL, False) + + params = [] + + try: + for wid in edit_wids: + params.append(wid.get_value()) + except EntryValueError: + warning_dialog(dialog, _("Invalid input for '%s'") % wid.desc) + else: + try: + dialog.res = run_script(params) + except CancelError: + pass + except Exception: + dlg.set_response_sensitive(gtk.RESPONSE_CANCEL, True) + error_dialog(dialog, proc_name) + raise + + gtk.main_quit() + + dialog.connect("response", response) + + edit_wids = [] + for i in range(len(params)): + pf_type = params[i][0] + name = params[i][1] + desc = params[i][2] + def_val = defaults[i] + + label = gtk.Label(desc) + label.set_use_underline(True) + label.set_alignment(0.0, 0.5) + table.attach(label, 1, 2, i, i+1, xoptions=gtk.FILL) + label.show() + + # Remove accelerator markers from tooltips + tooltip_text = desc.replace("_", "") + + if pf_type in (PF_SPINNER, PF_SLIDER, PF_RADIO, PF_OPTION): + wid = _edit_mapping[pf_type](def_val, params[i][4]) + elif pf_type in (PF_FILE, PF_FILENAME): + wid = _edit_mapping[pf_type](def_val, title= "%s - %s" % + (proc_name, tooltip_text)) + else: + wid = _edit_mapping[pf_type](def_val) + + + label.set_mnemonic_widget(wid) + + table.attach(wid, 2,3, i,i+1, yoptions=0) + + if pf_type != PF_TEXT: + wid.set_tooltip_text(tooltip_text) + else: + # Attach tip to TextView, not to ScrolledWindow + wid.view.set_tooltip_text(tooltip_text) + wid.show() + + wid.desc = desc + edit_wids.append(wid) + + progress_vbox = gtk.VBox(False, 6) + vbox.pack_end(progress_vbox, expand=False) + progress_vbox.show() + + progress = gimpui.ProgressBar() + progress_vbox.pack_start(progress) + progress.show() + +# progress_label = gtk.Label() +# progress_label.set_alignment(0.0, 0.5) +# progress_label.set_ellipsize(pango.ELLIPSIZE_MIDDLE) + +# attrs = pango.AttrList() +# attrs.insert(pango.AttrStyle(pango.STYLE_ITALIC, 0, -1)) +# progress_label.set_attributes(attrs) + +# progress_vbox.pack_start(progress_label) +# progress_label.show() + + dialog.show() + + gtk.main() + + if hasattr(dialog, "res"): + res = dialog.res + dialog.destroy() + return res + else: + dialog.destroy() + raise CancelError + +def _run(proc_name, params): + run_mode = params[0] + func = _registered_plugins_[proc_name][10] + + if run_mode == RUN_NONINTERACTIVE: + return apply(func, params[1:]) + + script_params = _registered_plugins_[proc_name][8] + + min_args = 0 + if len(params) > 1: + for i in range(1, len(params)): + param_type = _obj_mapping[script_params[i - 1][0]] + if not isinstance(params[i], param_type): + break + + min_args = i + + if len(script_params) > min_args: + start_params = params[:min_args + 1] + + if run_mode == RUN_WITH_LAST_VALS: + default_params = _get_defaults(proc_name) + params = start_params + default_params[min_args:] + else: + params = start_params + else: + run_mode = RUN_NONINTERACTIVE + + if run_mode == RUN_INTERACTIVE: + try: + res = _interact(proc_name, params[1:]) + except CancelError: + return + else: + res = apply(func, params[1:]) + + gimp.displays_flush() + + return res + +def main(): + """This should be called after registering the plug-in.""" + gimp.main(None, None, _query, _run) + +def fail(msg): + """Display an error message and quit""" + gimp.message(msg) + raise error, msg + +def N_(message): + return message |