I want my export operator to open the normal file selector but instead of asking for a specific file I want it to ask for a directory. The reason for this is that both my model and the textures should be stored in a directory tree and I want to user to select the root. For example the model file "somemodel.ext" links to the textures "tex1.png" and "tex2.png" the resulting file structure should look like this:

      + tex1.png
      + tex2.png
   + somemodel.ext

and I want to user to select the RootFolder in the export fileselect.

  • $\begingroup$ Using savePath = bpy.path.abspath('//' + folderName + '/') is working well for me. The textures I'm baking are going into the specified folder which is in the same directory as the .blend. I'll post a more complete code example if no one with more experience shows up with a better solution by tomorrow. $\endgroup$ Aug 5 '14 at 22:06
  • $\begingroup$ I know how to get a folder path but I want the filepicker (the thing that opens when you do Save As... for example) to notice that. And I surely don't want to export the files to a fixed location. But don't mind to post your code, anyway. If you want to discuss this come into the chat chat.stackexchange.com/rooms/8888/the-renderfarm $\endgroup$ Aug 5 '14 at 22:12
  • $\begingroup$ there are a couple Cycles bake Addons that have file selector dialogues. You could dig up one of those for reference. $\endgroup$ Aug 6 '14 at 8:58

The most compact solution that I found uses the method WindowsManager.fileselect_add() in invoke(), and doesn't use the Helpers. The documentation of fileselect_add says:

Opens a file selector with an operator. The string properties ‘filepath’, ‘filename’, ‘directory’ and a ‘files’ collection are assigned when present in the operator

Actually, you need to specify only a directory StringProperty in your operator. In so doing, the possibility to select the name of the file will disappear.

Full example, tested on Blender 2.79:

import bpy

class SelectDirExample(bpy.types.Operator):

    """Create render for all chracters"""
    bl_idname = "example.select_dir"
    bl_label = "Dir Selection Example Operator"
    bl_options = {'REGISTER'}

    # Define this to tell 'fileselect_add' that we want a directoy
    directory = bpy.props.StringProperty(
        name="Outdir Path",
        description="Where I will save my stuff"
        # subtype='DIR_PATH' is not needed to specify the selection mode.
        # But this will be anyway a directory path.

    def execute(self, context):

        print("Selected dir: '" + self.directory + "'")

        return {'FINISHED'}

    def invoke(self, context, event):
        # Open browser, take reference to 'self' read the path to selected
        # file, put path in predetermined self fields.
        # See: https://docs.blender.org/api/current/bpy.types.WindowManager.html#bpy.types.WindowManager.fileselect_add
        # Tells Blender to hang on for the slow user input
        return {'RUNNING_MODAL'}

def register():

def unregister():

# Invoke register if started from editor
if __name__ == "__main__":

You can test the manual file selection by hitting SPACE on a 3D view and searching for Dir Selection Example Operator.

Differently, when executed from Python code you can specify the directory on your own: bpy.ops.demo.select_dir_example(directory='/tmp')

  • 1
    $\begingroup$ I consider this the better answer now. Btw to show only folders by default you can add a property filter_folder = bpy.props.BoolProperty(default=True, options={'HIDDEN'}). For further reference on options you can set via properties consult the source code. $\endgroup$ Dec 19 '18 at 19:52
  • $\begingroup$ This doesn't appear to work in 2.82 $\endgroup$
    – Lucas
    Jul 30 at 0:57

I presume the main question here is selecting a root directory with the FileSelector.

bpy_extras.io_utils.ExportHelper includes FileSelectParams, which when set give the desired functionality.

The first attribute to be set is: use_filter_folder = True This will filter anything that is not a folder to the user. This also has the added advantage that the path your script gets back is folder.

You also need to set a file extension type, even though it is not used. It will cause an error on execution. Possible bug? filename_ext = "."

This is based on an for general file import - How do I get the filepath when I import a file?

Full sample

import bpy
from bpy_extras.io_utils import ExportHelper
import os.path

class ExportOperator(bpy.types.Operator, ExportHelper):
    """Operator for setting a directory path."""

    #: Name of function for calling the nif export operator.
    bl_idname = "export_scene.folder"

    #: How the nif import operator is labelled in the user interface.
    bl_label = "Export to folder"

    filename_ext = "."
    use_filter_folder = True

    def execute(self, context):
        userpath = self.properties.filepath
            msg = "Please select a directory not a file\n" + userpath
            self.report({'WARNING'}, msg)

        #Insert the desired logic here to write to the directory.


def menu_func_import(self, context):
        ExportOperator.bl_idname, text="Export Scene to folder")

def register():

def unregister():

if __name__ == "__main__":
  • $\begingroup$ thanks for pointing the possible bug out. Though appreciating the effort, this script doesn't LIMIT the user to only select directories. I could of course just ignore the filename the user gives it (something like "model1") but then I would have to deal with all those asking what the filename is, etc. $\endgroup$ Aug 6 '14 at 17:31
  • $\begingroup$ If the user does decide to enter in a path that is not a directory, then just sanitise with os.path.isdir(file_path). You can only be so defensive. $\endgroup$
    – neomonkeus
    Aug 6 '14 at 18:20
  • $\begingroup$ You can actually, instead of using ExportHelper, use your own filepath attribute defined as filepath = StringProperty(type='DIR_PATH'). $\endgroup$ Aug 9 '15 at 2:28
  • $\begingroup$ I think it should be StringProperty(subtype='DIR_PATH') $\endgroup$
    – Noam Peled
    Dec 12 '17 at 20:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.