Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Troubles with porting a script to Gimp3
#21
(03-05-2025, 10:44 PM)alvk Wrote:
(03-05-2025, 05:26 PM)Ofnuts Wrote: I can help with the API, but not with Scheme... If I had to do something like this, i would rewrite the whole thing in Python...

I agree with you, that Python would be better, but I'm not a programmer and rewriting this code in Python is far beyond my expertise. Anyway, thank you for your suggestions. You helped me a lot.

I took a crack at it, but I have no idea how fonts work in 3.0.
So here's some code that almost works, but can't read fonts or apply vectors   Huh
Maybe someone can suggest improvements or fix it from here... 3.0 API is a learning curve.
Also sorry for the bad UI, just threw it together without aesthetics in mind.

Code:
#!/usr/bin/env python3

import sys

from gi.repository import Gimp, GLib
from gi.repository import GObject
from gi.repository import Gtk
from gi.repository import Gdk
from gi.repository import Gegl

class text_plugin_window(Gtk.Window):
   def __init__(self):
       Gtk.Window.__init__(self, title="Text Label")

       self.set_default_size(600, 700) # width, height

       # Apply CSS
       self.apply_css()
       
       # Main layout
       self.main_box = Gtk.VBox(spacing=10)
       
       self.add(self.main_box)

       # Scrolled Window
       scrolled_window = Gtk.ScrolledWindow()
       scrolled_window.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
       self.main_box.pack_start(scrolled_window, True, True, 0)

       content_box = Gtk.VBox(spacing=10)
       content_box.set_border_width(15)
       content_box.get_style_context().add_class("content_box")
       scrolled_window.add(content_box)
       
       self.input_textview = self.add_labeled_textview(content_box, "Text Input:")

       self.font_chooser = Gtk.FontChooserWidget()
       self.font_chooser.set_font("Arial")
       content_box.pack_start(self.font_chooser, False, False, 0)

       self.font_color_button = Gtk.Button(label="Choose Color")
       self.font_color_button.connect("clicked", self.on_font_color_button_clicked)
       content_box.pack_start(self.font_color_button, False, False, 0)

       x_offset_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10)
       x_offset_box.get_style_context().add_class("offset_box")
       x_offset_label = Gtk.Label(label="Offset X:")
       x_offset_label.get_style_context().add_class("offset_label")
       x_offset_box.pack_start(x_offset_label, False, False, 0)
       self.x_offset_spin = Gtk.SpinButton.new_with_range(0, 99999, 1)
       self.x_offset_spin.set_value(0)
       x_offset_box.pack_start(self.x_offset_spin, True, True, 0)
       content_box.pack_start(x_offset_box, False, False, 0)

       y_offset_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10)
       y_offset_box.get_style_context().add_class("offset_box")
       y_offset_label = Gtk.Label(label="Offset Y:")
       y_offset_label.get_style_context().add_class("offset_label")
       y_offset_box.pack_start(y_offset_label, False, False, 0)
       self.y_offset_spin = Gtk.SpinButton.new_with_range(0, 99999, 1)
       self.y_offset_spin.set_value(0)
       y_offset_box.pack_start(self.y_offset_spin, True, True, 0)
       content_box.pack_start(y_offset_box, False, False, 0)

       outline_toggle_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10)
       outline_toggle_box.get_style_context().add_class("toggle_box")
       outline_label = Gtk.Label(label="Outline:")
       outline_toggle_box.pack_start(outline_label, False, False, 0)
       self.toggle_outline = Gtk.CheckButton()
       outline_toggle_box.pack_start(self.toggle_outline, False, False, 0)
       content_box.pack_start(outline_toggle_box, False, False, 0)

       self.outline_color_button = Gtk.Button(label="Choose Color")
       self.outline_color_button.connect("clicked", self.on_outline_color_button_clicked)
       content_box.pack_start(self.outline_color_button, False, False, 0)

       outline_thickness_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10)
       x_offset_box.get_style_context().add_class("thick_box")
       outline_thickness_label = Gtk.Label(label="Outline Thickness:")
       outline_thickness_label.get_style_context().add_class("offset_label")
       outline_thickness_box.pack_start(outline_thickness_label, False, False, 0)
       self.outline_thickness_spin = Gtk.SpinButton.new_with_range(0, 99999, 1)
       self.outline_thickness_spin.set_value(0)
       outline_thickness_box.pack_start(self.outline_thickness_spin, True, True, 0)
       content_box.pack_start(outline_thickness_box, False, False, 0)

       # OK Button
       ok_button_box = Gtk.Box(spacing=0)
       ok_button_box.get_style_context().add_class("ok_button_box")
       ok_button_box.set_border_width(15)
       self.ok_button = Gtk.Button(label="OK")
       self.ok_button.connect("clicked", self.on_ok_clicked)
       self.ok_button.get_style_context().add_class("ok_button")
       ok_button_box.pack_start(self.ok_button, True, False, 0)
       self.main_box.pack_start(ok_button_box, False, False, 0)
       
       # Set result storage
       self.results = []
       self.connect("destroy", Gtk.main_quit)

   def on_font_color_button_clicked(self, widget):
       color_dialog = Gtk.ColorChooserDialog("Select Color", self)
       response = color_dialog.run()
       if response == Gtk.ResponseType.OK:
           rgba = color_dialog.get_rgba()
           self.selected_font_color = rgba
       color_dialog.destroy()

   def on_outline_color_button_clicked(self, widget):
       color_dialog = Gtk.ColorChooserDialog("Select Color", self)
       response = color_dialog.run()
       if response == Gtk.ResponseType.OK:
           rgba = color_dialog.get_rgba()
           self.selected_outline_color = rgba
       color_dialog.destroy()

   def apply_css(self):
       css = """
           window {
               background-color: #3C3C3C;
           }

           label {
               color: #DCDCDC;
           }

           textview {
               background-color: #1A1A1A; color: #DCDCDC;
               min-height: 100px;
           }

           .content_box {
               padding-left: 20px;
               padding-right: 20px;
           }
           .textview_box {
               margin-top: 1rem;
               min-height: 100px;
           }
           
           .textview_text {
               padding: 10px;
               border-radius: 3px;
           }
           .textview_text text {
               background-color: #1A1A1A;
               color: #DCDCDC;
               caret-color: white;
           }

           .ok_button {
               color: #DCDCDC;
               background-color: #1A1A1A;  
               background-image:none;
           }
           .ok_button:hover {
               background-color: #3C3C3C;
           }
           
       """
       style_provider = Gtk.CssProvider()
       style_provider.load_from_data(css.encode("utf-8"))
       Gtk.StyleContext.add_provider_for_screen(Gdk.Screen.get_default(), style_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)

   def add_labeled_textview(self, parent, label_text, default_value=""):
       box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=5)
       box.get_style_context().add_class("textview_box")

       label = Gtk.Label(label=label_text)

       textview = Gtk.TextView()
       textview.set_wrap_mode(Gtk.WrapMode.WORD_CHAR)  # Wrap text at characters (ensures horizontal wrapping)
       textview.get_style_context().add_class("textview_text")

       # Set default text
       buffer = textview.get_buffer()
       buffer.set_text(default_value)

       box.pack_start(label, False, False, 0)
       box.pack_start(textview, False, False, 0)
       parent.pack_start(box, False, False, 0)

       return textview
   
   def get_text_from_textview(self, textview):
       buffer = textview.get_buffer()
       start_iter = buffer.get_start_iter()
       end_iter = buffer.get_end_iter()
       return buffer.get_text(start_iter, end_iter, True)
   
   def on_ok_clicked(self, widget):
       try:
           self.results = {
               "text": self.get_text_from_textview(self.input_textview),
               # "font": self.font_chooser.get_font() if self.font_chooser.get_font() else "Arial Regular",
               "font": self.font_chooser.get_font_desc(),
               "font_size": self.font_chooser.get_font_size(),
               "font_color": getattr(self, 'selected_font_color', Gdk.RGBA(0, 0, 0, 1)),
               "x_offset": self.x_offset_spin.get_value(),
               "y_offset": self.y_offset_spin.get_value(),
               "outline_toggle": self.toggle_outline.get_active(),
               "outline_color": getattr(self, 'selected_outline_color', Gdk.RGBA(1, 1, 1, 1)),
               "outline_thickness": self.outline_thickness_spin.get_value()
           }
       except Exception as e:
           Gimp.message(f"Error: {e}")
       self.destroy()  # Close the window
   
   def run(self):
       self.show_all()
       Gtk.main()
       return self.results

