gimpfu.py 20.3 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#   Gimp-Python - allows the writing of Gimp plugins 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 2 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, write to the Free Software
#   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
'''Simple interface to writing GIMP plugins 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 plugin if needed, and
cause your plugin function to be called when needed.

Gimpfu will also handle showing a user interface for editing plugin parameters
if the plugin 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 plugin
was run interactively.

When registering the plugin, you do not need to worry about specifying
the run_type parameter.  And if the plugin is an image plugin (the menu
path starts with <Image>/), the image and drawable parameters are also
automatically added.

A typical gimpfu plugin would look like this:
  from gimpfu import *

  def plugin_func(image, drawable, args):
40
              #do what plugins do best
41
  register(
42
43
44
45
46
47
48
49
50
51
52
              "plugin_func",
              "blurb",
              "help message",
              "author",
              "copyright",
              "year",
              "<Image>/Somewhere/My plugin",
              "*",
              [(PF_STRING, "arg", "The argument", "default-value")],
              [],
              plugin_func)
53
54
55
56
57
58
59
60
61
62
63
  main()

The call to "from gimpfu import *" will import all the gimp constants into
the plugin namespace, and also import the symbols gimp, pdb, register and
main.  This should be just about all any plugin needs.  You can use any
of the PF_* constants below as parameter types, and an appropriate user
interface element will be displayed when the plugin 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.'''

James Henstridge's avatar
James Henstridge committed
64
import string as _string
65
66
67
68
import gimp
from gimpenums import *
pdb = gimp.pdb

James Henstridge's avatar
James Henstridge committed
69
class error(RuntimeError):pass
70
class CancelError(RuntimeError):pass
71

72
73
74
PF_INT8        = PDB_INT8
PF_INT16       = PDB_INT16
PF_INT32       = PDB_INT32
75
PF_INT         = PF_INT32
76
77
PF_FLOAT       = PDB_FLOAT
PF_STRING      = PDB_STRING
78
PF_VALUE       = PF_STRING
79
80
81
82
83
84
#PF_INT8ARRAY   = PDB_INT8ARRAY
#PF_INT16ARRAY  = PDB_INT16ARRAY
#PF_INT32ARRAY  = PDB_INT32ARRAY
#PF_INTARRAY    = PF_INT32ARRAY
#PF_FLOATARRAY  = PDB_FLOATARRAY
#PF_STRINGARRAY = PDB_STRINGARRAY
85
PF_COLOR       = PDB_COLOR
86
PF_COLOUR      = PF_COLOR
87
88
89
90
91
92
93
94
95
96
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_SELECTION   = PDB_SELECTION
#PF_BOUNDARY    = PDB_BOUNDARY
#PF_PATH        = PDB_PATH
#PF_STATUS      = PDB_STATUS
97
98
99
100
101
102
103
104
105
106
107
108

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
109
PF_RADIO       = 1008
110
PF_TEXT        = 1009
111
PF_PALETTE     = 1010
112
113

_type_mapping = {
James Henstridge's avatar
James Henstridge committed
114
115
116
117
118
    PF_INT8        : PDB_INT8,
    PF_INT16       : PDB_INT16,
    PF_INT32       : PDB_INT32,
    PF_FLOAT       : PDB_FLOAT,
    PF_STRING      : PDB_STRING,
119
120
121
122
123
    #PF_INT8ARRAY   : PDB_INT8ARRAY,
    #PF_INT16ARRAY  : PDB_INT16ARRAY,
    #PF_INT32ARRAY  : PDB_INT32ARRAY,
    #PF_FLOATARRAY  : PDB_FLOATARRAY,
    #PF_STRINGARRAY : PDB_STRINGARRAY,
James Henstridge's avatar
James Henstridge committed
124
125
126
127
128
129
130
131
132
133
    PF_COLOUR      : PDB_COLOR,
    PF_REGION      : PDB_REGION,
    PF_IMAGE       : PDB_IMAGE,
    PF_LAYER       : PDB_LAYER,
    PF_CHANNEL     : PDB_CHANNEL,
    PF_DRAWABLE    : PDB_DRAWABLE,

    PF_TOGGLE      : PDB_INT32,
    PF_SLIDER      : PDB_FLOAT,
    PF_SPINNER     : PDB_INT32,
134

James Henstridge's avatar
James Henstridge committed
135
136
137
138
    PF_FONT        : PDB_STRING,
    PF_FILE        : PDB_STRING,
    PF_BRUSH       : PDB_STRING,
    PF_PATTERN     : PDB_STRING,
139
140
    PF_GRADIENT    : PDB_STRING,
    PF_RADIO       : PDB_STRING,
141
    PF_TEXT        : PDB_STRING,
142
    PF_PALETTE     : PDB_STRING,
143
144
145
146
147
}

