← All ideas
  • Published 6 Jan 2026

VSE edit right click images with Gimp

I wrote a addon that allows images on the VSE to be edited with gimp. by right clicking on it I like it to be part of blender, however i dont use 2 fact auth .. and without unable to give away may addon

(requesting a simpler contribution method)

Below is the script:

""" Open in GIMP - Blender VSE Addon Opens the selected image/video strip's source file in GIMP for editing.

Installation:

  1. Edit → Preferences → Add-ons
  2. Click "Install..."
  3. Select this .py file
  4. Enable the addon "Sequencer: Open in GIMP"

Usage:

  1. Select an image strip in the VSE
  2. Right-click → "Open in GIMP" OR use the shortcut: Ctrl+Shift+G OR go to Strip menu → "Open in GIMP"

Configuration:

  • Set your GIMP path in the addon preferences if it's not auto-detected

"""

bl_info = { "name": "Sequencer: Open in GIMP", "author": "Claude", "version": (1, 0, 1), "blender": (5, 0, 0), "location": "Sequencer > Strip > Open in GIMP, or Right-click menu", "description": "Open selected image strip in GIMP for external editing", "category": "Sequencer", }

import bpy import subprocess import os import platform

def get_default_gimp_path(): """Try to find GIMP installation path based on OS.""" system = platform.system()

if system == "Windows":
    # Common Windows installation paths
    possible_paths = [
        r"C:\Program Files\GIMP 2\bin\gimp-2.10.exe",
        r"C:\Program Files\GIMP 2\bin\gimp-2.99.exe",
        r"C:\Program Files\GIMP 3\bin\gimp-3.0.exe",
        r"C:\Program Files (x86)\GIMP 2\bin\gimp-2.10.exe",
        os.path.expandvars(r"%LOCALAPPDATA%\Programs\GIMP 2\bin\gimp-2.10.exe"),
    ]
    for path in possible_paths:
        if os.path.exists(path):
            return path
    # Fallback: try just "gimp" and hope it's in PATH
    return "gimp"

elif system == "Darwin":  # macOS
    possible_paths = [
        "/Applications/GIMP.app/Contents/MacOS/gimp",
        "/Applications/GIMP-2.10.app/Contents/MacOS/gimp",
    ]
    for path in possible_paths:
        if os.path.exists(path):
            return path
    return "gimp"

else:  # Linux
    return "gimp"


class OpenInGIMPPreferences(bpy.types.AddonPreferences): bl_idname = package

gimp_path: bpy.props.StringProperty(
    name="GIMP Executable Path",
    description="Path to GIMP executable. Leave empty for auto-detect",
    default="",
    subtype='FILE_PATH'
)

def draw(self, context):
    layout = self.layout
    layout.prop(self, "gimp_path")
    layout.label(text="Leave empty to auto-detect GIMP location")

    # Show detected path
    if not self.gimp_path:
        detected = get_default_gimp_path()
        layout.label(text=f"Auto-detected: {detected}", icon='INFO')


class SEQUENCER_OT_open_in_gimp(bpy.types.Operator): """Open the selected strip's source image in GIMP""" bl_idname = "sequencer.open_in_gimp" bl_label = "Open in GIMP" bl_options = {'REGISTER', 'UNDO'}

@classmethod
def poll(cls, context):
    # Check if we have an active strip that's an image or movie
    if context.scene.sequence_editor is None:
        return False

    strip = context.scene.sequence_editor.active_strip
    if strip is None:
        return False

    return strip.type in {'IMAGE', 'MOVIE'}

def execute(self, context):
    strip = context.scene.sequence_editor.active_strip

    if strip is None:
        self.report({'ERROR'}, "No strip selected")
        return {'CANCELLED'}

    # Get the file path
    filepath = None

    if strip.type == 'IMAGE':
        # For image strips, get directory + filename
        directory = bpy.path.abspath(strip.directory)

        if strip.elements:
            # For single-image strips (like from our import script), 
            # just get the first element
            # For multi-image sequences, get based on playhead position
            if len(strip.elements) == 1:
                # Single image strip - just open it
                filename = strip.elements[0].filename
            else:
                # Multi-image sequence - calculate which frame
                current_frame = context.scene.frame_current
                strip_start = strip.frame_final_start
                strip_offset = strip.frame_offset_start

                element_index = current_frame - strip_start + strip_offset
                element_index = max(0, min(element_index, len(strip.elements) - 1))
                filename = strip.elements[element_index].filename

            filepath = os.path.join(directory, filename)
        else:
            self.report({'ERROR'}, "Image strip has no elements")
            return {'CANCELLED'}

    elif strip.type == 'MOVIE':
        filepath = bpy.path.abspath(strip.filepath)
        self.report({'WARNING'}, "Opening video file - GIMP may not handle this well")

    if not filepath:
        self.report({'ERROR'}, "Could not determine file path")
        return {'CANCELLED'}

    if not os.path.exists(filepath):
        self.report({'ERROR'}, f"File not found: {filepath}")
        return {'CANCELLED'}

    # Get GIMP path from preferences
    prefs = context.preferences.addons[__package__].preferences
    gimp_path = prefs.gimp_path if prefs.gimp_path else get_default_gimp_path()

    # Open in GIMP
    try:
        if platform.system() == "Windows":
            # On Windows, use subprocess with shell=False for security
            subprocess.Popen([gimp_path, filepath])
        else:
            # On Linux/Mac
            subprocess.Popen([gimp_path, filepath])

        self.report({'INFO'}, f"Opened in GIMP: {os.path.basename(filepath)}")

    except FileNotFoundError:
        self.report({'ERROR'}, f"GIMP not found at: {gimp_path}\nSet the correct path in addon preferences")
        return {'CANCELLED'}
    except Exception as e:
        self.report({'ERROR'}, f"Failed to open GIMP: {str(e)}")
        return {'CANCELLED'}

    return {'FINISHED'}


