Using Blender's filebrowser with Python
Every now and then an addon requires that the user selects a specific file or path. Sure, you could just give users a simple string input and let them copy/paste into it but how much cooler would it be to let them pick a file from the filebrowser?
Luckily Blender provides a handy class that does almost everything for us.
Meet ImportHelper
Importhelper is a mix-in class found in the bpy_extras submodule. It includes an invoke() function that calls the filebrowser and a few helper functions used in Blender’s importer addons. To use it all we have to do is extend it in our operator. Let’s start by importing both ImportHelper and Operator.
from bpy_extras.io_utils import ImportHelper
from bpy.types import Operator
Now we can go ahead and create the operator:
class OT_TestOpenFilebrowser(Operator, ImportHelper):
bl_idname = "test.open_filebrowser"
bl_label = "Open the file browser (yay)"
def execute(self, context):
"""Do something with the selected file(s)."""
return {'FINISHED'}
Yup, that’s it. Our new operator already has an invoke() function that calls the filebrowser and when a user selects a file, it stores the file’s path in self.filepath. Note that this is a regular StringProperty inside ImportHelper that we inherited when we subclassed it.
To filter the types of files shown to the user we have to add a filter_glob property to our class. This is a StringProperty with the list of extensions we want to show. Each extension is written in wildcard style and is separated by a semi-colon. Note that strings longer that 255 could be cut (since that’s the internal buffer size).
filter_glob: StringProperty(
default='*.jpg;*.jpeg;*.png;*.tif;*.tiff;*.bmp',
options={'HIDDEN'}
)
Also keep in mind that users can disable filtering in the UI and select any kind of file. You might want to reject a file or do something different depending on the extension you receive. You can take care of that with good old splitext().
filename, extension = os.path.splitext(self.filepath)
And what about adding settings to the filebrowser screen? All you have to do is add properties to the operator as usual and they will show up in the browser.
some_boolean: BoolProperty(
name='Do a thing',
description='Do a thing with the file you\'ve selected',
default=True,
)
Final code
import bpy
import os
from bpy.props import StringProperty, BoolProperty
from bpy_extras.io_utils import ImportHelper
from bpy.types import Operator
class OT_TestOpenFilebrowser(Operator, ImportHelper):
bl_idname = "test.open_filebrowser"
bl_label = "Open the file browser (yay)"
filter_glob: StringProperty(
default='*.jpg;*.jpeg;*.png;*.tif;*.tiff;*.bmp',
options={'HIDDEN'}
)
some_boolean: BoolProperty(
name='Do a thing',
description='Do a thing with the file you\'ve selected',
default=True,
)
def execute(self, context):
"""Do something with the selected file(s)."""
filename, extension = os.path.splitext(self.filepath)
print('Selected file:', self.filepath)
print('File name:', filename)
print('File extension:', extension)
print('Some Boolean:', self.some_boolean)
return {'FINISHED'}
def register():
bpy.utils.register_class(OT_TestOpenFilebrowser)
def unregister():
bpy.utils.unregister_class(OT_TestOpenFilebrowser)
if __name__ == "__main__":
register()
# test call
bpy.ops.test.open_filebrowser('INVOKE_DEFAULT')
There’s also an ExportHelper class that includes some utilities to check for existing files and setting a default untitled filename in the browser.
Did I miss something? Let me know in the comments!
17 Comments
script worked fine, but I still cant build a blender script with a button for import file and one for export file! I just cant seem to get buttons to work for me to open anything other than blenders intentions, like “render.render” ! 5 days now and still cant get my head around it, but this is by far the best site visited, nice and clean looking easy to follow.
Hi Vnny, thanks for the comments! If you want to create custom buttons; you need to create, and register, your own operators and then add them to a panel as buttons (I assume that’s what you mean by Blender’s intentions).
One thing I can’t figure out is how to get the result of the file dialog back.
I want to get a filename in order to do something, let’s call it ActualOperation(myObject), where myObject is an ElementTree.
The examples all show the operation using the filepath to do ActualOperation, but as arguments are passsed in as properties, and only simple types are allowed, that is not possible.
So I want to do the obvious, which is get the filepath back from the file dialog somehow.
Hi Diego,
thanks for all the useful blog posts!
I try to make the Load button only available when a specify file type is selected like a .exe but could not find out how that works. Do you know if this is possible? and if so could you share some insights?
Hi! As far as I know you can’t affect the buttons in the filebrowser while it’s open. What you can do is to filter the files so only the ones you want to allow are visible. Check out the ‘filter_glob‘ property I mention in the post for this. Just replace ‘*.jpg;*.jpeg;*.png;*.tif;*.tiff;*.bmp’ for ‘*.exe;’, or the formats you want to enable.
Cheers o/
Hi Diego,
Thanks for this tutorial.
I’m using the ExportHelper class to export 3D objects. The functionality works fine. However, I want to add an additional feature to the class. I want the filename extension, “filename_ext”, to change depending on the user selection. For example, if the user selects the item “Animation” from the EnumProperty, then the “filename_ext” is set to a different extension.
Is this possible? if so, how would you do it?
Here is a code snippet for your reference.
# ExportHelper mixin class uses this
filename_ext = “.xml”
filter_glob = StringProperty(
default=”*.xml”,
options={‘HIDDEN’},
maxlen=255, # Max internal buffer length, longer would be clamped.
)
dataTypeToExport = EnumProperty(
name=“Export Type”,
description=“Choose data to export”,
items=((‘Mesh’, “Mesh Data Only”, “Export Mesh Data only”),
(‘Animation’, “Animation Data Only”, “Export Animation Data only”),
(‘NavMesh’, “Navigation Data”, “Export NavMesh Data only”)),
default=‘Mesh’,
)
Hey Harold,
I haven’t tested this, but it should be possible to change these values before showing the file browser. You would have to override the invoke() function and set those values before ‘context.window_manager.fileselect_add(self)‘. Basically copy and paste the invoke function in the ExportHelper class in [BLENDER DIR]/scripts/modules/bpy_extras/io_utils.py. Then you can change
the properties like ‘self.filename_ext = ‘.txt’‘.
I used this as an example, set it up, and it works perfectly — so, naturally I found a question about this.
I found that when I exit by clicking “Cancel” or by pressing ESC, I still get a full path name as a selected file from the dialog. How can I find out if the user cancelled and didn’t want to select a file?
Hi, never used this myself but there is a “cancel” callback that you can override in operator classes: https://docs.blender.org/api/current/bpy.types.Operator.html#bpy.types.Operator.cancel
It took me a while, playing around, to get this worked out. I was using the basic format from your UIList demo and had the NewItem() function called from the UIList to add a file name to a list. NewItem() called the OT_OpenFileBrowser.execute() function. Then I had code that read the filename and printed it out in NewItem(), but since I was using the same file over and over, I didn’t realize it wasn’t working. The execute() function does not return to the calling function. (It is SO hard to get used to things like this in the Blender API!)
So here’s what seems to happen, and an answer to my question:
When you call execute() in the OpenFileBrowser operator, it does not return to the calling function, so don’t try to find a way to call, then read the filename, and do something you another function or class. Instead, put all you want to do with the file name you get in the execute() function. So if you want to load the file, store the name, just check resolution in the file, or anything, that has to be in the execute() function in OpenFileBrowser.
Now here’s the part that surprised me. When you call execute() and the browser pops up, if it’s cancelled, that’s it. Nothing more in execute() is done. So if there’s nothing you need to do if they cancel picking a file, just put the code in execute() that you need if a file is picked and if no file is picked, it won’t be run.
I can see overriding the cancel function — thanks, because I’ll remember that, but in this case, I’m lucky I don’t need to do anything if it’s cancelled.
Thanks for the tutorials, this is the 2nd in just a couple weeks that’s been a big help, and thank you for helping with questions in the comments as well!
No problem!
Yup, operators always return a set with a single string (with only a few possible values). It’s best to keep whatever logic you can outside the op class, or as classmethods.
This is awesome! I’m writing a script that auto-loads all image textures from a single folder — how can I alter this to allow selection of directories? Currently if you hit “Okay” on a directory it just opens that directory in the file browser… Thanks!
Hi, I’m not sure how this can be done (if at all) on the operator side. With properties you can set the subtype to “DIR_PATH” and it will select directories, so if you have a string property to store the selection you can use that.
Hi Diego!
Do you know if there is any way to open the OS filebrowser from the python api?
Thanks!!
Hi! Sorry for the delay, depends on the OS but generally you want to call the filebrowser using subprocess.run(). On linux you can use ‘xdg-open’, on windows use ‘explorer’.
Cheers,
Hi,
I tested this and it works, but there is a problem: even if I select multiple files, only the last one gets printed. I am on Blender 3.2, not sure if it matters?
Any idea why?
How would I add instructions in the right hand pane of the file browser that comes up…just text
like…“please choose path to save rendered image”