class text_plugin(Gimp.PlugIn):
   def do_query_procedures(self):
       return [ "nc-text-plugin" ]

   def do_set_i18n (self, name):
       return False

   def do_create_procedure(self, name):
       procedure = Gimp.ImageProcedure.new(self, name,
                                           Gimp.PDBProcType.PLUGIN,
                                           self.add_labels, None)

       procedure.set_image_types("*")
       procedure.set_menu_label("Add Labels...")
       procedure.add_menu_path('<Image>/Text Label')
       procedure.set_documentation("Adds labels to a selected layer in GIMP",
                                   "Adds labels with optional outline.",
                                   name)
       procedure.set_attribution("Nicholas Chenevey", "Nicholas Chenevey", "2025")

       return procedure

   def add_labels(self, procedure, run_mode, image, drawables, config, run_data):
                   
       try:
           if run_mode == Gimp.RunMode.INTERACTIVE:
               window = text_plugin_window()
               result = window.run()

               user_text = result["text"]

               # I don't know how to get Gimp.Font from this...
               try:
                   font = result["font"]
                   Gimp.message(f"Family: {font.get_family()}, Style: {font.get_style()}, Weight: {font.get_weight()}, Stretch: {font.get_stretch()}")
                   return procedure.new_return_values(Gimp.PDBStatusType.SUCCESS, GLib.Error())
               except Exception as e:
                   Gimp.message(f"Error: {e}")
                   return procedure.new_return_values(Gimp.PDBStatusType.SUCCESS, GLib.Error())

               font_parts = result["font"].rsplit(' ', 1)
               font_name = font_parts[0]
               font_size = int(font_parts[1])
               font = Gimp.Font.get_by_name(font_name)

               font_color = result["font_color"]
               font_color = Gegl.Color.new(font_color.to_string())

               x = result["x_offset"]
               y = result["y_offset"]
               outline_toggle = result["outline_toggle"]

               outline_color = result["outline_color"]
               outline_color = Gegl.Color.new(outline_color.to_string())

               outline_thickness = result["outline_thickness"]


               success, off_x, off_y = Gimp.Drawable.get_offsets(drawables[0])
               text_layer_x = off_x + x
               text_layer_y = off_y + y

               Gimp.Image.undo_group_start(image)
               Gimp.context_push()

               text_layer = Gimp.TextLayer.new(image, user_text, font, font_size, Gimp.Unit.pixel())
               image.insert_layer(text_layer, None, -1)
               text_layer.set_color(font_color)

               if outline_toggle:
                   text_layer_group = Gimp.GroupLayer.new(image)
                   text_layer_group.set_name("Text_" + text_layer.get_text())

                   # text_layer_vectors = image.text_layer_to_vectors(text_layer)
                   text_layer_outline = text_layer.copy()

                   text_fg_old = Gimp.Context.get_foreground()

                   image.insert_layer(text_layer_group, None, -1)
                   # image.insert_vectors(text_layer_vectors, None, -1)
                   image.reorder_item(text_layer, text_layer_group, 0)
                   image.insert_layer(text_layer_outline, text_layer_group, -1)

                   Gimp.Context.set_foreground(outline_color)
                   Gimp.Context.set_stroke_method(Gimp.StrokeMethod.PAINT)
                   Gimp.Context.set_line_width(outline_thickness)
                   # text_layer_outline.edit_stroke_item(text_layer_vectors)

                   Gimp.Context.set_foreground(text_fg_old)
                   text_layer_group.set_expanded(False)
                   text_layer_outline.set_lock_content(True)

               text_layer.set_lock_content(True)

               Gimp.Image.undo_group_end(image)
               Gimp.displays_flush()
               Gimp.context_pop()
       except Exception as e:
           Gimp.message(f"Error: {font_name} and {e}")
           return procedure.new_return_values(Gimp.PDBStatusType.ERROR, GLib.Error())

       # return text_layer
       return procedure.new_return_values(Gimp.PDBStatusType.SUCCESS, GLib.Error())

