BlenderKit: fixes and optimizations

Search results aren't passed around back to search and are parsed only once. Same for previews.
Search function could run many times per second when more pages were loaded.
Search now causes almost no lag in Blender thanks to the fixes
Default page size in search request is now fit to the asset bar size settings.
several optimizations in the draw code - is now about 3x faster
rerequests now can return a fake response in case of a timeout
This commit is contained in:
Vilem Duha 2021-07-20 14:26:26 +02:00
parent f024bcb84d
commit 692a15f3c5
9 changed files with 159 additions and 151 deletions

View File

@ -19,7 +19,7 @@
bl_info = {
"name": "BlenderKit Online Asset Library",
"author": "Vilem Duha, Petr Dlouhy",
"version": (2, 93, 0),
"version": (3, 0, 0),
"blender": (2, 93, 0),
"location": "View3D > Properties > BlenderKit",
"description": "Online BlenderKit library (materials, models, brushes and more). Connects to the internet.",
@ -141,8 +141,6 @@ from bpy.types import (
@persistent
def scene_load(context):
if not bpy.app.background:
search.load_previews()
ui_props = bpy.context.scene.blenderkitUI
ui_props.assetbar_on = False
ui_props.turn_off = False

View File

@ -559,8 +559,15 @@ class BlenderKitAssetBarOperator(BL_UI_OT_draw_operator):
def search_more(self):
sro = bpy.context.window_manager.get('search results orig')
if sro is not None and sro.get('next') is not None:
blenderkit.search.search(get_next=True)
if sro is None:
return;
if sro.get('next') is None:
return
search_props = utils.get_search_props()
if search_props.is_searching:
return
blenderkit.search.search(get_next=True)
def update_images(self):
sr = bpy.context.window_manager.get('search results')

View File

@ -268,7 +268,7 @@ def rating_menu_draw(self, context):
layout = self.layout
ui_props = context.scene.blenderkitUI
sr = bpy.context.window_manager['search results orig']
sr = bpy.context.window_manager['search results']
asset_search_index = ui_props.active_index
if asset_search_index > -1:

View File

@ -76,9 +76,9 @@ def store_rating_local_empty(asset_id):
def store_rating_local(asset_id, type='quality', value=0):
context = bpy.context
context.window_manager['asset ratings'] = context.window_manager.get('asset ratings', {})
context.window_manager['asset ratings'][asset_id] = context.window_manager['asset ratings'].get(asset_id, {})
context.window_manager['asset ratings'][asset_id][type] = value
ar = context.window_manager['asset ratings']
ar[asset_id] = ar.get(asset_id, {})
ar[asset_id][type] = value
def get_rating(asset_id, headers):
@ -96,6 +96,8 @@ def get_rating(asset_id, headers):
url = paths.get_api_url() + 'assets/' + asset_id + '/rating/'
params = {}
r = rerequests.get(url, params=params, verify=True, headers=headers)
if r is None:
return
if r.status_code == 200:
rj = r.json()
ratings = {}

View File

@ -26,6 +26,13 @@ import logging
bk_logger = logging.getLogger('rerequests')
class FakeResponse():
def __init__(self, text='', status_code = 400):
self.text = text
self.status_code = status_code
def json(self):
return {}
def rerequest(method, url, recursion=0, **kwargs):
# first get any additional args from kwargs
immediate = False
@ -37,7 +44,9 @@ def rerequest(method, url, recursion=0, **kwargs):
response = requests.request(method, url, **kwargs)
except Exception as e:
print(e)
return None
tasks_queue.add_task((ui.add_report, (
'Connection error.', 10)))
return FakeResponse()
bk_logger.debug(url + str(kwargs))
bk_logger.debug(response.status_code)

View File

@ -52,6 +52,7 @@ import urllib
import queue
import logging
bk_logger = logging.getLogger('blenderkit')
search_start_time = 0
@ -70,6 +71,8 @@ def check_errors(rdata):
return False, 'Use login panel to connect your profile.'
else:
return False, rdata.get('detail')
if rdata.get('statusCode') is None and rdata.get('results') is None:
return False, 'Connection error'
return True, ''
@ -287,19 +290,8 @@ def parse_result(r):
# so blender's data is same as on server.
asset_data = {'thumbnail': tname,
'thumbnail_small': small_tname,
# 'thumbnails':allthumbs,
# 'download_url': durl, #made obsolete since files are stored in orig form.
# '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
@ -351,6 +343,7 @@ def parse_result(r):
# @bpy.app.handlers.persistent
def search_timer():
# this makes a first search after opening blender. showing latest assets.
# utils.p('timer search')
# utils.p('start search timer')
@ -381,7 +374,8 @@ def search_timer():
global search_threads
if len(search_threads) == 0:
# utils.p('end search timer')
props = utils.get_search_props()
props.is_searching = False
return 1.0
# don't do anything while dragging - this could switch asset during drag, and make results list length different,
# causing a lot of throuble.
@ -389,34 +383,29 @@ def search_timer():
# utils.p('end search timer')
return 0.5
for thread in search_threads:
# TODO this doesn't check all processes when one gets removed,
# but most of the time only one is running anyway
if not thread[0].is_alive():
search_threads.remove(thread) #
icons_dir = thread[1]
scene = bpy.context.scene
# these 2 lines should update the previews enum and set the first result as active.
wm = bpy.context.window_manager
asset_type = thread[2]
if asset_type == 'model':
props = scene.blenderkit_models
# json_filepath = os.path.join(icons_dir, 'model_searchresult.json')
if asset_type == 'scene':
props = scene.blenderkit_scene
# json_filepath = os.path.join(icons_dir, 'scene_searchresult.json')
if asset_type == 'hdr':
props = scene.blenderkit_HDR
# json_filepath = os.path.join(icons_dir, 'scene_searchresult.json')
if asset_type == 'material':
props = scene.blenderkit_mat
# json_filepath = os.path.join(icons_dir, 'material_searchresult.json')
if asset_type == 'brush':
props = scene.blenderkit_brush
# json_filepath = os.path.join(icons_dir, 'brush_searchresult.json')
props = utils.get_search_props()
search_name = f'bkit {asset_type} search'
wm[search_name] = []
if not thread[0].params.get('get_next'):
# wm[search_name] = []
result_field = []
else:
result_field = []
for r in wm[search_name]:
result_field.append(r.to_dict())
global reports_queue
@ -427,21 +416,19 @@ def search_timer():
return .2
rdata = thread[0].result
result_field = []
ok, error = check_errors(rdata)
if ok:
ui_props = bpy.context.scene.blenderkitUI
if not ui_props.assetbar_on:
bpy.ops.object.run_assetbar_fix_context()
for r in rdata['results']:
orig_len = len(result_field)
for ri, r in enumerate(rdata['results']):
asset_data = parse_result(r)
if asset_data != None:
result_field.append(asset_data)
load_preview(asset_data,ri + orig_len)
# Get ratings from BlenderKit server
user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
@ -455,32 +442,74 @@ def search_timer():
wm[search_name] = result_field
wm['search results'] = result_field
wm[search_name + ' orig'] = copy.deepcopy(rdata)
wm['search results orig'] = wm[search_name + ' orig']
load_previews()
#rdata=['results']=[]
wm[search_name + ' orig'] = rdata
wm['search results orig'] = rdata
# load_previews()
if len(result_field) < ui_props.scrolloffset or not (thread[0].params.get('get_next')):
# jump back
ui_props.scrolloffset = 0
props.is_searching = False
props.search_error = False
props.report = 'Found %i results. ' % (wm['search results orig']['count'])
if len(wm['search results']) == 0:
tasks_queue.add_task((ui.add_report, ('No matching results found.',)))
# undo push
# bpy.ops.wm.undo_push_context(message='Get BlenderKit search')
#show asset bar automatically, but only on first page - others are loaded also when asset bar is hidden.
if not ui_props.assetbar_on and not thread[0].params.get('get_next'):
bpy.ops.object.run_assetbar_fix_context()
else:
bk_logger.error(error)
props.report = error
props.search_error = True
props.is_searching = False
# print('finished search thread')
mt('preview loading finished')
# utils.p('end search timer')
return .3
def load_preview(asset, index):
scene = bpy.context.scene
# FIRST START SEARCH
props = scene.blenderkitUI
directory = paths.get_temp_dir('%s_search' % props.asset_type.lower())
s = bpy.context.scene
results = bpy.context.window_manager.get('search results')
tpath = os.path.join(directory, asset['thumbnail_small'])
if not asset['thumbnail_small']:
tpath = paths.get_addon_thumbnail_path('thumbnail_not_available.jpg')
iname = utils.previmg_name(index)
# if os.path.exists(tpath): # sometimes we are unlucky...
img = bpy.data.images.get(iname)
if img is None:
if not os.path.exists(tpath):
return
img = bpy.data.images.load(tpath)
img.name = iname
elif img.filepath != tpath:
if not os.path.exists(tpath):
return
# had to add this check for autopacking files...
if img.packed_file is not None:
img.unpack(method='USE_ORIGINAL')
img.filepath = tpath
img.reload()
if asset['assetType'] == 'hdr':
# to display hdr thumbnails correctly, we use non-color, otherwise looks shifted
image_utils.set_colorspace(img, 'Non-Color')
else:
image_utils.set_colorspace(img, 'sRGB')
def load_previews():
scene = bpy.context.scene
@ -491,39 +520,10 @@ def load_previews():
results = bpy.context.window_manager.get('search results')
#
if results is not None:
inames = []
tpaths = []
i = 0
for r in results:
tpath = os.path.join(directory, r['thumbnail_small'])
if not r['thumbnail_small']:
tpath = paths.get_addon_thumbnail_path('thumbnail_not_available.jpg')
if not os.path.exists(tpath):
continue
iname = utils.previmg_name(i)
# if os.path.exists(tpath): # sometimes we are unlucky...
img = bpy.data.images.get(iname)
if img is None:
img = bpy.data.images.load(tpath)
img.name = iname
elif img.filepath != tpath:
# had to add this check for autopacking files...
if img.packed_file is not None:
img.unpack(method='USE_ORIGINAL')
img.filepath = tpath
img.reload()
if r['assetType'] == 'hdr':
# to display hdr thumbnails correctly, we use non-color, otherwise looks shifted
image_utils.set_colorspace(img, 'Non-Color')
else:
image_utils.set_colorspace(img, 'sRGB')
load_preview(r,i)
i += 1
# print('previews loaded')
# line splitting for longer texts...
@ -848,6 +848,7 @@ def query_to_url(query={}, params={}):
if requeststring.find('+order:')==-1:
requeststring += '+order:' + ','.join(order)
requeststring += '&page_size=' + str(params['page_size'])
requeststring += '&addon_version=%s' % params['addon_version']
if params.get('scene_uuid') is not None:
requeststring += '&scene_uuid=%s' % params['scene_uuid']
@ -865,12 +866,12 @@ def parse_html_formated_error(text):
class Searcher(threading.Thread):
query = None
def __init__(self, query, params, orig_result, tempdir='', headers=None, urlquery=''):
def __init__(self, query, params, tempdir='', headers=None, urlquery=''):
super(Searcher, self).__init__()
self.query = query
self.params = params
self._stop_event = threading.Event()
self.result = orig_result
self.result = {}
self.tempdir = tempdir
self.headers = headers
self.urlquery = urlquery
@ -912,9 +913,10 @@ class Searcher(threading.Thread):
try:
rdata = r.json()
except Exception as e:
error_description = parse_html_formated_error(r.text)
reports_queue.put(error_description)
tasks_queue.add_task((ui.add_report, (error_description, 10, colors.RED)))
if hasattr(r,'text'):
error_description = parse_html_formated_error(r.text)
reports_queue.put(error_description)
tasks_queue.add_task((ui.add_report, (error_description, 10, colors.RED)))
bk_logger.error(e)
return
@ -984,8 +986,7 @@ class Searcher(threading.Thread):
# we save here because a missing thumbnail check is in the previous loop
# we can also prepend previous results. These have downloaded thumbnails already...
if params['get_next']:
rdata['results'][0:0] = self.result['results']
self.result = rdata
# with open(json_filepath, 'w', encoding = 'utf-8') as outfile:
# json.dump(rdata, outfile, ensure_ascii=False, indent=4)
@ -1038,7 +1039,10 @@ class Searcher(threading.Thread):
for tk, thread in threads_copy.items():
if not thread.is_alive():
thread.join()
del (thumb_sml_download_threads[tk])
try:
del (thumb_sml_download_threads[tk])
except Exception as e:
print(e)
i += 1
if self.stopped():
@ -1227,7 +1231,7 @@ def mt(text):
utils.p(text, alltime, since_last)
def add_search_process(query, params, orig_result):
def add_search_process(query, params):
global search_threads
while (len(search_threads) > 0):
@ -1238,12 +1242,12 @@ def add_search_process(query, params, orig_result):
tempdir = paths.get_temp_dir('%s_search' % query['asset_type'])
headers = utils.get_headers(params['api_key'])
if params['get_next']:
urlquery = orig_result['next']
if not params['get_next']:
if params.get('get_next'):
urlquery = params['next']
else:
urlquery = query_to_url(query, params)
thread = Searcher(query, params, orig_result, tempdir=tempdir, headers=headers, urlquery=urlquery)
thread = Searcher(query, params, tempdir=tempdir, headers=headers, urlquery=urlquery)
thread.start()
search_threads.append([thread, tempdir, query['asset_type'], {}]) # 4th field is for results
@ -1305,35 +1309,33 @@ def get_search_simple(parameters, filepath=None, page_size=100, max_results=1000
def search(category='', get_next=False, author_id=''):
''' initialize searching'''
global search_start_time
# print(category,get_next,author_id)
user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
search_start_time = time.time()
# mt('start')
scene = bpy.context.scene
ui_props = scene.blenderkitUI
props = utils.get_search_props()
if ui_props.asset_type == 'MODEL':
if not hasattr(scene, 'blenderkit'):
return;
props = scene.blenderkit_models
query = build_query_model()
if ui_props.asset_type == 'SCENE':
if not hasattr(scene, 'blenderkit_scene'):
return;
props = scene.blenderkit_scene
query = build_query_scene()
if ui_props.asset_type == 'HDR':
if not hasattr(scene, 'blenderkit_HDR'):
return;
props = scene.blenderkit_HDR
query = build_query_HDR()
if ui_props.asset_type == 'MATERIAL':
if not hasattr(scene, 'blenderkit_mat'):
return;
props = scene.blenderkit_mat
query = build_query_material()
if ui_props.asset_type == 'TEXTURE':
@ -1345,7 +1347,6 @@ def search(category='', get_next=False, author_id=''):
if ui_props.asset_type == 'BRUSH':
if not hasattr(scene, 'blenderkit_brush'):
return;
props = scene.blenderkit_brush
query = build_query_brush()
# crop long searches
@ -1357,8 +1358,11 @@ def search(category='', get_next=False, author_id=''):
idx = query['query'].find(' ', 142)
query['query'] = query['query'][:idx]
# it's possible get_net was requested more than once.
# it's possible get_next was requested more than once.
# print(category,props.is_searching, get_next)
# print(query)
if props.is_searching and get_next == True:
# print('return because of get next and searching is happening')
return;
if category != '':
@ -1379,19 +1383,21 @@ def search(category='', get_next=False, author_id=''):
# utils.p('searching')
props.is_searching = True
page_size = min(40, ui_props.wcount * user_preferences.max_assetbar_rows)
params = {
'scene_uuid': bpy.context.scene.get('uuid', None),
'addon_version': version_checker.get_addon_version(),
'api_key': user_preferences.api_key,
'get_next': get_next,
'free_first': props.free_only
'free_first': props.free_only,
'page_size': page_size,
}
orig_results = bpy.context.window_manager.get(f'bkit {ui_props.asset_type.lower()} search orig', {})
if orig_results != {}:
# ensure it's a copy in dict for what we are passing to thread:
orig_results = orig_results.to_dict()
add_search_process(query, params, orig_results)
orig_results = bpy.context.window_manager.get(f'bkit {ui_props.asset_type.lower()} search orig')
if orig_results is not None and get_next:
params['next'] = orig_results['next']
add_search_process(query, params)
tasks_queue.add_task((ui.add_report, ('BlenderKit searching....', 2)))
props.report = 'BlenderKit searching....'
@ -1435,6 +1441,7 @@ def search_update(self, context):
# return here since writing into search keywords triggers this update function once more.
return
print('search update search')
search()
@ -1567,6 +1574,7 @@ def register_search():
user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
if user_preferences.use_timers:
bpy.app.timers.register(search_timer)
categories.load_categories()
@ -1580,3 +1588,5 @@ def unregister_search():
if bpy.app.timers.is_registered(search_timer):
bpy.app.timers.unregister(search_timer)

View File

@ -40,10 +40,6 @@ import os
import logging
PROFILING = False
if PROFILING:
import cProfile
profiler = cProfile.Profile()
draw_time = 0
eval_time = 0
@ -422,19 +418,7 @@ def draw_tooltip_with_author(asset_data, x, y):
img=img,
gravatar=gimg)
profiler_step = 0
def draw_callback_2d_profiled(self,context):
global profiler,profiler_step
result = profiler.runcall(draw_callback_2d,self,context)
if profiler_step >= 1000:
profiler.print_stats(sort = 'cumulative')
profiler = cProfile.Profile()
profiler_step = 0
profiler_step+=1
if profiler_step%10 ==0:
print(profiler_step)
return
def draw_callback_2d(self, context):
if not utils.guard_from_crash():
@ -572,7 +556,7 @@ def draw_callback_2d_upload_preview(self, context):
img = utils.get_hidden_image(ui_props.thumbnail_image, 'upload_preview')
draw_tooltip(ui_props.bar_x, ui_props.bar_y, name=ui_props.tooltip, img=img)
draw_tooltip(ui_props.bar_x, ui_props.bar_y, name=props.name, img=img)
def is_upload_old(asset_data):
@ -760,7 +744,7 @@ def draw_asset_bar(self, context):
# report = 'BlenderKit - No matching results found.'
# ui_bgl.draw_text(report, ui_props.bar_x + ui_props.margin,
# ui_props.bar_y - 25 - ui_props.margin, 15)
if ui_props.draw_tooltip:
if ui_props.draw_tooltip and len(search_results)>ui_props.active_index:
r = search_results[ui_props.active_index]
draw_tooltip_with_author(r, ui_props.mouse_x, ui_props.mouse_y)
s = bpy.context.scene
@ -1162,7 +1146,6 @@ class ParticlesDropDialog(bpy.types.Operator):
# wm = context.window_manager
# return wm.invoke_props_dialog(self, width=400)
class AssetBarOperator(bpy.types.Operator):
'''runs search and displays the asset bar at the same time'''
bl_idname = "view3d.blenderkit_asset_bar"
@ -1186,8 +1169,15 @@ class AssetBarOperator(bpy.types.Operator):
def search_more(self):
sro = bpy.context.window_manager.get('search results orig')
if sro is not None and sro.get('next') is not None:
search.search(get_next=True)
if sro is None:
return;
if sro.get('next') is None:
return
search_props = utils.get_search_props()
if search_props.is_searching:
return
search.search(get_next=True)
def exit_modal(self):
try:
@ -1196,13 +1186,14 @@ class AssetBarOperator(bpy.types.Operator):
pass;
ui_props = bpy.context.scene.blenderkitUI
ui_props.tooltip = ''
# ui_props.tooltip = ''
ui_props.active_index = -3
ui_props.draw_drag_image = False
ui_props.draw_snapped_bounds = False
ui_props.has_hit = False
ui_props.assetbar_on = False
def modal(self, context, event):
# This is for case of closing the area or changing type:
@ -1286,16 +1277,16 @@ class AssetBarOperator(bpy.types.Operator):
# only generate tooltip once in a while
if (
event.type == 'LEFTMOUSE' or event.type == 'RIGHTMOUSE') and event.value == 'RELEASE' or event.type == 'ENTER' or ui_props.tooltip == '':
event.type == 'LEFTMOUSE' or event.type == 'RIGHTMOUSE') and event.value == 'RELEASE' or event.type == 'ENTER':
ao = bpy.context.active_object
if ui_props.asset_type == 'MODEL' and ao != None \
or ui_props.asset_type == 'MATERIAL' and ao != None and ao.active_material != None \
or ui_props.asset_type == 'BRUSH' and utils.get_active_brush() is not None \
or ui_props.asset_type == 'SCENE' or ui_props.asset_type == 'HDR':
export_data, upload_data = upload.get_upload_data(context=context, asset_type=ui_props.asset_type)
if upload_data:
# print(upload_data)
ui_props.tooltip = upload_data['displayName'] # search.generate_tooltip(upload_data)
# if upload_data:
# # print(upload_data)
# ui_props.tooltip = upload_data['displayName'] # search.generate_tooltip(upload_data)
return {'PASS_THROUGH'}
@ -1307,7 +1298,7 @@ class AssetBarOperator(bpy.types.Operator):
# If there aren't any results, we need no interaction(yet)
if sr is None:
return {'PASS_THROUGH'}
if len(sr) - ui_props.scrolloffset < (ui_props.wcount * ui_props.hcount) + 10:
if len(sr) - ui_props.scrolloffset < (ui_props.wcount * user_preferences.max_assetbar_rows) + 10:
self.search_more()
if event.type == 'WHEELUPMOUSE' or event.type == 'WHEELDOWNMOUSE' or event.type == 'TRACKPADPAN':
@ -1372,7 +1363,7 @@ class AssetBarOperator(bpy.types.Operator):
asset_data = sr[asset_search_index]
ui_props.draw_tooltip = True
ui_props.tooltip = asset_data['tooltip']
# ui_props.tooltip = asset_data['tooltip']
# bpy.ops.wm.call_menu(name='OBJECT_MT_blenderkit_asset_menu')
else:
@ -1538,18 +1529,16 @@ class AssetBarOperator(bpy.types.Operator):
update_ui_size(self.area, self.region)
if PROFILING:
self._handle_2d = bpy.types.SpaceView3D.draw_handler_add(draw_callback_2d_profiled, args, 'WINDOW', 'POST_PIXEL')
else:
self._handle_2d = bpy.types.SpaceView3D.draw_handler_add(draw_callback_2d, args, 'WINDOW', 'POST_PIXEL')
context.window_manager.modal_handler_add(self)
self._handle_2d = bpy.types.SpaceView3D.draw_handler_add(draw_callback_2d, args, 'WINDOW', 'POST_PIXEL')
ui_props.assetbar_on = True
# in an exceptional case these were accessed before drag start.
self.drag_start_x = 0
self.drag_start_y = 0
context.window_manager.modal_handler_add(self)
return {'RUNNING_MODAL'}
def execute(self, context):
@ -1815,8 +1804,11 @@ class AssetDragOperator(bpy.types.Operator):
sprops = bpy.context.scene.blenderkit_models
if event.type == 'WHEELUPMOUSE':
sprops.offset_rotation_amount += sprops.offset_rotation_step
return {'RUNNING_MODAL'}
elif event.type == 'WHEELDOWNMOUSE':
sprops.offset_rotation_amount -= sprops.offset_rotation_step
return {'RUNNING_MODAL'}
if event.type =='MOUSEMOVE':
@ -1837,6 +1829,7 @@ class AssetDragOperator(bpy.types.Operator):
if ui_props.asset_type == 'MODEL':
self.snapped_bbox_min = Vector(self.asset_data['bbox_min'])
self.snapped_bbox_max = Vector(self.asset_data['bbox_max'])
return {'RUNNING_MODAL'}
if event.type == 'LEFTMOUSE' and event.value == 'RELEASE':
self.mouse_release()# does the main job with assets
@ -1887,11 +1880,11 @@ class AssetDragOperator(bpy.types.Operator):
self._handle = bpy.types.SpaceView3D.draw_handler_add(draw_callback_dragging, args, 'WINDOW', 'POST_PIXEL')
self._handle_3d = bpy.types.SpaceView3D.draw_handler_add(draw_callback_3d_dragging, args, 'WINDOW',
'POST_VIEW')
context.window_manager.modal_handler_add(self)
bpy.context.window.cursor_set("NONE")
ui_props = bpy.context.scene.blenderkitUI
ui_props.dragging = True
context.window_manager.modal_handler_add(self)
return {'RUNNING_MODAL'}
else:
self.report({'WARNING'}, "View3D not found, cannot run operator")

View File

@ -1854,7 +1854,7 @@ class AssetPopupCard(bpy.types.Operator, ratings_utils.RatingsProperties):
# name_row = name_row.row()
for i, c in enumerate(cat_path):
cat_name = cat_path_names[i]
op = name_row.operator('view3d.blenderkit_asset_bar', text=cat_name + ' >', emboss=True)
op = name_row.operator('view3d.blenderkit_asset_bar', text=cat_name + ' >', emboss=False)
op.do_search = True
op.keep_running = True
op.tooltip = f"Browse {cat_name} category"

View File

@ -1274,16 +1274,11 @@ class AssetDebugPrint(Operator):
return {'CANCELLED'};
# update status in search results for validator's clarity
sr = bpy.context.window_manager['search results']
sro = bpy.context.window_manager['search results orig']['results']
result = None
for r in sr:
if r['id'] == self.asset_id:
result = r.to_dict()
if not result:
for r in sro:
if r['id'] == self.asset_id:
result = r.to_dict()
if not result:
ad = bpy.context.active_object.get('asset_data')
if ad:
@ -1329,14 +1324,10 @@ class AssetVerificationStatusChange(Operator):
return {'CANCELLED'};
# update status in search results for validator's clarity
sr = bpy.context.window_manager['search results']
sro = bpy.context.window_manager['search results orig']['results']
for r in sr:
if r['id'] == self.asset_id:
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))
@ -1353,7 +1344,6 @@ class AssetVerificationStatusChange(Operator):
def register_upload():
bpy.utils.register_class(UploadOperator)
# bpy.utils.register_class(FastMetadataMenu)
bpy.utils.register_class(FastMetadata)
bpy.utils.register_class(AssetDebugPrint)
bpy.utils.register_class(AssetVerificationStatusChange)
@ -1361,7 +1351,6 @@ def register_upload():
def unregister_upload():
bpy.utils.unregister_class(UploadOperator)
# bpy.utils.unregister_class(FastMetadataMenu)
bpy.utils.unregister_class(FastMetadata)
bpy.utils.unregister_class(AssetDebugPrint)
bpy.utils.unregister_class(AssetVerificationStatusChange)