# Rearranging elements of a panel

Is there a way to edit the position or hide some elements of an existing panel ? I know I can edit space_view3d.py but I prefer to do it properly.
right now i'm appending some labels and they appear at the bottom of the panel

I would like know if it's possible to insert them before some other elements of a panel

• You'd like to display your distance and median calculations at the top of the panel or in between the bool properties? – p2or Jul 5 '16 at 8:13
• @poor in between the bool properties – user2816 Jul 5 '16 at 8:19

The Blender interface is defined in Python scripts, and it is drawn in the order the code is executed. I don't believe there is a good way to 'insert' an UI element above an existing one once the code has moved on.

So to make an element show where you want, you just have to move the code to the proper location. In the case of Blender 2.77, it would be line 3366 of the space_view3d.py file.

• Editing of python files that come with blender is a bad suggestion. – p2or Jul 6 '16 at 16:32
• Agreed. I was looking for a quick and dirty solution. But the answers below is much better. – Mike Pan Jul 20 '16 at 4:47

Inserting UI elements in between existing properties is not supported at the moment. Also editing python files that come with blender is bad practice.

You can use append() or prepend() in order to add elements to the user interface of existing Panels, Headers and Menus:

import bpy

def draw_my_properties(self, context):
scene = context.scene
layout = self.layout

# display properties and values
col = layout.column(align=True)
col.label("My Values:")
col.prop(scene.render, "fps", text="FPS")
col.prop(scene, "frame_start", text="Frame Start")

def register():
# lets add the menu to the Mesh Display panel via append or prepend
bpy.types.VIEW3D_PT_view3d_meshdisplay.prepend(draw_my_properties)

def unregister():
bpy.types.VIEW3D_PT_view3d_meshdisplay.remove(draw_my_properties)

if __name__ == "__main__":
register()


However, here is some kind of hack based on this answer. Following Add-on code injects UI elements into the associated python file on the fly (space_view3d.py in this case). It's also equipped with a prepend() fallback if the specified line cannot be found:

bl_info = {
"name": "UI Elments In-Between",
"version": (0, 0, 1),
"blender": (2, 75, 0),
"location": "Edit Mode Property Panel (N) > Mesh Display",
"description": "",
"warning": "",
"wiki_url": "",
"tracker_url": "",
"category": "Development"}

import bpy

def draw_item(self, context):
layout = self.layout
scene = context.scene
layout.prop(scene.render, "fps", text="FPS")

class DrawFuncStore:
bpy_type = "VIEW3D_PT_view3d_meshdisplay"
bpy_type_class = getattr(bpy.types, bpy_type)
draw = None

insert_after = 'col.label(text="Normals:")'
insert_code  = '    col = layout.column(align=True)\n'\
'    col.label(text="My Values:")\n'\
'    col.prop(scene.render, "fps", text="Fps")\n'\
'    col.prop(scene, "frame_start", text="Frame Start")\n'

DrawFuncStore.draw = DrawFuncStore.bpy_type_class.draw
#module = bpy_type_class.__module__

filepath = DrawFuncStore.bpy_type_class.draw.__code__.co_filename
#print ("filepath: ", filepath )
if filepath == "<string>":
return
try:
file = open(filepath, "r")
except:
return

line_start = DrawFuncStore.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]]

# line offset
offset = -4

for i, line in enumerate(lines, 1):
if insert_after in line:
print("FOUND INSERT LINE")
lines.insert(i+offset, insert_code)
break
else:
return

# import bpy - hacky!
lines.insert(1, '    import bpy\n    from bpy.app.translations import contexts as i18n_contexts\n')

# Debug output
#f = open("/home/poor/Desktop/c.txt", "w").writelines(lines)
l = {}

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

#bpy_type_class.draw.__code__ = code_object # Doesn't work, since a single func is not a module
DrawFuncStore.bpy_type_class.draw = l['draw'] # exec defined our custom draw() func!

bpy.types.VIEW3D_PT_view3d_meshdisplay.prepend(draw_item)

if DrawFuncStore.draw is not None:
DrawFuncStore.bpy_type_class.draw = DrawFuncStore.draw
DrawFuncStore.draw = None

else:
bpy.types.VIEW3D_PT_view3d_meshdisplay.remove(draw_item)

def register():

def unregister():

if __name__ == "__main__":
register()


Note as @CoDEmanX mentioned:

This method only works as long as there's just one addon replacing the code object of a certain bpy type.

• thanks @poor, this is a better approach than editing the space_view3d.py file , also thanks for telling me about prepend() . – user2816 Jul 7 '16 at 10:37
• @root you are welcome! Since you've asked for the 'correct' solution, thats it for now. We can expand the answer, just let me know... – p2or Jul 7 '16 at 11:09

Editing the UI code can be a slippery slope.

I'd suggest overwriting the Panel classes draw method, or the Panel Class itself.

An example of overwriting the draw method

import bpy
#F8 will undo this change.
def f(self, context):
self.layout.label("Gone")

bpy.types.VIEW3D_PT_view3d_meshdisplay.draw = f

• thanks for this answer, having to redefine the draw method is probably overkill for something as simple as rearranging UI elements. What i thought was possible was having the class return a list of it's ui elements or maybe a way to iterate on them like nodes of a linked list, i can see now why this is not possible. – user2816 Jul 5 '16 at 18:22
• This is interesting to me, even though it's some time ago. I tried ti overwrite the draw method and it did do any thing. Do I need to reregistre or reload or something? Or is it different post 2.8? – DrDress Nov 26 '20 at 12:45