python: Port arguments code to use functions

This patch ports the first-party Python plug-ins
to use the new argument API.
This restores the ability to add dropdown menus from
enums and Gimp.Choice parameters, and also allows
defaults to be set for custom datatypes like Gegl.Color.
This commit is contained in:
Alx Sa 2024-06-07 03:09:43 +00:00
parent d1c4457fa3
commit 292cb01fc2
9 changed files with 280 additions and 515 deletions

View File

@ -265,30 +265,6 @@ def export_colorxhtml(procedure, run_mode, image, file, metadata, config, data):
class ColorXhtml(Gimp.PlugIn):
## Parameters ##
__gproperties__ = {
"source-file":(bool,
_("_Read characters from file, if true, or use text entry"),
_("_Read characters from file, if true, or use text entry"),
False,
GObject.ParamFlags.READWRITE),
"characters": (str,
_("_File to read or characters to use"),
_("_File to read or characters to use"),
"foo",
GObject.ParamFlags.READWRITE),
"font-size": (int,
_("Fo_nt size in pixels"),
_("Fo_nt size in pixels"),
5, 100, 10,
GObject.ParamFlags.READWRITE),
"separate": (bool,
_("_Write a separate CSS file"),
_("_Write a separate CSS file"),
False,
GObject.ParamFlags.READWRITE)
}
## GimpPlugIn virtual methods ##
def do_set_i18n(self, procname):
return True, 'gimp30-python', None
@ -314,10 +290,19 @@ class ColorXhtml(Gimp.PlugIn):
procedure.set_extensions ("html,xhtml");
procedure.add_argument_from_property(self, "source-file")
procedure.add_argument_from_property(self, "characters")
procedure.add_argument_from_property(self, "font-size")
procedure.add_argument_from_property(self, "separate")
procedure.add_boolean_argument ("source-file",
_("_Read characters from file, if true, or use text entry"),
_("Read characters from file, if true, or use text entry"),
False, GObject.ParamFlags.READWRITE)
procedure.add_string_argument ("characters", _("_File to read or characters to use"),
_("File to read or characters to use"),
"foo", GObject.ParamFlags.READWRITE)
procedure.add_int_argument ("font-size", _("Fo_nt size in pixels"),
_("Font size in pixels"), 5, 100, 10,
GObject.ParamFlags.READWRITE)
procedure.add_boolean_argument ("separate", _("_Write a separate CSS file"),
_("Write a separate CSS file"),
False, GObject.ParamFlags.READWRITE)
return procedure

View File

@ -31,19 +31,6 @@ def N_(message): return message
def _(message): return GLib.dgettext(None, message)
def foggify(procedure, run_mode, image, n_drawables, drawables, config, data):
Gegl.init(None)
_color = Gegl.Color.new("black")
_color.set_rgba(0.94, 0, 0, 1.0)
# Work around not being able to set default color by only setting it
# when color in our config is None. This won't help when resetting to
# factory default. This also fixes a critical when running without
# changing the color away from None.
color = config.get_property('color')
if color is None:
config.set_property('color', _color)
if run_mode == Gimp.RunMode.INTERACTIVE:
GimpUi.init('python-fu-foggify')
@ -105,32 +92,6 @@ def foggify(procedure, run_mode, image, n_drawables, drawables, config, data):
return procedure.new_return_values(Gimp.PDBStatusType.SUCCESS, GLib.Error())
class Foggify (Gimp.PlugIn):
## Parameters ##
__gproperties__ = {
"name": (str,
_("Layer _name"),
_("Layer name"),
_("Clouds"),
GObject.ParamFlags.READWRITE),
"turbulence": (float,
_("_Turbulence"),
_("Turbulence"),
0.0, 7.0, 1.0,
GObject.ParamFlags.READWRITE),
"opacity": (float,
_("O_pacity"),
_("Opacity"),
0.0, 100.0, 100.0,
GObject.ParamFlags.READWRITE),
}
# I use a different syntax for this property because I think it is
# supposed to allow setting a default, except it doesn't seem to
# work. I still leave it this way for now until we figure this out
# as it should be the better syntax.
color = GObject.Property(type =Gegl.Color, default=None,
nick =_("_Fog color"),
blurb=_("Fog color"))
## GimpPlugIn virtual methods ##
def do_set_i18n(self, procname):
return True, 'gimp30-python', None
@ -139,6 +100,11 @@ class Foggify (Gimp.PlugIn):
return [ 'python-fu-foggify' ]
def do_create_procedure(self, name):
Gegl.init(None)
_color = Gegl.Color.new("black")
_color.set_rgba(0.94, 0.71, 0.27, 1.0)
procedure = Gimp.ImageProcedure.new(self, name,
Gimp.PDBProcType.PLUGIN,
foggify, None)
@ -154,10 +120,15 @@ class Foggify (Gimp.PlugIn):
"1999,2007")
procedure.add_menu_path ("<Image>/Filters/Decor")
procedure.add_argument_from_property(self, "name")
procedure.add_argument_from_property(self, "color")
procedure.add_argument_from_property(self, "turbulence")
procedure.add_argument_from_property(self, "opacity")
procedure.add_string_argument ("name", _("Layer _name"), _("Layer name"),
_("Clouds"), GObject.ParamFlags.READWRITE)
procedure.add_color_argument ("color", _("_Fog color"), _("_Fog color"),
True, _color, GObject.ParamFlags.READWRITE)
procedure.add_double_argument ("turbulence", _("_Turbulence"), _("Turbulence"),
0.0, 7.0, 1.0, GObject.ParamFlags.READWRITE)
procedure.add_double_argument ("opacity", _("O_pacity"), _("Opacity"),
0.0, 100.0, 100.0, GObject.ParamFlags.READWRITE)
return procedure
Gimp.main(Foggify.__gtype__, sys.argv)

View File

