BlenderKit: add use_timers to debug timers and check stability issues

rename several asset data dict properties to be serverdata-compatible
 - this needed an update function on scene load for older scenes.
draw asset context menu also in selected model panel
remove brush mode filtering from search code(is filtered in Elastic for some time already)
This commit is contained in:
Vilém Duha 2020-06-23 11:06:15 +02:00
parent f1f69a63e0
commit 8bf7c77ca0
10 changed files with 288 additions and 209 deletions

View File

@ -1555,6 +1555,13 @@ class BlenderKitAddonPreferences(AddonPreferences):
default=True,
update=utils.save_prefs
)
use_timers: BoolProperty(
name="Use timers",
description="Use timers for bkit",
default=True,
update=utils.save_prefs
)
# allow_proximity : BoolProperty(
# name="allow proximity data reports",
# description="This sends anonymized proximity data \n \
@ -1673,13 +1680,16 @@ def register():
bkit_oauth.register()
tasks_queue.register()
bpy.app.timers.register(check_timers_timer, persistent=True)
user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
if user_preferences.use_timers:
bpy.app.timers.register(check_timers_timer, persistent=True)
bpy.app.handlers.load_post.append(scene_load)
def unregister():
bpy.app.timers.unregister(check_timers_timer)
if bpy.app.timers.is_registered(check_timers_timer):
bpy.app.timers.unregister(check_timers_timer)
ui_panels.unregister_ui_panels()
ui.unregister_ui()

View File

