Where can I find infromation about the needed environment of operators

system-info.txt (16.9 KB)

If am developing an AddOn. Currently I have a problemd with
byp.ops.sound.mixdown

After using it I get this error:
Error: Ç´£Lq
Traceback (most recent call last):
File “C:\Users\hcjev\AppData\Roaming\Blender Foundation\Blender\2.83\scripts\addons\XMLGenerator\SceneOperators.py”, line 1060, in execute
bpy.ops.sound.mixdown(filepath=export_name)
File “C:\Users\hcjev\Programme\Blender Foundation\Blender\2_83\2.83\scripts\modules\bpy\ops.py”, line 201, in call
ret = op_call(self.idname_py(), None, kw)
RuntimeError

location: :-1

location: :-1

XMLGenerator\SceneOperators.py", line 1060 and the lines bfore:
bpy.context.window.scene = s
bpy.context.window.workspace = bpy.data.workspaces[“Layout”]
bpy.context.area.ui_type = ‘VIEW_3D’
if bpy.context.scene.name == s.name:
print ( bpy.context.scene.name, export_name )
bpy.ops.sound.mixdown(filepath=export_name)

Hi, you can run an operator once to see how it is registered in the info window - and then typically you would be able to copy it and change any parameter as needed. Unless some operators don’t work like this and require you to swap contexts (there is another technique to swap the current running context with another context and do a fake run) but this is another topic, I just mention it just to be sure.

Based on what I tested it worked good for me.

import bpy
bpy.ops.sound.mixdown(filepath="C:\\Users\\cconst\\Desktop\\test.ogg", relative_path=True, container='OGG', codec='FLAC', format='S16')

Thank you cconst,

I already did it this way. But as you mentione some operators do not work this way. How to find the right context to swap to was my problem. Can you help me with this problem

The example I put down here I could solve. It had to do with filename and directories.

Best Regards
Detlef

This is a part of some other script I made some time ago.

import bpy

class SaveRenderResult(bpy.types.Operator):
    bl_idname = "render.save_renderesult"
    bl_label = "Save Render Result"

    def execute(self, context):
        
        for area in context.screen.areas:
            if area.type == 'IMAGE_EDITOR':
                ctx = bpy.context.copy()
                ctx['area'] = area
                ctx['region'] = [x for x in area.regions if x.type == 'WINDOW'][0]
                bpy.ops.image.save_as(ctx, save_as_render=True, copy=True, filepath="//untitled.png", relative_path=True, show_multiview=False, use_multiview=False)
                print('done')
                return {'FINISHED'}
            
        print('could not find window area with render result')
        return {'FINISHED'}

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


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

try:
    unregister()
except:
    pass

register()

The thing to note is that you create copy of the current context but change only the area and region properties, to what the operator requires.

Since in this case the image.save_as is context protected it means that it should be called only from within the Image Editor. Typically a menu or a panel in this area.

P.S. You can consider that the entire operator in this case is a “wrapper” of the actual operator, that wants only to do only a fake context switch.

Thank you cconst,

I hope that helps. I understand that I have to loop through the areas and I have to switch to an appropriate workspace before.
Also thanks for updating my python knowledge in ths example:"[x for x in area.regions if x.type == ‘WINDOW’][0]"

Altough I am a developer for nearly 40 years, python is new to me and has some interresting short ways of using expessions in variable assignment.

Best Regards
Detlef

It’s worth pointing out also that sometimes you’ll get a failed context and won’t know why- in these situations I just open up Blender’s source and search for the operator’s poll function. With your development background this might also be something that you’d find beneficial. I keep a github mirror of the blender source available for easy searching, and since operators all follow a standard naming convention the code is pretty easy to find.

As an example, say I’m looking for the poll function for bpy.ops.mesh.bevel(), the registered name is going to be MESH_OT_bevel (operators are always OT, and the ‘namespace’ of the operator is always capitalized before the OT prefix). Searching the repository for MESH_OT_bevel yields three results: a header file, mesh_ops.c where the operator is registered with Blender, and the one I want in edit_mesh_bevel.c. Scrolling down past all of the static const definitions, I’m looking for ot->poll where the poll function pointer is being set (if you’re interested in seeing the code for the operator itself, that pointer is usually ot->exec or ot->invoke).

Once you have the function name, you can search for where it’s defined- it will be a bool return type and will usually not be in the same source file so you’ll need to search for it. In my case I found it here. Looking at the code I can see that it’s just checking to see if the object is in edit mode and that’s it- which means I could run it from the python console if I wanted without needing to create a context override for the area or whatever.

That all sounds over the top and complicated but in reality it takes about 15 seconds to track something down and understand what’s going on, and in the end you will have a better understanding of what makes Blender tick- which is always beneficial.

2 Likes

This part can be considered as “borrowing” an object instance to be used as a replacement.

In the example above there must be an Image Editor somewhere in the window, otherwise the operator fails. Which is a bit of a restriction. Now I can consider a better way to do this, though I haven’t tried it so far, there is another technique to create a dummy window, rather than having the user be aware of manually managing the areas, which means far better way for

import bpy

# show the user preferences
# (spawns a new window, pointing to the "user preferences")
bpy.ops.screen.userpref_show("INVOKE_DEFAULT")

# from the window manager get the last window
# * last window is the most recently created)
# * since the window is brand new it has no splits (1 area)
area = bpy.context.window_manager.windows[-1].screen.areas[0]

# change are to something
area.type = "IMAGE_EDITOR"

Check for list comprehensions examples, and generally for “functional programming patterns” in Python. The idea is to compact lines of code as much as possible, into one-liners when you have the chance. :slight_smile:

This is also a good thing to point out. At least that way you can double check any details. As in the case of the sound.mixdown operator.

https://github.com/blender/blender/search?q=sound.mixdown