Files
blender-addons/blenderkit/bg_blender.py
Vilém Duha a18b93cc72 BlenderKit: Fix timers failing.
In some cases timers got unregistered, not sure yet why.
This checks if timers are fine regularly.
Also fixes unregistration, that sometimes did throw an error.
2019-11-10 19:58:22 +01:00

243 lines
8.0 KiB
Python

# ##### 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 #####
if "bpy" in locals():
from importlib import reload
utils = reload(utils)
else:
from blenderkit import utils
import bpy
import sys, threading, os
import re
from bpy.props import (
EnumProperty,
)
bg_processes = []
class threadCom: # object passed to threads to read background process stdout info
''' Object to pass data between thread and '''
def __init__(self, eval_path_computing, eval_path_state, eval_path, process_type, proc, location=None, name=''):
# self.obname=ob.name
self.name = name
self.eval_path_computing = eval_path_computing # property that gets written to.
self.eval_path_state = eval_path_state # property that gets written to.
self.eval_path = eval_path # property that gets written to.
self.process_type = process_type
self.outtext = ''
self.proc = proc
self.lasttext = ''
self.message = '' # the message to be sent.
self.progress = 0.0
self.location = location
self.error = False
self.log = ''
def threadread(tcom):
'''reads stdout of background process, done this way to have it non-blocking. this threads basically waits for a stdout line to come in, fills the data, dies.'''
found = False
while not found:
inline = tcom.proc.stdout.readline()
# print('readthread', time.time())
inline = str(inline)
s = inline.find('progress{')
if s > -1:
e = inline.find('}')
tcom.outtext = inline[s + 9:e]
found = True
if tcom.outtext.find('%') > -1:
tcom.progress = float(re.findall('\d+\.\d+|\d+', tcom.outtext)[0])
return
if s == -1:
s = inline.find('Remaining')
if s > -1:
# e=inline.find('}')
tcom.outtext = inline[s: s + 18]
found = True
return
if len(inline) > 3:
print(inline, len(inline))
# if inline.find('Error'):
# tcom.error = True
# tcom.outtext = inline[2:]
def progress(text, n=None):
'''function for reporting during the script, works for background operations in the header.'''
# for i in range(n+1):
# sys.stdout.flush()
text = str(text)
if n is None:
n = ''
else:
n = ' ' + ' ' + str(int(n * 1000) / 1000) + '% '
spaces = ' ' * (len(text) + 55)
sys.stdout.write('progress{%s%s}\n' % (text, n))
sys.stdout.flush()
@bpy.app.handlers.persistent
def bg_update():
'''monitoring of background process'''
text = ''
s = bpy.context.scene
global bg_processes
if len(bg_processes) == 0:
return 2
for p in bg_processes:
# proc=p[1].proc
readthread = p[0]
tcom = p[1]
if not readthread.is_alive():
readthread.join()
# readthread.
if tcom.error:
estring = tcom.eval_path_computing + ' = False'
exec(estring)
tcom.lasttext = tcom.outtext
if tcom.outtext != '':
tcom.outtext = ''
estring = tcom.eval_path_state + ' = tcom.lasttext'
exec(estring)
# print(tcom.lasttext)
if 'finished successfully' in tcom.lasttext:
bg_processes.remove(p)
estring = tcom.eval_path_computing + ' = False'
exec(estring)
else:
readthread = threading.Thread(target=threadread, args=([tcom]), daemon=True)
readthread.start()
p[0] = readthread
# if len(bg_processes) == 0:
# bpy.app.timers.unregister(bg_update)
if len(bg_processes) > 0:
return .3
return 1.
process_types = (
('UPLOAD', 'Upload', ''),
('THUMBNAILER', 'Thumbnailer', ''),
)
process_sources = (
('MODEL', 'Model', 'set of objects'),
('SCENE', 'Scene', 'set of scenes'),
('MATERIAL', 'Material', 'any .blend Material'),
('TEXTURE', 'Texture', 'a texture, or texture set'),
('BRUSH', 'Brush', 'brush, can be any type of blender brush'),
)
class KillBgProcess(bpy.types.Operator):
'''Remove processes in background.'''
bl_idname = "object.kill_bg_process"
bl_label = "Kill Background Process"
bl_options = {'REGISTER'}
process_type: EnumProperty(
name="Type",
items=process_types,
description="Type of process",
default="UPLOAD",
)
process_source: EnumProperty(
name="Source",
items=process_sources,
description="Source of process",
default="MODEL",
)
def execute(self, context):
s = bpy.context.scene
cls = bpy.ops.object.convert.__class__
# first do the easy stuff...TODO all cases.
props = utils.get_upload_props()
if self.process_type == 'UPLOAD':
props.uploading = False
if self.process_type == 'THUMBNAILER':
props.is_generating_thumbnail = False
global blenderkit_bg_process
# print('killing', self.process_source, self.process_type)
# then go kill the process. this wasn't working for unsetting props and that was the reason for changing to the method above.
processes = bg_processes
for p in processes:
tcom = p[1]
# print(tcom.process_type, self.process_type)
if tcom.process_type == self.process_type:
source = eval(tcom.eval_path)
print(source.bl_rna.name, self.process_source)
print(source.name)
kill = False
if source.bl_rna.name == 'Object' and self.process_source == 'MODEL':
if source.name == bpy.context.active_object.name:
kill = True
if source.bl_rna.name == 'Material' and self.process_source == 'MATERIAL':
if source.name == bpy.context.active_object.active_material.name:
kill = True
if source.bl_rna.name == 'Brush' and self.process_source == 'BRUSH':
brush = utils.get_active_brush()
if brush is not None and source.name == brush.name:
kill = True
if kill:
estring = tcom.eval_path_computing + ' = False'
exec(estring)
processes.remove(p)
tcom.proc.kill()
return {'FINISHED'}
def add_bg_process(location=None, name=None, eval_path_computing='', eval_path_state='', eval_path='', process_type='',
process=None):
'''adds process for monitoring'''
global bg_processes
tcom = threadCom(eval_path_computing, eval_path_state, eval_path, process_type, process, location, name)
readthread = threading.Thread(target=threadread, args=([tcom]), daemon=True)
readthread.start()
bg_processes.append([readthread, tcom])
# if not bpy.app.timers.is_registered(bg_update):
# bpy.app.timers.register(bg_update, persistent=True)
def register():
bpy.utils.register_class(KillBgProcess)
bpy.app.timers.register(bg_update, persistent=True)
def unregister():
bpy.utils.unregister_class(KillBgProcess)
if bpy.app.timers.is_registered(bg_update):
bpy.app.timers.unregister(bg_update)