# Blender UI Multithreading Progressbar

The following question might've been asked several times, but I've found no satisfactory answer.

Short question: How can I show progress of background task in Blender UI?

Explanation: I have a long running task which I want to run in the background. I know there's some gotcha while using threads in python script, but since the task does not affect blender objects or data in any way, there's no reason the user should wait for it to complete (they can continue with other things in their pipeline).

My thread works perfectly fine without any crashes, and I can show the progress on console. However to show the progress in the UI I do the following:

• Have an integer/float property with subtype 'PERCENTAGE' which will be set to current progress. This might crash blender, since I'm actually modifying blender data. I've even tried this with some Blender versions and it kinda worked for pre-2.65. However it does not work for latest Blender builds.

• In recent Blender version, I've found a new api named window_manager.progress_begin, window_manager.progress_update and window_manager.progress_end. I'm not sure if they are really intended to show progress, and if that is the case, how to use them.

• EDIT: I've found an add-on template Always On Blank Render Engine by @Atom which seems to do the trick. It basically extends RenderEngine so all events can be captured. This is the first time I've tried it, and it can show progress similar to rendering progress (which is exactly what I want). However with a very quick test it seems that it'll also open a render preview window when the task is run, and also it shows the label as "Rendering" near the progressbar :-( Has anyone got any experience with extending RenderEngine?

Is there any option I can show progress in the UI? Or, is there any future plan regarding this?

• A bit of a hack could be to use BGL and draw a small overlay onto 3dview? Jun 14 '13 at 10:16
• Well.. good idea. But since it also accesses blender data I doubt it'll work from other thread. Jun 14 '13 at 10:45
• If you are happy with just a text-based progress bar at the top of the window, maybe this can help: dalaifelinto.com/?p=645 Jun 15 '13 at 3:51
• That's interesting! I'll take a look into that. Thanks @MikePan! Jun 15 '13 at 5:46

The general mechanism to solve UI thread safety issues is to pass data from the worker thread to the main thread and then update the UI in the main thread using that data.

In this case you would create a modal operator that gets called regularly with a timer, see Text Editor > Templates > Python > Operator Modal Timer for an example. Each time the operator modal function is called, you would check if the data has changed and update the UI accordingly.

How you pass the data between threads depends on the multithreading mechanism you are using. In Python there are a queue and multiprocessing.Queue modules that can be used with the threading and multiprocessing modules respectively. You put data in the queue in the worker thread and then get it out in the main thread.

• So updating the PERCENTAGE property seems the only way, isn't it? Jun 14 '13 at 13:33
• I don't know of a way to create a progress bar exactly like you get for rendering or baking physics from a Python script. Using a percentage property is one way to get something that looks similar, but you could also use a text label or do custom OpenGL drawing in the 3D view or timeline. Maybe someone else knows a better way to do it, I only intended to explain how you can safely update the UI when working with multiple threads. Jun 14 '13 at 15:08
• Actually I already knew about modal operators, but was confused that it'd crash due to reason I mentioned in the question. Your statement about queue is promising, and I'll test it now. Meanwhile I've also found a one more trick that we can use (which includes creating RenderEngine). I'll update my question or add new answer after a bit more research. Jun 14 '13 at 15:46
• This is the core of the answer! Even if I use any of the options I mentioned in question, I WILL have to manage my thread with this mechanism. Jun 15 '13 at 5:44

I've been able to create a progress bar in the top header for a long-running method. It looks as follows and it disappears once progress reaches 100%.

It does update a custom Scene 'PERCENTAGE' property, but using threading.Thread in combination with a timer-modal operator, I have not encountered any crashes.

This is the code I used:

ProgressWidget

import bpy
import time

class ProgressWidget(object):
update_every = 0.2 # seconds

widget_visible = False
last_updated = 0

@staticmethod
def update_widget(context, force=False):
sec_since_update = time.time() - ProgressWidget.last_updated

if not force and sec_since_update < ProgressWidget.update_every:
return

# Update the top header
for area in bpy.context.screen.areas:
if area.type == 'INFO':
area.tag_redraw()

ProgressWidget.last_updated = time.time()

@staticmethod
def draw(self, context):
if ProgressWidget.get_progress(context) < 100:

# Shows the custom progress property as a "bar"
self.layout.prop(context.scene, "ProgressWidget_progress", text="Progress", slider=True)

else:
ProgressWidget.hide()

@staticmethod
def create_progress_property():
bpy.types.Scene.ProgressWidget_progress = bpy.props.IntProperty(default=0, min=0, max=100, step=1, subtype='PERCENTAGE')

@staticmethod
def set_progress(context, value):
if ProgressWidget.widget_visible:
context.scene.ProgressWidget_progress = value

