Background: the shellout.py (7 years old script) is an approach for calling NikCollection Filters and using the result in GIMP. It still worked with win10 & win11 until GIMP 3.x.
I’m having trouble converting the Python plug-in script from GIMP 2.x to 3.x.
Thank @Ofnuts for pointing out the helloworld plugin example, that helped me understand the basics a little bit.
TLDR:
1. What are the correct replacements for PF_RATIO & PF_OPTION? Can you provide corresponding code with GIMP 3.x procedure?
2. What is the appropriate way to debug python plugins?
------------------------------
1. I tried using add_enum_argument() and add_choice_argument(), but it seems I don’t fully understand the documentation.
2. I cannot properly debug the code:
- Running "C:\Program Files\gimp3\bin\gimp-console.exe" does not print any useful information when the plugin code is incorrect; it simply does not appear in the menu.
- Running the shellout.py script in VSCode using its interpreter "C:\Program Files\gimp3\bin\python.exe" is also not helpful. It executes but only outputs: "shellout.py is a GIMP plug-in and must be run by GIMP to be used."
(Yesterday, 04:35 PM)iiey Wrote: TLDR:
1. What are the correct replacements for PF_RATIO & PF_OPTION? Can you provide corresponding code with GIMP 3.x procedure?
2. What is the appropriate way to debug python plugins?
#!/usr/bin/env python3
#
# 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 3 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, see <https://www.gnu.org/licenses/>.
import gi
gi.require_version('Gimp', '3.0')
from gi.repository import Gimp
gi.require_version('GimpUi', '3.0')
from gi.repository import GimpUi
gi.require_version('Gegl', '0.4')
from gi.repository import Gegl
from gi.repository import GObject
from gi.repository import GLib
from gi.repository import Gio
import time
import sys
Temporarily, just testing widgets for subclasses of GimpResource.
Temporarily, just testing Brush subclass of Resource.
FUTURE: For all the parameter types.
Formerly PF_ constants, now all parameters for which GimpParamSpecs exist.
Not localized, no i18n
'''
def process_args(brush, font, gradient, palette, pattern, choice_val):
'''
Test the args are sane.
'''
assert brush is not None
assert isinstance(brush, Gimp.Brush)
id = brush.get_id()
name = brush.get_name()
assert id is not None
msg = "Brush: {} (ID: {})".format(name, id)
print(msg)
Gimp.message(msg)
assert font is not None
assert isinstance(font, Gimp.Font)
id = font.get_id()
name = font.get_name()
assert id is not None
msg = "Font: {} (ID: {})".format(name, id)
print(msg)
Gimp.message(msg)
assert gradient is not None
assert isinstance(gradient, Gimp.Gradient)
id = gradient.get_id()
name = gradient.get_name()
assert id is not None
msg = "Gradient: {} (ID: {})".format(name, id)
print(msg)
Gimp.message(msg)
assert palette is not None
assert isinstance(palette, Gimp.Palette)
id = palette.get_id()
name = palette.get_name()
assert id is not None
msg = "Palette: {} (ID: {})".format(name, id)
print(msg)
Gimp.message(msg)
assert pattern is not None
assert isinstance(pattern, Gimp.Pattern)
id = pattern.get_id()
name = pattern.get_name()
assert id is not None
msg = "Pattern: {} (ID: {})".format(name, id)
print(msg)
Gimp.message(msg)
assert choice_val is not None
assert isinstance(choice_val, str)
msg = "Choice : {}".format(choice_val)
print(msg)
Gimp.message(msg)
return
def test_dialog(procedure, run_mode, image, drawables, config, data):
'''
Just a standard shell for a plugin.
'''
if run_mode == Gimp.RunMode.INTERACTIVE:
GimpUi.init('python-fu-test-dialog')
Gegl.init(None)
dialog = GimpUi.ProcedureDialog(procedure=procedure, config=config)
dialog.fill(None)
if not dialog.run():
dialog.destroy()
return procedure.new_return_values(Gimp.PDBStatusType.CANCEL, GLib.Error())
else:
dialog.destroy()
Yesterday, 08:06 PM (This post was last modified: Yesterday, 08:06 PM by nchen.)
(Yesterday, 04:50 PM)iiey Wrote: Hi nchen,
Thank you for your reply, I will take your example with choice as reference and test it out.
Hello iiey,
You're welcome! Hope that helps.
Were you trying to get Radio options working?
You can theoretically use any GTK 3 widgets with the GimpUI dialog.
I just learned GTK recently because I didn't know how to use GimpUI...
Here is an example I just created with Radio option:
Code:
#!/usr/bin/env python3
#
# 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 3 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, see <https://www.gnu.org/licenses/>.
import gi
gi.require_version('Gimp', '3.0')
from gi.repository import Gimp
gi.require_version('GimpUi', '3.0')
from gi.repository import GimpUi
gi.require_version('Gegl', '0.4')
from gi.repository import Gegl
from gi.repository import GObject
from gi.repository import GLib
from gi.repository import Gio
from gi.repository import Gtk
from gi.repository import Gdk
import time
import sys
Temporarily, just testing widgets for subclasses of GimpResource.
Temporarily, just testing Brush subclass of Resource.
FUTURE: For all the parameter types.
Formerly PF_ constants, now all parameters for which GimpParamSpecs exist.
Not localized, no i18n
'''
def process_args(brush, font, gradient, palette, pattern, choice_val):
'''
Test the args are sane.
'''
assert brush is not None
assert isinstance(brush, Gimp.Brush)
id = brush.get_id()
name = brush.get_name()
assert id is not None
msg = "Brush: {} (ID: {})".format(name, id)
print(msg)
Gimp.message(msg)
assert font is not None
assert isinstance(font, Gimp.Font)
id = font.get_id()
name = font.get_name()
assert id is not None
msg = "Font: {} (ID: {})".format(name, id)
print(msg)
Gimp.message(msg)
assert gradient is not None
assert isinstance(gradient, Gimp.Gradient)
id = gradient.get_id()
name = gradient.get_name()
assert id is not None
msg = "Gradient: {} (ID: {})".format(name, id)
print(msg)
Gimp.message(msg)
assert palette is not None
assert isinstance(palette, Gimp.Palette)
id = palette.get_id()
name = palette.get_name()
assert id is not None
msg = "Palette: {} (ID: {})".format(name, id)
print(msg)
Gimp.message(msg)
assert pattern is not None
assert isinstance(pattern, Gimp.Pattern)
id = pattern.get_id()
name = pattern.get_name()
assert id is not None
msg = "Pattern: {} (ID: {})".format(name, id)
print(msg)
Gimp.message(msg)
assert choice_val is not None
assert isinstance(choice_val, str)
msg = "Choice : {}".format(choice_val)
print(msg)
Gimp.message(msg)
return
class CustomProcedureDialog(GimpUi.ProcedureDialog):
def __init__(self, procedure, config):
super().__init__(procedure=procedure, config=config)
self.set_default_size(600, 100)
# Apply CSS
self.apply_css()
# Get the main content area of the ProcedureDialog
content_area = self.get_content_area()
# Create a scrolled window for the entire dialog
scrolled_window = Gtk.ScrolledWindow()
scrolled_window.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
scrolled_window.set_size_request(400, 300) # Adjust scrolled window size request dims
# Create a container to hold all the UI elements inside the scroll area
content_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=10)
content_box.set_border_width(10)
content_box.get_style_context().add_class("content_box")
# Populate GIMP's default argument UI elements,
# This adds the GIMP argument widgets to the content area
self.fill(None)
# Retrieve all GIMP UI elements that were just added
for child in content_area.get_children():
# Skip the GtkBox and GtkButtonBox elements (ok buttons at the bottom to add them later)
if child.get_name() == "GtkBox" or child.get_name() == "GtkButtonBox":
continue
content_area.remove(child) # Remove them temporarily
content_box.pack_start(child, False, False, 5) # Repack them inside the scrollable box
def get_radio_result(self):
"""Store the radio button selection before closing."""
if self.png_radio.get_active():
self.selected_format = "png"
elif self.jpg_radio.get_active():
self.selected_format = "jpg"
def test_dialog(procedure, run_mode, image, drawables, config, data):
'''
Just a standard shell for a plugin.
'''