What's the proper way to connect C code with Python code(import-export menu buttons)?

I’ve been working on adding, for the lack of a better way to put it, support for a particular 3D model format to Blender, and so far, my code’s been structured in this way:

  • the payload is all C code, and for each of the main functions(an import and an export), they are plugged as the exec parameter in their respective Operators. The codefile is in C:\blender-git\blender\source\blender\editors\io
  • the way to access this functionality hooks into the interface via Python scripts that I’ve installed as addons and each addon adds a button to import/export menu.

The python code for adding the button:

def menu_func_import(self, context):
    self.layout.operator(OPERATOR_NAME, text=FILE_NAME_AND_EXTENSION)

def register():
    bpy.types.TOPBAR_MT_file_import.append(menu_func_import)

You will notice there’s no usual class registration in register(), as this method doesn’t require classes.

So far, it’s been working simply by finding the appropriate Operator I’ve added to C:\blender-git\blender\source\blender\editors\io\io_ops.c with WM_operatortype_append();, however this is not only an incorrect way of connecting the Python and C code, there’s also no way to use OrientationHelper to correctly align the model on import, and format filter when you’re looking for the file to import.

So, the question thus is, how does one correctly invoke C code from Python parts that describe interface?

You can do only simple read-print operations in menu_func_import however if you try to change the context (writing) it will not work.

Thus you will have to write your own operator like this:
\programs\graphics\blender\3.1\scripts\templates_py\operator_simple.py
As you see menu_func_import can only construct the user interface or print something, but it can’t do any fancy operations.

For more information double check how the import operator is defined, so you can use the correct settings.
eg: blender\3.1\scripts\addons\io_scene_obj

To import c code, it means that the code will have to be a dynamic library (.dll / .so / .dylib) and have the appropriate functions exported (see DLLEXPORT). You can write the code anywhere you want and in the case of the importer inside the operator execute method.

import bpy
import ctypes
MB_OK = 0x0
ICON_INFO = 0x40
msg = f'you have {len(bpy.context.scene.objects)} object(s) in scene'
result = ctypes.windll.user32.MessageBoxA(0, msg.encode('utf-8'), 'Info'.encode('utf-8'), MB_OK | ICON_INFO)

About the programming logic of your importer, I don’t know exactly how you will go about it. At somepoint if you have a data structure of some kind that you will have to iterate and then reconstruct the objects/meshes/etc in the Blender scene. Say the DLL gives you a JSON string, that has some fields binary encoded as MD5 strings.

If you try to do deep linking (your exporter DLL calls Blender API functions) is much more complex and difficult to accomplish as Blender has no wrappers and no DLLs exist, only the Python API.

However you can indeed run blender.exe as a subprocess (see n-tier architecture) from command line or even from C# assuming the host computer has Blender installed. And open a document, change the contents of it, save it. Instead to do this from blender > import from your application you do this from your application > export to blender.

1 Like

Thanks for the reply!

I guess I wasn’t clear enough on several things I’ll try to clarify now, one by one - the import/export already works, and menu_func_import is only used there as a way to a) make a button that b) executes code referred to via the operator name. So, while getting me 90% of the way, it doesn’t get me the remaining 10%(namely here, orientation correction and filtering formats when we’re looking for a file to import, but there are other ways of doing it; and whatever else I’m missing out on). However, several other importers(I did look at some of them, but not all of them) have more elaborate ways of doing that, such as entire Python classes that do other things besides adding buttons. :slight_smile:
I was just looking for an easy way to correct the orientation of the model, and clearly, some of the other formats use orientation_helper to do just that, and successfully. But for that you need a proper class, not just a menu_func_import.
I also didn’t mean that I’d import C code, so far I didn’t have to resort to anything like dynamic libraries. The importing was strictly of 3d model files.

I’ve spent last few hours looking for alternatives, and like so many times, it seems it’s a good idea to look closely into how Collada operates.
Other than that, now that I’ve hopefully managed to clear up some of my own misformulations, I’ll try to reiterate my quandaries and what I’m hoping to do:

  • menu_func_import, or even the overarching class you might use, can only do one thing for your C code – find the operator that you’ve declared and pointed to your code via exec parameter(there are other ones but I haven’t done much with them).
  • taking as an example the OBJ format, there is a proper class, and… that’s where everything happens. They don’t utilize any C code. It’s all right there, in Python. So it seems unobvious, if it’s even possible(which it might not be), as to how one would have a class, use its perks, and then also specify an operator with C functions that one would love to use for the brunt of the import/export process.

I hope it’s clearer now what I’m trying to do. Thanks again.