@ -232,7 +232,9 @@ def add_bg_process(location=None, name=None, eval_path_computing='', eval_path_s
def register():
bpy.utils.register_class(KillBgProcess)
bpy.app.timers.register(bg_update)
user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
if user_preferences.use_timers:
bpy.app.timers.register(bg_update)
def unregister():

View File

@ -190,7 +190,7 @@ def report_usages():
for ob in asset_obs:
asset_data = ob['asset_data']
abid = asset_data['asset_base_id']
abid = asset_data['assetBaseId']
if assets.get(abid) is None:
asset_usages[abid] = {'count': 1}
@ -201,7 +201,7 @@ def report_usages():
# brushes
for b in bpy.data.brushes:
if b.get('asset_data') != None:
abid = b['asset_data']['asset_base_id']
abid = b['asset_data']['assetBaseId']
asset_usages[abid] = {'count': 1}
assets[abid] = b['asset_data']
# materials
@ -211,7 +211,7 @@ def report_usages():
if m is not None and m.get('asset_data') is not None:
abid = m['asset_data']['asset_base_id']
abid = m['asset_data']['assetBaseId']
if assets.get(abid) is None:
asset_usages[abid] = {'count': 1}
assets[abid] = m['asset_data']
@ -288,12 +288,12 @@ def append_asset(asset_data, **kwargs): # downloaders=[], location=None,
if user_preferences.api_key == '':
user_preferences.asset_counter += 1
if asset_data['asset_type'] == 'scene':
if asset_data['assetType'] == 'scene':
scene = append_link.append_scene(file_names[0], link=False, fake_user=False)
props = scene.blenderkit
parent = scene
if asset_data['asset_type'] == 'model':
if asset_data['assetType'] == 'model':
s = bpy.context.scene
downloaders = kwargs.get('downloaders')
s = bpy.context.scene
@ -383,7 +383,7 @@ def append_asset(asset_data, **kwargs): # downloaders=[], location=None,
lib = group.library
lib['asset_data'] = asset_data
elif asset_data['asset_type'] == 'brush':
elif asset_data['assetType'] == 'brush':
# TODO if already in scene, should avoid reappending.
inscene = False
@ -414,7 +414,7 @@ def append_asset(asset_data, **kwargs): # downloaders=[], location=None,
props = brush.blenderkit
parent = brush
elif asset_data['asset_type'] == 'material':
elif asset_data['assetType'] == 'material':
inscene = False
for m in bpy.data.materials:
if m.blenderkit.id == asset_data['id']:
@ -433,11 +433,11 @@ def append_asset(asset_data, **kwargs): # downloaders=[], location=None,
parent = material
scene['assets used'] = scene.get('assets used', {})
scene['assets used'][asset_data['asset_base_id']] = asset_data.copy()
scene['assets used'][asset_data['assetBaseId']] = asset_data.copy()
scene['assets rated'] = scene.get('assets rated', {})
id = asset_data['asset_base_id']
id = asset_data['assetBaseId']
scene['assets rated'][id] = scene['assets rated'].get(id, False)
parent['asset_data'] = asset_data # TODO remove this??? should write to blenderkit Props?
@ -477,7 +477,7 @@ def timer_update(): # TODO might get moved to handle all blenderkit stuff, not
file_names = paths.get_download_filenames(asset_data)
wm = bpy.context.window_manager
at = asset_data['asset_type']
at = asset_data['assetType']
if ((bpy.context.mode == 'OBJECT' and (at == 'model' \
or at == 'material'))) \
or ((at == 'brush') \
@ -505,15 +505,15 @@ def timer_update(): # TODO might get moved to handle all blenderkit stuff, not
else:
done = try_finished_append(asset_data, **tcom.passargs)
if not done:
at = asset_data['asset_type']
at = asset_data['assetType']
tcom.passargs['retry_counter'] = tcom.passargs.get('retry_counter', 0) + 1
if at in ('model', 'material'):
download(asset_data, **tcom.passargs)
elif asset_data['asset_type'] == 'material':
elif asset_data['assetType'] == 'material':
download(asset_data, **tcom.passargs)
elif asset_data['asset_type'] == 'scene':
elif asset_data['assetType'] == 'scene':
download(asset_data, **tcom.passargs)
elif asset_data['asset_type'] == 'brush' or asset_data['asset_type'] == 'texture':
elif asset_data['assetType'] == 'brush' or asset_data['assetType'] == 'texture':
download(asset_data, **tcom.passargs)
if bpy.context.scene['search results'] is not None and done:
for sres in bpy.context.scene['search results']:
@ -675,7 +675,7 @@ def check_downloading(asset_data, **kwargs):
for p in download_threads:
p_asset_data = p[1]
if p_asset_data['id'] == asset_data['id']:
at = asset_data['asset_type']
at = asset_data['assetType']
if at in ('model', 'material'):
downloader = {'location': kwargs['model_location'],
'rotation': kwargs['model_rotation']}
@ -737,7 +737,7 @@ def asset_in_scene(asset_data):
scene = bpy.context.scene
au = scene.get('assets used', {})
id = asset_data['asset_base_id']
id = asset_data['assetBaseId']
if id in au.keys():
ad = au[id]
if ad.get('file_name') != None:
@ -831,15 +831,15 @@ def start_download(asset_data, **kwargs):
# props = utils.get_search_props()
# props.report = str('asset ')
if not done:
at = asset_data['asset_type']
at = asset_data['assetType']
if at in ('model', 'material'):
downloader = {'location': kwargs['model_location'],
'rotation': kwargs['model_rotation']}
download(asset_data, downloaders=[downloader], **kwargs)
elif asset_data['asset_type'] == 'scene':
elif asset_data['assetType'] == 'scene':
download(asset_data, **kwargs)
elif asset_data['asset_type'] == 'brush' or asset_data['asset_type'] == 'texture':
elif asset_data['assetType'] == 'brush' or asset_data['assetType'] == 'texture':
download(asset_data)
@ -911,10 +911,10 @@ class BlenderkitDownloadOperator(bpy.types.Operator):
au = s.get('assets used')
if au == None:
s['assets used'] = {}
if asset_data['asset_base_id'] in s.get('assets used'):
asset_data = s['assets used'][asset_data['asset_base_id']].to_dict()
if asset_data['assetBaseId'] in s.get('assets used'):
asset_data = s['assets used'][asset_data['assetBaseId']].to_dict()
atype = asset_data['asset_type']
atype = asset_data['assetType']
if bpy.context.mode != 'OBJECT' and (
atype == 'model' or atype == 'material') and bpy.context.view_layer.objects.active is not None:
bpy.ops.object.mode_set(mode='OBJECT')
@ -953,7 +953,9 @@ def register_download():
bpy.utils.register_class(BlenderkitKillDownloadOperator)
bpy.app.handlers.load_post.append(scene_load)
bpy.app.handlers.save_pre.append(scene_save)
bpy.app.timers.register(timer_update)
user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
if user_preferences.use_timers:
bpy.app.timers.register(timer_update)
def unregister_download():

View File

@ -170,7 +170,7 @@ def extract_filename_from_url(url):
def get_download_filenames(asset_data):
dirs = get_download_dirs(asset_data['asset_type'])
dirs = get_download_dirs(asset_data['assetType'])
file_names = []
# fn = asset_data['file_name'].replace('blend_', '')
if asset_data.get('url') is not None:

View File

@ -139,7 +139,7 @@ def upload_rating(asset):
headers = utils.get_headers(api_key)
bkit_ratings = asset.bkit_ratings
# print('rating asset', asset_data['name'], asset_data['asset_base_id'])
# print('rating asset', asset_data['name'], asset_data['assetBaseId'])
url = paths.get_api_url() + 'assets/' + asset['asset_data']['id'] + '/rating/'
ratings = [
@ -168,7 +168,7 @@ def upload_rating(asset):
s = bpy.context.scene
s['assets rated'] = s.get('assets rated', {})
if bkit_ratings.rating_quality > 0.1 and bkit_ratings.rating_work_hours > 0.1:
s['assets rated'][asset['asset_data']['asset_base_id']] = True
s['assets rated'][asset['asset_data']['assetBaseId']] = True
class StarRatingOperator(bpy.types.Operator):

View File

@ -101,14 +101,33 @@ def refresh_token_timer():
return max(3600, user_preferences.api_key_life - 3600)
def update_assets_data():# updates assets data on scene load.
'''updates some properties that were changed on scenes with older assets.
The properties were mainly changed from snake_case to CamelCase to fit the data that is coming from the server.
'''
for ob in bpy.context.scene.objects:
if ob.get('asset_data') != None:
ad = ob['asset_data']
if not ad.get('assetBaseId'):
ad['assetBaseId'] = ad['asset_base_id'],#this should stay ONLY for compatibility with older scenes
ad['assetType'] = ad['asset_type'],#this should stay ONLY for compatibility with older scenes
ad['canDownload'] = ad['can_download'],#this should stay ONLY for compatibility with older scenes
ad['verificationStatus'] = ad['verification_status'],#this should stay ONLY for compatibility with older scenes
ad['author'] = {}
ad['author']['id'] = ad['author_id'],#this should stay ONLY for compatibility with older scenes
@persistent
def scene_load(context):
'''
Loads categories , checks timers registration, and updates scene asset data.
Should (probably)also update asset data from server (after user consent)
'''
wm = bpy.context.window_manager
fetch_server_data()
categories.load_categories()
if not bpy.app.timers.is_registered(refresh_token_timer):
bpy.app.timers.register(refresh_token_timer, persistent=True, first_interval=36000)
update_assets_data()
def fetch_server_data():
@ -132,6 +151,12 @@ last_clipboard = ''
def check_clipboard():
'''
Checks clipboard for an exact string containing asset ID.
The string is generated on www.blenderkit.com as for example here:
https://www.blenderkit.com/get-blenderkit/54ff5c85-2c73-49e9-ba80-aec18616a408/
'''
# clipboard monitoring to search assets from web
if platform.system() != 'Linux':
global last_clipboard
@ -148,6 +173,97 @@ def check_clipboard():
search_props.search_keywords = last_clipboard
# don't run search after this - assigning to keywords runs the search_update function.
def parse_result(r):
'''
needed to generate some extra data in the result(by now)
Parameters
----------
r - search result, also called asset_data
'''
scene = bpy.context.scene
# TODO remove this fix when filesSize is fixed.
# this is a temporary fix for too big numbers from the server.
try:
r['filesSize'] = int(r['filesSize'] / 1024)
except:
utils.p('asset with no files-size')
asset_type = r['assetType']
if len(r['files']) > 0:
allthumbs = []
durl, tname = None, None
for f in r['files']:
if f['fileType'] == 'thumbnail':
tname = paths.extract_filename_from_url(f['fileThumbnailLarge'])
small_tname = paths.extract_filename_from_url(f['fileThumbnail'])
allthumbs.append(tname) # TODO just first thumb is used now.
tdict = {}
for i, t in enumerate(allthumbs):
tdict['thumbnail_%i'] = t
if f['fileType'] == 'blend':
durl = f['downloadUrl'].split('?')[0]
# fname = paths.extract_filename_from_url(f['filePath'])
if durl and tname:
tooltip = generate_tooltip(r)
# for some reason, the id was still int on some occurances. investigate this.
r['author']['id'] = str(r['author']['id'])
#some helper props, but generally shouldn't be renaming/duplifiying original properties,
# so blender's data is same as on server.
asset_data = {'thumbnail': tname,
'thumbnail_small': small_tname,
# 'thumbnails':allthumbs,
'download_url': durl,
# 'id': r['id'],
# 'asset_base_id': r['assetBaseId'],#this should stay ONLY for compatibility with older scenes
# 'name': r['name'],
# 'asset_type': r['assetType'], #this should stay ONLY for compatibility with older scenes
'tooltip': tooltip,
# 'tags': r['tags'],
# 'can_download': r.get('canDownload', True),#this should stay ONLY for compatibility with older scenes
# 'verification_status': r['verificationStatus'],#this should stay ONLY for compatibility with older scenes
# 'author_id': r['author']['id'],#this should stay ONLY for compatibility with older scenes
# 'author': r['author']['firstName'] + ' ' + r['author']['lastName']
# 'description': r['description'],
}
asset_data['downloaded'] = 0
# parse extra params needed for blender here
params = utils.params_to_dict(r['parameters'])
if asset_type == 'model':
if params.get('boundBoxMinX') != None:
bbox = {
'bbox_min': (
float(params['boundBoxMinX']),
float(params['boundBoxMinY']),
float(params['boundBoxMinZ'])),
'bbox_max': (
float(params['boundBoxMaxX']),
float(params['boundBoxMaxY']),
float(params['boundBoxMaxZ']))
}
else:
bbox = {
'bbox_min': (-.5, -.5, 0),
'bbox_max': (.5, .5, 1)
}
asset_data.update(bbox)
if asset_type == 'material':
asset_data['texture_size_meters'] = params.get('textureSizeMeters', 1.0)
asset_data.update(tdict)
if r['assetBaseId'] in scene.get('assets used', {}).keys():
asset_data['downloaded'] = 100
#attempt to switch to use original data gradually, since the parsing as itself should become obsolete.
asset_data.update(r)
return asset_data
# @bpy.app.handlers.persistent
def timer_update():
@ -221,84 +337,8 @@ def timer_update():
if ok:
bpy.ops.object.run_assetbar_fix_context()
for r in rdata['results']:
# TODO remove this fix when filesSize is fixed.
# this is a temporary fix for too big numbers from the server.
try:
r['filesSize'] = int(r['filesSize'] / 1024)
except:
utils.p('asset with no files-size')
if r['assetType'] == asset_type:
if len(r['files']) > 0:
furl = None
tname = None
allthumbs = []
durl, tname = None, None
for f in r['files']:
if f['fileType'] == 'thumbnail':
tname = paths.extract_filename_from_url(f['fileThumbnailLarge'])
small_tname = paths.extract_filename_from_url(f['fileThumbnail'])
allthumbs.append(tname) # TODO just first thumb is used now.
tdict = {}
for i, t in enumerate(allthumbs):
tdict['thumbnail_%i'] = t
if f['fileType'] == 'blend':
durl = f['downloadUrl'].split('?')[0]
# fname = paths.extract_filename_from_url(f['filePath'])
if durl and tname:
tooltip = generate_tooltip(r)
# for some reason, the id was still int on some occurances. investigate this.
r['author']['id'] = str(r['author']['id'])
asset_data = {'thumbnail': tname,
'thumbnail_small': small_tname,
# 'thumbnails':allthumbs,
'download_url': durl,
'id': r['id'],
'asset_base_id': r['assetBaseId'],
'name': r['name'],
'asset_type': r['assetType'],
'tooltip': tooltip,
'tags': r['tags'],
'can_download': r.get('canDownload', True),
'verification_status': r['verificationStatus'],
'author_id': r['author']['id'],
# 'author': r['author']['firstName'] + ' ' + r['author']['lastName']
# 'description': r['description'],
}
asset_data['downloaded'] = 0
# parse extra params needed for blender here
params = utils.params_to_dict(r['parameters'])
if asset_type == 'model':
if params.get('boundBoxMinX') != None:
bbox = {
'bbox_min': (
float(params['boundBoxMinX']),
float(params['boundBoxMinY']),
float(params['boundBoxMinZ'])),
'bbox_max': (
float(params['boundBoxMaxX']),
float(params['boundBoxMaxY']),
float(params['boundBoxMaxZ']))
}
else:
bbox = {
'bbox_min': (-.5, -.5, 0),
'bbox_max': (.5, .5, 1)
}
asset_data.update(bbox)
if asset_type == 'material':
asset_data['texture_size_meters'] = params.get('textureSizeMeters', 1.0)
asset_data.update(tdict)
if r['assetBaseId'] in scene.get('assets used', {}).keys():
asset_data['downloaded'] = 100
result_field.append(asset_data)
asset_data = parse_result(r)
result_field.append(asset_data)
# results = rdata['results']
s[search_name] = result_field
@ -846,18 +886,18 @@ class Searcher(threading.Thread):
# filter results here:
# todo remove this in future
nresults = []
for d in rdata.get('results', []):
# TODO this code is for filtering brush types, should vanish after we implement filter in Elastic
mode = None
if query['asset_type'] == 'brush':
for p in d['parameters']:
if p['parameterType'] == 'mode':
mode = p['value']
if query['asset_type'] != 'brush' or (
query.get('mode') != None and query['mode']) == mode:
nresults.append(d)
rdata['results'] = nresults
# nresults = []
# for d in rdata.get('results', []):
# # TODO this code is for filtering brush types, should vanish after we implement filter in Elastic
# mode = None
# if query['asset_type'] == 'brush':
# for p in d['parameters']:
# if p['parameterType'] == 'mode':
# mode = p['value']
# if query['asset_type'] != 'brush' or (
# query.get('mode') != None and query['mode']) == mode:
# nresults.append(d)
# rdata['results'] = nresults
# print('number of results: ', len(rdata.get('results', [])))
if self.stopped():
@ -1307,7 +1347,9 @@ def register_search():
for c in classes:
bpy.utils.register_class(c)
bpy.app.timers.register(timer_update)
user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
if user_preferences.use_timers:
bpy.app.timers.register(timer_update)
categories.load_categories()
@ -1317,5 +1359,6 @@ def unregister_search():
for c in classes:
bpy.utils.unregister_class(c)
if bpy.app.timers.is_registered(timer_update):
bpy.app.timers.unregister(timer_update)

View File

@ -30,8 +30,10 @@ import queue
@persistent
def scene_load(context):
if not (bpy.app.timers.is_registered(queue_worker)):
bpy.app.timers.register(queue_worker)
user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
if user_preferences.use_timers:
if not (bpy.app.timers.is_registered(queue_worker)):
bpy.app.timers.register(queue_worker)
def get_queue():

View File

@ -257,7 +257,7 @@ def draw_ratings_bgl():
# b = utils.get_active_brush()
# thumbnail_image = b.icon_filepath
directory = paths.get_temp_dir('%s_search' % asset_data['asset_type'])
directory = paths.get_temp_dir('%s_search' % asset_data['assetType'])
tpath = os.path.join(directory, asset_data['thumbnail_small'])
img = utils.get_hidden_image(tpath, 'rating_preview')
@ -274,7 +274,7 @@ def draw_ratings_bgl():
ui.rating_ui_width + ui.margin,
ui.rating_ui_height + 2 * ui.margin + font_size,
bgcol)
if asset_data['asset_type'] == 'model':
if asset_data['assetType'] == 'model':
ui_img_name = 'rating_ui.png'
else:
ui_img_name = 'rating_ui_empty.png'
@ -303,7 +303,7 @@ def draw_ratings_bgl():
img = utils.get_thumbnail('bar_slider.png')
# for a in range(0,11):
if work_hours > 0.2:
if asset_data['asset_type'] == 'model':
if asset_data['assetType'] == 'model':
complexity = math.log2(work_hours) + 2 # real complexity
complexity = (1. / 9.) * (complexity - 1) * ui.workhours_bar_x_max
else:
@ -321,7 +321,7 @@ def draw_ratings_bgl():
# (0.5,1,2,4,8,16,32,64,128,256)
# ratings have to be different for models and brushes+materials.
scalevalues, xs = get_rating_scalevalues(asset_data['asset_type'])
scalevalues, xs = get_rating_scalevalues(asset_data['assetType'])
for v, x in zip(scalevalues, xs):
ui_bgl.draw_rect(ui.rating_x + ui.workhours_bar_x + int(
x * ui.workhours_bar_x_max) - 1 + ui.workhours_bar_slider_size / 2,
@ -650,7 +650,7 @@ def draw_callback_3d_progress(self, context):
tcom = threaddata[2]
if tcom.passargs.get('downloaders'):
for d in tcom.passargs['downloaders']:
if asset_data['asset_type'] == 'model':
if asset_data['assetType'] == 'model':
draw_bbox(d['location'], d['rotation'], asset_data['bbox_min'], asset_data['bbox_max'],
progress=tcom.progress)
@ -669,7 +669,7 @@ def draw_callback_2d_progress(self, context):
asset_data = threaddata[1]
tcom = threaddata[2]
directory = paths.get_temp_dir('%s_search' % asset_data['asset_type'])
directory = paths.get_temp_dir('%s_search' % asset_data['assetType'])
tpath = os.path.join(directory, asset_data['thumbnail_small'])
img = utils.get_hidden_image(tpath, asset_data['id'])
@ -679,7 +679,7 @@ def draw_callback_2d_progress(self, context):
loc = view3d_utils.location_3d_to_region_2d(bpy.context.region, bpy.context.space_data.region_3d,
d['location'])
if loc is not None:
if asset_data['asset_type'] == 'model':
if asset_data['assetType'] == 'model':
# models now draw with star trek mode, no need to draw percent for the image.
draw_downloader(loc[0], loc[1], percent=tcom.progress, img=img)
else:
@ -823,11 +823,11 @@ def draw_callback_2d_search(self, context):
# object type icons - just a test..., adds clutter/ not so userfull:
# icons = ('type_finished.png', 'type_template.png', 'type_particle_system.png')
if (result.get('can_download', True)) == 0:
if (result.get('canDownload', True)) == 0:
img = utils.get_thumbnail('locked.png')
ui_bgl.draw_image(x + 2, y + 2, 24, 24, img, 1)
v_icon = verification_icons[result.get('verification_status', 'validated')]
v_icon = verification_icons[result.get('verificationStatus', 'validated')]
if v_icon is not None:
img = utils.get_thumbnail(v_icon)
ui_bgl.draw_image(x + ui_props.thumb_size - 26, y + 2, 24, 24, img, 1)
@ -881,7 +881,7 @@ def draw_callback_2d_search(self, context):
gimg = None
atip = ''
if bpy.context.window_manager.get('bkit authors') is not None:
a = bpy.context.window_manager['bkit authors'].get(r['author_id'])
a = bpy.context.window_manager['bkit authors'].get(r['author']['id'])
if a is not None and a != '':
if a.get('gravatarImg') is not None:
gimg = utils.get_hidden_image(a['gravatarImg'], a['gravatarHash'])
@ -999,7 +999,7 @@ def is_rating_possible():
b = utils.get_active_brush()
ad = b.get('asset_data')
if ad is not None:
rated = bpy.context.scene['assets rated'].get(ad['asset_base_id'])
rated = bpy.context.scene['assets rated'].get(ad['assetBaseId'])
return True, rated, b, ad
if ao is not None:
ad = None
@ -1008,7 +1008,7 @@ def is_rating_possible():
while ad is None or (ad is None and ao_check.parent is not None):
ad = ao_check.get('asset_data')
if ad is not None:
rated = bpy.context.scene['assets rated'].get(ad['asset_base_id'])
rated = bpy.context.scene['assets rated'].get(ad['assetBaseId'])
# originally hidden for already rated assets
return True, rated, ao_check, ad
elif ao_check.parent is not None:
@ -1021,7 +1021,7 @@ def is_rating_possible():
if m is not None:
ad = m.get('asset_data')
if ad is not None:
rated = bpy.context.scene['assets rated'].get(ad['asset_base_id'])
rated = bpy.context.scene['assets rated'].get(ad['assetBaseId'])
return True, rated, m, ad
# if t>2 and t<2.5:
@ -1092,7 +1092,7 @@ def interact_rating(r, mx, my, event):
if ui.dragging_rating_work_hours:
xv = rmx - ui.workhours_bar_x - ui.workhours_bar_slider_size / 2
ratio = xv / ui.workhours_bar_x_max
if asset_data['asset_type'] == 'model':
if asset_data['assetType'] == 'model':
wh_log2 = ratio * 9 - 1
wh = 2 ** wh_log2
else:
@ -1255,6 +1255,14 @@ class AssetBarOperator(bpy.types.Operator):
areas = []
#timers testing - seems timers might be causing crashes. testing it this way now.
if not user_preferences.use_timers:
search.timer_update()
download.timer_update()
tasks_queue.queue_worker()
bg_blender.bg_update()
if bpy.context.scene != self.scene:
self.exit_modal()
return {'CANCELLED'}
@ -1505,7 +1513,7 @@ class AssetBarOperator(bpy.types.Operator):
# check if asset is locked and let the user know in that case
asset_search_index = ui_props.active_index
asset_data = sr[asset_search_index]
if not asset_data['can_download']:
if not asset_data.get('canDownload'):
message = "Let's support asset creators and Blender development."
link_text = 'Unlock the asset.'
url = paths.get_bkit_url() + '/get-blenderkit/' + asset_data['id'] + '/?from_addon'
@ -1667,7 +1675,7 @@ class AssetBarOperator(bpy.types.Operator):
if event.type == 'W' and ui_props.active_index > -1:
sr = bpy.context.scene['search results']
asset_data = sr[ui_props.active_index]
a = bpy.context.window_manager['bkit authors'].get(asset_data['author_id'])
a = bpy.context.window_manager['bkit authors'].get(asset_data['author']['id'])
if a is not None:
utils.p('author:', a)
if a.get('aboutMeUrl') is not None:
@ -1676,7 +1684,7 @@ class AssetBarOperator(bpy.types.Operator):
if event.type == 'A' and ui_props.active_index > -1:
sr = bpy.context.scene['search results']
asset_data = sr[ui_props.active_index]
a = asset_data['author_id']
a = asset_data['author']['id']
if a is not None:
sprops = utils.get_search_props()
sprops.search_keywords = ''

View File

@ -94,15 +94,19 @@ def draw_ratings(layout, context):
# op = row.operator("object.blenderkit_rating_upload", text="Send rating", icon='URL')
# return op
def draw_not_logged_in(source):
title = "User not logged in"
def draw_message(source, context):
layout = source.layout
label_multiline(layout, text='Please login or sign up '
'to upload files.')
'to upload files.')
draw_login_buttons(layout)
bpy.context.window_manager.popup_menu(draw_message, title=title, icon='INFO')
def draw_upload_common(layout, props, asset_type, context):
op = layout.operator("wm.url_open", text="Read upload instructions",
icon='QUESTION')
@ -434,6 +438,7 @@ class VIEW3D_PT_blenderkit_model_properties(Panel):
draw_panel_model_rating(self, context)
draw_asset_context_menu(self, context, ad)
# if 'rig' in ad['tags']:
# # layout.label(text = 'can make proxy')
# layout.operator('object.blenderkit_make_proxy', text = 'Make Armature proxy')
@ -494,9 +499,9 @@ class VIEW3D_PT_blenderkit_profile(Panel):
row = layout.row()
row.label(text='My plan:')
row.label(text='%s plan' % pn, icon_value=my_icon.icon_id)
if pn =='Free':
if pn == 'Free':
layout.operator("wm.url_open", text="Change plan",
icon='URL').url = paths.get_bkit_url() + paths.BLENDERKIT_PLANS
icon='URL').url = paths.get_bkit_url() + paths.BLENDERKIT_PLANS
# storage statistics
# if me.get('sumAssetFilesSize') is not None: # TODO remove this when production server has these too.
@ -903,81 +908,87 @@ class VIEW3D_PT_blenderkit_unified(Panel):
layout.label(text='not yet implemented')
def draw_asset_context_menu(self, context, asset_data):
layout = self.layout
ui_props = context.scene.blenderkitUI
author_id = str(asset_data['author']['id'])
wm = bpy.context.window_manager
if wm.get('bkit authors') is not None:
a = bpy.context.window_manager['bkit authors'].get(author_id)
if a is not None:
# utils.p('author:', a)
if a.get('aboutMeUrl') is not None:
op = layout.operator('wm.url_open', text="Open Author's Website")
op.url = a['aboutMeUrl']
op = layout.operator('view3d.blenderkit_search', text="Show Assets By Author")
op.keywords = ''
op.author_id = author_id
op = layout.operator('view3d.blenderkit_search', text='Search Similar')
op.keywords = asset_data['name'] + ' ' + asset_data['description'] + ' ' + ' '.join(asset_data['tags'])
if asset_data.get('canDownload') != 0:
if len(bpy.context.selected_objects) > 0 and ui_props.asset_type == 'MODEL':
aob = bpy.context.active_object
if aob is None:
aob = bpy.context.selected_objects[0]
op = layout.operator('scene.blenderkit_download', text='Replace Active Models')
op.asset_type = ui_props.asset_type
op.asset_index = ui_props.active_index
op.model_location = aob.location
op.model_rotation = aob.rotation_euler
op.target_object = aob.name
op.material_target_slot = aob.active_material_index
op.replace = True
wm = bpy.context.window_manager
profile = wm.get('bkit profile')
if profile is not None:
# validation
if utils.profile_is_validator():
layout.label(text='Validation tools:')
if asset_data['verificationStatus'] != 'uploaded':
op = layout.operator('object.blenderkit_change_status', text='set Uploaded')
op.asset_id = asset_data['id']
op.state = 'uploaded'
if asset_data['verificationStatus'] != 'validated':
op = layout.operator('object.blenderkit_change_status', text='Validate')
op.asset_id = asset_data['id']
op.state = 'validated'
if asset_data['verificationStatus'] != 'on_hold':
op = layout.operator('object.blenderkit_change_status', text='Put on Hold')
op.asset_id = asset_data['id']
op.state = 'on_hold'
if asset_data['verificationStatus'] != 'rejected':
op = layout.operator('object.blenderkit_change_status', text='Reject')
op.asset_id = asset_data['id']
op.state = 'rejected'
if author_id == str(profile['user']['id']):
layout.label(text='Management tools:')
row = layout.row()
row.operator_context = 'INVOKE_DEFAULT'
op = row.operator('object.blenderkit_change_status', text='Delete')
op.asset_id = asset_data['id']
op.state = 'deleted'
# else:
# #not an author - can rate
# draw_ratings(layout, context)
class OBJECT_MT_blenderkit_asset_menu(bpy.types.Menu):
bl_label = "Asset options:"
bl_idname = "OBJECT_MT_blenderkit_asset_menu"
def draw(self, context):
layout = self.layout
ui_props = context.scene.blenderkitUI
sr = bpy.context.scene['search results']
# sr = bpy.context.scene['search results']
sr = bpy.context.scene['search results orig']['results']
asset_data = sr[ui_props.active_index]
author_id = str(asset_data['author']['id'])
wm = bpy.context.window_manager
if wm.get('bkit authors') is not None:
a = bpy.context.window_manager['bkit authors'].get(author_id)
if a is not None:
# utils.p('author:', a)
if a.get('aboutMeUrl') is not None:
op = layout.operator('wm.url_open', text="Open Author's Website")
op.url = a['aboutMeUrl']
op = layout.operator('view3d.blenderkit_search', text="Show Assets By Author")
op.keywords = ''
op.author_id = author_id
op = layout.operator('view3d.blenderkit_search', text='Search Similar')
op.keywords = asset_data['name'] + ' ' + asset_data['description'] + ' ' + ' '.join(asset_data['tags'])
if asset_data.get('canDownload') != 0:
if len(bpy.context.selected_objects)>0 and ui_props.asset_type == 'MODEL':
aob = bpy.context.active_object
if aob is None:
aob = bpy.context.selected_objects[0]
op = layout.operator('scene.blenderkit_download', text='Replace Active Models')
op.asset_type = ui_props.asset_type
op.asset_index = ui_props.active_index
op.model_location = aob.location
op.model_rotation = aob.rotation_euler
op.target_object = aob.name
op.material_target_slot = aob.active_material_index
op.replace = True
wm = bpy.context.window_manager
profile = wm.get('bkit profile')
if profile is not None:
# validation
if utils.profile_is_validator():
layout.label(text='Validation tools:')
if asset_data['verificationStatus'] != 'uploaded':
op = layout.operator('object.blenderkit_change_status', text='set Uploaded')
op.asset_id = asset_data['id']
op.state = 'uploaded'
if asset_data['verificationStatus'] != 'validated':
op = layout.operator('object.blenderkit_change_status', text='Validate')
op.asset_id = asset_data['id']
op.state = 'validated'
if asset_data['verificationStatus'] != 'on_hold':
op = layout.operator('object.blenderkit_change_status', text='Put on Hold')
op.asset_id = asset_data['id']
op.state = 'on_hold'
if asset_data['verificationStatus'] != 'rejected':
op = layout.operator('object.blenderkit_change_status', text='Reject')
op.asset_id = asset_data['id']
op.state = 'rejected'
if author_id == str(profile['user']['id']):
layout.label(text='Management tools:')
row = layout.row()
row.operator_context = 'INVOKE_DEFAULT'
op = row.operator('object.blenderkit_change_status', text='Delete')
op.asset_id = asset_data['id']
op.state = 'deleted'
# else:
# #not an author - can rate
# draw_ratings(layout, context)
draw_asset_context_menu(self, context, asset_data)
class SetCategoryOperator(bpy.types.Operator):

View File

@ -822,11 +822,12 @@ class AssetVerificationStatusChange(Operator):
for r in sr:
if r['id'] == self.asset_id:
r['verification_status'] = self.state
r['verificationStatus'] = self.state
for r in sro:
if r['id'] == self.asset_id:
r['verificationStatus'] = self.state
thread = threading.Thread(target=verification_status_change_thread,
args=(self.asset_id, self.state, preferences.api_key))
thread.start()