mirror of https://github.com/GNOME/gimp.git
Refactored the interactive mode code. Moved all widget code to a new
2008-07-29 Lars-Peter Clausen <lars@metafoo.de> * plug-ins/pygimp/gimpfu.py: Refactored the interactive mode code. Moved all widget code to a new subclass of gimp.Dialog. Added support to specifiy a custom layout for plugins. * plug-ins/pygimp/plug-ins/sphere.py: Added testcode for the new layout parameter of register. svn path=/branches/soc-2008-python/; revision=26336
This commit is contained in:
parent
abf1a8ba05
commit
e0ea2fb2e7
|
@ -1,3 +1,11 @@
|
|||
2008-07-29 Lars-Peter Clausen <lars@metafoo.de>
|
||||
|
||||
* plug-ins/pygimp/gimpfu.py: Refactored the interactive mode code. Moved all
|
||||
widget code to a new subclass of gimp.Dialog.
|
||||
Added support to specifiy a custom layout for plugins.
|
||||
* plug-ins/pygimp/plug-ins/sphere.py: Added testcode for the new layout
|
||||
parameter of register.
|
||||
|
||||
2008-07-25 Lars-Peter Clausen <lars@metafoo.de>
|
||||
|
||||
* plug-ins/pygimp/gimpui.override: Let ZoomPreview.get_drawable and
|
||||
|
|
|
@ -69,7 +69,7 @@ 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 gtk
|
||||
import string as _string
|
||||
import gimp
|
||||
from gimp.enums import *
|
||||
|
@ -97,7 +97,6 @@ PF_VALUE = PF_STRING
|
|||
#PF_STRINGARRAY = int(PDB_STRINGARRAY)
|
||||
PF_COLOR = int(PDB_COLOR)
|
||||
PF_COLOUR = PF_COLOR
|
||||
PF_REGION = int(PDB_REGION)
|
||||
PF_DISPLAY = int(PDB_DISPLAY)
|
||||
PF_IMAGE = int(PDB_IMAGE)
|
||||
PF_LAYER = int(PDB_LAYER)
|
||||
|
@ -127,6 +126,12 @@ PF_FILENAME = 1011
|
|||
PF_DIRNAME = 1012
|
||||
PF_OPTION = 1013
|
||||
|
||||
LAY_EXPANDER = 0
|
||||
LAY_GROUP = 1
|
||||
LAY_TABLE = 2
|
||||
LAY_FIELD = 3
|
||||
LAY_PREVIEW = 4
|
||||
|
||||
_type_mapping = {
|
||||
PF_INT8 : PDB_INT8,
|
||||
PF_INT16 : PDB_INT16,
|
||||
|
@ -139,7 +144,6 @@ _type_mapping = {
|
|||
#PF_FLOATARRAY : PDB_FLOATARRAY,
|
||||
#PF_STRINGARRAY : PDB_STRINGARRAY,
|
||||
PF_COLOR : PDB_COLOR,
|
||||
PF_REGION : PDB_REGION,
|
||||
PF_DISPLAY : PDB_DISPLAY,
|
||||
PF_IMAGE : PDB_IMAGE,
|
||||
PF_LAYER : PDB_LAYER,
|
||||
|
@ -176,7 +180,6 @@ _obj_mapping = {
|
|||
#PF_FLOATARRAY : list,
|
||||
#PF_STRINGARRAY : list,
|
||||
PF_COLOR : gimp.color.RGB,
|
||||
PF_REGION : int,
|
||||
PF_DISPLAY : gimp.Display,
|
||||
PF_IMAGE : gimp.Image,
|
||||
PF_LAYER : gimp.Layer,
|
||||
|
@ -201,11 +204,455 @@ _obj_mapping = {
|
|||
PF_OPTION : int,
|
||||
}
|
||||
|
||||
class EntryValueError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class ScriptFuDialog(gimp.ui.Dialog):
|
||||
|
||||
def __init__(self, proc_name, params, defaults, domain, blurb, layout):
|
||||
gimp.ui.Dialog.__init__(self, proc_name, 'python-fu', None, 0, None,
|
||||
proc_name, (gtk.STOCK_CANCEL,
|
||||
gtk.RESPONSE_CANCEL, gtk.STOCK_OK,
|
||||
gtk.RESPONSE_OK))
|
||||
|
||||
|
||||
self.proc_name = proc_name
|
||||
self.params = params
|
||||
self.field_widgets = []
|
||||
self.params_by_name = {}
|
||||
|
||||
for i in range(len(params)):
|
||||
self.params_by_name[params[i][1]] = (params[i][0], params[i][2],
|
||||
defaults[i]) + tuple(params[i][4:])
|
||||
|
||||
|
||||
|
||||
self.tooltips = gtk.Tooltips()
|
||||
|
||||
self.set_alternative_button_order((gtk.RESPONSE_OK, gtk.RESPONSE_CANCEL))
|
||||
self.set_transient()
|
||||
vbox = gtk.VBox(False, 12)
|
||||
vbox.set_border_width(12)
|
||||
self.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 = gimp.ui.HintBox(blurb)
|
||||
vbox.pack_start(box, expand=False)
|
||||
box.show()
|
||||
|
||||
if layout:
|
||||
table = self.process_layout(layout)
|
||||
else:
|
||||
table = gtk.Table(len(params), 2, False)
|
||||
table.set_row_spacings(6)
|
||||
table.set_col_spacings(6)
|
||||
|
||||
row = 0
|
||||
|
||||
for param in params:
|
||||
label, widget = self.create_field(param[1])
|
||||
table.attach(label, 0, 1, row, row+1, xoptions=gtk.FILL)
|
||||
table.attach(widget, 1, 2, row, row+1, yoptions=0)
|
||||
row += 1
|
||||
vbox.pack_start(table, expand=False)
|
||||
table.show()
|
||||
|
||||
progress_vbox = gtk.VBox(False, 6)
|
||||
vbox.pack_end(progress_vbox, expand=False)
|
||||
progress_vbox.show()
|
||||
|
||||
progress = gimp.ui.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()
|
||||
|
||||
|
||||
self.tooltips.enable()
|
||||
self.connect("response", self.response)
|
||||
|
||||
def response(self, dialog, id):
|
||||
if id == gtk.RESPONSE_OK:
|
||||
self.set_response_sensitive(gtk.RESPONSE_OK, False)
|
||||
self.set_response_sensitive(gtk.RESPONSE_CANCEL, False)
|
||||
|
||||
params = []
|
||||
|
||||
try:
|
||||
for wid in self.field_widgets:
|
||||
params.append(wid.get_value())
|
||||
except EntryValueError:
|
||||
self.warning_dialog(dialog, _("Invalid input for '%s'") % wid.desc)
|
||||
else:
|
||||
try:
|
||||
self.result = self.run_script(params)
|
||||
except Exception:
|
||||
self.set_response_sensitive(gtk.RESPONSE_CANCEL, True)
|
||||
self.error_dialog()
|
||||
raise
|
||||
|
||||
gtk.main_quit()
|
||||
|
||||
|
||||
def warning_dialog(self, primary, secondary=None):
|
||||
dlg = gtk.MessageDialog(self, 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(self):
|
||||
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 occured running %s") % self.proc_name
|
||||
dlg = gtk.MessageDialog(self, 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()
|
||||
|
||||
def create_field(self, name):
|
||||
# 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))
|
||||
|
||||
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(), {}, {})
|
||||
|
||||
class SliderEntry(gtk.HScale):
|
||||
# bounds is (upper, lower, step)
|
||||
def __init__(self, default=0.0, bounds=(0.0, 100.0, 5.0)):
|
||||
self.adj = gtk.Adjustment(default, bounds[0],
|
||||
bounds[1], bounds[2],
|
||||
bounds[2], 0.0)
|
||||
gtk.HScale.__init__(self, self.adj)
|
||||
|
||||
def get_value(self):
|
||||
return self.adj.value
|
||||
|
||||
class SpinnerEntry(gtk.SpinButton):
|
||||
# bounds is (upper, lower, step)
|
||||
def __init__(self, default=0.0, bounds=(0.0, 100.0, 5.0)):
|
||||
self.adj = gtk.Adjustment(default, bounds[0],
|
||||
bounds[1], bounds[2],
|
||||
bounds[2], 0.0)
|
||||
gtk.SpinButton.__init__(self, self.adj, 1, 0)
|
||||
|
||||
def get_value(self):
|
||||
try:
|
||||
return int(self.get_text())
|
||||
except ValueError, e:
|
||||
raise EntryValueError, e.args
|
||||
|
||||
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=''):
|
||||
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_INT8ARRAY : ArrayEntry,
|
||||
#PF_INT16ARRAY : ArrayEntry,
|
||||
#PF_INT32ARRAY : ArrayEntry,
|
||||
#PF_FLOATARRAY : ArrayEntry,
|
||||
#PF_STRINGARRAY : ArrayEntry,
|
||||
PF_COLOR : gimp.ui.ColorSelector,
|
||||
PF_IMAGE : gimp.ui.ImageSelector,
|
||||
PF_LAYER : gimp.ui.LayerSelector,
|
||||
PF_CHANNEL : gimp.ui.ChannelSelector,
|
||||
PF_DRAWABLE : gimp.ui.DrawableSelector,
|
||||
PF_VECTORS : gimp.ui.VectorsSelector,
|
||||
|
||||
PF_TOGGLE : ToggleEntry,
|
||||
PF_SLIDER : SliderEntry,
|
||||
PF_SPINNER : SpinnerEntry,
|
||||
PF_RADIO : RadioEntry,
|
||||
PF_OPTION : ComboEntry,
|
||||
|
||||
PF_FONT : gimp.ui.FontSelector,
|
||||
PF_FILE : FileSelector,
|
||||
PF_FILENAME : FilenameSelector,
|
||||
PF_DIRNAME : DirnameSelector,
|
||||
PF_BRUSH : gimp.ui.BrushSelector,
|
||||
PF_PATTERN : gimp.ui.PatternSelector,
|
||||
PF_GRADIENT : gimp.ui.GradientSelector,
|
||||
PF_PALETTE : gimp.ui.PaletteSelector,
|
||||
PF_TEXT : TextEntry
|
||||
}
|
||||
|
||||
param = self.params_by_name[name]
|
||||
label = gtk.Label(param[1])
|
||||
label.set_use_underline(True)
|
||||
label.set_alignment(0.0, 0.5)
|
||||
label.show()
|
||||
|
||||
widget = _edit_mapping[param[0]](*param[2:])
|
||||
|
||||
label.set_mnemonic_widget(widget)
|
||||
|
||||
if param[0] != PF_TEXT:
|
||||
self.tooltips.set_tip(widget, param[1], None)
|
||||
else:
|
||||
#Attach tip to TextView, not to ScrolledWindow
|
||||
self.tooltips.set_tip(widget.view, param[1], None)
|
||||
widget.show()
|
||||
|
||||
widget.desc = param[1]
|
||||
self.field_widgets.append(widget)
|
||||
|
||||
return (label, widget)
|
||||
|
||||
def create_expander(self, label, children):
|
||||
expander = gtk.Expander(label)
|
||||
expander.add(self.process_layout(children))
|
||||
expander.show()
|
||||
return expander
|
||||
|
||||
def create_group(self, label, children):
|
||||
group = gtk.Frame(label)
|
||||
table = self.process_layout(children)
|
||||
table.set_border_width(6)
|
||||
group.add(table)
|
||||
group.show()
|
||||
return group
|
||||
|
||||
def create_table(self, children):
|
||||
return self.process_layout(children)
|
||||
|
||||
def create_preview(self, type = None):
|
||||
return gimp.ui.ZoomPreview()
|
||||
|
||||
def process_layout(self, layout):
|
||||
|
||||
table = gtk.Table(len(layout), 2, False)
|
||||
table.set_row_spacings(6)
|
||||
table.set_col_spacings(6)
|
||||
table.show()
|
||||
|
||||
mappings = {LAY_EXPANDER: self.create_expander,
|
||||
LAY_GROUP: self.create_group,
|
||||
LAY_TABLE: self.create_table,
|
||||
LAY_PREVIEW: self.create_preview,
|
||||
LAY_FIELD: self.create_field}
|
||||
|
||||
row = 0
|
||||
for item in layout:
|
||||
func = mappings[item[0]]
|
||||
result = func(*item[1:])
|
||||
if isinstance(result, tuple):
|
||||
table.attach(result[0], 0, 1, row, row+1, xoptions=gtk.FILL)
|
||||
table.attach(result[1], 1, 2, row, row+1, yoptions=0)
|
||||
else:
|
||||
table.attach(result, 0, 2, row, row+1, xoptions=gtk.FILL)
|
||||
row += 1
|
||||
|
||||
return table
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
_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):
|
||||
menu=None, domain=None, on_query=None, on_run=None,
|
||||
layout=None):
|
||||
'''This is called to register a new plug-in.'''
|
||||
|
||||
# First perform some sanity checks on the data
|
||||
|
@ -281,14 +728,14 @@ def register(proc_name, blurb, help, author, copyright, date, label,
|
|||
date, label, imagetypes,
|
||||
plugin_type, params, results,
|
||||
function, menu, domain,
|
||||
on_query, on_run)
|
||||
on_query, on_run, layout)
|
||||
|
||||
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]
|
||||
on_query, on_run, layout) = _registered_plugins_[plugin]
|
||||
|
||||
def make_params(params):
|
||||
return [(_type_mapping[x[0]],
|
||||
|
@ -319,10 +766,7 @@ def _query():
|
|||
on_query()
|
||||
|
||||
def _get_defaults(proc_name):
|
||||
(blurb, help, author, copyright, date,
|
||||
label, imagetypes, plugin_type,
|
||||
params, results, function, menu, domain,
|
||||
on_query, on_run) = _registered_plugins_[proc_name]
|
||||
params = _registered_plugins_[proc_name][8]
|
||||
|
||||
key = "python-fu-save--" + proc_name
|
||||
|
||||
|
@ -333,7 +777,6 @@ def _get_defaults(proc_name):
|
|||
return [x[3] for x in params]
|
||||
|
||||
def _set_defaults(proc_name, defaults):
|
||||
|
||||
key = "python-fu-save--" + proc_name
|
||||
gimp.shelf.shelf[key] = defaults
|
||||
|
||||
|
@ -341,12 +784,15 @@ 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]
|
||||
on_query, on_run, layout) = _registered_plugins_[proc_name]
|
||||
|
||||
if on_run:
|
||||
on_run()
|
||||
|
||||
def run_script(run_params):
|
||||
params = start_params + tuple(run_params)
|
||||
_set_defaults(proc_name, params)
|
||||
return apply(function, params)
|
||||
return function(*params)
|
||||
|
||||
params = params[len(start_params):]
|
||||
|
||||
|
@ -354,394 +800,13 @@ def _interact(proc_name, start_params):
|
|||
if len(params) == 0:
|
||||
return run_script([])
|
||||
|
||||
import pygtk
|
||||
pygtk.require('2.0')
|
||||
|
||||
import gtk
|
||||
# import pango
|
||||
|
||||
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 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, 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))
|
||||
|
||||
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(), {}, {})
|
||||
|
||||
class SliderEntry(gtk.HScale):
|
||||
# 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],
|
||||
bounds[2], 0)
|
||||
gtk.HScale.__init__(self, self.adj)
|
||||
|
||||
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)):
|
||||
self.adj = gtk.Adjustment(default, bounds[0],
|
||||
bounds[1], bounds[2],
|
||||
bounds[2], 0)
|
||||
gtk.SpinButton.__init__(self, self.adj, 1, 0)
|
||||
|
||||
def get_value(self):
|
||||
try:
|
||||
return int(self.get_text())
|
||||
except ValueError, e:
|
||||
raise EntryValueError, e.args
|
||||
|
||||
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=''):
|
||||
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_INT8ARRAY : ArrayEntry,
|
||||
#PF_INT16ARRAY : ArrayEntry,
|
||||
#PF_INT32ARRAY : ArrayEntry,
|
||||
#PF_FLOATARRAY : ArrayEntry,
|
||||
#PF_STRINGARRAY : ArrayEntry,
|
||||
PF_COLOR : gimp.ui.ColorSelector,
|
||||
PF_REGION : IntEntry, # should handle differently ...
|
||||
PF_IMAGE : gimp.ui.ImageSelector,
|
||||
PF_LAYER : gimp.ui.LayerSelector,
|
||||
PF_CHANNEL : gimp.ui.ChannelSelector,
|
||||
PF_DRAWABLE : gimp.ui.DrawableSelector,
|
||||
PF_VECTORS : gimp.ui.VectorsSelector,
|
||||
|
||||
PF_TOGGLE : ToggleEntry,
|
||||
PF_SLIDER : SliderEntry,
|
||||
PF_SPINNER : SpinnerEntry,
|
||||
PF_RADIO : RadioEntry,
|
||||
PF_OPTION : ComboEntry,
|
||||
|
||||
PF_FONT : gimp.ui.FontSelector,
|
||||
PF_FILE : FileSelector,
|
||||
PF_FILENAME : FilenameSelector,
|
||||
PF_DIRNAME : DirnameSelector,
|
||||
PF_BRUSH : gimp.ui.BrushSelector,
|
||||
PF_PATTERN : gimp.ui.PatternSelector,
|
||||
PF_GRADIENT : gimp.ui.GradientSelector,
|
||||
PF_PALETTE : gimp.ui.PaletteSelector,
|
||||
PF_TEXT : TextEntry
|
||||
}
|
||||
|
||||
if on_run:
|
||||
on_run()
|
||||
|
||||
tooltips = gtk.Tooltips()
|
||||
|
||||
dialog = gimp.ui.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 = gimp.ui.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 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()
|
||||
|
||||
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:
|
||||
#Attach tip to TextView, not to ScrolledWindow
|
||||
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 = gimp.ui.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()
|
||||
|
||||
tooltips.enable()
|
||||
dialog = ScriptFuDialog(proc_name, params, defaults, domain, blurb, layout)
|
||||
# FIXME: This is really ugly!
|
||||
dialog.run_script = run_script
|
||||
dialog.show()
|
||||
|
||||
gtk.main()
|
||||
|
||||
if hasattr(dialog, 'res'):
|
||||
|
@ -757,7 +822,7 @@ def _run(proc_name, params):
|
|||
func = _registered_plugins_[proc_name][10]
|
||||
|
||||
if run_mode == RUN_NONINTERACTIVE:
|
||||
return apply(func, params[1:])
|
||||
return func(params[1:])
|
||||
|
||||
script_params = _registered_plugins_[proc_name][8]
|
||||
|
||||
|
@ -787,7 +852,7 @@ def _run(proc_name, params):
|
|||
except CancelError:
|
||||
return
|
||||
else:
|
||||
res = apply(func, params[1:])
|
||||
res = func(*params[1:])
|
||||
|
||||
gimp.displays_flush()
|
||||
|
||||
|
|
|
@ -101,12 +101,20 @@ register(
|
|||
(PF_INT, "radius", "Radius for sphere", 100),
|
||||
(PF_SLIDER, "light", "Light angle", 45, (0,360,1)),
|
||||
(PF_TOGGLE, "shadow", "Shadow?", 1),
|
||||
(PF_RADIO, "foo", "Test", "foo", (("Foo", "foo"), ("Bar", "bar"))),
|
||||
(PF_RADIO, "foo", "Test 123", "foo", (("Foo", "foo"), ("Bar", "bar"))),
|
||||
(PF_COLOR, "bg-color", "Background", (1.0, 1.0, 1.0)),
|
||||
(PF_COLOR, "sphere-color", "Sphere", "orange")
|
||||
],
|
||||
[],
|
||||
sphere,
|
||||
menu="<Image>/Filters/Languages/Python-Fu/Test")
|
||||
menu="<Image>/Filters/Languages/Python-Fu/Test",
|
||||
layout=((LAY_FIELD, "radius"),
|
||||
(LAY_FIELD, "light"),
|
||||
(LAY_EXPANDER, "Test expander", ((LAY_FIELD, "shadow"),
|
||||
(LAY_FIELD, "foo"))),
|
||||
(LAY_GROUP, "Test group", ((LAY_FIELD, "bg-color"),
|
||||
(LAY_FIELD, "sphere-color")))
|
||||
)
|
||||
)
|
||||
|
||||
main()
|
||||
|
|
Loading…
Reference in New Issue