# Get list of operators in a menu layout

I would like to get the list of operators, and their order, in a menu. For example I need to get that list for the file menu (bpy.types.INFO_MT_file).

The idea is to add an element in a specific point of the menu, which is not possible with the append or prepend function. But if I can get the list of operators, I can rebuild an entire menu and insert my element where I want, that will do the trick.

So far I can do it with my plugins (overwriting the INFO_MT_file Class). But if another plugin do the same thing as I'm doing, we will destroy each other's layout. So any way to get a menu's children ?

Cheers !

Inserting layout elements in between is not supported and I wouldn't recommend to hack such a feature in with Python.

You can get the filename of the file from which a draw function originates, granted it is a python script:

bpy.types.INFO_MT_file.draw.__code__.co_filename


... then check the code of that file.

Here is an example how to "inject" code:

bl_info = {
"author": "CoDEmanX",
"version": (1, 0),
"blender": (2, 65, 0),

import bpy

def main():
insert_code = '    layout.operator("object.select_all", text="Some OT")\n'
bpy_type = "INFO_MT_file"
bpy_type_class = getattr(bpy.types, bpy_type)
#module = bpy_type_class.__module__

filepath = bpy_type_class.draw.__code__.co_filename
if filepath == "<string>":
print("Aborting, modifications are active")
return
try:
file = open(filepath, "r")
except:
print("%s couldn't be accessed, aborting." % filepath)
return

line_start = bpy_type_class.draw.__code__.co_firstlineno - 1

for i in range(line_start, len(lines)):
line = lines[i]
if not line[0].isspace() and line.lstrip()[0] not in ("#", "\n", "\r"):
break

line_end = i

# Unindent draw func by one level, since it won't sit inside a class
lines = [l[4:] for l in lines[line_start:line_end]]

for i, line in enumerate(lines, 1):
if insert_after in line:
print("FOUND INSERT LINE")
lines.insert(i, insert_code)
break
else:
print("COULDN'T FIND INSERTION POINT")
return

# Debug output
#f = open("D:\\s.txt", "w").writelines(lines)

l = {}
exec("".join(lines), {}, l)
print(l)

#bpy_type_class.draw.__code__ = code_object # Doesn't work, since a single func is not a module

bpy_type_class.draw = l['draw'] # exec defined our custom draw() func!

def register():
main()

def unregister():

if __name__ == "__main__":
register()


But the problem remains: if a second addon tries to do the same, it will revert what the first did, unless you write a system that solely manages adding stuff.

It is also possible to replace the UILayout type to intercept every draw operation, which also allows to "remove" stuff like operators or properties from interface. The problem is worse here however: if a second script tries to replace the UILayout type, the entire layout system will crash and all elements disappear from UI unless they are drawn by C code alone.

You can find an implementation here: