import bpy
from bpy.types import Operator, PropertyGroup
from bpy_extras.io_utils import ExportHelper #, ImportHelper
import os
class SelectDirExample(Operator):
bl_idname = "object.select_dir_example"
bl_label = "Select Dir Example"
bl_options = {'REGISTER', 'UNDO'}
input1: bpy.props.FloatProperty(name="Input 1", default=1.0, min=0.0, max=100.0)
input2: bpy.props.FloatProperty(name="Input 2", default=1.0, min=0.0, max=100.0)
folder: bpy.props.StringProperty(name="Choose Folder")
def draw_folder_selection(self, layout, property):
row = layout.row(align=True)
row.prop(self, property)
ops = row.operator('select.directory_call_operator', text="", icon='FILEBROWSER')
ops.operator_call = 'object.select_dir_example'
# StringProperties need extra "" and \\ need to be doubled with my_string.replace("\\","\\\\")
# E.g. my_string : StringProperty
# f'my_string="{self.my_string}"'
ops.operator_properties = f'input1={self.input1}, input2={self.input2}'
ops.operator_folder_property_name = "folder"
ops.old_filepathold_directory = self.folder
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False # No animation.
layout.prop(self, 'input1')
layout.prop(self, 'input2')
self.draw_folder_selection(layout, 'folder')
def invoke(self, context, event):
return_code = context.window_manager.invoke_props_dialog(self, width = 300)
return return_code
def execute(self, context):
input1 = self.input1
input2 = self.input2
folder = self.folder
# My processing goes here
print(f"Folder location is: {folder}")
# Finished processing.
return {'FINISHED'}
# Use 'ImportHelper' when something is imported from this directory
class Select_OT_directory_call_operator(Operator, ExportHelper):
bl_idname = "select.directory_call_operator"
bl_label = "Select Directory"
bl_description = " "
bl_options = {'REGISTER', 'INTERNAL'}
filename_ext = ""
filename = ""
filepath = ""
filter_glob: bpy.props.StringProperty(
default="",
options={'HIDDEN'},
maxlen=255, # Max internal buffer length, longer would be clamped.
)
use_filter_folder = True
filepathdirectory: bpy.props.StringProperty(name="Folder Path", maxlen=0maxlen=1024, default=" "default="")
operator_call : bpy.props.StringProperty(options={'HIDDEN'}, description="operator to call")
operator_properties : bpy.props.StringProperty(options={'HIDDEN'}, description="operator properties diffrent from folder")
operator_folder_property_name : bpy.props.StringProperty(options={'HIDDEN'}, description="folder property of the operator to apply the new path to")
old_filepathold_directory : bpy.props.StringProperty(options={'HIDDEN'}, description="old filepath of the operator used for cancel")
def call_operator(self, filepath: str):
filepath = filepath.replace("\\","\\\\") # needed because a str is placed into a str
exec(f'bpy.ops.{self.operator_call}("INVOKE_DEFAULT", {self.operator_properties}, {self.operator_folder_property_name}="{filepath}")')
def invoke(self, context, event):
super().invoke(context, event)
self.filepath = ""
return {'RUNNING_MODAL'}
def execute(self, context):
user_path = self.properties.filepathdirectory
if (not os.path.isdir(user_path)):
msg = f"Please select a directory not a file\n{user_path}"
self.report({'ERROR'}, msg)
self.cancel(context)
return {'CANCELLED'}
self.call_operator(user_path)
return {'FINISHED'}
def cancel(self, context):
self.call_operator(self.old_filepathold_directory)
def register():
bpy.utils.register_class(SelectDirExample)
bpy.utils.register_class(Select_OT_directory_call_operator)
def unregister():
bpy.utils.unregister_class(SelectDirExample)
bpy.utils.unregister_class(Select_OT_directory_call_operator)
if __name__ == "__main__":
register()
EDIT 2 add invoke to delete filename on startup and use directory
instead of filepath
because this will only return the selected directory even if a file is selected