Can someone make this in to a working addon? Batch import .obj

Hi!

I found this script and have changed it a Little bit, can some one make it in to a working addon?

import os
import bpy

put the location to the folder where the objs are located here in this fashion

path_to_obj_dir = os.path.join(‘C:\’, ‘Users’, ‘YOUR_NAME’, ‘Desktop’, ‘OBJS’)

path_to_obj_dir = os.path.join(‘C:\Temp\obj’)
n = 0
m = 0

get list of all files in directory

file_list = os.listdir(path_to_obj_dir)

get a list of files ending in ‘obj’

obj_list = [item for item in file_list if item[-3:] == ‘obj’]

loop through the strings in obj_list and add the files to the scene

for item in obj_list:
path_to_file = os.path.join(path_to_obj_dir, item)
bpy.ops.import_scene.obj(filepath = path_to_file)
n = n + 1
m = n-1
bpy.context.scene.layers[n] = True
bpy.context.scene.layers[m] = False
scene = bpy.context.scene
for obj in scene.objects:
obj.location.x += 1.0

Here’s a start, whipped up in about half an hour:


bl_info = {"name":"Batch OBJ Import","category":"Import-Export"}
import bpy
import re
import os


class OBJIM_OT_batch_import_objs_from_dir(bpy.types.Operator):
    bl_idname = "objim.batch"
    bl_label = "Batch Import .OBJ's in Path..."
    directory = bpy.props.StringProperty(subtype="DIR_PATH")
    name_filter = bpy.props.StringProperty()
    def invoke(self,context,event):
        context.window_manager.fileselect_add(self)
        return {"RUNNING_MODAL"}
    def execute(self,context):
        objdir = self.directory
        if not self.name_filter:
            isobj = lambda _:_.endswith(".obj")
            objs = list(filter(isobj,os.listdir(objdir)))
        else:
            pat = re.compile(self.name_filter).search
            isobj = lambda _:_.endswith(".obj") and pat(_)
            objs = list(filter(isobj,os.listdir(objdir)))
        obj_paths = (os.path.join(objdir,n) for n in objs)
        for objpath in obj_paths:
            bpy.ops.import_scene.obj(filepath=objpath)
        return {"FINISHED"}


def register():
    bpy.utils.register_module(__name__)

I wasn’t sure what the intentions were behind the lines with m and n – dealing with layers.

I also left out the bit where all scene objects move +1 on X.

Those bits, you may need to explain what is wanted, or perhaps tweak the script.

The intention with the script is that It imports one obj on the first picked layer, then it moves to next layer and imports next .obj
Very nice to have when you got a lots of obj: to import and want to have them on seperate layers.

//W

Hi,

@wilnix and @dustractor
If you are going to invoke the file browser, I suggest using the files collection already available, the filter_glob, and adding the select all button to the draw method. This gives the opportunity to leave out a pesky obj monster that may be lurking.


bl_info = {"name":"Batch OBJ Import","category":"Import-Export"}
import bpy
from bpy.types import Operator, OperatorFileListElement
from bpy.props import (StringProperty, CollectionProperty, BoolProperty)
from bpy_extras.io_utils import ImportHelper


import os




class OBJIM_OT_batch_import_objs_from_dir(Operator, ImportHelper):
    bl_idname = "objim.batch"
    bl_label = "Batch Import .OBJ's in Path..."
    directory = StringProperty(subtype="DIR_PATH")
    filename_ext = ".obj"
    filter_glob = StringProperty(default="*.obj", options={'HIDDEN'})
    files = CollectionProperty(name="OBJ Files", type=OperatorFileListElement)
    
    seperate_layers = BoolProperty(name="Seperate Layers",
                                   default=False,
                                   description="Import each OBJ into its own layer")
    
    def invoke(self,context,event):
        context.window_manager.fileselect_add(self)
        return {"RUNNING_MODAL"}


    def draw(self, context):
        layout = self.layout
        layout.operator("file.select_all_toggle")
        layout.prop(self, "seperate_layers", toggle=True)


    def execute(self,context):


        for layer, file in enumerate(self.files):
            objpath = os.path.join(self.directory, file.name)
            bpy.ops.import_scene.obj(filepath=objpath)
            if self.seperate_layers:
                layers = [i == layer for i in range(20)]
                for obj in context.selected_objects:
                    obj.layers = layers
        return {"FINISHED"}




