2

If I have a basic save/export operator like this:

class SaveData(bpy.types.Operator):
    bl_idname = "my.save"
    bl_label = "Save"
    filepath: bpy.props.StringProperty(subtype="FILE_PATH")
def execute(self, context):
    do_save(ensure_ext(self.filepath, ".mytype"))
    return {"FINISHED"}

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

What is the right/idiomatic way to prevent that that from saving a file named .mytype if the user just hits Save without supplying a filename? This bad behavior appears to be common behavior among many Blender plugins (whether they give a Python traceback or save a .ext file just depends on whether they happen to call ensure_ext() or something similar)

What I have tried:

  • The check method is not clearly documented, but it appears to be for checking parameters before invoke, not after.
  • The execute method could check for empty self.filepath but I've found zero examples of that. This would occur after the file selector has closed, so it would require popping another modal to warn the user that nothing happened.
  • The native Save/Save As for .blend files aggressively puts .blend into the filepath if you try to clear it, so I guess technically you can't save with no filename, but it will happily write a file named .blend. There also doesn't seem to be any way to turn on that behavior for other file selectors.
  • The FILE_OT_execute (which is the operator for the Save or Load button in the file selector) has a promising property named need_active which looks (from reading the C source) like it makes the button do nothing in case no file is selected. But I don't understand how I would set that property because that is used internally by fileselect_add(). This behavior would match other Windows programs I've tested.
Ben Jackson
  • 213
  • 1
  • 9

1 Answers1

3

ImportHelper and ExportHelper mixin classes.

If you look at either the import or export operator templates in the text editor will see they use the helper mixin classes ImportHelper and ExportHelper

Here is the code for export helper from bpy_extras.io_utils find it in the scripts/modules folder of your blender installation.

class ExportHelper:
    filepath: StringProperty(
        name="File Path",
        description="Filepath used for exporting the file",
        maxlen=1024,
        subtype='FILE_PATH',
    )
    check_existing: BoolProperty(
        name="Check Existing",
        description="Check and warn on overwriting existing files",
        default=True,
        options={'HIDDEN'},
    )
# subclasses can override with decorator
# True == use ext, False == no ext, None == do nothing.
check_extension = True

def invoke(self, context, _event):
    import os
    if not self.filepath:
        blend_filepath = context.blend_data.filepath
        if not blend_filepath:
            blend_filepath = "untitled"
        else:
            blend_filepath = os.path.splitext(blend_filepath)[0]

        self.filepath = blend_filepath + self.filename_ext

    context.window_manager.fileselect_add(self)
    return {'RUNNING_MODAL'}

def check(self, _context):
    import os
    change_ext = False
    change_axis = _check_axis_conversion(self)

    check_extension = self.check_extension

    if check_extension is not None:
        filepath = self.filepath
        if os.path.basename(filepath):
            filepath = bpy.path.ensure_ext(
                filepath,
                self.filename_ext
                if check_extension
                else "",
            )

            if filepath != self.filepath:
                self.filepath = filepath
                change_ext = True

    return (change_ext or change_axis)

which as you can see sets the name to "untitled." if not set.

Check out the templates to see other properties that are used to extend this

# ExportHelper mixin class uses this
filename_ext = ".txt"

filter_glob: StringProperty( default="*.txt", options={'HIDDEN'}, maxlen=255,

Also a files collection bpy/file browser: get selected file names where directory is used to designate the folder the files reside in.

Set custom location for import/export helper

batFINGER
  • 84,216
  • 10
  • 108
  • 233