@staticmethod
def get_progress(context):
if ProgressWidget.widget_visible:
return context.scene.ProgressWidget_progress
else:
return 0

@staticmethod
def show(context):
if not ProgressWidget.widget_visible:
ProgressWidget.create_progress_property()

bpy.app.handlers.scene_update_pre.append(ProgressWidget.update_widget)

ProgressWidget.widget_visible = True

ProgressWidget.set_progress(context, 0)

@staticmethod
def hide():
bpy.app.handlers.scene_update_pre.remove(ProgressWidget.update_widget)

ProgressWidget.widget_visible = False

# Creates a widget simulator in the text editor side panel
# The code below is only used for widget development

class CUSTOM_PT_testPanel(bpy.types.Panel):
"""Adds a custom panel to the TEXT_EDITOR"""
bl_idname = 'TEXT_PT_testPanel'
bl_space_type = "TEXT_EDITOR"
bl_region_type = "UI"
bl_label = "Progress Simulator"

def draw(self, context):
self.layout.operator("custom.show_progress_widget")
self.layout.operator("custom.make_progress")

class CUSTOM_OT_show_progress_widget(bpy.types.Operator):
bl_idname = "custom.show_progress_widget"
bl_label="Show Widget in Header"

def execute(self, context):

ProgressWidget.show(context) # Call this to start showing progress

return {'FINISHED'}

class CUSTOM_OT_make_progress(bpy.types.Operator):
bl_idname = "custom.make_progress"
bl_label="Make progress"

def execute(self, context):
new_progress_value = (ProgressWidget.get_progress(context) + 25) % 125

# This line will update the progress widget
# call it from your long-running modal operator (0-100)
# When progress is 100 or more, the widget disappears
ProgressWidget.set_progress(context, new_progress_value)

return {'FINISHED'}

if __name__ == "__main__":
bpy.utils.register_class(CUSTOM_PT_testPanel)
bpy.utils.register_class(CUSTOM_OT_show_progress_widget)
bpy.utils.register_class(CUSTOM_OT_make_progress)



Tested with a Timer Modal Operator + Long Running Background Thread:

import bpy

class ModalTimerOperator(bpy.types.Operator):
"""Operator which runs its self from a timer"""
bl_idname = "wm.modal_timer_operator"
bl_label = "Modal Timer Operator"

_timer = None
th = None
prog = 0
stop_early = False

def modal(self, context, event):
if event.type in {'RIGHTMOUSE', 'ESC'}:
self.cancel(context)

self.stop_early = True
self.th.join()
print('DONE EARLY')

return {'CANCELLED'}

if event.type == 'TIMER':
context.scene.ProgressWidget_progress = self.prog

if not self.th.isAlive():
self.th.join()
print('DONE')
return {'FINISHED'}

return {'PASS_THROUGH'}

def execute(self, context):

import time
for i in range(10):
if self.stop_early:
return

time.sleep(.5)
self.prog += 10

bpy.ops.custom.show_progress_widget()
self.th.start()

wm = context.window_manager
self._timer = wm.event_timer_add(0.1, context.window)
return {'RUNNING_MODAL'}

def cancel(self, context):
wm = context.window_manager
wm.event_timer_remove(self._timer)

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

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

if __name__ == "__main__":
register()

# test call
bpy.ops.wm.modal_timer_operator()


• Hi there, so far I see, your ProgressWidget is exactly what I'm looking for. On blender 2.90.1 it does not run, because the methode scene_update_pre is unknown. Do you know how to change it to work on 2.90.1? I would like to use the progress bar placed on space type 'VIEW_3D' in region 'UI'. If i change 'bpy.types.INFO_HT_header.append(ProgressWidget.draw)' with the id of my panel it shows up at the right place. I guess I have to modify if area.type == 'INFO:' to if area.type == 'VIEW_3D':. Is there other changes necessary? Oct 16 '20 at 11:35
• @Daniel I can't test right now, but it looks like using depsgraph_update_pre might work: devtalk.blender.org/t/… Oct 16 '20 at 11:55
• I modified scene_update_pre but during the other code is working I can't see any change of the progress bar. It shows only at the end the correct value. I'm importing several stl files and create meshes from them and at the beginning of each step your command bpy.ops.custom.make_progress() is called. Oct 16 '20 at 12:24
• hi there. I've read your solution again, and I think I did not understand it well. I've seen you on arc.dev, and I would like to offer you to implement your code in my script. Are you interested? Then I'm going to contact you on LinkedIn. Oct 18 '20 at 19:55
• Hi @Daniel, Sorry, but at the moment, my schedule is completely full. I would recommend you post the offer on blenderartists.org/c/jobs/paid-work/53 Oct 19 '20 at 10:26