def register():
    bpy.utils.register_module(__name__)




if __name__ == "__main__":
    register()    
    # test call
    bpy.ops.objim.batch('INVOKE_DEFAULT')

Otherwise, if you are going to import all objs in a folder, why bother with the file browser?

PS: I stuck in a flag for separate layers. You’ll have to put the workings in for “the first picked layer”
As obj files are often a bunch of objects, I put each file import on a separate layer.

PPS… also the import will have no layers if more than 20 files are selected.

Please pardon the threadjack on this but how would you do the reverse??

basically i have a blend file with a number of mesh objects that i would like to export as separate OBJ files.

https://www.dropbox.com/s/kt83zpmc2iakocw/blossomskirt6.blend <— the file im working on

Manual method would be
Select first mesh
Export (use “only selection” flag)
If more meshes then next mesh and repeat Export step Else End

Hi robertltux,

probably don’t need to invoke the filebrowser, could rather just have a FILE_DIR stringprop for which directory to export to. Anyway heres a start.

It exports each mesh in selected_objects to <object.name>.obj in the directory selected.


import bpy




# ExportHelper is a helper class, defines filename and
# invoke() function which calls the file selector.
from bpy_extras.io_utils import ExportHelper
from bpy.props import StringProperty, BoolProperty, EnumProperty
from bpy.types import Operator
from os.path import join


class ExportSomeData(Operator, ExportHelper):
    """This appears in the tooltip of the operator and in the generated docs"""
    bl_idname = "export_test.some_data"  # important since its how bpy.ops.import_test.some_data is constructed
    bl_label = "Export Some Data"


    # ExportHelper mixin class uses this
    filename_ext = ".obj"
    directory = StringProperty(subtype="DIR_PATH")
    filter_glob = StringProperty(
            default="*.obj",
            options={'HIDDEN'},
            )


    
    @classmethod
    def poll(cls, context):
        return bool(len([obj.name for obj in context.selected_objects if obj.type == 'MESH']))


    def execute(self, context):
        scene = context.scene
        objs = [obj for obj in context.selected_objects if obj.type == 'MESH']
        active_obj = scene.objects.active
        for obj in objs:
            bpy.ops.object.select_all(action='DESELECT')
            
            obj.select = True
            
            filepath = join(self.directory, "%s.obj" % obj.name)
            bpy.ops.export_scene.obj(filepath=filepath, use_selection=True)
        
        # return state
        for obj in objs:
            obj.select = True
        scene.objects.active = active_obj
        return {'FINISHED'}




# Only needed if you want to add into a dynamic menu
def menu_func_export(self, context):
    self.layout.operator(ExportSomeData.bl_idname, text="Batch Export OBJs")




def register():
    bpy.utils.register_class(ExportSomeData)
    bpy.types.INFO_MT_file_export.append(menu_func_export)




def unregister():
    bpy.utils.unregister_class(ExportSomeData)
    bpy.types.INFO_MT_file_export.remove(menu_func_export)




if __name__ == "__main__":
    register()


    # test call
    bpy.ops.export_test.some_data('INVOKE_DEFAULT')

and it does what is says on the tin (as soon as somebody makes said tin)!

im not sure why but it does a full material and assets export for each OBJ

Without looking into it too deeply, I’d assume it’s a scene exporter, and even tho the only_selected is checked it gives the assets from the scene.

A possible work around would be to create a dummy empty scene, link the object, export the scene, unlink the object etc.

Untested


import bpy