_registered_plugins_ = {}

def register(func_name, blurb, help, author, copyright, date, menupath,
148
149
                 imagetypes, params, results, function,
                 on_query=None, on_run=None):
James Henstridge's avatar
James Henstridge committed
150
    '''This is called to register a new plugin.'''
151

James Henstridge's avatar
James Henstridge committed
152
153
    # First perform some sanity checks on the data
    def letterCheck(str):
154
155
156
157
158
159
        allowed = _string.letters + _string.digits + '_'
        for ch in str:
            if not ch in allowed:
                    return 0
        else:
            return 1
160

James Henstridge's avatar
James Henstridge committed
161
    if not letterCheck(func_name):
162
        raise error, "function name contains illegal characters"
163

James Henstridge's avatar
James Henstridge committed
164
    for ent in params:
165
166
167
        if len(ent) < 4:
            raise error, ("parameter definition must contain at least 4 "
                        "elements (%s given: %s)" % (len(ent), ent))
168

169
170
        if type(ent[0]) != type(42):
            raise error, "parameter types must be integers"
171

172
173
        if not letterCheck(ent[1]):
            raise error, "parameter name contains illegal characters"
174

James Henstridge's avatar
James Henstridge committed
175
    for ent in results:
176
177
178
        if len(ent) < 3:
            raise error, ("result definition must contain at least 3 elements "
                        "(%s given: %s)" % (len(ent), ent))
179

180
181
        if type(ent[0]) != type(42):
            raise error, "result types must be integers"
182

183
184
        if not letterCheck(ent[1]):
            raise error, "result name contains illegal characters"
185

186
187
    if menupath[:8] == '<Image>/' or \
       menupath[:7] == '<Load>/' or \
188
189
       menupath[:7] == '<Save>/' or \
       menupath[:10] == '<Toolbox>/':
190
        plugin_type = PLUGIN
James Henstridge's avatar
James Henstridge committed
191
    else:
192
        raise error, "Invalid menu path"
James Henstridge's avatar
James Henstridge committed
193
194
195

    if not func_name[:7] == 'python_' and \
       not func_name[:10] == 'extension_' and \
196
197
       not func_name[:8] == 'plug_in_' and \
       not func_name[:5] == 'file_':
198
           func_name = 'python_fu_' + func_name
James Henstridge's avatar
James Henstridge committed
199
200

    _registered_plugins_[func_name] = (blurb, help, author, copyright,
201
202
203
                                       date, menupath, imagetypes,
                                       plugin_type, params, results,
                                       function, on_query, on_run)
204
205

file_params = [(PDB_STRING, "filename", "The name of the file"),
206
               (PDB_STRING, "raw_filename", "The name of the file")]
207
208

def _query():
James Henstridge's avatar
James Henstridge committed
209
    for plugin in _registered_plugins_.keys():
210
211
212
        (blurb, help, author, copyright, date,
         menupath, imagetypes, plugin_type,
         params, results, function,
213
         on_query, on_run) = _registered_plugins_[plugin]
James Henstridge's avatar
James Henstridge committed
214

215
216
217
218
        def make_params(params):
            return [(_type_mapping[x[0]], x[1], x[2]) for x in params]

        params = make_params(params)
219
220
        # add the run mode argument ...
        params.insert(0, (PDB_INT32, "run_mode",
221
                                     "Interactive, Non-Interactive"))
222
223
224
225
226
227
228
229
230
231
232
        if plugin_type == PLUGIN:
            if menupath[:7] == '<Load>/':
                params[1:1] = file_params
            elif menupath[:10] != '<Toolbox>/':
                params.insert(1, (PDB_IMAGE, "image",
                                  "The image to work on"))
                params.insert(2, (PDB_DRAWABLE, "drawable",
                                  "The drawable to work on"))
                if menupath[:7] == '<Save>/':
                    params[3:3] = file_params

233
        results = make_params(results)
234
235
236
237
238
        gimp.install_procedure(plugin, blurb, help, author, copyright,
                               date, menupath, imagetypes, plugin_type,
                               params, results)
        if on_query:
            on_query()
James Henstridge's avatar
James Henstridge committed
239
240
241
242
243

def _get_defaults(func_name):
    import gimpshelf
    (blurb, help, author, copyright, date,
     menupath, imagetypes, plugin_type,
244
245
     params, results, function,
     on_query, on_run) = _registered_plugins_[func_name]
246

James Henstridge's avatar
James Henstridge committed
247
    key = "python-fu-save--" + func_name
248

James Henstridge's avatar
James Henstridge committed
249
    if gimpshelf.shelf.has_key(key):
250
        return gimpshelf.shelf[key]
James Henstridge's avatar
James Henstridge committed
251
    else:
252
        # return the default values
253
        return [x[3] for x in params]
254
255

def _set_defaults(func_name, defaults):
James Henstridge's avatar
James Henstridge committed
256
    import gimpshelf
257

James Henstridge's avatar
James Henstridge committed
258
259
    key = "python-fu-save--" + func_name
    gimpshelf.shelf[key] = defaults
260

261
def _interact(func_name, start_params):
James Henstridge's avatar
James Henstridge committed
262
263
    (blurb, help, author, copyright, date,
     menupath, imagetypes, plugin_type,
264
265
     params, results, function,
     on_query, on_run) = _registered_plugins_[func_name]
266

267
    def run_script(run_params):
268
269
        params = start_params + tuple(run_params)
        return apply(function, params)
270

James Henstridge's avatar
James Henstridge committed
271
    # short circuit for no parameters ...
272
273
    if len(params) == 0:
         return run_script([])
274

275
276
277
    import pygtk
    pygtk.require('2.0')

James Henstridge's avatar
James Henstridge committed
278
279
    import gtk
    import gimpui
280

James Henstridge's avatar
James Henstridge committed
281
    gtk.rc_parse(gimp.gtkrc())
282

James Henstridge's avatar
James Henstridge committed
283
    defaults = _get_defaults(func_name)
284
285

    class EntryValueError(Exception):
286
        pass
287
288

    def error_dialog(parent, msg):
289
290
291
292
293
        dlg = gtk.MessageDialog(parent, gtk.DIALOG_DESTROY_WITH_PARENT,
                                        gtk.MESSAGE_WARNING, gtk.BUTTONS_CLOSE,
                                        msg)
        dlg.run()
        dlg.destroy()
294

James Henstridge's avatar
James Henstridge committed
295
296
    # define a mapping of param types to edit objects ...
    class StringEntry(gtk.Entry):
297
298
299
        def __init__(self, default=''):
            gtk.Entry.__init__(self)
            self.set_text(str(default))
300

301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
        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))
            
320
        def set_value(self, text):
321
            self.buffer.set_text(text)
322

323
324
325
326
        def get_value(self):
            return self.buffer.get_text(self.buffer.get_start_iter(),
                                        self.buffer.get_end_iter())

James Henstridge's avatar
James Henstridge committed
327
    class IntEntry(StringEntry):
328
329
330
331
332
        def get_value(self):
            try:
                return int(self.get_text())
            except ValueError, e:
                raise EntryValueError, e.args
333

James Henstridge's avatar
James Henstridge committed
334
    class FloatEntry(StringEntry):
335
336
337
338
339
            def get_value(self):
                try:
                    return float(self.get_text())
                except ValueError, e:
                    raise EntryValueError, e.args
340

341
#    class ArrayEntry(StringEntry):
342
343
#            def get_value(self):
#                return eval(self.get_text(), {}, {})
344

James Henstridge's avatar
James Henstridge committed
345
    class SliderEntry(gtk.HScale):
346
347
348
349
        # bounds is (upper, lower, step)
        def __init__(self, default=0, bounds=(0, 100, 5)):
            self.adj = gtk.Adjustment(default, bounds[0],
                                      bounds[1], bounds[2],
350
                                      bounds[2], 0)
351
            gtk.HScale.__init__(self, self.adj)
352

353
354
        def get_value(self):
            return self.adj.value
355

James Henstridge's avatar
James Henstridge committed
356
    class SpinnerEntry(gtk.SpinButton):
357
358
359
360
        # bounds is (upper, lower, step)
        def __init__(self, default=0, bounds=(0, 100, 5)):
            self.adj = gtk.Adjustment(default, bounds[0],
                                        bounds[1], bounds[2],
361
                                        bounds[2], 0)
362
            gtk.SpinButton.__init__(self, self.adj, 1, 0)
363

364
365
366
367
368
        def get_value(self):
            try:
                return int(self.get_text())
            except ValueError, e:
                raise EntryValueError, e.args
369

James Henstridge's avatar
James Henstridge committed
370
    class ToggleEntry(gtk.ToggleButton):
371
372
        def __init__(self, default=0):
            gtk.ToggleButton.__init__(self)
373

374
375
376
            self.label = gtk.Label("No")
            self.add(self.label)
            self.label.show()
377

378
            self.connect("toggled", self.changed)
379

380
            self.set_active(default)