Gimp.main(text_plugin.__gtype__, sys.argv)
Reply
#22
alvk: Hi! You can actually create GimpFont property for the plugin, and then GIMP will automatically generate a selector for you. You can see example of how to do this in our test dialogue: https://gitlab.gnome.org/GNOME/gimp/-/bl...og.py#L158

You can run the test dialogue in GIMP to what it looks like by going to Filters -> Development -> Examples -> Test Dialog.
Reply
#23
(03-20-2025, 01:18 PM)CmykStudent Wrote: alvk: Hi! You can actually create GimpFont property for the plugin, and then GIMP will automatically generate a selector for you. You can see example of how to do this in our test dialogue: https://gitlab.gnome.org/GNOME/gimp/-/bl...og.py#L158

You can run the test dialogue in GIMP to what it looks like by going to Filters -> Development -> Examples -> Test Dialog.

Ahhh, this is so helpful. Thank you! I had never used GTK so working with it directly was painful. Happy that I can make dialogs look correctly in 3.0 now  Smile

I'll try to fix the plugin so maybe it will work as the OP desires.

Would you by chance have any advice on how I can implement the vector graphics from the text layer?
Reply
#24
(03-20-2025, 01:18 PM)CmykStudent Wrote: alvk: Hi! You can actually create GimpFont property for the plugin, and then GIMP will automatically generate a selector for you. You can see example of how to do this in our test dialogue: https://gitlab.gnome.org/GNOME/gimp/-/bl...og.py#L158

You can run the test dialogue in GIMP to what it looks like by going to Filters -> Development -> Examples -> Test Dialog.

Thank you for your message. I haven't used Python for scripting in Gimp, only Script Fu.

nchen thank you for your effort to rewrite my script in Python.
Reply


Forum Jump: