Bl_idname conventions/rules etc

Hi there,

Pretty new to python, and I’m try to learn by taking existing examples and deconstructing/modifying them to do what I want them to do and figuring out what does what as I go

so I was following an example which opens a dialog window and has bl_idname = “export.some.data”

and then at the end has a test call:
bpy.ops.export.some_data

So far so good. But I wanted to make an importer, so I thought I should be able to change
bl_idname = import.some_data

and at the end have

bpy.ops.import.some_data

but this doesn’t work -the word “import” being highlighted in pink as it is at the top for “import bpy” made me think that this was probably going to be the case, so what naming convention would you give to a script that is designed to import something?

Also, it seems like whatever the first bl_idname is when I first run the script, that doesn’t change if I change the name and run the script again, is that right?

ie if I start with
bl_idname =" export.some_data"
and then
bpy.ops.export.some_data

the script runs, but then if I change that to

bl_idname =" export.some_datadata"
and then
bpy.ops.export.some_datadata

that does not work,

but

bpy.ops.export.some_data at the end still works, until I restart Blender

Did you put quotations marks between import.some_data ? Python won’t consider it as a string unless you do.

FYI import is a keyword in python that can’t be overriden.

Maybe you could post a little bit more of your script so we can see what’s going on ?

Realistically at this stage it’s reptty much identical to the example on the Blender API guide here:

https://docs.blender.org/api/3.0/bpy.types.Operator.html#bpy.types.Operator

I’ve just renamed a few things:

import bpy


class ImportMap(bpy.types.Operator):
    """Test exporter which just writes hello world"""
    #bl_idname = "export.some_data"
    bl_idname = "import_map.xml"
    bl_label = "Import Map"

    filepath: bpy.props.StringProperty(subtype="FILE_PATH")

    @classmethod
    def poll(cls, context):
        return context.object is not None

    def execute(self, context):
        file = open(self.filepath, 'w')
        #file.write("Hello World " + context.object.name)
        return {'FINISHED'}

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


# Only needed if you want to add into a dynamic menu
def menu_func(self, context):
    self.layout.operator_context = 'INVOKE_DEFAULT'
    self.layout.operator(ExportSomeData.bl_idname, text="Import Map")


# Register and add to the file selector (required to also use F3 search "Text Export Operator" for quick access)
bpy.utils.register_class(ImportMap)
bpy.types.TOPBAR_MT_file_export.append(menu_func)


# test call
#
#bpy.ops.export.some_data('INVOKE_DEFAULT')
bpy.ops.import_map.xml('INVOKE_DEFAULT')

I just tested it and I think this is the culprit. The poll prevents you from executing the operator if it doesn’t return True. So you can either remove these lines or keep them but you’ll have to make sure to select an object in code with for instance bpy.data.objects["Cube"].select_set(True), and then you can run the script. I’ve also modified the menu_func function to add the operator to the File > Import menu. Also, it’s better to use a with enclosure rather than simply open because if you forget to write file.close() at the end or if your script returns early because of an Exception, the file handle will never actually be released leading to memory leak. Here’s a suggestion.

import bpy


class ImportMap(bpy.types.Operator):
    """Test exporter which just writes hello world"""
    bl_idname = "import_map.xml"
    bl_label = "Import Map"

    filepath: bpy.props.StringProperty(subtype="FILE_PATH")

    def execute(self, context):
        with open(self.filepath, 'w') as f:
            f.write("Hello World " + context.object.name)
        return {'FINISHED'}

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


def menu_func(self, context):
    self.layout.operator_context = 'INVOKE_DEFAULT'
    self.layout.operator(ImportMap.bl_idname, text="Import Map")


def register():
    bpy.utils.register_class(ImportMap)
    bpy.types.TOPBAR_MT_file_import.append(menu_func)


if __name__ == "__main__":
    register()
    bpy.ops.import_map.xml('INVOKE_DEFAULT')