class SEQUENCER_OT_open_source_folder(bpy.types.Operator): """Open the folder containing the strip's source file""" bl_idname = "sequencer.open_source_folder" bl_label = "Open Source Folder" bl_options = {'REGISTER'}

@classmethod
def poll(cls, context):
    if context.scene.sequence_editor is None:
        return False
    strip = context.scene.sequence_editor.active_strip
    if strip is None:
        return False
    return strip.type in {'IMAGE', 'MOVIE', 'SOUND'}

def execute(self, context):
    strip = context.scene.sequence_editor.active_strip

    # Get directory
    if strip.type == 'IMAGE':
        directory = bpy.path.abspath(strip.directory)
    elif strip.type in {'MOVIE', 'SOUND'}:
        directory = os.path.dirname(bpy.path.abspath(strip.filepath))
    else:
        self.report({'ERROR'}, "Unsupported strip type")
        return {'CANCELLED'}

    if not os.path.exists(directory):
        self.report({'ERROR'}, f"Folder not found: {directory}")
        return {'CANCELLED'}

    # Open folder in system file browser
    system = platform.system()
    try:
        if system == "Windows":
            os.startfile(directory)
        elif system == "Darwin":
            subprocess.Popen(["open", directory])
        else:
            subprocess.Popen(["xdg-open", directory])

        self.report({'INFO'}, f"Opened folder: {directory}")
    except Exception as e:
        self.report({'ERROR'}, f"Failed to open folder: {str(e)}")
        return {'CANCELLED'}

    return {'FINISHED'}


Menu drawing functions

def draw_strip_menu(self, context): """Add to Strip menu""" layout = self.layout layout.separator() layout.operator("sequencer.open_in_gimp", icon='IMAGE_DATA') layout.operator("sequencer.open_source_folder", icon='FILE_FOLDER')

def draw_context_menu(self, context): """Add to right-click context menu""" layout = self.layout layout.separator() layout.operator("sequencer.open_in_gimp", icon='IMAGE_DATA') layout.operator("sequencer.open_source_folder", icon='FILE_FOLDER')

Keymap

addon_keymaps = []

def register_keymaps(): wm = bpy.context.window_manager kc = wm.keyconfigs.addon

if kc:
    # Sequencer keymap
    km = kc.keymaps.new(name='Sequencer', space_type='SEQUENCE_EDITOR')

    # Ctrl+Shift+G to open in GIMP
    kmi = km.keymap_items.new(
        "sequencer.open_in_gimp",
        type='G',
        value='PRESS',
        ctrl=True,
        shift=True
    )
    addon_keymaps.append((km, kmi))

    # Ctrl+Shift+F to open folder
    kmi = km.keymap_items.new(
        "sequencer.open_source_folder",
        type='F',
        value='PRESS',
        ctrl=True,
        shift=True
    )
    addon_keymaps.append((km, kmi))


def unregister_keymaps(): for km, kmi in addon_keymaps: km.keymap_items.remove(kmi) addon_keymaps.clear()

Registration

classes = [ OpenInGIMPPreferences, SEQUENCER_OT_open_in_gimp, SEQUENCER_OT_open_source_folder, ]

def register(): for cls in classes: bpy.utils.register_class(cls)

# Add to menus
bpy.types.SEQUENCER_MT_strip.append(draw_strip_menu)
bpy.types.SEQUENCER_MT_context_menu.append(draw_context_menu)

# Register keymaps
register_keymaps()


def unregister(): # Unregister keymaps unregister_keymaps()

# Remove from menus
bpy.types.SEQUENCER_MT_strip.remove(draw_strip_menu)
bpy.types.SEQUENCER_MT_context_menu.remove(draw_context_menu)

for cls in reversed(classes):
    bpy.utils.unregister_class(cls)


if name == " main ": register()

100%

Development Status: Open

Upvote 2 Downvote 0
peter.boos
6 Jan 2026

2 Comments

kalpit

what if we want krita as our editor.

Lauloque

Hello, Welcome to RCS.

This platform is meant for posting and discussing feature proposals for Blender. To share or discuss about an addon you made, please use other platforms, such as Blender Artists Community or Blender Today . Though this platform really isn't ideal for long code snippets...