@ -177,21 +177,6 @@ def gradient_css_save(procedure, config, data):
GLib.Error('File saving failed: {}'.format(file.get_path())))
class GradientsSaveAsCSS (Gimp.PlugIn):
## Parameters ##
__gproperties__ = {
"run-mode": (Gimp.RunMode,
_("Run mode"),
_("The run mode"),
Gimp.RunMode.NONINTERACTIVE,
GObject.ParamFlags.READWRITE),
"gradient": (Gimp.Gradient,
_("_Gradient to use"), "",
GObject.ParamFlags.READWRITE),
"file": (Gio.File,
_("_File"), None,
GObject.ParamFlags.READWRITE),
}
## GimpPlugIn virtual methods ##
def do_set_i18n(self, procname):
return True, 'gimp30-python', None
@ -213,9 +198,14 @@ class GradientsSaveAsCSS (Gimp.PlugIn):
"2011")
procedure.add_menu_path('<Gradients>/Gradients Menu')
procedure.add_argument_from_property(self, "run-mode")
procedure.add_argument_from_property(self, "gradient")
procedure.add_argument_from_property(self, "file")
procedure.add_enum_argument ("run-mode", _("Run mode"),
_("The run mode"), Gimp.RunMode,
Gimp.RunMode.NONINTERACTIVE,
GObject.ParamFlags.READWRITE)
procedure.add_gradient_argument ("gradient", _("_Gradient to use"),
"", GObject.ParamFlags.READWRITE)
procedure.add_file_argument ("file", _("_File"),
"", GObject.ParamFlags.READWRITE)
return procedure
Gimp.main(GradientsSaveAsCSS.__gtype__, sys.argv)

View File

@ -87,14 +87,6 @@ class StringEnum:
return key
raise AttributeError("No such key string " + key)
OUTPUT_FORMAT_LABELS = (_("Pixel count"), _("Normalized"), _("Percent"))
output_format_enum = StringEnum(
"pixel count", OUTPUT_FORMAT_LABELS[0],
"normalized", OUTPUT_FORMAT_LABELS[1],
"percent", OUTPUT_FORMAT_LABELS[2]
)
def histogram_export(procedure, img, layers, gio_file,
bucket_size, sample_average, output_format):
layers = img.list_selected_layers()
@ -139,12 +131,12 @@ def histogram_export(procedure, img, layers, gio_file,
histo_config.set_property('channel', channel)
result = histo_proc.run(histo_config)
if output_format == output_format_enum.pixel_count:
if output_format == "pixel-count":
count = int(result.index(5))
else:
pixels = result.index(4)
count = (result.index(5) / pixels) if pixels else 0
if output_format == output_format_enum.percent:
if output_format == "percent":
count = "%.2f%%" % (count * 100)
row.append(str(count))
writer.writerow(row)
@ -177,30 +169,7 @@ def run(procedure, run_mode, image, n_layers, layers, config, data):
GimpUi.init("histogram-export.py")
dialog = GimpUi.ProcedureDialog.new(procedure, config, _("Histogram Export..."))
vbox = dialog.fill_box ("histogram-box", ["file", "bucket-size", "sample-average"])
#TODO: For now, we'll manually create a GimpStringComboBox
#for the GUI version of 'output-format'. Once we can
#use Gimp.Choice in Python, we can replace the str
#parameter without changing any third-party function calls
hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=6)
vbox.pack_start(hbox, False, False, 0)
hbox.set_visible(True)
label = Gtk.Label.new_with_mnemonic(_("Output _format"))
hbox.pack_start(label, False, False, 0)
label.set_visible(True)
output_format_store = store = Gtk.ListStore(str, str)
output_format_store.append(["pixel count", OUTPUT_FORMAT_LABELS[0]])
output_format_store.append(["normalized", OUTPUT_FORMAT_LABELS[1]])
output_format_store.append(["percent", OUTPUT_FORMAT_LABELS[2]])
combo = GimpUi.prop_string_combo_box_new (config, "output-format", output_format_store, 0, 1)
hbox.pack_start(combo, False, False, 0)
combo.set_visible(True)
dialog.fill(["histogram-box"])
dialog.fill()
if not dialog.run():
return procedure.new_return_values(Gimp.PDBStatusType.CANCEL,
@ -223,32 +192,6 @@ def run(procedure, run_mode, image, n_layers, layers, config, data):
class HistogramExport(Gimp.PlugIn):
## Parameters ##
__gproperties__ = {
# TODO: GFile props still don't have labels + only load existing files
# (here we likely want to create a new file).
"file": (Gio.File,
_("Histogram File"),
"Histogram export file",
GObject.ParamFlags.READWRITE),
"bucket-size": (float,
_("_Bucket Size"),
"Bucket Size",
0.001, 1.0, 0.01,
GObject.ParamFlags.READWRITE),
"sample-average": (bool,
_("Sample _Average"),
"Sample Average",
False,
GObject.ParamFlags.READWRITE),
"output-format": (str,
_("Output _format"),
"Output format: 'pixel count', 'normalized', 'percent'",
"pixel count",
GObject.ParamFlags.READWRITE),
}
## GimpPlugIn virtual methods ##
def do_set_i18n(self, procname):
return True, 'gimp30-python', None
@ -274,10 +217,20 @@ class HistogramExport(Gimp.PlugIn):
"2014")
procedure.add_menu_path("<Image>/Colors/Info/")
procedure.add_argument_from_property(self, "file")
procedure.add_argument_from_property(self, "bucket-size")
procedure.add_argument_from_property(self, "sample-average")
procedure.add_argument_from_property(self, "output-format")
# TODO: GFile props still don't have labels + only load existing files
# (here we likely want to create a new file).
procedure.add_file_argument ("file", _("Histogram File"),
_("Histogram export file"), GObject.ParamFlags.READWRITE)
procedure.add_double_argument ("bucket-size", _("_Bucket Size"), _("Bucket Size"),
0.001, 1.0, 0.01, GObject.ParamFlags.READWRITE)
procedure.add_boolean_argument ("sample-average", _("Sample _Average"), _("Sample Average"),
False, GObject.ParamFlags.READWRITE)
choice = Gimp.Choice.new()
choice.add("pixel-count", 0, _("Pixel Count"), "")
choice.add("normalized", 1, _("Normalized"), "")
choice.add("percent", 2, _("Percent"), "")
procedure.add_choice_argument ("output-format", _("Output _format"), _("Output format"),
choice, "percent", GObject.ParamFlags.READWRITE)
return procedure