381

382
383
384
385
386
        def changed(self, tog):
            if tog.get_active():
                self.label.set_text("Yes")
            else:
                self.label.set_text("No")
387

388
389
        def get_value(self):
            return self.get_active()
390

391
    class RadioEntry(gtk.Frame):
392
        def __init__(self, default=0, items=(("Yes", 1), ("No", 0))):
393
            gtk.Frame.__init__(self)
394
395

            box = gtk.VBox(False, 5)
396
397
            self.add(box)
            box.show()
398

399
400
            button = None

401
402
403
404
            for (label, value) in items:
                button = gtk.RadioButton(button, label)
                box.pack_start(button)
                button.show()
405
406
407

                button.connect("toggled", self.changed, value)

408
                if value == default:
409
                    button.set_active(True)
410
                    self.active_value = value
411

412
        def changed(self, radio, value):
413
            if radio.get_active():
414
                self.active_value = value
415

416
        def get_value(self):
417
            return self.active_value
James Henstridge's avatar
James Henstridge committed
418
419

    _edit_mapping = {
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
            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_COLOUR      : gimpui.ColourSelector,
            PF_REGION      : IntEntry,  # should handle differently ...
            PF_IMAGE       : gimpui.ImageSelector,
            PF_LAYER       : gimpui.LayerSelector,
            PF_CHANNEL     : gimpui.ChannelSelector,
            PF_DRAWABLE    : gimpui.DrawableSelector,

            PF_TOGGLE      : ToggleEntry,
            PF_SLIDER      : SliderEntry,
            PF_SPINNER     : SpinnerEntry,
            PF_RADIO       : RadioEntry,

            PF_FONT        : gimpui.FontSelector,
            PF_FILE        : gimpui.FileSelector,
            PF_BRUSH       : gimpui.BrushSelector,
            PF_PATTERN     : gimpui.PatternSelector,
            PF_GRADIENT    : gimpui.GradientSelector,
447
            PF_PALETTE     : gimpui.PaletteSelector,
448
            PF_TEXT        : TextEntry
James Henstridge's avatar
James Henstridge committed
449
450
    }

451
    if on_run:
452
        on_run()
453

454
455
    need_progress = menupath[:8] != '<Image>/'

James Henstridge's avatar
James Henstridge committed
456
457
458
    tooltips = gtk.Tooltips()

    dialog = gtk.Dialog(func_name, None, 0,
459
460
                        (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
                        gtk.STOCK_OK, gtk.RESPONSE_OK))
461

462
    hbox = gtk.HBox(False, 5)
463
    hbox.set_border_width(5)
464
    dialog.vbox.pack_start(hbox, expand=False)
465
466
    hbox.show()

467
    table = gtk.Table(len(params), 2, False)
James Henstridge's avatar
James Henstridge committed
468
    table.set_border_width(5)
469
    table.set_row_spacings(4)
James Henstridge's avatar
James Henstridge committed
470
    table.set_col_spacings(10)
471
    hbox.pack_end(table, expand=False)
James Henstridge's avatar
James Henstridge committed
472
473
    table.show()

474
475
    vbox = gtk.VBox(False, 10)
    hbox.pack_start(vbox, expand=False)
James Henstridge's avatar
James Henstridge committed
476
    vbox.show()
477
478
479

    pix = _get_logo()
    if pix:
480
        vbox.pack_start(pix, expand=False)
481
        pix.show()
482

James Henstridge's avatar
James Henstridge committed
483
    label = gtk.Label(blurb)
484
    label.set_line_wrap(True)
James Henstridge's avatar
James Henstridge committed
485
    label.set_justify(gtk.JUSTIFY_LEFT)
486
    label.set_size_request(100, -1)
487
    vbox.pack_start(label, expand=False)
James Henstridge's avatar
James Henstridge committed
488
489
    label.show()

490
491
492
    progress_callback = None

    def response(dlg, id):
493
494
        if id == gtk.RESPONSE_OK:
            params = []
495

496
497
498
499
500
501
502
503
            try:
                for wid in edit_wids:
                    params.append(wid.get_value())
            except EntryValueError:
                error_dialog(dialog, 'Invalid input for "%s"' % wid.desc)
            else:
                _set_defaults(func_name, params)
                dialog.res = run_script(params)
504

505
506
        if progress_callback:
            gimp.progress_uninstall(progress_callback)
507

508
        gtk.main_quit()
509
510
511

    dialog.connect("response", response)

