Why can't we simply assign simple script to hotkeys or buttons?

Maybe I am just too formatted by other softwares APIs, it seems impossible to make a button that just calls a function? Or add a hotkey that just call a script or a function?

Just today, I wanted a button that jumps to a specific frame. I can already type bpy.context.scene.frame_set(someFrame) in the console, so why not allow the user to do “just that” in either a UI element or a hotkey?

Other softwares usually have a simple structure like this:

makeButton(cmd="bpy.context.scene.frame_set(someFrame)", text="Jump!", icon='NEXT_KEYFRAME')

And that’s it, no questions asked. One line and it’s done. But here in Blender, in addition to the button, I had to create an operator, which itself need an IntProperty, and register all that to Blender.

Some softwares allow me to make a new hotkey in their hotkey editor and just assign bpy.context.scene.frame_set(someFrame) to it. Some even allow me to create full-fledged python scripts that can run alone without any registration of any sort. If you can do it in their script editor, you can do it in your UI elements and hotkeys.

Every post I found about the subject so far were answered by making either an operator or a bpy property.
Custom hotkeys are even worse in that way, because not only you need to make a custom operator, but you also need to store it in an addon.

But either solution is just a pain. It’s lines of code with more chances of messing up and bloating Blender with operators and addons that look so unnecessary to me.

3 Likes

hmm
there are some “execute script” operators I recall, perhaps they can take text as argument too ?

check out bpy. utils module

Or, if not a button, then why can’t we attach a script to an object or a collection, and run it every time the ‘target’ is appended or linked? The closest I could come to it is to define a bunch of custom properties and inputs for geometry node trees that I have to scroll to and set up manually every time I use one of my assets: I’m too lazy for that. :laughing:

https://devtalk.blender.org/t/gsoc-2020-custom-menus-weekly-reports/

and

https://developer.blender.org/T54862

Two of my personal absolute favorite still missing features that looked promising but have stalled for over two years now. :frowning:

Exactly! I know of at least 2 Applications that have such a simpler way-
you basicly don´t need any programming knowledge and its really unbelivably useful!
In Maya, you can simply create a button and Paste some .mel code in there- which make the button more or less into a macro. really simple and powerful.

and in Rhino 3d, you can create a Button, and in its optionbox you can also just write a few Lines of Rhinoscript making it into a macroeditor, or even tell rhino to execute a pytonscript from a location on your hd. so powerful.

soooo much easyier then Blender! I mean Blender can do all that, but you really need to know some programming basics- and its way more work…

So “+1” for a simple way of assigning macros/code to a Button in Blender!

@L0Lock as far as I can see an operator that executes a string-property as code provides this functionality. Did you try doing that?

I’m not sure of what you mean.

I guess I should show a real example.

I know it’s not always a good idea to always compare Blender to how other softwares do and the BF doesn’t want to just copy whatever other solutions do, but it so happens that the other software I have the most experience programming with, and in Python, is Maya. And it has some nice features I highly miss. Don’t take this as a person venting out frustration (even though I am frustrated), I just want to point out where I’m coming from. I don’t pretend everything Maya does is better nor that Blender should copy it nor that it’s an easy task to change anything in any direction.

Make a button for a Python addon

In Maya

If somewhere in an addon I wanted to just make a button that just sets the current scene frame to frame 10, here’s what it could look like in Maya (just for the “create a button that does this” part):

cmds.button(label="Set Frame 10", command="cmds.currentTime(10,edit=True)")

That’s it. cmds.button makes the button, and I tell what to execute via its command flag. I can write either a straight maya py command, or I could also define a python function containing what I want to do, and put the function name after command=. End of story. Simple and efficient.

Full code to just make such button somewhere, it’s just a matter of defining a function that creates ui elements and calling that function:

import maya.cmds as cmds

def showUI():
    myWin = cmds.window(title="A window where to put a button", widthHeight=(356,24))
    cmds.columnLayout()
    cmds.button(label="Set Frame 10", command="cmds.currentTime(10,edit=True)")
    cmds.showWindow(myWin)
showUI()

I have now a button that jumps to frame 10:

image

If I wanted to have such a button visible at startup without requiring to run it manually, the easiest way would be to instead use the shelf, which is an even easier solution I explain down bellow.

But otherwise, it’s as easy as putting your script in this file: %USERPROFILE%\documents\maya\<version>scripts\userSetup.py

But called via executeDeffered() like this:

import maya.cmds as cmds
from maya.utils import executeDeferred

def showUI():
    myWin = cmds.window(title="A window where to put a button", widthHeight=(356,24))
    cmds.columnLayout()
    cmds.button(label="Set Frame 10", command="cmds.currentTime(10,edit=True)")
    cmds.showWindow(myWin)
executeDeferred(showUI)

It remains somewhat simple and fast to do. But again: there’s an even simpler solution I detail below.

In Blender