View File

@ -33,52 +33,6 @@ otherwise copies the given palette and returns it.
"""
class PaletteOffset (Gimp.PlugIn):
## Parameter: run-mode ##
@GObject.Property(type=Gimp.RunMode,
default=Gimp.RunMode.NONINTERACTIVE,
nick="Run mode", blurb="The run mode")
def run_mode(self):
"""Read-write integer property."""
return self._run_mode
@run_mode.setter
def run_mode(self, run_mode):
self._run_mode = run_mode
## Parameter: palette ##
@GObject.Property(type=Gimp.Palette,
nick= _("Palette"),
blurb= _("Palette"))
def palette(self):
return self._palette
@palette.setter
def palette(self, palette):
self._palette = palette
## Parameter: amount ##
@GObject.Property(type=int,
default=1,
nick= _("Off_set"),
blurb= _("Offset"))
def amount(self):
return self._amount
@amount.setter
def amount(self, amount):
self._amount = amount
## Return: new-palette ##
@GObject.Property(type=Gimp.Palette,
nick=_("The edited palette"),
blurb=_("The newly created palette when read-only, otherwise the input palette"))
def new_palette(self):
return self.new_palette
@new_palette.setter
def new_palette(self, new_palette):
self.new_palette = new_palette
## GimpPlugIn virtual methods ##
def do_set_i18n(self, procname):
return True, 'gimp30-python', None
@ -98,10 +52,18 @@ class PaletteOffset (Gimp.PlugIn):
procedure.set_attribution("Joao S. O. Bueno Calligaris, Carol Spears",
"(c) Joao S. O. Bueno Calligaris",
"2004, 2006")
procedure.add_argument_from_property(self, "run-mode")
procedure.add_argument_from_property(self, "palette")
procedure.add_argument_from_property(self, "amount")
procedure.add_return_value_from_property(self, "new-palette")
procedure.add_enum_argument ("run-mode", _("Run mode"),
_("The run mode"), Gimp.RunMode,
Gimp.RunMode.NONINTERACTIVE,
GObject.ParamFlags.READWRITE)
procedure.add_palette_argument ("palette", _("_Palette"),
_("Palette"),
GObject.ParamFlags.READWRITE)
procedure.add_int_argument ("amount", _("O_ffset"), _("Offset"),
1, GLib.MAXINT, 1, GObject.ParamFlags.READWRITE)
procedure.add_palette_return_value ("new-palette", _("The edited palette"),
_("The newly created palette when read-only, otherwise the input palette"),
GObject.ParamFlags.READWRITE)
procedure.add_menu_path ('<Palettes>/Palettes Menu')
else:
procedure = None
@ -128,34 +90,16 @@ class PaletteOffset (Gimp.PlugIn):
GimpUi.init ("palette-offset.py")
use_header_bar = Gtk.Settings.get_default().get_property("gtk-dialogs-use-header")
dialog = GimpUi.Dialog(use_header_bar=use_header_bar,
title=_("Offset Palette..."))
dialog = GimpUi.ProcedureDialog(procedure=procedure, config=config)
dialog.fill(["palette", "amount"])
if not dialog.run():
dialog.destroy()
return procedure.new_return_values(Gimp.PDBStatusType.CANCEL, GLib.Error())
else:
dialog.destroy()
dialog.add_button(_("_Cancel"), Gtk.ResponseType.CANCEL)
dialog.add_button(_("_OK"), Gtk.ResponseType.OK)
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL,
homogeneous=False, spacing=12)
dialog.get_content_area().add(box)
box.show()
label = Gtk.Label.new(_("Offset"))
box.pack_start(label, False, False, 1)
label.show()
amount = self.set_property("amount", amount)
spin = GimpUi.prop_spin_button_new(self, "amount", 1.0, 5.0, 0)
spin.set_activates_default(True)
box.pack_end(spin, False, False, 1)
spin.show()
dialog.show()
if dialog.run() != Gtk.ResponseType.OK:
return procedure.new_return_values(Gimp.PDBStatusType.CANCEL,
GLib.Error("Canceled"))
amount = self.get_property("amount")
config.set_property("palette", None)
amount = config.get_property("amount")
palette = config.get_property("palette")
#If palette is read only, work on a copy:
editable = palette.is_editable()

View File

@ -77,12 +77,6 @@ GRAIN_SCALE = (1.0, 1.0 , 1.0,
100., 256., 256.,
256., 360.,)
SELECT_ALL = 0
SELECT_SLICE = 1
SELECT_AUTOSLICE = 2
SELECT_PARTITIONED = 3
SELECTIONS = (SELECT_ALL, SELECT_SLICE, SELECT_AUTOSLICE, SELECT_PARTITIONED)
try:
from colormath.color_objects import RGBColor, LabColor, LCHabColor
@ -222,7 +216,7 @@ def palette_sort(palette, selection, slice_expr, channel1, ascending1,
num_colors = palette.get_color_count()
start, nrows, length = None, None, None
if selection == SELECT_AUTOSLICE:
if selection == "auto-slice":
def find_index(color, startindex=0):
for i in range(startindex, num_colors):
c = palette.entry_get_color(i)
@ -262,9 +256,9 @@ def palette_sort(palette, selection, slice_expr, channel1, ascending1,
except ValueError:
# bad expression is okay here, just assume one row
nrows = 1
# remaining behavior is implemented by SELECT_SLICE 'inheritance'.
selection = SELECT_SLICE
elif selection in (SELECT_SLICE, SELECT_PARTITIONED):
# remaining behavior is implemented by "slice-array" 'inheritance'.
selection = "slice-array"
elif selection in ("slice-array", "partitioned"):
start, nrows, length = parse_slice(slice_expr, num_colors)
channels_getter_1 = channel_getters[channel1]
@ -283,14 +277,15 @@ def palette_sort(palette, selection, slice_expr, channel1, ascending1,
result.append((index, entry))
return result
if selection == SELECT_ALL:
print (selection)
if selection == "all":
entry_list = get_colors(0, num_colors)
entry_list.sort(key=lambda v: v[0])
for i in range(num_colors):
palette.entry_set_name(i, entry_list[i][1][0])
palette.entry_set_color(i, entry_list[i][1][1])
elif selection == SELECT_PARTITIONED:
elif selection == "partitioned":
if num_colors < (start + length * nrows) - 1:
raise ValueError('Not enough entries in palette to '
'sort complete rows! Got %d, expected >=%d' %
@ -313,13 +308,13 @@ def palette_sort(palette, selection, slice_expr, channel1, ascending1,
old_partition = this_partition
base = rowstart
for size in partition_spans:
palette_sort(palette, SELECT_SLICE, '%d:1,%d' % (base, size),
palette_sort(palette, "slice-array", '%d:1,%d' % (base, size),
channel1, ascending1,
channel2, ascending2,
quantize, 0, 1.0)
base += size
else:
# SELECT_SLICE and SELECT_AUTOSLICE
# "slice-array" and "auto-slice"
stride = length
if num_colors < (start + stride * nrows) - 1:
raise ValueError('Not enough entries in palette to sort '
@ -348,74 +343,7 @@ You can optionally install colormath (https://pypi.python.org/pypi/colormath/1.0
to GIMP's Python to get even more channels to choose from.
"""
selections_option = [ _("All"), _("Slice / Array"), _("Autoslice (fg->bg)"), _("Partitioned") ]
class PaletteSort (Gimp.PlugIn):
## Parameters ##
__gproperties__ = {
"run-mode": (Gimp.RunMode,
_("Run mode"),
"The run mode",
Gimp.RunMode.INTERACTIVE,
GObject.ParamFlags.READWRITE),
"palette": (Gimp.Palette,
_("_Palette"),
_("Palette"),
GObject.ParamFlags.READWRITE),
"selections": (int,
_("Select_ions"),
str(selections_option),
0, 3, 0,
GObject.ParamFlags.READWRITE),
# TODO: It would be much simpler to replace the slice expression with three
# separate parameters: start-index, number-of-rows, row_length
"slice_expr": (str,
_("Slice _expression"),
slice_expr_doc,
"",
GObject.ParamFlags.READWRITE),
"channel1": (int,
_("Channel _to sort"),
"Channel to sort: " + str(AVAILABLE_CHANNELS),
0, len(AVAILABLE_CHANNELS), 3,
GObject.ParamFlags.READWRITE),
"ascending1": (bool,
_("_Ascending"),
_("Ascending"),
True,
GObject.ParamFlags.READWRITE),
"channel2": (int,
_("Secondary C_hannel to sort"),
"Secondary Channel to sort: " + str(AVAILABLE_CHANNELS),
0, len(AVAILABLE_CHANNELS), 5,
GObject.ParamFlags.READWRITE),
"ascending2": (bool,
_("Ascen_ding"),
_("Ascending"),
True,
GObject.ParamFlags.READWRITE),
"quantize": (float,
_("_Quantization"),
_("Quantization"),
0.0, 1.0, 0.0,
GObject.ParamFlags.READWRITE),
"pchannel": (int,
_("Partitionin_g channel"),
"Partitioning channel: " + str(AVAILABLE_CHANNELS),
0, len(AVAILABLE_CHANNELS), 3,
GObject.ParamFlags.READWRITE),
"pquantize": (float,
_("Partition q_uantization"),
_("Partition quantization"),
0.0, 1.0, 0.0,
GObject.ParamFlags.READWRITE),
# Returned value
"new_palette": (Gimp.Palette,
_("Palette"),
_("Palette"),
GObject.ParamFlags.READWRITE),
}
## GimpPlugIn virtual methods ##
def do_set_i18n(self, procname):
return True, 'gimp30-python', None
@ -440,18 +368,53 @@ class PaletteSort (Gimp.PlugIn):
"2006-2014")
procedure.add_menu_path ('<Palettes>/Palettes Menu')
procedure.add_argument_from_property(self, "run-mode")
procedure.add_argument_from_property(self, "palette")
procedure.add_argument_from_property(self, "selections")
procedure.add_argument_from_property(self, "slice_expr")
procedure.add_argument_from_property(self, "channel1")
procedure.add_argument_from_property(self, "ascending1")
procedure.add_argument_from_property(self, "channel2")
procedure.add_argument_from_property(self, "ascending2")
procedure.add_argument_from_property(self, "quantize")
procedure.add_argument_from_property(self, "pchannel")
procedure.add_argument_from_property(self, "pquantize")
procedure.add_return_value_from_property(self, "new_palette")
procedure.add_enum_argument ("run-mode", _("Run mode"),
_("The run mode"), Gimp.RunMode,
Gimp.RunMode.INTERACTIVE,
GObject.ParamFlags.READWRITE)
procedure.add_palette_argument ("palette", _("_Palette"),
_("Palette"),
GObject.ParamFlags.READWRITE)
selection_choice = Gimp.Choice.new()
selection_choice.add("all", 0, _("All"), "")
selection_choice.add("slice-array", 1, _("Slice / Array"), "")
selection_choice.add("auto-slice", 2, _("Autoslice (fg->bg)"), "")
selection_choice.add("partitioned", 3, _("Partitioned"), "")
procedure.add_choice_argument ("selections", _("Select_ions"), _("Selections"),
selection_choice, "all", GObject.ParamFlags.READWRITE)
# TODO: It would be much simpler to replace the slice expression with three
# separate parameters: start-index, number-of-rows, row_length
procedure.add_string_argument ("slice-expr", _("Slice _expression"),
slice_expr_doc, "",
GObject.ParamFlags.READWRITE)
channel_choice = Gimp.Choice.new()
self.add_choices (channel_choice)
procedure.add_choice_argument ("channel1", _("Channel _to sort"), _("Channel to sort"),
channel_choice, "luma", GObject.ParamFlags.READWRITE)
procedure.add_boolean_argument ("ascending1", _("_Ascending"), _("Ascending"),
True, GObject.ParamFlags.READWRITE)
channel_choice2 = Gimp.Choice.new()
self.add_choices (channel_choice2)
procedure.add_choice_argument ("channel2", _("Secondary C_hannel to sort"),
_("Secondary Channel to sort"), channel_choice2,
"saturation", GObject.ParamFlags.READWRITE)
procedure.add_boolean_argument ("ascending2", _("Ascen_ding"), _("Ascending"),
True, GObject.ParamFlags.READWRITE)
procedure.add_double_argument ("quantize", _("_Quantization"),
_("Quantization"), 0.0, 1.0, 0.0,
GObject.ParamFlags.READWRITE)
pchannel_choice = Gimp.Choice.new()
self.add_choices (pchannel_choice)
procedure.add_choice_argument ("pchannel", _("Partitionin_g channel"),
_("Partitioning channel"), pchannel_choice,
"luma", GObject.ParamFlags.READWRITE)
procedure.add_double_argument ("pquantize", _("Partition q_uantization"),
_("Partition quantization"), 0.0, 1.0, 0.0,
GObject.ParamFlags.READWRITE)
procedure.add_palette_return_value ("new-palette", _("Palette"),
_("Palette"), GObject.ParamFlags.READWRITE)
return procedure
@ -478,13 +441,9 @@ class PaletteSort (Gimp.PlugIn):
dialog = GimpUi.ProcedureDialog(procedure=procedure, config=config)
dialog.fill (["palette"])
dialog.get_int_combo("selections", GimpUi.IntStore.new (selections_option))
dialog.fill (["selections","slice-expr"])
dialog.get_int_combo("channel1", GimpUi.IntStore.new (AVAILABLE_CHANNELS))
dialog.fill (["channel1", "ascending1"])
dialog.get_int_combo("channel2", GimpUi.IntStore.new (AVAILABLE_CHANNELS))
dialog.fill (["channel2","ascending2", "quantize"])
dialog.get_int_combo("pchannel", GimpUi.IntStore.new (AVAILABLE_CHANNELS))
dialog.fill (["pchannel","pquantize"])
if not dialog.run():
@ -495,13 +454,13 @@ class PaletteSort (Gimp.PlugIn):
palette = config.get_property("palette")
selection = config.get_property("selections")
slice_expr = config.get_property ("slice_expr")
channel1 = config.get_property("channel1")
slice_expr = config.get_property("slice-expr")
channel1 = config.get_choice_id("channel1")
ascending1 = config.get_property ("ascending1")
channel2 = config.get_property("channel2")
channel2 = config.get_choice_id("channel2")
ascending2 = config.get_property ("ascending2")
quantize = config.get_property ("quantize")
pchannel = config.get_property("pchannel")
pchannel = config.get_choice_id("pchannel")
pquantize = config.get_property ("pquantize")
try:
new_palette = palette_sort(palette, selection, slice_expr, channel1, ascending1,
@ -516,4 +475,18 @@ class PaletteSort (Gimp.PlugIn):
return_val.insert(1, value)
return return_val
def add_choices (self, choice):
#TODO: Re-incorporate LAB and LCHab options with GeglColor
choice.add("red", 0, _("Red"), "")
choice.add("green", 1, _("Green"), "")
choice.add("blue", 2, _("Blue"), "")
choice.add("luma", 3, _("Luma (Y)"), "")
choice.add("hue", 4, _("Hue"), "")
choice.add("saturation", 5, _("Saturation"), "")
choice.add("value", 6, _("Value"), "")
choice.add("saturation-hsl", 7, _("Saturation (HSL)"), "")
choice.add("lightness-hsl", 8, _("Lightness (HSL)"), "")
choice.add("index", 9, _("Index"), "")
choice.add("random", 10, _("Random"), "")
Gimp.main(PaletteSort.__gtype__, sys.argv)

View File

@ -111,43 +111,6 @@ def run(procedure, config, data):
return retval
class PaletteToGradient (Gimp.PlugIn):
## Parameter: run mode ##
@GObject.Property(type=Gimp.RunMode,
default=Gimp.RunMode.NONINTERACTIVE,
nick="Run mode", blurb="The run mode")
def run_mode(self):
'''The run mode (unused)'''
return self.runmode
@run_mode.setter
def run_mode(self, runmode):
self.runmode = runmode
## Parameter: palette ##
@GObject.Property(type=Gimp.Palette,
default=None,
nick= _("_Palette"))
def palette(self):
'''Palette or None for the currently selected palette'''
return self.palette
@palette.setter
def palette(self, palette):
self.palette = palette
## Properties: return values ##
@GObject.Property(type=Gimp.Gradient,
default="",
nick=_("The newly created gradient"),
blurb=_("The newly created gradient"))
def new_gradient(self):
"""Read-write integer property."""
return self.new_gradient
@new_gradient.setter
def new_gradient(self, new_gradient):
self.new_gradient = new_gradient
## GimpPlugIn virtual methods ##
def do_set_i18n(self, procname):
return True, 'gimp30-python', None
@ -176,12 +139,17 @@ class PaletteToGradient (Gimp.PlugIn):
if procedure is not None:
procedure.set_attribution("Carol Spears, reproduced from previous work by Adrian Likins and Jeff Trefftz",
"Carol Spears", "2006")
# We don't build a GParamSpec ourselves because passing it
# around is apparently broken in Python. Hence this trick.
# See pygobject#227
procedure.add_argument_from_property(self, "run-mode")
procedure.add_argument_from_property(self, "palette")
procedure.add_return_value_from_property(self, "new-gradient")
procedure.add_enum_argument ("run-mode", _("Run mode"),
_("The run mode"), Gimp.RunMode,
Gimp.RunMode.NONINTERACTIVE,
GObject.ParamFlags.READWRITE)
procedure.add_palette_argument ("palette", _("_Palette"),
_("Palette"),
GObject.ParamFlags.READWRITE)
procedure.add_gradient_return_value ("new-gradient", _("The newly created gradient"),
_("The newly created gradient"),
GObject.ParamFlags.READWRITE)
procedure.add_menu_path ('<Palettes>/Palettes Menu')

View File

@ -480,10 +480,15 @@ class SelectionShape(Shape):
return x + dist * cos(perpendicular), y + dist * sin(perpendicular)
shapes = [
CircleShape(), RackShape(), FrameShape(), SelectionShape(),
PolygonShape(), SineShape(), BumpShape()
]
shapes = {
"circle": CircleShape(),
"rack": RackShape(),
"frame": FrameShape(),
"selection": SelectionShape(),
"polygon-star": PolygonShape(),
"sine": SineShape(),
"bumps": BumpShape()
}
### Tools
@ -651,16 +656,18 @@ class SaveToPathTool():
control_points, False)
tools = [
PreviewTool(),
StrokePaintTool(_("PaintBrush"), "gimp-paintbrush"),
PencilTool(), AirBrushTool(), StrokeTool(),
StrokePaintTool(_("Ink"), 'gimp-ink'),
StrokePaintTool(_("MyPaintBrush"), 'gimp-mybrush')
tools = {
"preview": PreviewTool(),
"paintbrush": StrokePaintTool(_("PaintBrush"), "gimp-paintbrush"),
"penciltool": PencilTool(),
"airbrush": AirBrushTool(),
"stroke": StrokeTool(),
"ink": StrokePaintTool(_("Ink"), 'gimp-ink'),
"mypaintbrush": StrokePaintTool(_("MyPaintBrush"), 'gimp-mybrush')
# Clone does not work properly when an image is not set. When that happens, drawing fails, and
# I am unable to catch the error. This causes the plugin to crash, and subsequent problems with undo.
# StrokePaintTool("Clone", 'gimp-clone', False)
]
}
class PatternParameters:
@ -671,7 +678,7 @@ class PatternParameters:
"""
def __init__(self):
if not hasattr(self, 'curve_type'):
self.curve_type = 0
self.curve_type = "spyrograph"
# Pattern
if not hasattr(self, 'pattern_notation'):
@ -710,7 +717,7 @@ class PatternParameters:
# Shape
if not hasattr(self, 'shape_index'):
self.shape_index = 0 # Index in the shapes array
self.shape_index = "circle" # Index in the shapes array
if not hasattr(self, 'sides'):
self.sides = 5
if not hasattr(self, 'morph'):
@ -725,7 +732,7 @@ class PatternParameters:
# Drawing style
if not hasattr(self, 'tool_index'):
self.tool_index = 0 # Index in the tools array.
self.tool_index = "preview" # Index in the tools array.
if not hasattr(self, 'long_gradient'):
self.long_gradient = False
@ -1029,7 +1036,12 @@ class LissaCurveType:
return False
curve_types = [SpyroCurveType(), EpitrochoidCurvetype(), SineCurveType(), LissaCurveType()]
curve_types = {
"spyrograph": SpyroCurveType(),
"epitrochoid": EpitrochoidCurvetype(),
"sine": SineCurveType(),
"lissajous": LissaCurveType()
}
# Drawing engine. Also implements drawing incrementally.
# We don't draw the entire stroke, because it could take several seconds,
@ -1434,10 +1446,14 @@ class SpyroWindow():
myscale.spin.set_width_chars(6)
return adj, myscale
def set_combo_in_table(txt_list, table, row, callback):
def set_combo_in_table(txt_list, is_dictionary, table, row, callback):
combo = Gtk.ComboBoxText.new()
for txt in txt_list:
combo.append_text(_(txt))
if (is_dictionary == False):
for txt in txt_list:
combo.append_text(_(txt))
else:
for key in txt_list:
combo.append (key, txt_list[key].name)
combo.set_halign(Gtk.Align.FILL)
table.attach(combo, 1, row, 1, 1)
@ -1455,14 +1471,14 @@ class SpyroWindow():
row = 0
label_in_table(_("Curve Type"), table, row,
_("An Epitrochoid pattern is when the moving gear is on the outside of the fixed gear."))
self.curve_type_combo = set_combo_in_table([ct.name for ct in curve_types], table, row,
self.curve_type_combo = set_combo_in_table(curve_types, True, table, row,
self.curve_type_changed)
row += 1
label_in_table(_("Tool"), table, row,
_("The tool with which to draw the pattern. "
"The Preview tool just draws quickly."))
self.tool_combo = set_combo_in_table([tool.name for tool in tools], table, row,
self.tool_combo = set_combo_in_table(tools, True, table, row,
self.tool_combo_changed)
self.long_gradient_checkbox = Gtk.CheckButton(label=_("Long Gradient"))
@ -1545,12 +1561,12 @@ class SpyroWindow():
row = 0
label_in_table(_("Fixed Gear Teeth"), kit_table, row, fixed_gear_tooltip)
self.kit_outer_teeth_combo = set_combo_in_table([str(t) for t in ring_teeth], kit_table, row,
self.kit_outer_teeth_combo = set_combo_in_table([str(t) for t in ring_teeth], False, kit_table, row,
self.kit_outer_teeth_combo_changed)
row += 1
label_in_table(_("Moving Gear Teeth"), kit_table, row, moving_gear_tooltip)
self.kit_inner_teeth_combo = set_combo_in_table([str(t) for t in wheel_teeth], kit_table, row,
self.kit_inner_teeth_combo = set_combo_in_table([str(t) for t in wheel_teeth], False, kit_table, row,
self.kit_inner_teeth_combo_changed)
row += 1
@ -1667,7 +1683,7 @@ class SpyroWindow():
"Frame hugs the boundaries of the rectangular selection, "
"use hole=100 in Gear notation to touch boundary. "
"Selection will hug boundaries of current selection - try something non-rectangular."))
self.shape_combo = set_combo_in_table([shape.name for shape in shapes], table, row,
self.shape_combo = set_combo_in_table(shapes, True, table, row,
self.shape_combo_changed)
row += 1
@ -1733,7 +1749,7 @@ class SpyroWindow():
row = 0
label_in_table(_("Save"), table, row,
_("Choose whether to save as new layer, redraw on last active layer, or save to path"))
self.save_option_combo = set_combo_in_table(save_options, table, row,
self.save_option_combo = set_combo_in_table(save_options, False, table, row,
self.save_option_changed)
self.save_option_combo.show()
@ -1918,7 +1934,7 @@ class SpyroWindow():
def update_view(self):
""" Update the UI to reflect the values in the Pattern Parameters. """
self.curve_type_combo.set_active(self.p.curve_type)
self.curve_type_combo.set_active_id(self.p.curve_type)
self.curve_type_side_effects()
self.pattern_notebook.set_current_page(pattern_notation_page[self.p.pattern_notation])
@ -1941,7 +1957,7 @@ class SpyroWindow():
self.doughnut.set_width(self.p.doughnut_width)
self.petals_changed_side_effects()
self.shape_combo.set_active(self.p.shape_index)
self.shape_combo.set_active_id(self.p.shape_index)
self.shape_combo_side_effects()
self.sides_adj.set_value(self.p.sides)
self.morph_adj.set_value(self.p.morph)
@ -1949,7 +1965,7 @@ class SpyroWindow():
self.shape_rotation_adj.set_value(self.p.shape_rotation)
self.margin_adj.set_value(self.p.margin_pixels)
self.tool_combo.set_active(self.p.tool_index)
self.tool_combo.set_active_id(self.p.tool_index)
self.long_gradient_checkbox.set_active(self.p.long_gradient)
self.save_option_combo.set_active(self.p.save_option)
@ -1987,7 +2003,7 @@ class SpyroWindow():
self.doughnut_width_myscale.set_sensitive(False)
def curve_type_changed(self, val):
self.p.curve_type = val.get_active()
self.p.curve_type = val.get_active_id()
self.curve_type_side_effects()
self.redraw()
@ -2086,7 +2102,7 @@ class SpyroWindow():
self.equal_w_h_checkbox.set_sensitive(shapes[self.p.shape_index].can_equal_w_h())
def shape_combo_changed(self, val):
self.p.shape_index = val.get_active()
self.p.shape_index = val.get_active_id()
self.shape_combo_side_effects()
self.redraw()
@ -2116,7 +2132,7 @@ class SpyroWindow():
self.long_gradient_checkbox.set_sensitive(tools[self.p.tool_index].can_color)
def tool_combo_changed(self, val):
self.p.tool_index = val.get_active()
self.p.tool_index = val.get_active_id()
self.tool_changed_side_effects()
self.redraw()
@ -2204,80 +2220,6 @@ class SpyroWindow():
class SpyrogimpPlusPlugin(Gimp.PlugIn):
## Parameters ##
__gproperties__ = {
"curve-type" : (int,
_("The curve type { Spyrograph (0), Epitrochoid (1), Sine (2), Lissajous(3) }"),
_("The curve type { Spyrograph (0), Epitrochoid (1), Sine (2), Lissajous(3) }"),
0, 3, 0,
GObject.ParamFlags.READWRITE),
"shape": (int,
_("Shape of fixed gear"),
_("Shape of fixed gear"),
0, GLib.MAXINT, 0,
GObject.ParamFlags.READWRITE),
"sides": (int,
_("Number of sides of fixed gear (3 or greater). Only used by some shapes."),
_("Number of sides of fixed gear (3 or greater). Only used by some shapes."),
3, GLib.MAXINT, 3,
GObject.ParamFlags.READWRITE),
"morph": (float,
_("Morph shape of fixed gear, between 0 and 1. Only used by some shapes."),
_("Morph shape of fixed gear, between 0 and 1. Only used by some shapes."),
0.0, 1.0, 0.0,
GObject.ParamFlags.READWRITE),
"fixed-teeth": (int,
_("Number of teeth for fixed gear"),
_("Number of teeth for fixed gear"),
0, GLib.MAXINT, 96,
GObject.ParamFlags.READWRITE),
"moving-teeth": (int,
_("Number of teeth for moving gear"),
_("Number of teeth for moving gear"),
0, GLib.MAXINT, 36,
GObject.ParamFlags.READWRITE),
"hole-percent": (float,
_("Location of hole in moving gear in percent, where 100 means that "
"the hole is at the edge of the gear, and 0 means the hole is at the center"),
_("Location of hole in moving gear in percent, where 100 means that "
"the hole is at the edge of the gear, and 0 means the hole is at the center"),
0.0, 100.0, 100.0,
GObject.ParamFlags.READWRITE),
"margin": (int,
_("Margin from selection, in pixels"),
_("Margin from selection, in pixels"),
0, GLib.MAXINT, 0,
GObject.ParamFlags.READWRITE),
"equal-w-h": (bool,
_("Make height and width equal"),
_("Make height and width equal"),
False,
GObject.ParamFlags.READWRITE),
"pattern-rotation": (float,
_("Pattern rotation, in degrees"),
_("Pattern rotation, in degrees"),
-360.0, 360.0, 0.0,
GObject.ParamFlags.READWRITE),
"shape-rotation": (float,
_("Shape rotation of fixed gear, in degrees"),
_("Shape rotation of fixed gear, in degrees"),
-360.0, 360.0, 0.0,
GObject.ParamFlags.READWRITE),
"tool": (int,
_("Tool to use for drawing the pattern."),
_("Tool to use for drawing the pattern."),
0, GLib.MAXINT, 1,
GObject.ParamFlags.READWRITE),
"long-gradient" : (bool,
_("Whether to apply a long gradient to match the length of the pattern. "
"Only applicable to some of the tools."),
_("Whether to apply a long gradient to match the length of the pattern. "
"Only applicable to some of the tools."),
False,
GObject.ParamFlags.READWRITE),
}
## GimpPlugIn virtual methods ##
def do_set_i18n(self, procname):
return True, 'gimp30-python', None
@ -2302,19 +2244,67 @@ class SpyrogimpPlusPlugin(Gimp.PlugIn):
"2018")
procedure.add_menu_path ("<Image>/Filters/Render/")
procedure.add_argument_from_property(self, "curve-type")
procedure.add_argument_from_property(self, "shape")
procedure.add_argument_from_property(self, "sides")
procedure.add_argument_from_property(self, "morph")
procedure.add_argument_from_property(self, "fixed-teeth")
procedure.add_argument_from_property(self, "moving-teeth")
procedure.add_argument_from_property(self, "hole_percent")
procedure.add_argument_from_property(self, "margin")
procedure.add_argument_from_property(self, "equal-w-h")
procedure.add_argument_from_property(self, "pattern-rotation")
procedure.add_argument_from_property(self, "shape-rotation")
procedure.add_argument_from_property(self, "tool")
procedure.add_argument_from_property(self, "long-gradient")
curve_choice = Gimp.Choice.new()
curve_choice.add("spyrograph", 0, _("Spyrograph"), "")
curve_choice.add("epitrochoid", 1, _("Epitrochoid"), "")
curve_choice.add("sine", 2, _("Sine"), "")
curve_choice.add("lissajous", 3, _("Lissajous"), "")
procedure.add_choice_argument ("curve-type", _("Curve Type"), _("Curve Type"),
curve_choice, "spyrograph", GObject.ParamFlags.READWRITE)
shape_choice = Gimp.Choice.new()
shape_choice.add("circle", 0, _("Circle"), "")
shape_choice.add("rack", 1, _("rack"), "")
shape_choice.add("frame", 2, _("frame"), "")
shape_choice.add("selection", 3, _("Selection"), "")
shape_choice.add("polygon-star", 4, _("Polygon-Star"), "")
shape_choice.add("sine", 5, _("Sine"), "")
shape_choice.add("bumps", 6, _("Bumps"), "")
procedure.add_choice_argument ("shape", _("Shape"), _("Shape"),
shape_choice, "circle", GObject.ParamFlags.READWRITE)
procedure.add_int_argument ("sides", _("Si_des"),
_("Number of sides of fixed gear (3 or greater). Only used by some shapes."),
3, GLib.MAXINT, 3, GObject.ParamFlags.READWRITE)
procedure.add_double_argument ("morph", _("_Morph"),
_("Morph shape of fixed gear, between 0 and 1. Only used by some shapes."),
0.0, 1.0, 0.0, GObject.ParamFlags.READWRITE)
procedure.add_int_argument ("fixed-teeth", _("Fi_xed Gear Teeth"),
_("Number of teeth for fixed gear."),
0, GLib.MAXINT, 96, GObject.ParamFlags.READWRITE)
procedure.add_int_argument ("moving-teeth", _("Mo_ving Gear Teeth"),
_("Number of teeth for fixed gear."),
0, GLib.MAXINT, 36, GObject.ParamFlags.READWRITE)
procedure.add_double_argument ("hole_percent", _("_Hole Radius (%)"),
_("Location of hole in moving gear in percent, where 100 means that "
"the hole is at the edge of the gear, and 0 means the hole is at the center"),
0.0, 100.0, 100.0, GObject.ParamFlags.READWRITE)
procedure.add_int_argument ("margin", _("Margin (_px)"),
_("Margin from selection, in pixels"),
0, GLib.MAXINT, 0, GObject.ParamFlags.READWRITE)
procedure.add_boolean_argument ("equal-w-h", _("Make width and height equal"),
_("Make width and height equal"),
False, GObject.ParamFlags.READWRITE)
procedure.add_double_argument ("pattern-rotation", _("_Rotation"),
_("Pattern rotation, in degrees"),
-360.0, 360.0, 0.0, GObject.ParamFlags.READWRITE)
procedure.add_double_argument ("shape-rotation", _("_Rotation"),
_("Shape rotation of fixed gear, in degrees"),
-360.0, 360.0, 0.0, GObject.ParamFlags.READWRITE)
tool_choice = Gimp.Choice.new()
tool_choice.add("preview", 0, _("Preview"), "")
tool_choice.add("paintbrush", 1, _("PaintBrush"), "")
tool_choice.add("pencil", 2, _("Pencil"), "")
tool_choice.add("airbrush", 3, _("AirBrush"), "")
tool_choice.add("stroke", 4, _("Stroke"), "")
tool_choice.add("ink", 5, _("Ink"), "")
tool_choice.add("mypaintbrush", 6, _("MyPaintBrush"), "")
#TODO: Add Clone option once it's fixed
procedure.add_choice_argument ("tool", _("Tool"), _("Tool"),
tool_choice, "preview", GObject.ParamFlags.READWRITE)
procedure.add_boolean_argument ("long-gradient",
_("Long _Gradient"),
_("Whether to apply a long gradient to match the length of the pattern. "
"Only applicable to some of the tools."),
False, GObject.ParamFlags.READWRITE)
return procedure

View File

@ -26,6 +26,8 @@ from gi.repository import Gio
import time
import sys
def N_(message): return message
def _(message): return GLib.dgettext(None, message)
'''
A Python plugin.
@ -126,25 +128,6 @@ def test_dialog(procedure, run_mode, image, n_drawables, drawables, config, data
class TestDialogPlugin (Gimp.PlugIn):
## Parameters ##
# See comments about this in foggify.py, from which we borrowed
brush = GObject.Property(type = Gimp.Brush,
nick = "_Brush",
blurb = "Brush")
font = GObject.Property(type = Gimp.Font,
nick = "_Font",
blurb = "Font")
gradient = GObject.Property(type = Gimp.Gradient,
nick = "_Gradient",
blurb = "Gradient")
palette = GObject.Property(type = Gimp.Palette,
nick = "_Palette",
blurb = "Palette")
pattern = GObject.Property(type = Gimp.Pattern,
nick = "Pa_ttern",
blurb = "Pattern")
# FUTURE all other Gimp classes that have GimpParamSpecs
## GimpPlugIn virtual methods ##
@ -169,11 +152,19 @@ class TestDialogPlugin (Gimp.PlugIn):
# Top level menu "Test"
procedure.add_menu_path ("<Image>/Filters/Development/Demos")
procedure.add_argument_from_property(self, "brush")
procedure.add_argument_from_property(self, "font")
procedure.add_argument_from_property(self, "gradient")
procedure.add_argument_from_property(self, "palette")
procedure.add_argument_from_property(self, "pattern")
procedure.add_brush_argument ("brush", _("_Brush"), _("Brush"),
GObject.ParamFlags.READWRITE)
procedure.add_font_argument ("font", _("_Font"), _("Font"),
GObject.ParamFlags.READWRITE)
procedure.add_gradient_argument ("gradient", _("_Gradient"),
_("Gradient"),
GObject.ParamFlags.READWRITE)
procedure.add_palette_argument ("palette", _("_Palette"),
_("Palette"),
GObject.ParamFlags.READWRITE)
procedure.add_pattern_argument ("pattern", _("Pa_ttern"),
_("Pattern"),
GObject.ParamFlags.READWRITE)
return procedure