Drawing in blender from a Thread

Hello,

I’m trying to draw a cube using blender function (bpy.ops.mesh.primitive_cube_add) from a thread launched in an operator. When executing the operator I got an error saying “context is incorrect”. I’ve tryed forcing the context to VIEW_3D (self.context.area.type = “VIEW_3D”) but I got an other error saying that area is of type NONE.

The question is very simple, does any one know if it is possible to draw meshes in blender from a thread?

Thank you for your reponse.

The ‘context’ is closely related to the active screen in Blender. If you are running your script from the command line then there is no context for your program to edit. Besides that, you next best level of control is to have the mouse cursor in the correct window when your script is running.

If all else fails, have you tried adding the mesh directly to the scene data instead of trying to use an operator?

I did a short video showing some of the things you can do to Blender inside a thread. In my example, I do not actually add an object to the scene (that is what you are calling drawing but it is really object creation), however. I just modify existing objects, like the camera.

https://vimeo.com/21276947

Very interesting video. Thank you.

I’ve tryed reading blender scene properties like object list, etc and that works fine in the thread. The problem only came when I try to add a mesh to the scene. This is the code I’m using if someone want to try something different:


import os
from threading import Thread

class Thread_1(Thread):
    def __init__(self, context):
        Thread.__init__(self)
        self.context = context
    
    def run(self):
        time.sleep(5)
        print("Inside the THREAD")
        print(bpy.data.objects)
        #tmw_estado = bpy.context.area.type
        print(bpy.context)
        print(self.context)
        #self.context.area.type = "VIEW_3D"
        bpy.ops.mesh.primitive_cube_add(view_align=False, enter_editmode=False, location=(1.9524, 2.66758, 0.82796), rotation=(0, 0, 0), layers=(True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False))
        time.sleep(10)

# Calling the THREAD somewhere in the code
thread1 = Thread_1(context)
thread1.start()




A thread can not use the context or bpy.ops. The context can not be passed or faked.
So you must create the cube directly by specifying coordinates within bpy.data.

Brecht (the guy behind cycles and many other uber cool things) made a recent commit out of a request , now its possible to pass context as a argument to an operator and so its possible from a single context or even no context at all to call all operators. Beware the awesomness of blender development unfolds before your eyes :smiley:

http://www.pasteall.org/31435/python

This commit is in trunk and already usable. Old way remains too so that argument is optional.

Nice to know. I would be surprised if it works reliably from within a thread, however.

Also, did you notice in the code…he is using the context to fetch from the window manager. But if you are in a thread or rendering, that will not work. And if you already have a context why would you need to fake one…?

So what about this usage…
I have a blank scene and I want to use bpy.ops to create a cube in the render_pre event which has no context. The expected result should be that I see a cube rendered.

Brecht’s method does not seem to work. I just get a screen has no area attribute error.


import bpy
   
def renderPre(passedScene):
    window = bpy.context.window_manager.windows[0]
    screen = window.screen
    area = screen.areas[1]
     
    ctx = {'window': window, 'screen': screen, 'area': area}

    print(ctx)
    print(dir(ctx))
    ctx.area.type = "VIEW_3D"
    bpy.ops.mesh.primitive_cube_add(view_align=False, enter_editmode=False, location=(1.9524, 2.66758, 0.82796), rotation=(0, 0, 0), layers=(True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False))

            
bpy.app.handlers.render_pre.append(renderPre)

Brecht method does not seem to work because you dont use Brecht method. If you did your code would passing ctx as first parameter of your operator :wink:

Sometimes you need the context to setup a bpy.ops operation. Which is what I was trying to do with the cube create.

But do you see the flaw in referencing the context to create a fake context…?

window = bpy.<b>context</b>.window_manager.windows[0]

context exists as an object AFAIK always, its the area that is not there in some circumstances hence the problem that Brech solved. I think the above code just references the first blender window.

The guy that did the request was trying to make a background script trigger operators for batch processing rendering and he could not do it because of lack of areas and after the commit he could, so you would better ask brecht himself why this is works and when it does not.

All I know is that your above code does not follow Brechts method cause it should have been

bpy.ops.mesh.primitive_cube_add(ctx, view_align=False, enter_editmode=False, location=(1.9524, 2.66758, 0.82796), rotation=(0, 0, 0), layers=(True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False))

For the rest Brecht is your man, if you still cant do it and is actually a bug or ommision contact him to add it I am sure he wont say no :wink:

Thank you both for your imformation about the context! Unfortunately I’m working with older version of blender and I’m not going to be able to use new operators. So I keep what Atoms says that “The thread can’t use the context of bpy.ops”.

Thank you very much again!