Creating a valid context for uv editing

Hey

I have a bit of an odd problem…

I have a threaded process that once finish will push a job/function to queue that is being executed by blender persistent handler :

@persistent
def load_handler(dummy):
    bpy.app.timers.register(blenderModuleTest.runFunctions)

def register():
    bpy.app.handlers.load_post.append(load_handler)

The runFunction just grabs item from queue and runs it ()

The problem I have is that the function is meant to apply automatic uv to selected meshes. Even tho I can get the selection/etc/etc I end up with many errors. All of them state that I’m in “Wrong context” Some tries resulted in >

geometry.py", line 233, in internalUvSelected
    bpy.ops.object.mode_set(mode='EDIT')
  File "C:\Program Files\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: Operator bpy.ops.object.mode_set.poll() failed, context is incorrect

The path of error above is as follows>

                        ob.select_set(state=True)
                        bpy.context.view_layer.objects.active = ob
                        bpy.ops.object.mode_set(mode='EDIT')
                        bpy.ops.mesh.select_all(action='SELECT')
                        bpy.ops.uv.smart_project()

if I just call uv.smart_project()

I get this lovely error log :slight_smile:

Context : {'area': bpy.data.screens['Layout']...Area, 'blend_data': <bpy_struct, BlendData at 0x000002AC1926C3A8>, 'collection': bpy.data.collections['Collection'], 'engine': 'BLENDER_EEVEE', 'evaluated_depsgraph_get': <bpy_func Context.evaluated_depsgraph_get()>, 'gizmo_group': None, 'layer_collection': bpy.data.scenes['Scene']...LayerCollection, 'mode': 'OBJECT', 'preferences': <bpy_struct, Preferences at 0x00007FF761A34BA0>, 'region': bpy.data.screens['Layout']...Region, 'region_data': None, 'scene': bpy.data.scenes['Scene'], 'screen': bpy.data.screens['Layout'], 'space_data': None, 'tool_settings': bpy.data.scenes['Scene'].tool_settings, 'view_layer': bpy.data.scenes['Scene'].view_layers["View Layer"], 'window': bpy.data.window_managers['WinMan']...Window, 'window_manager': bpy.data.window_managers['WinMan'], 'workspace': None}

Traceback (most recent call last):
 File "C:\Program Files\Blender Foundation\Blender 2.83\2.83\scripts\startup\bl_operators\uvcalc_smart_project.py", line 1034, in poll
   return context.active_object is not None
AttributeError: 'Context' object has no attribute 'active_object'

location: C:\Program Files\Blender Foundation\Blender 2.83\2.83\scripts\modules\bpy\ops.py:201

location: C:\Program Files\Blender Foundation\Blender 2.83\2.83\scripts\modules\bpy\ops.py:201
Traceback (most recent call last):
 File "C:\Program Files\Blender Foundation\Blender 2.83\2.83\scripts\startup\bl_operators\uvcalc_smart_project.py", line 1034, in poll
   return context.active_object is not None
AttributeError: 'Context' object has no attribute 'active_object'

location: C:\Program Files\Blender Foundation\Blender 2.83\2.83\scripts\modules\bpy\ops.py:201

location: C:\Program Files\Blender Foundation\Blender 2.83\2.83\scripts\modules\bpy\ops.py:201
Traceback (most recent call last):
 File "C:\Program Files\Blender Foundation\Blender 2.83\2.83\scripts\startup\bl_operators\uvcalc_smart_project.py", line 1042, in execute
   self.stretch_to_bounds,
 File "C:\Program Files\Blender Foundation\Blender 2.83\2.83\scripts\startup\bl_operators\uvcalc_smart_project.py", line 746, in main
   ob for ob in context.selected_editable_objects
AttributeError: 'Context' object has no attribute 'selected_editable_objects'

location: C:\Program Files\Blender Foundation\Blender 2.83\2.83\scripts\modules\bpy\ops.py:201

location: C:\Program Files\Blender Foundation\Blender 2.83\2.83\scripts\modules\bpy\ops.py:201
Error: Traceback (most recent call last):
 File "C:\Program Files\Blender Foundation\Blender 2.83\2.83\scripts\startup\bl_operators\uvcalc_smart_project.py", line 1042, in execute
   self.stretch_to_bounds,
 File "C:\Program Files\Blender Foundation\Blender 2.83\2.83\scripts\startup\bl_operators\uvcalc_smart_project.py", line 746, in main
   ob for ob in context.selected_editable_objects
AttributeError: 'Context' object has no attribute 'selected_editable_objects'

location: C:\Program Files\Blender Foundation\Blender 2.83\2.83\scripts\modules\bpy\ops.py:201

Traceback (most recent call last):
 File "C:\Program Files\Blender Foundation\Blender 2.83\2.83\scripts\addons\renderManager\blenderMenuGUI.py", line 45, in runInternalFastTimer
   executeInternalFastCommands()
 File "C:\Insignia\Blender\renderManager\libRM\gui\mainWindow.py", line 829, in executeInternalFastCommands
   function[0](function[1])  # [function,[arguments]]
 File "C:\Insignia\Blender\renderManager\libRM\geometry\geometry.py", line 236, in internalUvSelected
   bpy.ops.uv.smart_project()
 File "C:\Program Files\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: Error: Traceback (most recent call last):
 File "C:\Program Files\Blender Foundation\Blender 2.83\2.83\scripts\startup\bl_operators\uvcalc_smart_project.py", line 1042, in execute
   self.stretch_to_bounds,
 File "C:\Program Files\Blender Foundation\Blender 2.83\2.83\scripts\startup\bl_operators\uvcalc_smart_project.py", line 746, in main
   ob for ob in context.selected_editable_objects
AttributeError: 'Context' object has no attribute 'selected_editable_objects'

I’m out of ideas speend on it weeks now.

How can I get a valid/proper context so that I can apply uv ?

As a more “clean” example of the issue here is some more “code” that I’m not quite sure how to properly execute to test… but it shows the intent/workflow>

import bpy
import queue
from bpy.app.handlers import persistent
import threading




### Thread save queue for job list to run
execution_queue = queue.Queue()


### Test job
def makeUV():
    bpy.ops.uv.smart_project()




### Get Job & run it
def executeInternalFastCommands():
    while execution_queue.empty():
        f = execution_queue.get()
        f()# run all functions from queue
    pass


### Make sure we call it every 0.05 seconds
def runInternal():
    executeInternalFastCommands()
    return 0.05



### Register timmer/function caller
@persistent
def load_handler(dummy):
    bpy.app.timers.register(runInternal)



def register():
    bpy.app.handlers.load_post.append(load_handler)
    
    
    
    
### Send job from Thread
def createTestActionInThread():
    execution_queue.put(makeUV) # add job to queue that blender will run in its main thread


### Start thread and ask it to create a job for us
def testAction():
    thread = threading.Thread(target=createTestActionInThread, name='Daemon')
    thread.daemon = True
    thread.start()
    

if __name__ == "__main__":
    register()
    testAction()