4
$\begingroup$

I am able to change the text in the 2.80 status bar with:

bpy.types.WorkSpace.status_text_set("My Text")

Reset with:

bpy.types.WorkSpace.status_text_set(None)

But how do I set the nice little icons??

enter image description here

$\endgroup$
1
  • $\begingroup$ I couldn't find an answer to this one but you may be able to use Unicode characters. I'll keep looking $\endgroup$
    – Moog
    Oct 17, 2019 at 12:42

1 Answer 1

6
$\begingroup$

The bpy.types.WorkSpace.status_text_set() function can only display a plain text string in the status bar. The string is displayed with the highest priority - "on top of" any keymap tips.

It is possible to display the usage tips for first 3 mouse buttons (press and drag event for each button) by registering a proper keymap. Here's an example:

import bpy


class ClickOperator(bpy.types.Operator):
    bl_idname = "object.click_operator"
    bl_label = "My Click Operator"

    def execute(self, context):
        print('clicked')
        return {'FINISHED'}

class DragOperator(bpy.types.Operator):
    bl_idname = "object.drag_operator"
    bl_label = "My Drag Operator"

    def execute(self, context):
        print('drag started')
        return {'FINISHED'}


addon_keymaps = []


def register():
    bpy.utils.register_class(ClickOperator)
    bpy.utils.register_class(DragOperator)

    wm = bpy.context.window_manager
    km = wm.keyconfigs.addon.keymaps.new(name='Object Mode', space_type='EMPTY')
    kmi = km.keymap_items.new(ClickOperator.bl_idname, 'RIGHTMOUSE', 'PRESS', ctrl=True, alt=True)
    kmi = km.keymap_items.new(DragOperator.bl_idname, 'EVT_TWEAK_R', 'ANY', ctrl=True, alt=True)
    addon_keymaps.append(km)


def unregister():
    bpy.utils.unregister_class(ClickOperator)
    bpy.utils.unregister_class(DragOperator)

    wm = bpy.context.window_manager
    for km in addon_keymaps:
        wm.keyconfigs.addon.keymaps.remove(km)
    del addon_keymaps[:]


if __name__ == "__main__":
    register()

Press "Ctrl+Alt" in object mode to see the keymap tips.

The cursor keymap is displayed with the lowest priority - anything else overrides it. Some documentation on how to register keymaps may be found here: Blender Addon Tutorial - Keymap.

Apart from the plain text and cursor keymap, the statusbar can display a modal keymap. For example, if you press "G" in the 3D View, the keymap for the "Grab" mode will be displayed, showing not only the mouse buttons, but all the other keys used in that mode. The modal keymap has a priority over the cursor keymap, but can be overridden by the text output (bpy.types.WorkSpace.status_text_set()). It is possible to create custom modal operators in Blender Python, as well as registering additional keymaps for the existing modes. But I'm not aware of any way to register a modal keymap for a custom modal operator. Not sure if it's a bug or design decision, or just something I don't know how to use. The catch is with bpy.types.KeyMapItems.new_modal(propvalue, type, value, any=False, shift=False, ctrl=False, alt=False, oskey=False, key_modifier='NONE') and what to pass as propvalue parameter for a custom modal operator. Anyway, creating of unnecessary modal operators is considered a bad practice: Blender UI Paradigms - Non Modal.

Other than in a cursor or modal keymap, there's no supported way of sticking a hotkey tip into the statusbar. This is, I believe by design, as there's no place for everything, and showing some random hotkeys will just be confusing.


If you're interested in how exactly this is handled in the source code, below is a starting point.

When the bpy.types.WorkSpace.status_text_set() python function is executed, the ED_workspace_status_text(bContext *C, const char *str) function of area.c get's called:

void ED_workspace_status_text(bContext *C, const char *str)
{
  wmWindow *win = CTX_wm_window(C);
  WorkSpace *workspace = CTX_wm_workspace(C);

  /* Can be NULL when running operators in background mode. */
  if (workspace == NULL) {
    return;
  }

  if (str) {
    if (workspace->status_text == NULL) {
      workspace->status_text = MEM_mallocN(UI_MAX_DRAW_STR, "headerprint");
    }
    BLI_strncpy(workspace->status_text, str, UI_MAX_DRAW_STR);
  }
  else if (workspace->status_text) {
    MEM_freeN(workspace->status_text);
    workspace->status_text = NULL;
  }

  /* Redraw status bar. */
  for (ScrArea *sa = win->global_areas.areabase.first; sa; sa = sa->next) {
    if (sa->spacetype == SPACE_STATUSBAR) {
      ED_area_tag_redraw(sa);
      break;
    }
  }
}

which basically just sets workspace->status_text to the passed parameter, and invokes the area redraw.

The function responsible for the statusbar tips output is void uiTemplateInputStatus(uiLayout *layout, struct bContext *C) from interface_templates.c:

void uiTemplateInputStatus(uiLayout *layout, struct bContext *C)
{
  wmWindow *win = CTX_wm_window(C);
  WorkSpace *workspace = CTX_wm_workspace(C);

  /* Workspace status text has priority. */
  if (workspace->status_text) {
    uiItemL(layout, workspace->status_text, ICON_NONE);
    return;
  }

  if (WM_window_modal_keymap_status_draw(C, win, layout)) {
    return;
  }

  /* Otherwise should cursor keymap status. */
  for (int i = 0; i < 3; i++) {
    uiLayout *box = uiLayoutRow(layout, false);
    uiLayout *col = uiLayoutColumn(box, false);
    uiLayout *row = uiLayoutRow(col, true);
    uiLayoutSetAlignment(row, UI_LAYOUT_ALIGN_LEFT);

    const char *msg = WM_window_cursor_keymap_status_get(win, i, 0);
    const char *msg_drag = WM_window_cursor_keymap_status_get(win, i, 1);

    if (msg || (msg_drag == NULL)) {
      uiItemL(row, msg ? msg : "", (ICON_MOUSE_LMB + i));
    }

    if (msg_drag) {
      uiItemL(row, msg_drag, (ICON_MOUSE_LMB_DRAG + i));
    }

    /* Use trick with empty string to keep icons in same position. */
    row = uiLayoutRow(col, false);
    uiItemL(row, "                                                                   ", ICON_NONE);
  }
}

which, in case there is a status text (set by ED_workspace_status_text), just shows it, overriding everything else and returns. Then it calls WM_window_modal_keymap_status_draw(C, win, layout) which checks if there is a modal keymap to output and displays it. In case there was, we return. Otherwise our function outputs the cursor keymap - first 3 mouse buttons, press and drag.

$\endgroup$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .