Scene-per-tab / event handler for tab switching?

I work mostly with video, and often need to edit multiple scenes in the same file. For example, I might want to have two or three video clips in compositor tabs, then edit the composite outputs in individual video sequencer scenes, and then combine the edited sequences into a final render. So, I often have to switch tabs and then immediately switch scenes. It would be nice if the tabs could just each be associated with a scene.

I’m pretty good with python, but I’m a little lost in the blender API. Is there anything I can hook into to be notified when the tab changes, so that I can trigger the scene change? Also, is it even possible to switch the current scene from python?

Sorry if this is a total newbie question. :slight_smile: Doing it manually doesn’t show anything in the info window, and my google/grep skills are coming up a bit short.

Okay, so a tab is a workspace, and setting the current scene is easy enough:

 C.scene == D.scenes['sequence']            # ==> bool, but C.scene is read-only
 C.window.scene = D.scenes['composite']     # this works!

Getting the current workspace is easy:

 C.workspace == D.workspaces['Scripting']   # ==> bool, but C.workspace is read-only

… but if the following is still true, apparently there just isn’t an event triggered on change:

https://blender.stackexchange.com/questions/135968/how-can-i-catch-a-workspace-change-event?noredirect=1&lq=1

That thread seems to suggest polling, but python ought to let me do something like this, if I can find the right place to put it:

def set_workspace(self, ws):
    self._workspace = ws
    print('workspace is now: ', ws)
    
def get_workspace(self):
    return self._workspace

bpy.types.Window.workspace = property(get_workspace, set_workspace)
1 Like

You can use the msgbus submodule to set callbacks on rna changes.

import bpy

def rna_callback():
    print("Workspace changed")

def subscribe():
    handle = object()
    bpy.msgbus.subscribe_rna(
        key=(bpy.types.Window, "workspace"),
        owner=handle,
        args=(),  # Callback args
        notify=rna_callback)
    return handle

def unsubscribe(handle):
    bpy.msgbus.clear_by_owner(handle)

if __name__ == "__main__":
    bpy._handle = subscribe()
2 Likes

Wow, thanks, @kaio!! This whole thing is now way easier than I was expecting.

I’m pretty sure I can put all this together into an addon, and have it remember and restore the scene each time I change tabs.

So now I guess the next question is: what’s the right place to store this information, if I want it to persist when I save the .blend file?

Afaik the subscription lasts until you load a new file. If you want it to persist between files, you can write a function which reapplies the callback and append it to bpy.app.handlers.load_post. Handlers are interfaced as lists.

import bpy

# File load callback
@bpy.app.handlers.persistent  # Stay persistent across file loads
def load_rna_subscription(*args):
    bpy._handle = subscribe()

bpy.app.handlers.load_post.append(load_rna_subscription)

As for where to store the subscribe handle, python is persistent between files, so should be fine to store it in modules, classes, etc. In the example I’ve been using bpy._handle because I’m lazy, but it would make sense to store it somewhere in your future addon module.

1 Like

This looked promising:

C.workspace['my-custom-property'] = 'xyz'

… But that doesn’t seem to persist when I save and restart blender.

Not sure what you’re trying to store? Workspace name?

Generally, subclasses of bpy.types.ID allow serializing custom properties. There are some exceptions like the window manager and workspace which only allow runtime properties.

However, each workspace has its own screen. And screens allow storing custom properties.

import bpy

ws = bpy.context.workspace
scr = ws.screens.get(ws.name)
scr["string"] = "my string"

And to retrieve it:

import bpy

ws = bpy.context.workspace
scr = ws.screens.get(ws.name)
string = scr.get("string")
if string:
    print(string)
1 Like

Aha! If the rule is the workspace only allows runtime properties, then I guess there’s a documentation bug, but it feels more like a bug bug. :slight_smile:

Basically, for each workspace (or at least some workspaces), I want to store the scene it’s associated with, so that when I switch back to that workspace, it switches the current scene… Then I’ll be able to have a tab at the top for each scene.

If storing it on the screen rather than the workspace itself is the way to do that, then so be it. Thanks!

Probably is!

I’ve read the window manager at least is supposed to not save its properties. When a custom property is changed through the ui, blender pushes an undo if the property is stored in a serializable location. Some addons however leverage the fact that the window manager doesn’t store the properties, in order to avoid pushing an undo.