# ExportHelper is a helper class, defines filename and
# invoke() function which calls the file selector.
from bpy_extras.io_utils import ExportHelper
from bpy.props import StringProperty, BoolProperty, EnumProperty
from bpy.types import Operator
from os.path import join




class ExportSomeData(Operator, ExportHelper):
    """This appears in the tooltip of the operator and in the generated docs"""
    bl_idname = "export_test.some_data"  # important since its how bpy.ops.import_test.some_data is constructed
    bl_label = "Export Some Data"




    # ExportHelper mixin class uses this
    filename_ext = ".obj"
    directory = StringProperty(subtype="DIR_PATH")
    filter_glob = StringProperty(
            default="*.obj",
            options={'HIDDEN'},
            )


    
    @classmethod
    def poll(cls, context):
        return bool(len([obj.name for obj in context.selected_objects if obj.type == 'MESH']))




    def execute(self, context):
        scene = context.scene
        
        objs = [obj for obj in context.selected_objects if obj.type == 'MESH']
        active_obj = scene.objects.active
        dummy_scene = bpy.data.scenes.new("Dummy")
        context.screen.scene = dummy_scene
        for obj in objs:
            #bpy.ops.object.select_all(action='DESELECT')
            
            dummy_scene.objects.link(obj)
            
            filepath = join(self.directory, "%s.obj" % obj.name)
            bpy.ops.export_scene.obj(filepath=filepath)
            dummy_scene.objects.unlink(obj)
        
        # return state
        bpy.ops.scene.delete()
        context.screen.scene = scene
        return {'FINISHED'}




# Only needed if you want to add into a dynamic menu
def menu_func_export(self, context):
    self.layout.operator(ExportSomeData.bl_idname, text="Batch Export OBJs")




def register():
    bpy.utils.register_class(ExportSomeData)
    bpy.types.INFO_MT_file_export.append(menu_func_export)




def unregister():
    bpy.utils.unregister_class(ExportSomeData)
    bpy.types.INFO_MT_file_export.remove(menu_func_export)




if __name__ == "__main__":
    register()




    # test call
    bpy.ops.export_test.some_data('INVOKE_DEFAULT')

actually i was going fuzz head i forgot that the folder i did the export to was not empty.

it does need the bits to make a full addon (unless its hiding in the addons list)

Hi batFinger
I can not get this to work as an Addon, first what should I call the .py file?

/Wilnix

And the Code:
bl_info = {“name”:“Batch OBJ Import”,“category”:“Import-Export”}
import bpy
from bpy.types import Operator, OperatorFileListElement
from bpy.props import (StringProperty, CollectionProperty, BoolProperty)
from bpy_extras.io_utils import ImportHelper

import os

class OBJIM_OT_batch_import_objs_from_dir(Operator, ImportHelper):
bl_idname = “objim.batch”
bl_label = “Batch Import .OBJ’s in Path…”
directory = StringProperty(subtype=“DIR_PATH”)
filename_ext = “.obj”
filter_glob = StringProperty(default="*.obj", options={‘HIDDEN’})
files = CollectionProperty(name=“OBJ Files”, type=OperatorFileListElement)

seperate_layers = BoolProperty(name="Seperate Layers",
                               default=False,
                               description="Import each OBJ into its own layer")

def invoke(self,context,event):
    context.window_manager.fileselect_add(self)
    return {"RUNNING_MODAL"}


def draw(self, context):
    layout = self.layout
    layout.operator("file.select_all_toggle")
    layout.prop(self, "seperate_layers", toggle=True)


def execute(self,context):


    for layer, file in enumerate(self.files):
        objpath = os.path.join(self.directory, file.name)
        bpy.ops.import_scene.obj(filepath=objpath)
        if self.seperate_layers:
            layers = [i == layer for i in range(20)]
            for obj in context.selected_objects:
                obj.layers = layers
    return {"FINISHED"}

def register():
bpy.utils.register_module(name)

if name == “main”:
register()
# test call
bpy.ops.objim.batch(‘INVOKE_DEFAULT’)