140
155
PF_RADIO : PDB_STRING,
141
156
PF_TEXT : PDB_STRING,
142
157
PF_PALETTE : PDB_STRING,
158
PF_FILENAME : PDB_STRING,
159
PF_DIRNAME : PDB_STRING,
145
162
_registered_plugins_ = {}
147
def register(func_name, blurb, help, author, copyright, date, menupath,
164
def register(proc_name, blurb, help, author, copyright, date, label,
148
165
imagetypes, params, results, function,
149
on_query=None, on_run=None):
150
'''This is called to register a new plugin.'''
166
menu=None, domain=None, on_query=None, on_run=None):
167
'''This is called to register a new plug-in.'''
151
169
# First perform some sanity checks on the data
152
170
def letterCheck(str):
153
allowed = _string.letters + _string.digits + '_'
171
allowed = _string.letters + _string.digits + '_' + '-'
155
173
if not ch in allowed:
159
if not letterCheck(func_name):
160
raise error, "function name contains illegal characters"
178
if not letterCheck(proc_name):
179
raise error, "procedure name contains illegal characters"
161
181
for ent in params:
163
183
raise error, ("parameter definition must contain at least 4 "
164
"elements (%s given: %s)" % (len(ent), ent))
165
if type(ent[0]) != type(42):
184
"elements (%s given: %s)" % (len(ent), ent))
186
if type(ent[0]) != int:
166
187
raise error, "parameter types must be integers"
167
189
if not letterCheck(ent[1]):
168
190
raise error, "parameter name contains illegal characters"
169
192
for ent in results:
171
194
raise error, ("result definition must contain at least 3 elements "
172
"(%s given: %s)" % (len(ent), ent))
195
"(%s given: %s)" % (len(ent), ent))
173
197
if type(ent[0]) != type(42):
174
198
raise error, "result types must be integers"
175
200
if not letterCheck(ent[1]):
176
201
raise error, "result name contains illegal characters"
177
if menupath[:8] == '<Image>/' or \
178
menupath[:7] == '<Load>/' or \
179
menupath[:7] == '<Save>/' or \
180
menupath[:10] == '<Toolbox>/':
183
raise error, "Invalid menu path"
185
if not func_name[:7] == 'python_' and \
186
not func_name[:10] == 'extension_' and \
187
not func_name[:8] == 'plug_in_' and \
188
not func_name[:5] == 'file_':
189
func_name = 'python_fu_' + func_name
191
_registered_plugins_[func_name] = (blurb, help, author, copyright,
192
date, menupath, imagetypes,
205
if not proc_name[:7] == 'python-' and \
206
not proc_name[:7] == 'python_' and \
207
not proc_name[:10] == 'extension-' and \
208
not proc_name[:10] == 'extension_' and \
209
not proc_name[:8] == 'plug-in-' and \
210
not proc_name[:8] == 'plug_in_' and \
211
not proc_name[:5] == 'file-' and \
212
not proc_name[:5] == 'file_':
213
proc_name = 'python-fu-' + proc_name
215
# if menu is not given, derive it from label
216
if menu is None and label:
217
fields = _string.split(label, '/')
220
menu = _string.join(fields, '/')
222
_registered_plugins_[proc_name] = (blurb, help, author, copyright,
223
date, label, imagetypes,
193
224
plugin_type, params, results,
194
function, on_query, on_run)
225
function, menu, domain, on_query, on_run)
196
227
file_params = [(PDB_STRING, "filename", "The name of the file"),
197
(PDB_STRING, "raw_filename", "The name of the file")]
228
(PDB_STRING, "raw-filename", "The name of the file")]
200
231
for plugin in _registered_plugins_.keys():
201
232
(blurb, help, author, copyright, date,
202
menupath, imagetypes, plugin_type,
203
params, results, function,
233
label, imagetypes, plugin_type,
234
params, results, function, menu, domain,
204
235
on_query, on_run) = _registered_plugins_[plugin]
206
fn = lambda x: (_type_mapping[x[0]], x[1], x[2])
207
params = map(fn, params)
237
def make_params(params):
238
return [(_type_mapping[x[0]], x[1], x[2]) for x in params]
240
params = make_params(params)
208
241
# add the run mode argument ...
209
params.insert(0, (PDB_INT32, "run_mode",
210
"Interactive, Non-Interactive"))
242
params.insert(0, (PDB_INT32, "run-mode",
243
"Interactive, Non-Interactive"))
211
245
if plugin_type == PLUGIN:
212
if menupath[:7] == '<Load>/':
248
elif menu[:6] == '<Load>':
213
249
params[1:1] = file_params
214
elif menupath[:10] != '<Toolbox>/':
250
elif menu[:7] == '<Image>' or menu[:6] == '<Save>':
215
251
params.insert(1, (PDB_IMAGE, "image",
216
252
"The image to work on"))
217
253
params.insert(2, (PDB_DRAWABLE, "drawable",
218
254
"The drawable to work on"))
219
if menupath[:7] == '<Save>/':
255
if menu[:6] == '<Save>':
220
256
params[3:3] = file_params
222
results = map(fn, results)
258
results = make_params(results)
262
(domain, locale_dir) = domain
263
gimp.domain_register(domain, locale_dir)
265
gimp.domain_register(domain)
223
267
gimp.install_procedure(plugin, blurb, help, author, copyright,
224
date, menupath, imagetypes, plugin_type,
268
date, label, imagetypes, plugin_type,
272
gimp.menu_register(plugin, menu)
229
def _get_defaults(func_name):
276
def _get_defaults(proc_name):
231
278
(blurb, help, author, copyright, date,
232
menupath, imagetypes, plugin_type,
233
params, results, function,
234
on_query, on_run) = _registered_plugins_[func_name]
236
key = "python-fu-save--" + func_name
279
label, imagetypes, plugin_type,
280
params, results, function, menu, domain,
281
on_query, on_run) = _registered_plugins_[proc_name]
283
key = "python-fu-save--" + proc_name
237
285
if gimpshelf.shelf.has_key(key):
238
286
return gimpshelf.shelf[key]
240
288
# return the default values
241
return map(lambda x: x[3], params)
289
return [x[3] for x in params]
243
def _set_defaults(func_name, defaults):
291
def _set_defaults(proc_name, defaults):
246
key = "python-fu-save--" + func_name
294
key = "python-fu-save--" + proc_name
247
295
gimpshelf.shelf[key] = defaults
249
def _interact(func_name, start_params):
297
def _interact(proc_name, start_params):
250
298
(blurb, help, author, copyright, date,
251
menupath, imagetypes, plugin_type,
252
params, results, function,
253
on_query, on_run) = _registered_plugins_[func_name]
299
label, imagetypes, plugin_type,
300
params, results, function, menu, domain,
301
on_query, on_run) = _registered_plugins_[proc_name]
255
303
def run_script(run_params):
256
304
params = start_params + tuple(run_params)
264
312
pygtk.require('2.0')
269
gtk.rc_parse(gimp.gtkrc())
271
defaults = _get_defaults(func_name)
318
defaults = _get_defaults(proc_name)
273
320
class EntryValueError(Exception):
276
def error_dialog(parent, msg):
323
def warning_dialog(parent, primary, secondary=None):
277
324
dlg = gtk.MessageDialog(parent, gtk.DIALOG_DESTROY_WITH_PARENT,
278
325
gtk.MESSAGE_WARNING, gtk.BUTTONS_CLOSE,
328
dlg.format_secondary_text(secondary)
332
def error_dialog(parent, proc_name):
333
import sys, traceback
335
exc_str = exc_only_str = _('Missing exception information')
338
etype, value, tb = sys.exc_info()
339
exc_str = ''.join(traceback.format_exception(etype, value, tb))
340
exc_only_str = ''.join(traceback.format_exception_only(etype, value))
342
etype = value = tb = None
344
title = _("An error occured running %s") % proc_name
345
dlg = gtk.MessageDialog(parent, gtk.DIALOG_DESTROY_WITH_PARENT,
346
gtk.MESSAGE_ERROR, gtk.BUTTONS_CLOSE,
348
dlg.format_secondary_text(exc_only_str)
350
alignment = gtk.Alignment(0.0, 0.0, 1.0, 1.0)
351
alignment.set_padding(0, 0, 12, 12)
352
dlg.vbox.pack_start(alignment)
355
expander = gtk.Expander(_("_More Information"));
356
expander.set_use_underline(True)
357
expander.set_spacing(6)
358
alignment.add(expander)
361
scrolled = gtk.ScrolledWindow()
362
scrolled.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
363
scrolled.set_size_request(-1, 200)
364
expander.add(scrolled)
368
label = gtk.Label(exc_str)
369
label.set_alignment(0.0, 0.0)
370
label.set_padding(6, 6)
371
label.set_selectable(True)
372
scrolled.add_with_viewport(label)
375
def response(widget, id):
378
dlg.connect("response", response)
379
dlg.set_resizable(True)
283
382
# define a mapping of param types to edit objects ...
284
383
class StringEntry(gtk.Entry):
285
384
def __init__(self, default=''):
286
385
gtk.Entry.__init__(self)
287
386
self.set_text(str(default))
288
388
def get_value(self):
289
389
return self.get_text()
341
447
bounds[1], bounds[2],
343
449
gtk.SpinButton.__init__(self, self.adj, 1, 0)
344
451
def get_value(self):
346
453
return int(self.get_text())
347
454
except ValueError, e:
348
455
raise EntryValueError, e.args
349
457
class ToggleEntry(gtk.ToggleButton):
350
458
def __init__(self, default=0):
351
459
gtk.ToggleButton.__init__(self)
352
self.label = gtk.Label("No")
461
self.label = gtk.Label(_("No"))
353
462
self.add(self.label)
354
463
self.label.show()
355
465
self.connect("toggled", self.changed)
356
467
self.set_active(default)
357
469
def changed(self, tog):
358
470
if tog.get_active():
359
self.label.set_text("Yes")
471
self.label.set_text(_("Yes"))
361
self.label.set_text("No")
473
self.label.set_text(_("No"))
362
475
def get_value(self):
363
476
return self.get_active()
364
478
class RadioEntry(gtk.Frame):
365
def __init__(self, default=0, items=(("Yes", 1), ("No", 0))):
479
def __init__(self, default=0, items=((_("Yes"), 1), (_("No"), 0))):
366
480
gtk.Frame.__init__(self)
367
box = gtk.VBox(gtk.FALSE, 5)
482
box = gtk.VBox(False, 6)
371
488
for (label, value) in items:
372
489
button = gtk.RadioButton(button, label)
373
button.connect("toggled", self.changed, value)
374
490
box.pack_start(button)
493
button.connect("toggled", self.changed, value)
376
495
if value == default:
377
button.set_active(gtk.TRUE)
496
button.set_active(True)
378
497
self.active_value = value
379
499
def changed(self, radio, value):
380
500
if radio.get_active():
381
501
self.active_value = value
382
503
def get_value(self):
383
504
return self.active_value
506
def FileSelector(default=''):
507
if default and default.endswith('/'):
508
selector = DirnameSelector
509
if default == '/': default = ''
511
selector = FilenameSelector
512
return selector(default)
514
class FilenameSelector(gtk.FileChooserButton):
515
def __init__(self, default='', save_mode=False):
516
gtk.FileChooserButton.__init__(self,
517
_("Python-Fu File Selection"))
518
self.set_action(gtk.FILE_CHOOSER_ACTION_OPEN)
520
self.set_filename(default)
523
return self.get_filename()
525
class DirnameSelector(gtk.FileChooserButton):
526
def __init__(self, default=''):
527
gtk.FileChooserButton.__init__(self,
528
_("Python-Fu Folder Selection"))
529
self.set_action(gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER)
531
self.set_filename(default)
534
return self.get_filename()
385
536
_edit_mapping = {
386
537
PF_INT8 : IntEntry,
387
538
PF_INT16 : IntEntry,
420
need_progress = menupath[:8] != '<Image>/'
422
574
tooltips = gtk.Tooltips()
424
dialog = gtk.Dialog(func_name, None, 0,
425
(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
426
gtk.STOCK_OK, gtk.RESPONSE_OK))
428
hbox = gtk.HBox(gtk.FALSE, 5)
429
hbox.set_border_width(5)
430
dialog.vbox.pack_start(hbox, expand=gtk.FALSE)
433
table = gtk.Table(len(params), 2, gtk.FALSE)
434
table.set_border_width(5)
435
table.set_row_spacings(4)
436
table.set_col_spacings(10)
437
hbox.pack_end(table, expand=gtk.FALSE)
576
dialog = gimpui.Dialog(proc_name, 'python-fu', None, 0, None, proc_name,
577
(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
578
gtk.STOCK_OK, gtk.RESPONSE_OK))
580
dialog.set_alternative_button_order((gtk.RESPONSE_OK, gtk.RESPONSE_CANCEL))
582
dialog.set_transient()
584
vbox = gtk.VBox(False, 12)
585
vbox.set_border_width(12)
586
dialog.vbox.pack_start(vbox)
592
(domain, locale_dir) = domain
593
trans = gettext.translation(domain, locale_dir, fallback=True)
595
trans = gettext.translation(domain, fallback=True)
596
blurb = trans.ugettext(blurb)
597
box = gimpui.HintBox(blurb)
598
vbox.pack_start(box, expand=False)
601
table = gtk.Table(len(params), 2, False)
602
table.set_row_spacings(6)
603
table.set_col_spacings(6)
604
vbox.pack_start(table, expand=False)
440
vbox = gtk.VBox(gtk.FALSE, 10)
441
hbox.pack_start(vbox, expand=gtk.FALSE)
446
vbox.pack_start(pix, expand=gtk.FALSE)
449
label = gtk.Label(blurb)
450
label.set_line_wrap(gtk.TRUE)
451
label.set_justify(gtk.JUSTIFY_LEFT)
452
label.set_size_request(100, -1)
453
vbox.pack_start(label, expand=gtk.FALSE)
456
progress_callback = None
458
607
def response(dlg, id):
459
608
if id == gtk.RESPONSE_OK:
609
dlg.set_response_sensitive(gtk.RESPONSE_OK, False)
610
dlg.set_response_sensitive(gtk.RESPONSE_CANCEL, False)
463
615
for wid in edit_wids:
464
616
params.append(wid.get_value())
465
617
except EntryValueError:
466
error_dialog(dialog, 'Invalid input for "%s"' % wid.desc)
618
warning_dialog(dialog, _("Invalid input for '%s'") % wid.desc)
468
_set_defaults(func_name, params)
469
dialog.res = run_script(params)
471
if progress_callback:
472
gimp.progress_uninstall(progress_callback)
620
_set_defaults(proc_name, params)
622
dialog.res = run_script(params)
624
dlg.set_response_sensitive(gtk.RESPONSE_CANCEL, True)
625
error_dialog(dialog, proc_name)
504
662
edit_wids.append(wid)
507
frame = gtk.Frame("Script Progress")
508
frame.set_border_width(5)
509
dialog.vbox.pack_start(frame)
512
vbox = gtk.VBox(gtk.FALSE, 5)
513
vbox.set_border_width(5)
517
progress_label = gtk.Label("(none)")
518
progress_label.set_alignment(0.0, 0.5)
519
vbox.pack_start(progress_label)
520
progress_label.show()
522
progress = gtk.ProgressBar()
523
progress.set_text(" ")
524
vbox.pack_start(progress)
527
def progress_update(message=-1, fraction=None):
531
progress.set_text(message)
533
progress.set_text(" ")
535
if fraction is not None:
536
progress.set_fraction(fraction)
538
while gtk.events_pending():
541
def progress_start(message, cancelable):
542
progress_update(message, 0.0)
545
progress_update(None, 0.0)
547
def progress_text(message):
548
progress_update(message)
550
def progress_value(percentage):
551
progress_update(fraction=percentage)
553
progress_callback = gimp.progress_install(progress_start, progress_end,
554
progress_text, progress_value)
664
progress_vbox = gtk.VBox(False, 6)
665
vbox.pack_end(progress_vbox, expand=False)
668
progress = gimpui.ProgressBar()
669
progress_vbox.pack_start(progress)
672
# progress_label = gtk.Label()
673
# progress_label.set_alignment(0.0, 0.5)
674
# progress_label.set_ellipsize(pango.ELLIPSIZE_MIDDLE)
676
# attrs = pango.AttrList()
677
# attrs.insert(pango.AttrStyle(pango.STYLE_ITALIC, 0, -1))
678
# progress_label.set_attributes(attrs)
680
# progress_vbox.pack_start(progress_label)
681
# progress_label.show()
556
683
tooltips.enable()