4
$\begingroup$

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

enter image description here

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

enter image description here

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

3 Answers 3

0
$\begingroup$

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.

enter image description here

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

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:

enter image description here

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():  
    # remove the menu
    bpy.types.VIEW3D_PT_view3d_meshdisplay.remove(draw_my_properties)

if __name__ == "__main__":
    register()

More information and Addon code: https://blender.stackexchange.com/a/34490/3710


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:

enter image description here

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

def insert_menu():
    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")
        lines = file.readlines()
    except:
        append_menu()
        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:
        append_menu()
        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!

def append_menu():
    bpy.types.VIEW3D_PT_view3d_meshdisplay.prepend(draw_item)

def remove_menu():
    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():
    insert_menu()

def unregister():
    remove_menu()

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.

$\endgroup$
2
  • 1
    $\begingroup$ thanks @poor, this is a better approach than editing the space_view3d.py file , also thanks for telling me about prepend() . $\endgroup$
    – user2816
    Jul 7, 2016 at 10:37
  • 3
    $\begingroup$ @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... $\endgroup$
    – p2or
    Jul 7, 2016 at 11:09
1
$\begingroup$

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
$\endgroup$
2
  • $\begingroup$ 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. $\endgroup$
    – user2816
    Jul 5, 2016 at 18:22
  • $\begingroup$ 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? $\endgroup$
    – DrDress
    Nov 26, 2020 at 12:45

You must log in to answer this question.