blendercam/scripts/addons/cam/async_op.py

106 wiersze
3.5 KiB
Python
Czysty Zwykły widok Historia

2024-01-10 11:15:10 +00:00
import bpy
import sys
import types
2024-01-10 11:15:10 +00:00
@types.coroutine
def progress_async(text, n=None, value_type='%'):
2024-01-10 11:15:10 +00:00
"""function for reporting during the script, works for background operations in the header."""
throw_exception = yield ('progress', {'text': text, 'n': n, "value_type": value_type})
2024-01-10 11:15:10 +00:00
if throw_exception is not None:
raise throw_exception
2024-01-10 11:15:10 +00:00
class AsyncCancelledException(Exception):
pass
2024-01-10 11:15:10 +00:00
class AsyncOperatorMixin:
def __init__(self):
self.timer = None
self.coroutine = None
self._is_cancelled = False
2024-01-10 11:15:10 +00:00
def modal(self, context, event):
2024-01-26 15:02:54 +00:00
if bpy.app.background:
return {'PASS_THROUGH'}
2024-01-10 11:15:10 +00:00
if event.type == 'TIMER':
try:
if self.tick(context):
return {'RUNNING_MODAL'}
else:
context.window_manager.event_timer_remove(self.timer)
bpy.context.workspace.status_text_set(None)
return {'FINISHED'}
except Exception as e:
context.window_manager.event_timer_remove(self.timer)
bpy.context.workspace.status_text_set(None)
self.report({'ERROR'}, str(e))
2024-01-10 11:15:10 +00:00
return {'FINISHED'}
elif event.type == 'ESC':
self._is_cancelled = True
2024-01-10 11:15:10 +00:00
self.tick(context)
context.window_manager.event_timer_remove(self.timer)
bpy.context.workspace.status_text_set(None)
return {'FINISHED'}
if 'BLOCKING' in self.bl_options:
return {'RUNNING_MODAL'}
else:
return {'PASS_THROUGH'}
def show_progress(self, context, text, n, value_type):
2024-01-10 11:15:10 +00:00
if n is not None:
2024-01-10 14:29:44 +00:00
progress_text = f"{text}: {n:.2f}{value_type}"
2024-01-10 11:15:10 +00:00
else:
progress_text = f"{text}"
2024-01-10 14:29:44 +00:00
bpy.context.workspace.status_text_set(progress_text + " (Press ESC to cancel)")
2024-01-10 11:15:10 +00:00
sys.stdout.write(f"Progress: {progress_text}\n")
sys.stdout.flush()
def tick(self, context):
if self.coroutine == None:
self.coroutine = self.execute_async(context)
2024-01-10 11:15:10 +00:00
try:
if self._is_cancelled:
(msg, args) = self.coroutine.send(AsyncCancelledException("Cancelled with ESC key"))
2024-01-10 11:15:10 +00:00
raise StopIteration
else:
(msg, args) = self.coroutine.send(None)
if msg == 'progress':
self.show_progress(context, **args)
2024-01-10 11:15:10 +00:00
else:
sys.stdout.write(f"{msg},{args}")
return True
except StopIteration:
return False
2024-01-26 15:02:54 +00:00
except Exception as e:
print("Exception thrown in tick:", e)
2024-01-10 11:15:10 +00:00
def execute(self, context):
2024-01-10 16:06:13 +00:00
if bpy.app.background:
# running in background - don't run as modal,
# otherwise tests all fail
while self.tick(context) == True:
2024-01-10 16:06:13 +00:00
pass
return {'FINISHED'}
else:
self.timer = context.window_manager.event_timer_add(.001, window=context.window)
2024-01-10 16:06:13 +00:00
context.window_manager.modal_handler_add(self)
return {'RUNNING_MODAL'}
2024-01-10 11:15:10 +00:00
class AsyncTestOperator(bpy.types.Operator, AsyncOperatorMixin):
2024-01-10 11:15:10 +00:00
"""test async operator"""
bl_idname = "object.cam_async_test_operator"
bl_label = "Test operator for async stuff"
bl_options = {'REGISTER', 'UNDO', 'BLOCKING'}
2024-01-10 11:15:10 +00:00
async def execute_async(self, context):
2024-01-10 11:15:10 +00:00
for x in range(100):
await progress_async("Async test:", x)
2024-01-10 11:15:10 +00:00
# bpy.utils.register_class(AsyncTestOperator)