Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I get a Blender exporter script to run from the command line?

I am trying to export some objects from blender into a proprietary format. I'd like the script I write to be able to export the objects from blender from the File drop down and from the command line. I am using blender 2.66 on ubuntu 12.04 LTS. Below is the file I currently am trying to get to run.

# Required Blender information.
bl_info = {
           "name": "My Exporter",
           "author": "",
           "version": (1, 0),
           "blender": (2, 65, 0),
           "location": "File > Export > Test (.tst)",
           "description": "",
           "warning": "",
           "wiki_url": "",
           "tracker_url": "",
           "category": "Import-Export"
          }

# Import the Blender required namespaces.
import bpy
from bpy_extras.io_utils import ExportHelper


# The main exporter class.
class MyExporter(bpy.types.Operator, ExportHelper):
   bl_idname       = "test.tst";
   bl_label        = "My Exporter";
   bl_options      = {'PRESET'};

   filename_ext    = ".tst";

   object_count    = 0;

   def __init__(self):
      pass

   def execute(self, context):
      print("Execute was called.");

      # Parse all the objects in the scene.
      return {'FINISHED'};

   def export_object(self, gameObject):
      if (gameObject.type != "MESH"):
         print("Object was not of type mesh.");
      else:
         object_count += 1;

      return;


# Define a function to create the menu option for exporting.
def create_menu(self, context):
   self.layout.operator(MyExporter.bl_idname,text="test (.tst)");

# Define the Blender required registration functions.
def register():
   """
   Handles the registration of the Blender Addon.
   """
   bpy.utils.register_module(__name__);
   bpy.types.INFO_MT_file_export.append(create_menu);

def unregister():
   """
   Handles the unregistering of this Blender Addon.
   """
   bpy.utils.unregister_module(__name__);
   bpy.types.INFO_MT_file_export.remove(create_menu);

# Handle running the script from Blender's text editor.
if (__name__ == "__main__"):
   print("Registering.");
   register();

   print("Executing.");

   # I have tried with these lines uncommented to force it to run
   # the execute function, but I get an error saying:
   #    exporter = MyExporter();
   #    TypeError: bpy_struct.__new__(type): expected a single argument

   #exporter = MyExporter();
   #exporter.execute(bpy.context.scene);

I have tried the following command:

blender model.blend --background --python myexporter.py

From which I get the following as output:

Note: No (valid) '~/.config/blender/2.66/config/startup.blend' found,
      fall back to built-in default.

Read new prefs: ~/.config/blender/2.66/config/userpref.blend
found bundled python: ~/blender/2.66/python
read blend: ~/model.blend
Registering.
Executing.

Blender quit

The execute function from the MyExporter class never seems to get called. I even tried calling the execute function directly but, if you read the comment above that area, I seem to be missing something there as well.

Everything works fine when the script is added as an add-on to blender. It calls execute perfectly. So at least I have half of it working.

Thank you in advance for any help you can give me. If I made a stupid mistake I apologize, I am learning python at the same time as I write this script.

like image 796
Jason Smith Avatar asked Dec 12 '22 14:12

Jason Smith


1 Answers

I finally figured this out and thought it would be a good idea to come back and share the answer. First here is the file that works.

# Required Blender information.
bl_info = {
           "name": "My Exporter",
           "author": "",
           "version": (1, 0),
           "blender": (2, 65, 0),
           "location": "File > Export > Test (.tst)",
           "description": "",
           "warning": "",
           "wiki_url": "",
           "tracker_url": "",
           "category": "Import-Export"
          }

# Import the Blender required namespaces.
import sys, getopt

import bpy
from bpy_extras.io_utils import ExportHelper



# The main exporter class.
class MyExporter(bpy.types.Operator, ExportHelper):
   bl_idname       = "export_scene.my_exporter";
   bl_label        = "My Exporter";
   bl_options      = {'PRESET'};

   filename_ext    = ".tst";

   object_count    = 0;

   def __init__(self):
      pass

   def execute(self, context):
      print("Execute was called.");

      self.parse_command_line_options();

      if (self.filepath == ""):
         print("No sutable filename was provided to save to.");
         return {'FINISHED'};

      # Get all the mesh objects in the scene.
      objList = [object for object in bpy.context.scene.objects if object.type == 'MESH'];

      # Now process all the objects that we found.
      for gameObject in objList:
         self.export_object(gameObject);

      # Parse all the objects in the scene.
      return {'FINISHED'};


   def export_object(self, gameObject):
      if (gameObject.type != "MESH"):
         print("Object was not of type mesh.");
      else:
         self.object_count += 1;

      return;


   def parse_command_line_options(self):
      modelFile = "";
      myArgs = [];
      argsStartPos = 0;

      if (("--" in sys.argv) == False):
         return;

      argsStartPos = sys.argv.index("--");
      argsStartPos += 1;
      myArgs = sys.argv[argsStartPos:];

      try:
         opts, args = getopt.getopt(myArgs, 'hm:', ["help", "model-file="]);
      except getOpt.GetoptError:
         print("Opt Error.");
         return;

      for opt, arg in opts:
         if (opt in ("-h", "--help")):
            print("Run this as the following blender command.");
            print("\tblender <blend file> --background --python <script file> -- -m <output file>");
         elif (opt in ("-m", "--model-file")):
            modelFile = arg;

      if (modelFile != ""):
         self.filepath = modelFile;



# Define a function to create the menu option for exporting.
def create_menu(self, context):
   self.layout.operator(MyExporter.bl_idname,text="test (.tst)");

# Define the Blender required registration functions.
def register():
   """
   Handles the registration of the Blender Addon.
   """
   bpy.utils.register_module(__name__);
   bpy.types.INFO_MT_file_export.append(create_menu);

def unregister():
   """
   Handles the unregistering of this Blender Addon.
   """
   bpy.utils.unregister_module(__name__);
   bpy.types.INFO_MT_file_export.remove(create_menu);


# Handle running the script from Blender's text editor.
if (__name__ == "__main__"):
   print("Registering.");
   register();

   print("Executing.");
   bpy.ops.export_scene.my_exporter();

The key things that I was missing were setting the bl_idname for my exporter class incorrectly and then the call of bpy.ops.export_scene.my_exporter();

More information about what I messed up can be found here: http://www.blender.org/documentation/blender_python_api_2_66_release/bpy.ops.html

Now since I also wanted this to run from the command line and not just the File drop down menu, I added a parse function to parse the command line options sent to Blender. Blender has a switch '--' that defines the end of blender argument input, so if that switch is detected then I grab any arguments after it and parse those as my scripts arguments. This, so far, seems to work perfectly.

Currently, the model file supports -h to print some help information and -m to specify the file to save the conversion data to.

Well, I hope that this information helps someone else out there. Good luck.

Update: If you see one or more messages like the following on the command line when running your script then use the answer from this question to solve that problem.

skipping driver 'alpha * auto', automatic scripts are disabled
like image 117
Jason Smith Avatar answered Dec 28 '22 09:12

Jason Smith