If bpy was “a bit more like maya”, we could make such button like this:

self.layout.button(text="Jump to frame 10", command="bpy.context.scene.frame_current = 10")

And, in a full code, we need to make a form of UI through a python class that needs to be registered, like this sidebar panel (i used the simple panel template):

import bpy

class TEST_PT_Panel(bpy.types.Panel):
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'UI'
    bl_label = "SetFrame"
    bl_category = 'Item'

    def draw(self, context):
        layout = self.layout
        self.layout.button(text="Set frame 10", command="bpy.context.scene.frame_current = 10")

def register():
    bpy.utils.register_class(HelloWorldPanel)

def unregister():
    bpy.utils.unregister_class(HelloWorldPanel)

if __name__ == "__main__":
    register()

It’s a bit longer and trickier, but it’s still simple, right? Unfortunately: that isn’t possible AFAIK.

So first, our simple “do this” must be put in an operator that needs to be registered.

import bpy

class TEST_OT_setFrame10(bpy.types.Operator):
    bl_idname = "scene.set_frame_10"
    bl_label = "Set frame 10"

    def execute(self, context):
        main(context)
        bpy.context.scene.frame_current = 10
        return {'FINISHED'}

class TEST_PT_Panel(bpy.types.Panel):
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'UI'
    bl_label = "Set Frame"
    bl_category = 'Item'

    def draw(self, context):
        self.layout.operator("scene.set_frame_10", text="Set frame 10")

def register():
    bpy.utils.register_class(TEST_OT_setFrame10)
    bpy.utils.register_class(TEST_PT_Panel)

def unregister():
    bpy.utils.unregister_class(TEST_OT_setFrame10)
    bpy.utils.unregister_class(TEST_PT_Panel)

if __name__ == "__main__":
    register()

If I wanted to have such a button visible at startup without requiring to run it manually, I can either save it in the startup file and enable automatically runninf python files which is quite a security threat, or I must turn it into an addon.

Now the code looks like this:

bl_info = {
    "name": "Set Frame 10",
    "author": "Me",
    "version": (1, 0),
    "blender": (3, 3, 0),
    "location": "View3D > Sidebar > Sef Frame",
    "description": "Jump to frame 10",
    "warning": "",
    "doc_url": "",
    "category": "Animation",
}


import bpy

class TEST_OT_setFrame10(bpy.types.Operator):
    bl_idname = "scene.set_frame_10"
    bl_label = "Set frame 10"

    def execute(self, context):
        main(context)
        bpy.context.scene.frame_current = 10
        return {'FINISHED'}

class TEST_PT_Panel(bpy.types.Panel):
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'UI'
    bl_label = "Set Frame"
    bl_category = 'Item'

    def draw(self, context):
        self.layout.operator("scene.set_frame_10", text="Set frame 10")


def register():
    bpy.utils.register_class(TEST_OT_setFrame10)
    bpy.utils.register_class(TEST_PT_Panel)


def unregister():
    bpy.utils.unregister_class(TEST_OT_setFrame10)
    bpy.utils.unregister_class(TEST_PT_Panel)


if __name__ == "__main__":
    register()

Also, it’s easy to forget this when we know programming, but because bpy forces the user to use functions and classes very quickly for anything, it quite significantly raises the entry level for doing anything. Comparing to Maya where you can run a simple command very easilly, here you have to manage contexts, changing syntax and access violations.

Make a button without an addon

In Blender

You simply cant do that easily and safely.

If you want a custom button for custom things, the only way to do it is to learn Python and BPY and make yourself an addon, or waste time making long scripts and run them manually, or store them in file and enable auto script run which shouldn’t be required.
Making addons is fine, but two issues here: it’s trickier and time-consuming, even for people who know programming on Blender, and it’s not user-friendly. I mean, that solution is basically as saying “Blender has no solution, make it yourself”.

In Maya

Maya has a shelf where anyone can create buttons that can do different things. That’s where addon developers usually put buttons for their addons, kind of Blender’s viewport sidebar.
But more interesting for this topic: that’s where users create their own buttons, containing macros and “anything they often use”.

I.E. as an animator, I often have different cameras to switch to while working, so I made buttons that switch the active viewport to specific cameras. I also made buttons that essentially act as viewport presets to quickly switch certain things on and off in one click. All this never required me to make an addon, nor operators, nor manage registrations, nor nothing. Just plain “do this” in python or Mel (Maya’s native scripting language).

But back to my example: If I wanted to make such a shelf button that jumps to frame 10, here are the steps anyone knowing maya python would do:

  1. open the script editor
  2. write:
    import maya.cmds as cmds
    cmds.currentTime(10,edit=True)
    
  3. select all, and MMB drag it to the shelf

Now I have a shelf button that makes Maya jump to frame 10:

image

