2025-07-01
This commit is contained in:
@@ -0,0 +1,161 @@
|
||||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
import logging
|
||||
import queue
|
||||
from typing import Tuple
|
||||
|
||||
import bpy
|
||||
from bpy.app.handlers import persistent
|
||||
|
||||
from . import utils
|
||||
|
||||
|
||||
bk_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@persistent
|
||||
def scene_load(context):
|
||||
if bpy.app.background is True:
|
||||
return
|
||||
if not (bpy.app.timers.is_registered(queue_worker)):
|
||||
bpy.app.timers.register(queue_worker)
|
||||
|
||||
|
||||
def get_queue():
|
||||
# we pick just a random one of blender types, to try to get a persistent queue
|
||||
t = bpy.types.Scene
|
||||
if not hasattr(t, "task_queue"):
|
||||
t.task_queue = queue.Queue()
|
||||
return t.task_queue
|
||||
|
||||
|
||||
class task_object:
|
||||
def __init__(
|
||||
self,
|
||||
command="",
|
||||
arguments=(),
|
||||
wait=0,
|
||||
only_last=False,
|
||||
fake_context=False,
|
||||
fake_context_area="VIEW_3D",
|
||||
):
|
||||
self.command = command
|
||||
self.arguments = arguments
|
||||
self.wait = wait
|
||||
self.only_last = only_last
|
||||
self.fake_context = fake_context
|
||||
self.fake_context_area = fake_context_area
|
||||
|
||||
|
||||
def add_task(
|
||||
task: Tuple,
|
||||
wait=0,
|
||||
only_last=False,
|
||||
fake_context=False,
|
||||
fake_context_area="VIEW_3D",
|
||||
):
|
||||
q = get_queue()
|
||||
taskob = task_object(
|
||||
task[0],
|
||||
task[1],
|
||||
wait=wait,
|
||||
only_last=only_last,
|
||||
fake_context=fake_context,
|
||||
fake_context_area=fake_context_area,
|
||||
)
|
||||
q.put(taskob)
|
||||
|
||||
|
||||
# @bpy.app.handlers.persistent
|
||||
def queue_worker():
|
||||
# utils.p('start queue worker timer')
|
||||
|
||||
# bk_logger.debug('timer queue worker')
|
||||
time_step = 0.3
|
||||
q = get_queue()
|
||||
# save some performance by returning early
|
||||
if q.empty():
|
||||
return time_step
|
||||
back_to_queue = [] # delayed events
|
||||
stashed = {}
|
||||
# first round we get all tasks that are supposed to be stashed and run only once (only_last option)
|
||||
# stashing finds tasks with the property only_last and same command and executes only the last one.
|
||||
while not q.empty():
|
||||
task = q.get()
|
||||
if task.only_last:
|
||||
# this now makes the keys not only by task, but also two arguments.
|
||||
# by now stashing is only used for ratings, where the first argument is url, second rating type.
|
||||
# This enables fast rating of multiple assets while allowing larger delay for uploading of ratings.
|
||||
# this avoids a duplicate request error on the server
|
||||
stashed[f"{task.command}-{task.arguments[0]}-{task.arguments[1]}"] = task
|
||||
else:
|
||||
back_to_queue.append(task)
|
||||
# return tasks to que except for stashed
|
||||
for task in back_to_queue:
|
||||
q.put(task)
|
||||
# return stashed tasks to queue
|
||||
for k in stashed.keys():
|
||||
q.put(stashed[k])
|
||||
# second round, execute or put back waiting tasks.
|
||||
back_to_queue = []
|
||||
while not q.empty():
|
||||
# print('window manager', bpy.context.window_manager)
|
||||
task = q.get()
|
||||
|
||||
if task.wait > 0:
|
||||
task.wait -= time_step
|
||||
back_to_queue.append(task)
|
||||
else:
|
||||
bk_logger.debug(
|
||||
"task queue task:" + str(task.command) + str(task.arguments)
|
||||
)
|
||||
try:
|
||||
if task.fake_context:
|
||||
fc = utils.get_fake_context(
|
||||
bpy.context, area_type=task.fake_context_area
|
||||
)
|
||||
if bpy.app.version < (4, 0, 0):
|
||||
task.command(fc, *task.arguments)
|
||||
else:
|
||||
with bpy.context.temp_override(**fc):
|
||||
task.command(*task.arguments)
|
||||
else:
|
||||
task.command(*task.arguments)
|
||||
except Exception as e:
|
||||
bk_logger.error(
|
||||
"task queue failed task:"
|
||||
+ str(task.command)
|
||||
+ str(task.arguments)
|
||||
+ str(e)
|
||||
)
|
||||
# bk_logger.exception('Got exception on main handler')
|
||||
# raise
|
||||
# print('queue while 2')
|
||||
for task in back_to_queue:
|
||||
q.put(task)
|
||||
# utils.p('end queue worker timer')
|
||||
return time_step
|
||||
|
||||
|
||||
def register():
|
||||
bpy.app.handlers.load_post.append(scene_load)
|
||||
|
||||
|
||||
def unregister():
|
||||
bpy.app.handlers.load_post.remove(scene_load)
|
||||
Reference in New Issue
Block a user