pythonpluginsimportblendermultipleselection

Importing multiple files in blender import plugin


I'm writing an import plugin for blender 2.8x and I'd like to make use of the multiple file selection feature. Unfortunately, I can't find any provision for this in 'ImportHelper' (the class I derive from) and web searches haven't yielded anything that seems to work and I can't seem to find anything in the documentation either.


Solution

  • As explained in Luther's answer, you need to add a files : CollectionProperty attribute to your class to enable multifile support.

    However, in addition you also need a directory: StringProperty attribute. Then you need to build the full filepath using os.path.join.

    See below for the code from a complete working import addon that supports multifile import. It is a modification of the "Operator File Import" template (operator_file_import.py). The code was tested on Blender 3.4.1.

    operator_file_import.multiple.py :

    import os
    import bpy
    
    def read_some_data(context, filepath, use_some_setting):
        print("Importing:", filepath)
        f = open(filepath, 'r', encoding='utf-8')
        try:
          data = f.read() # Fails on some latin-1 encoded text files, but not relevant to this importer example.
        except:
          pass
        f.close()
    
        # would normally load the data here
        #print(data)
    
        return {'FINISHED'}
    
    
    # ImportHelper is a helper class, defines filename and
    # invoke() function which calls the file selector.
    from bpy_extras.io_utils import ImportHelper
    from bpy.props import StringProperty, BoolProperty, EnumProperty, CollectionProperty
    from bpy.types import Operator
    
    
    class ImportSomeData(Operator, ImportHelper):
        """This appears in the tooltip of the operator and in the generated docs"""
        bl_idname = "import_test.some_data"  # important since its how bpy.ops.import_test.some_data is constructed
        bl_label = "Import Some Data"
    
        # ImportHelper mixin class uses this
        filename_ext = ".txt"
    
        filter_glob: StringProperty(
            default="*.txt",
            options={'HIDDEN'},
            maxlen=255,  # Max internal buffer length, longer would be clamped.
        )
    
        # List of operator properties, the attributes will be assigned
        # to the class instance from the operator settings before calling.
        use_setting: BoolProperty(
            name="Example Boolean",
            description="Example Tooltip",
            default=True,
        )
    
        type: EnumProperty(
            name="Example Enum",
            description="Choose between two items",
            items=(
                ('OPT_A', "First Option", "Description one"),
                ('OPT_B', "Second Option", "Description two"),
            ),
            default='OPT_A',
        )
    
        ###########################################
        # necessary to support multi-file import
        files: CollectionProperty(
            type=bpy.types.OperatorFileListElement,
            options={'HIDDEN', 'SKIP_SAVE'},
        )
    
        directory: StringProperty(
            subtype='DIR_PATH',
        )
        ###########################################
    
        def execute(self, context):
            for current_file in self.files:
                filepath = os.path.join(self.directory, current_file.name)
                read_some_data(context, filepath, self.use_setting)
            return {'FINISHED'}
    
    
    # Only needed if you want to add into a dynamic menu.
    def menu_func_import(self, context):
        self.layout.operator(ImportSomeData.bl_idname, text="Text Import Operator")
    
    
    # Register and add to the "file selector" menu (required to use F3 search "Text Import Operator" for quick access).
    def register():
        bpy.utils.register_class(ImportSomeData)
        bpy.types.TOPBAR_MT_file_import.append(menu_func_import)
    
    
    def unregister():
        bpy.utils.unregister_class(ImportSomeData)
        bpy.types.TOPBAR_MT_file_import.remove(menu_func_import)
    
    
    if __name__ == "__main__":
        register()
    
        # test call
        bpy.ops.import_test.some_data('INVOKE_DEFAULT')
    

    These are the relevant changes to the basic import template:

    1. Import os to use os.path.join:

      import os
      
    2. Import CollectionProperty:

      from bpy.props import StringProperty, BoolProperty, EnumProperty, CollectionProperty
      
    3. Add the files and directory properties:

      ###########################################
      # necessary to support multi-file import
      files: CollectionProperty(
          type=bpy.types.OperatorFileListElement,
          options={'HIDDEN', 'SKIP_SAVE'},
      )
      
      directory: StringProperty(
          subtype='DIR_PATH',
      )
      ###########################################
      
    4. Loop through self.files and build the full paths using os.path.join:

      def execute(self, context):
          for current_file in self.files:
              filepath = os.path.join(self.directory, current_file.name)
              read_some_data(context, filepath, self.use_setting)
          return {'FINISHED'}