Skip to main content
added 2268 characters in body
Source Link
Marty Fouts
  • 33.5k
  • 10
  • 37
  • 80

Addition

To make this answer clearer, here is the code for an operator and panel. The panel is added as the "TLA" tab in the Video Sequence Editor's side panel and contains a button labeled "Add Images". Pressing the button will bring up the file dialog, but with no filtering (I've removed the glob variable). It assumes that Channel 1 is empty and appends all of the files from the dialog to that image strip.

import bpy
from bpy.types import Operator
from bpy.types import Panel
from bpy.props import StringProperty
from bpy.props import CollectionProperty
from bpy_extras.io_utils import ImportHelper
from pathlib import Path

class TLA_OT_addimages(Operator, ImportHelper):
    """ add images to an image strip in the VSE"""
    bl_idname = "vse.addimages"
    bl_label = "Add Images"
    bl_options = {"REGISTER", "UNDO"}
    directory: StringProperty(
        options={'HIDDEN'}
    )
    # This is used by fileselect_add if it is present.
    files: CollectionProperty(type=bpy.types.PropertyGroup)
    @classmethod
    def poll(cls, context):
        return True
    def execute(self, context):
        self.report({'INFO'},
            f"execute()")
        directory_path = Path(self.filepath).parent
        file_list = []
        for file in self.files:
            file_list.append({"name":file.name})
        bpy.ops.sequencer.image_strip_add(
            directory=str(directory_path),
            files=file_list,
            relative_path=True,
            channel=1,
            fit_method='FIT'
        )
        return {'FINISHED'}

class TLA_PT_sidebar(Panel):
    """Display test button"""
    bl_label = "Add images"
    bl_space_type = "SEQUENCE_EDITOR"
    bl_region_type = "UI"
    bl_category = "TLA"
    def draw(self, context):
        col = self.layout.column(align=True)
        prop = col.operator("vse.addimages")

 
classes = [
    TLA_OT_addimages,
    TLA_PT_sidebar,
]

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


def unregister():
    for c in classes:
        bpy.utils.unregister_class(c)

because the operator is invoked from the UI of the sequence editor it does not need a context overrride.

Addition

To make this answer clearer, here is the code for an operator and panel. The panel is added as the "TLA" tab in the Video Sequence Editor's side panel and contains a button labeled "Add Images". Pressing the button will bring up the file dialog, but with no filtering (I've removed the glob variable). It assumes that Channel 1 is empty and appends all of the files from the dialog to that image strip.

import bpy
from bpy.types import Operator
from bpy.types import Panel
from bpy.props import StringProperty
from bpy.props import CollectionProperty
from bpy_extras.io_utils import ImportHelper
from pathlib import Path

class TLA_OT_addimages(Operator, ImportHelper):
    """ add images to an image strip in the VSE"""
    bl_idname = "vse.addimages"
    bl_label = "Add Images"
    bl_options = {"REGISTER", "UNDO"}
    directory: StringProperty(
        options={'HIDDEN'}
    )
    # This is used by fileselect_add if it is present.
    files: CollectionProperty(type=bpy.types.PropertyGroup)
    @classmethod
    def poll(cls, context):
        return True
    def execute(self, context):
        self.report({'INFO'},
            f"execute()")
        directory_path = Path(self.filepath).parent
        file_list = []
        for file in self.files:
            file_list.append({"name":file.name})
        bpy.ops.sequencer.image_strip_add(
            directory=str(directory_path),
            files=file_list,
            relative_path=True,
            channel=1,
            fit_method='FIT'
        )
        return {'FINISHED'}

class TLA_PT_sidebar(Panel):
    """Display test button"""
    bl_label = "Add images"
    bl_space_type = "SEQUENCE_EDITOR"
    bl_region_type = "UI"
    bl_category = "TLA"
    def draw(self, context):
        col = self.layout.column(align=True)
        prop = col.operator("vse.addimages")

 
classes = [
    TLA_OT_addimages,
    TLA_PT_sidebar,
]

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


def unregister():
    for c in classes:
        bpy.utils.unregister_class(c)

because the operator is invoked from the UI of the sequence editor it does not need a context overrride.

Source Link
Marty Fouts
  • 33.5k
  • 10
  • 37
  • 80

The ImportHelper class is not well documented, so I wrote this example to help explain it:

#-----------------------------------------------------------------------------
# Here's what the ImportHelper class does as a mix-in:
# It adds a StringProperty named 'filepath'
# It adds an invoke function
# It adds a "check function.
#
# The invoke function calls context.window_manager.fileselect_add
# This class is the same as the above class, except it makes these things
# explicit
# https://docs.blender.org/api/current/bpy.types.WindowManager.html#bpy.types.WindowManager.fileselect_add
#
from bpy.types import Operator
from pathlib import Path

class TLA_OT_stlhandlerv2(Operator):
    """ Import an STL and do things with it"""
    bl_idname = "import.stlhandlerv2"
    bl_label = "Process STL"
    bl_options = {"REGISTER", "UNDO"}

    # This is usually obtained from the ImportHelper mixin
    # It is used by fileselect_add if it is present.
    filepath: StringProperty(
        name="File Path",
        description="Filepath used for importing the file",
        maxlen=1024,
        subtype='FILE_PATH',
    )

    # This is used by fileselect_add if it is present.
    # It is not well documented.
    filter_glob: StringProperty(
        default='*.stl',
        options={'HIDDEN'}
    )

    # This is used by fileselect_add if it is present.
    directory: StringProperty(
        options={'HIDDEN'}
    )

    # This is used by fileselect_add if it is present.
    files: CollectionProperty(type=bpy.types.PropertyGroup)

    @classmethod
    def poll(cls, context):
        return context.mode == "OBJECT"

    def execute(self, context):
        directory_path = Path(self.filepath).parent
        for file in self.files:
            bpy.ops.import_mesh.stl(filepath=str(directory_path / file.name))
            print(f'Do Your thing here')
        return {'FINISHED'}

    # This is usually obtained from the ImportHelper mixin
    def invoke(self, context, _event):
        context.window_manager.fileselect_add(self)
        return {'RUNNING_MODAL'}

This example happens to use a dialog box to collect files that it then processes using the operator that imports an STL file. It implements this workflow:

When the handler is invoked, the fileselect_add function is called as a modal operator. As a side effect, it has access to filepath, filter_glob, and directory and uses them to set up the the dialog box before it calls it.

When fileselect_path completes, it sets those variables, in case they have changed, and it fills out the files variable with the names of all of the files that were marked as selected in the dialog.

Note: Because the variable names are hard wired in the helper functions you must use the same spelling in your operator class.

Once fileselect_path completes, your execute function is called. It can use the values to do whatever it wants. In my example, it invokes the stl importer.

The other bits in the template file are just a way of demonstrating how to add your operator to a menu to invoke it.