James Henstridge's avatar
James Henstridge committed
512
513
    edit_wids = []
    for i in range(len(params)):
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
        pf_type = params[i][0]
        name = params[i][1]
        desc = params[i][2]
        def_val = defaults[i]

        label = gtk.Label(desc)
        label.set_alignment(1.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):
            wid = _edit_mapping[pf_type](def_val, params[i][4])
        else:
            wid = _edit_mapping[pf_type](def_val)
        
529
        table.attach(wid, 2,3, i,i+1, yoptions=0)
530

531
532
533
534
535
536
        if pf_type != PF_TEXT:
            tooltips.set_tip(wid, desc, None)         
        else:
            #Attach tip to TextView, not to ScrolledWindow
            tooltips.set_tip(wid.view, desc, None)         
        wid.show()
537

538
539
        wid.desc = desc
        edit_wids.append(wid)
James Henstridge's avatar
James Henstridge committed
540

541
    if need_progress:
542
543
544
545
        frame = gtk.Frame("Script Progress")
        frame.set_border_width(5)
        dialog.vbox.pack_start(frame)
        frame.show()
546

547
        vbox = gtk.VBox(False, 5)
548
549
550
        vbox.set_border_width(5)
        frame.add(vbox)
        vbox.show()
551

552
553
554
555
        progress_label = gtk.Label("(none)")
        progress_label.set_alignment(0.0, 0.5)
        vbox.pack_start(progress_label)
        progress_label.show()
556

557
558
559
560
        progress = gtk.ProgressBar()
        progress.set_text(" ")
        vbox.pack_start(progress)
        progress.show()
561

562
563
564
565
566
567
568
        def progress_update(message=-1, fraction=None):
            if message == -1:
                pass
            elif message:
                progress.set_text(message)
            else:
                progress.set_text(" ")
569

570
571
            if fraction is not None:
                progress.set_fraction(fraction)
572

573
574
            while gtk.events_pending():
                gtk.main_iteration()
575

576
577
        def progress_start(message, cancelable):
            progress_update(message, 0.0)
578

579
580
        def progress_end():
            progress_update(None, 0.0)
581

582
583
        def progress_text(message):
            progress_update(message)
584

585
586
        def progress_value(percentage):
            progress_update(fraction=percentage)
587

588
589
        progress_callback = gimp.progress_install(progress_start, progress_end,
                                                  progress_text, progress_value)
590

James Henstridge's avatar
James Henstridge committed
591
592
    tooltips.enable()
    dialog.show()
593

594
    gtk.main()
595

596
597
    if hasattr(dialog, 'res'):
        res = dialog.res
598
599
        dialog.destroy()
        return res
600
    else:
601
        dialog.destroy()
602
        raise CancelError
603

604
def _run(func_name, params):
James Henstridge's avatar
James Henstridge committed
605
606
    run_mode = params[0]
    plugin_type = _registered_plugins_[func_name][7]
607
    menupath = _registered_plugins_[func_name][5]
James Henstridge's avatar
James Henstridge committed
608
609
    func = _registered_plugins_[func_name][10]

610
    if plugin_type == PLUGIN and menupath[:10] != '<Toolbox>/':
611
612
        if menupath[:7] == '<Save>/':
            end = 5
613
        else:
614
            end = 3
615

616
617
        start_params = params[1:end]
        extra_params = params[end:]
James Henstridge's avatar
James Henstridge committed
618
    else:
619
620
        start_params = ()
        extra_params = params[1:]
621

James Henstridge's avatar
James Henstridge committed
622
    if run_mode == RUN_INTERACTIVE:
623
        try:
624
            res = _interact(func_name, start_params)
625
        except CancelError:
626
            return
627
    else:
628
629
        if run_mode == RUN_WITH_LAST_VALS:
            extra_params = _get_defaults(func_name)
630

631
632
        params = start_params + tuple(extra_params)
        res = apply(func, params)
633
634
635
636
637

    if run_mode != RUN_NONINTERACTIVE:
        gimp.displays_flush()

    return res
638
639

def main():
James Henstridge's avatar
James Henstridge committed
640
641
    '''This should be called after registering the plugin.'''
    gimp.main(None, None, _query, _run)
642

643
644
645
646
647
def fail(msg):
    '''Display and error message and quit'''
    gimp.message(msg)
    raise error, msg

648
649
650
651
652
653
654
655
656
657
658
def _get_logo():
    import gtk, gobject

    import os.path
    logofile = os.path.join(os.path.dirname(__file__), 'pygimp-logo.png')

    try:
        pixbuf = gtk.gdk.pixbuf_new_from_file(logofile)
    except gobject.GError, e:
        return None

659
    image = gtk.Image()
660
661
    image.set_from_pixbuf(pixbuf)

662
    return image