Let’s make it even harder: let’s pretend that I don’t know any programming, and thus need first to discover what code does the thing I want in a button:

  1. open the script editor, which also have a console that shows everything Maya does in Mel (maya’s native language):
    image

  2. set the current frame to 10 with the UI

  3. copy what showed up in the script editor: currentTime 10;

  4. MMB drag the selection to the shelf

I have now a button that makes Maya jump to frame 10 without ever needing to learn programming:

image

This illustrates another thing that Maya does nicely and I wish Blender did better:

Discoverability of scripting

Maya does a spectacular job at showing you how to make it do anything via scripts.

Blender does show up some things you do in the Info editor, but it shows so few things! Meanwhile in Maya:

Everything you do (every event from running a function to just opening a header menu), and even everything the program or an addon does, is prompted as Mel in the console. So that if you want to learn how to script anything in Maya, you can first do the thing with the UI and the Script Editor shows you exactly how it’s done in script.

Even if you are not a programmer and don’t intend to be, you can make so many things from macros to viewport presets to importing a character asset extremely easily thanks to this Script Editor that prompts everything and the toolshelf system that makes creating user content a child’s game.

A user completely scripting-agnostic can for example make a button that exports a selection of objects with a specific set of export options by just doing it once, then select-dragging what showed up in the console to the shelf.

That’s an insane level of user-friendlyness compared to Blender’s current state of “learn python and bpy and make an addon”, and for proof: almost everyone I know at work have such user made shelf buttons for anything they like, even though maybe five percent of them actually knows any scripting.

I’d also like to point out that the API’s doc is also IMHO slightly more helpful.
Taking the example of the currentTime command. Here is the python doc of it:

currentTime command (python)

What I love in Maya’s api docs is that you alsways find cool usage examples for almost everything. I can’t imagine the time spent for writing examples for even the most trivial of things, but man that helps so much!

Making custom keyboard shortcut

In Maya

I open the Hotkeys Editor:

On the right I can create “runtime commands”. Containing the simplest scripts to full plugins.

On the right, i can see any runtime command I created and assign a hotkey of my chosing.

On Blender

You can’t do it as is.

Again, I must first create an operator, then encapsulate it in an addon if I want to always have it present in a safe manner at startup, then I can set a custom hotkey from Blender’s keymap preferences. Or assign the hotkey from the script which is its own diffiulty.

What about using keymap editor directly? This works:

wm.context_set_int
scene.frame_current
10

It’s possible to change quite a lot of things with just keymap editor and calling things like wm.context_set_int, wm.context_set_float, wm.context_set_bool, wm.context_toggle, wm.context_set_enum directly. There are some cases where data is read only though and this will only work for very simple stuff.

I wish I knew that!

Though, some issues:

  • it looks like it can only set a value, you can’t make anything else, which other software can let you do.
  • it looks unintuitive to use. Think of any user that doesn’t know bpy or python, figuring out how to program anyting is hard on itself. But figuring out that bpy.context.scene.frame_current should be changed to scene.frame_current here, and firstly you should understand the concept of integers, floats, booleans, toggles, enums, and chose whatever you need as wm.context_set_<>.

It would be way simpler and more versatile if we were able to, for example:

  • paste a command as is
  • paste a path to a python file or a text datablock name that contains python code
  • set a custom name for the keymap entry

It’s little details like these that can make a whole lot of difference in what can be achieved, who can do it and in terms: how often people will do it.

Yeah, at the moment it’s kinda hidden functionality, but you can still figure things out without coding experience if you turn on python tooltips and info editor for logging.

Here is some useful use cases, they should go into 3D View (Global) category to work for both object and edit mode:

Changing 3d view grid scale (and perspective snapping increment as result) on the fly like Hammer editor:

image

wm.context_scale_float
space_data.overlay.grid_scale
10.0 and 0.1

Changing 3d gizmos size on fly (without going into preferences) like some other 3D apps:

wm.context_set_int
preferences.view.gizmo_size
relative yes
-20 and 20

Rotating material preview hdri like Substance Painer:

image

wm.context_set_float
space_data.shading.studiolight_rotate_z
relative yes
0.261799 and -0.261799

(0.261799 is 15 degrees in radians)

Switching to specific workspace by name directly without cycling through them:

image

wm.context_set_id
window.workspace
value: tab name

This one needs to go into Screen / Screen (Global) category to work.

The documentations say otherwise. :confused: That’s not good enough, IMHO. Where other softwares make so many things accessible to almost everyone, here we just expect people to learn programming for the slightest things. Which, to me, sounds contradictory: is Blender made for artists or programmers?

Except it actually logs so few things.


Also, I wish keymap customization was detailed on the keymap’s manual page so that anyone could learn about it:
Keymap — Blender Manual

Not on a different page in a corner named “Advanced”. Customizing your keymap shouldn’t be an “advanced” thing for “advanced” users, it should be basic. Keymap Customization — Blender Manual