This can be done using by making use of the context pointer, mapping each row to some data which the menu can access, looking up the chain of open menus.
e.g.
import bpy
import os
Set these to dir and ID that make sense.
PATH_SEARCH = "~"
CONTEXT_ID = "my_menu_id"
This operator is just an example, it only reports the filpath given to it.
class PathExampleOperator(bpy.types.Operator):
bl_idname = "path.example"
bl_label = "Path Example"
filepath: bpy.props.StringProperty()
def execute(self, context):
self.report({'INFO'}, "File: %r" % self.filepath)
return {'FINISHED'}
class PathMenu(bpy.types.Menu):
bl_label = "Directory Menu"
bl_idname = "FILE_MT_dynamic_path_menu"
_parents = {}
@staticmethod
def _calc_path(layout):
result = []
while layout:
layout, payload = PathMenu._parents.get(layout, (None, None))
result.append(payload)
result.reverse()
return result
def draw(self, context):
layout = self.layout
parent_id = getattr(context, CONTEXT_ID, None)
if parent_id is None:
# This is the root level menu, use the base path.
parent_path = os.path.expanduser(PATH_SEARCH)
# Avoid accumulating indefinitely.
PathMenu._parents.clear()
else:
parent_path = os.path.join(*PathMenu._calc_path(parent_id))
for filename in sorted(os.listdir(parent_path)):
# Skip hidden.
if filename.startswith("."):
continue
filepath = os.path.join(parent_path, filename)
if os.path.isdir(filepath):
row = layout.row()
row.context_pointer_set(CONTEXT_ID, row)
# The payload could be anything, it so happens
# in this case that the `filepath` makes sense to use.
payload = filepath
PathMenu._parents[row] = (parent_id, payload)
row.menu(PathMenu.bl_idname, text=filename)
else:
# Not a directory, show a file.
props = layout.operator(PathExampleOperator.bl_idname, text=filename, icon='FILE')
props.filepath = filepath
def draw_item(self, context):
layout = self.layout
layout.menu(PathMenu.bl_idname)
def register():
bpy.utils.register_class(PathMenu)
bpy.utils.register_class(PathExampleOperator)
# lets add ourselves to the main header
bpy.types.INFO_HT_header.append(draw_item)
def unregister():
bpy.utils.unregister_class(PathMenu)
bpy.utils.unregister_class(PathExampleOperator)
bpy.types.INFO_HT_header.remove(draw_item)
if name == "main":
register()
bpy.ops.wm.call_menu(name=PathMenu.bl_idname)
blender_path/2.79/scripts/templatesit is added automatically to the menu. As it is now, If you add a new folder into the templates folder, the files of the subfolder are added to the menu, but in the bottom level (along with the folder). The concept is to make the "New Folder" item, (and any folder) a menu so themenu > submenu> subsubmenu > itemchoice is:folder/subfolder/subsubfolder/file. – batFINGER Jun 12 '18 at 08:12__init__method, and set the filepath class property. Hopefully being able to pass props to menu, or have some ref to the parent menu, to submenu more effectively, is coming in 2.8. 8^) @yhoyo Here is a randomly expanding menu example – batFINGER Jun 14 '18 at 11:07