home *** CD-ROM | disk | FTP | other *** search
Wrap
# Source Generated with Decompyle++ # File: in.pyo (Python 2.6) '''Simple interface to 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"), (PF_DRAWABLE, "drawable", "Input drawable"), (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_COLOR = PDB_COLOR PF_COLOUR = PF_COLOR PF_REGION = PDB_REGION 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 = 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_COLOR: PDB_COLOR, PF_REGION: PDB_REGION, 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_COLOR: gimpcolor.RGB, PF_REGION: 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.''' def letterCheck(str): allowed = _string.letters + _string.digits + '_' + '-' for ch in str: if ch not in allowed: return 0 else: return 1 return ch not in allowed if not letterCheck(proc_name): raise error, 'procedure name contains illegal characters' letterCheck(proc_name) for ent in params: if len(ent) < 4: raise error, 'parameter definition must contain at least 4 elements (%s given: %s)' % (len(ent), ent) len(ent) < 4 if type(ent[0]) != int: raise error, 'parameter types must be integers' type(ent[0]) != int if not letterCheck(ent[1]): raise error, 'parameter name contains illegal characters' letterCheck(ent[1]) for ent in results: if len(ent) < 3: raise error, 'result definition must contain at least 3 elements (%s given: %s)' % (len(ent), ent) len(ent) < 3 if type(ent[0]) != type(42): raise error, 'result types must be integers' type(ent[0]) != type(42) if not letterCheck(ent[1]): raise error, 'result name contains illegal characters' letterCheck(ent[1]) plugin_type = PLUGIN if not (proc_name[:7] == 'python-') and not (proc_name[:7] == 'python_') and not (proc_name[:10] == 'extension-') and not (proc_name[:10] == 'extension_') and not (proc_name[:8] == 'plug-in-') and not (proc_name[:8] == 'plug_in_') and not (proc_name[:5] == 'file-') and not (proc_name[:5] == 'file_'): proc_name = 'python-fu-' + proc_name 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 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[:6] == '<Load>': params[0:0] = file_params elif menu[:7] == '<Image>' or menu[:6] == '<Save>': params.insert(0, (PDB_IMAGE, 'image', 'Input image', None)) params.insert(1, (PDB_DRAWABLE, 'drawable', 'Input drawable', None)) if menu[:6] == '<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) params.insert(0, (PDB_INT32, 'run-mode', 'Interactive, Non-Interactive')) results = make_params(results) if domain: try: (domain, locale_dir) = domain gimp.domain_register(domain, locale_dir) except ValueError: gimp.domain_register(domain) except: None<EXCEPTION MATCH>ValueError None<EXCEPTION MATCH>ValueError 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() continue 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] 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):] if len(params) == 0: return run_script([]) import pygtk pygtk.require('2.0') import gimpui import gtk 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 import 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 = None value = None tb = None title = _('An error occured 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, 1, 1) 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) 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() class StringEntry((gtk.Entry,)): def __init__(self, default = ('',)): gtk.Entry.__init__(self) self.set_text(str(default)) 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 = None raise EntryValueError, e.args class FloatEntry((StringEntry,)): def get_value(self): try: return float(self.get_text()) except ValueError: e = None raise EntryValueError, e.args def precision(step): if math.fabs(step) >= 1 or step == 0: digits = 0 else: digits = abs(math.floor(math.log10(math.fabs(step)))) if digits > 20: digits = 20 return int(digits) class SliderEntry('SliderEntry', (gtk.HScale,)): def __init__(self, default = None, bounds = (0, (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('SpinnerEntry', (gtk.SpinButton,)): def __init__(self, default = None, bounds = (0, (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 continue 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 = (None, '')): if default and default.endswith('/'): selector = DirnameSelector if default == '/': default = '' else: selector = FilenameSelector return selector(default) class FilenameSelector((gtk.FileChooserButton,)): def __init__(self, default = '', save_mode = (False,)): gtk.FileChooserButton.__init__(self, _('Python-Fu File Selection')) self.set_action(gtk.FILE_CHOOSER_ACTION_OPEN) if default: self.set_filename(default) def get_value(self): return self.get_filename() 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_COLOR: gimpui.ColorSelector, PF_REGION: IntEntry, 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() tooltips = gtk.Tooltips() 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) try: dialog.res = run_script(params) except Exception: dlg.set_response_sensitive(gtk.RESPONSE_CANCEL, True) error_dialog(dialog, proc_name) raise except: None<EXCEPTION MATCH>Exception None<EXCEPTION MATCH>Exception 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.5) table.attach(label, 1, 2, i, i + 1, xoptions = gtk.FILL) label.show() if pf_type in (PF_SPINNER, PF_SLIDER, PF_RADIO, PF_OPTION): wid = _edit_mapping[pf_type](def_val, params[i][4]) 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: tooltips.set_tip(wid, desc, None) else: tooltips.set_tip(wid.view, desc, None) 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() tooltips.enable() dialog.show() gtk.main() if hasattr(dialog, 'res'): res = dialog.res dialog.destroy() return res 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(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 None None<EXCEPTION MATCH>CancelError 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 and error message and quit''' gimp.message(msg) raise error, msg def N_(message): return message