BlenderKit: optimize search

quite a large overhaul of how search results are loaded.
this saves time due to not closing the session and enabling small previews to be first.
Also use the dict_param paraameter so that the results responses are a bit smaller.
This commit is contained in:
Vilem Duha 2021-07-26 23:08:13 +02:00
parent 0d6b2e8767
commit 69ad4c43d0
8 changed files with 173 additions and 151 deletions

View File

@ -1704,7 +1704,7 @@ class BlenderKitAddonPreferences(AddonPreferences):
max_assetbar_rows: IntProperty(name="Max Assetbar Rows",
description="max rows of assetbar in the 3D view",
default=1,
min=0,
min=1,
max=20)
thumb_size: IntProperty(name="Assetbar thumbnail Size", default=96, min=-1, max=256)

View File

@ -518,7 +518,7 @@ class BlenderKitAssetBarOperator(BL_UI_OT_draw_operator):
self.active_index = widget.search_index
self.draw_tooltip = True
self.tooltip = asset_data['tooltip']
# self.tooltip = asset_data['tooltip']
ui_props = scene.blenderkitUI
ui_props.active_index = widget.search_index +self.scroll_offset

View File

@ -52,7 +52,6 @@ import urllib
import queue
import logging
bk_logger = logging.getLogger('blenderkit')
search_start_time = 0
@ -77,9 +76,12 @@ def check_errors(rdata):
search_threads = []
thumb_sml_download_threads = {}
thumb_full_download_threads = {}
thumb_workers_sml = []
thumb_workers_full = []
thumb_sml_download_threads = queue.Queue()
thumb_full_download_threads = queue.Queue()
reports_queue = queue.Queue()
all_thumbs_loaded = True
rtips = ['Click or drag model or material in scene to link/append ',
"Please rate responsively and plentifully. This helps us distribute rewards to the authors.",
@ -282,7 +284,7 @@ def parse_result(r):
if r['available_resolutions']: # should check only for non-empty sequences
r['max_resolution'] = max(r['available_resolutions'])
tooltip = generate_tooltip(r)
# tooltip = generate_tooltip(r)
# for some reason, the id was still int on some occurances. investigate this.
r['author']['id'] = str(r['author']['id'])
@ -290,13 +292,13 @@ def parse_result(r):
# so blender's data is same as on server.
asset_data = {'thumbnail': tname,
'thumbnail_small': small_tname,
'tooltip': tooltip,
# 'tooltip': tooltip,
}
asset_data['downloaded'] = 0
# parse extra params needed for blender here
params = utils.params_to_dict(r['parameters'])
params = r['dictParameters']#utils.params_to_dict(r['parameters'])
if asset_type == 'model':
if params.get('boundBoxMinX') != None:
@ -343,7 +345,6 @@ 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')
@ -371,6 +372,19 @@ def search_timer():
# check_clipboard()
# finish loading thumbs from queues
global all_thumbs_loaded
if not all_thumbs_loaded:
ui_props = bpy.context.scene.blenderkitUI
search_name = f'bkit {ui_props.asset_type.lower()} search'
wm = bpy.context.window_manager
if wm.get(search_name) is not None:
all_loaded = True
for ri, r in enumerate(wm[search_name]):
if not r.get('thumb_small_loaded'):
all_loaded = all_loaded and load_preview(r, ri)
all_thumbs_loaded = all_loaded
global search_threads
if len(search_threads) == 0:
# utils.p('end search timer')
@ -384,6 +398,8 @@ def 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
@ -417,18 +433,16 @@ def search_timer():
rdata = thread[0].result
ok, error = check_errors(rdata)
if ok:
ui_props = bpy.context.scene.blenderkitUI
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)
all_thumbs_loaded = all_thumbs_loaded and load_preview(asset_data, ri + orig_len)
# Get ratings from BlenderKit server
user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
@ -437,13 +451,14 @@ def search_timer():
if utils.profile_is_validator():
for r in rdata['results']:
if ratings_utils.get_rating_local(asset_data['id']) is None:
rating_thread = threading.Thread(target=ratings_utils.get_rating, args=([r['id'], headers]), daemon=True)
rating_thread = threading.Thread(target=ratings_utils.get_rating, args=([r['id'], headers]),
daemon=True)
rating_thread.start()
wm[search_name] = result_field
wm['search results'] = result_field
#rdata=['results']=[]
# rdata=['results']=[]
wm[search_name + ' orig'] = rdata
wm['search results orig'] = rdata
@ -456,7 +471,7 @@ def search_timer():
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.
# 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()
@ -469,9 +484,11 @@ def search_timer():
# print('finished search thread')
mt('preview loading finished')
# utils.p('end search timer')
if not all_thumbs_loaded:
return .1
return .3
def load_preview(asset, index):
scene = bpy.context.scene
# FIRST START SEARCH
@ -479,11 +496,13 @@ def load_preview(asset, index):
directory = paths.get_temp_dir('%s_search' % props.asset_type.lower())
s = bpy.context.scene
results = bpy.context.window_manager.get('search results')
loaded = True
tpath = os.path.join(directory, asset['thumbnail_small'])
if not asset['thumbnail_small']:
tpath = paths.get_addon_thumbnail_path('thumbnail_not_available.jpg')
if not asset['thumbnail_small'] or not os.path.exists(tpath):
# tpath = paths.get_addon_thumbnail_path('thumbnail_notready.jpg')
asset['thumb_small_loaded'] = False
loaded = False
iname = utils.previmg_name(index)
@ -492,14 +511,16 @@ def load_preview(asset, index):
if img is None:
if not os.path.exists(tpath):
return
return False
img = bpy.data.images.load(tpath)
img.name = iname
elif img.filepath != tpath:
if not os.path.exists(tpath):
return
#unload loaded previews from previous results
bpy.data.images.remove(img)
return False
# had to add this check for autopacking files...
if img.packed_file is not None:
if bpy.data.use_autopack and img.packed_file is not None:
img.unpack(method='USE_ORIGINAL')
img.filepath = tpath
img.reload()
@ -508,6 +529,8 @@ def load_preview(asset, index):
image_utils.set_colorspace(img, 'Non-Color')
else:
image_utils.set_colorspace(img, 'sRGB')
asset['thumb_small_loaded'] = True
return loaded
def load_previews():
@ -521,7 +544,7 @@ def load_previews():
if results is not None:
i = 0
for r in results:
load_preview(r,i)
load_preview(r, i)
i += 1
@ -635,14 +658,47 @@ def generate_author_textblock(adata):
t = writeblockm(t, adata, key='aboutMe', pretext='', width=col_w)
return t
def download_image(session, url, filepath):
r = None
try:
r = session.get(url, stream=False)
except Exception as e:
bk_logger.error('Thumbnail download failed')
bk_logger.error(str(e))
if r and r.status_code == 200:
with open(filepath, 'wb') as f:
f.write(r.content)
def thumb_download_worker(queue_sml, queue_full):
# print('thumb downloader', self.url)
# utils.p('start thumbdownloader thread')
while 1:
session = None
#start a session only for single search usually.
if not queue_sml.empty() or not queue_full.empty():
session = requests.Session()
while not queue_sml.empty():
url, filepath = queue_sml.get()
download_image(session,url, filepath)
exit_full = False
# download full resolution image, but only if no small thumbs are waiting.
while not queue_full.empty() and queue_sml.empty():
url, filepath = queue_full.get()
download_image(session,url, filepath)
if queue_sml.empty() and queue_full.empty():
if session is not None:
session.close()
time.sleep(.5)
class ThumbDownloader(threading.Thread):
query = None
def __init__(self, url, path):
def __init__(self, url, path, session):
super(ThumbDownloader, self).__init__()
self.url = url
self.path = path
self.session = session
self._stop_event = threading.Event()
def stop(self):
@ -656,7 +712,7 @@ class ThumbDownloader(threading.Thread):
# utils.p('start thumbdownloader thread')
r = None
try:
r = requests.get(self.url, stream=False)
r = self.session.get(self.url, stream=False)
except Exception as e:
bk_logger.error('Thumbnail download failed')
bk_logger.error(str(e))
@ -670,7 +726,6 @@ class ThumbDownloader(threading.Thread):
# utils.p('end thumbdownloader thread')
def write_gravatar(a_id, gravatar_path):
'''
Write down gravatar path, as a result of thread-based gravatar image download.
@ -693,14 +748,14 @@ def fetch_gravatar(adata):
'''
# utils.p('fetch gravatar')
#fetch new avatars if available already
# fetch new avatars if available already
if adata.get('avatar128') is not None:
avatar_path = paths.get_temp_dir(subdir='bkit_g/') + adata['id']+ '.jpg'
avatar_path = paths.get_temp_dir(subdir='bkit_g/') + adata['id'] + '.jpg'
if os.path.exists(avatar_path):
tasks_queue.add_task((write_gravatar, (adata['id'], avatar_path)))
return;
url= paths.get_bkit_url() + adata['avatar128']
url = paths.get_bkit_url() + adata['avatar128']
r = rerequests.get(url, stream=False)
# print(r.body)
if r.status_code == 200:
@ -714,7 +769,7 @@ def fetch_gravatar(adata):
utils.p('avatar for author not available.')
return
#older gravatar code
# older gravatar code
if adata.get('gravatarHash') is not None:
gravatar_path = paths.get_temp_dir(subdir='bkit_g/') + adata['gravatarHash'] + '.jpg'
@ -824,6 +879,7 @@ def query_to_url(query={}, params={}):
requeststring += '+'
requeststring += q + ':' + str(query[q]).lower()
# add dict_parameters to make results smaller
# result ordering: _score - relevance, score - BlenderKit score
order = []
if params['free_first']:
@ -844,8 +900,9 @@ def query_to_url(query={}, params={}):
order.append('-score,_score')
else:
order.append('_score')
if requeststring.find('+order:')==-1:
if requeststring.find('+order:') == -1:
requeststring += '+order:' + ','.join(order)
requeststring += '&dict_parameters=1'
requeststring += '&page_size=' + str(params['page_size'])
requeststring += '&addon_version=%s' % params['addon_version']
@ -882,7 +939,7 @@ class Searcher(threading.Thread):
return self._stop_event.is_set()
def run(self):
global reports_queue
global reports_queue, thumb_sml_download_threads, thumb_full_download_threads
maxthreads = 50
query = self.query
@ -912,7 +969,7 @@ class Searcher(threading.Thread):
try:
rdata = r.json()
except Exception as e:
if hasattr(r,'text'):
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)))
@ -987,62 +1044,21 @@ class Searcher(threading.Thread):
# we can also prepend previous results. These have downloaded thumbnails already...
self.result = rdata
# with open(json_filepath, 'w', encoding = 'utf-8') as outfile:
# json.dump(rdata, outfile, ensure_ascii=False, indent=4)
killthreads_sml = []
for k in thumb_sml_download_threads.keys():
if k not in thumb_small_filepaths:
killthreads_sml.append(k) # do actual killing here?
killthreads_full = []
for k in thumb_full_download_threads.keys():
if k not in thumb_full_filepaths:
killthreads_full.append(k) # do actual killing here?
# TODO do the killing/ stopping here. remember threads might have finished inbetween.
if self.stopped():
utils.p('stopping search : ' + str(query))
# utils.p('end search thread')
return
# this loop handles downloading of small thumbnails
for imgpath, url in sml_thbs:
if imgpath not in thumb_sml_download_threads and not os.path.exists(imgpath):
thread = ThumbDownloader(url, imgpath)
# thread = threading.Thread(target=download_thumbnail, args=([url, imgpath]),
# daemon=True)
thread.start()
thumb_sml_download_threads[imgpath] = thread
# threads.append(thread)
if not os.path.exists(imgpath):
thumb_sml_download_threads.put((url, imgpath))
if len(thumb_sml_download_threads) > maxthreads:
while len(thumb_sml_download_threads) > maxthreads:
threads_copy = thumb_sml_download_threads.copy() # because for loop can erase some of the items.
for tk, thread in threads_copy.items():
if not thread.is_alive():
thread.join()
# utils.p(x)
del (thumb_sml_download_threads[tk])
# utils.p('fetched thumbnail ', i)
i += 1
if self.stopped():
utils.p('stopping search : ' + str(query))
# utils.p('end search thread')
return
idx = 0
while len(thumb_sml_download_threads) > 0:
threads_copy = thumb_sml_download_threads.copy() # because for loop can erase some of the items.
for tk, thread in threads_copy.items():
if not thread.is_alive():
thread.join()
try:
del (thumb_sml_download_threads[tk])
except Exception as e:
print(e)
i += 1
if self.stopped():
# utils.p('end search thread')
@ -1051,13 +1067,11 @@ class Searcher(threading.Thread):
return
# start downloading full thumbs in the end
tsession = requests.Session()
for imgpath, url in full_thbs:
if imgpath not in thumb_full_download_threads and not os.path.exists(imgpath):
thread = ThumbDownloader(url, imgpath)
# thread = threading.Thread(target=download_thumbnail, args=([url, imgpath]),
# daemon=True)
thread.start()
thumb_full_download_threads[imgpath] = thread
if not os.path.exists(imgpath):
thumb_full_download_threads.put((url, imgpath))
# utils.p('end search thread')
mt('thumbnails finished')
@ -1067,7 +1081,7 @@ def build_query_common(query, props):
query_common = {}
if props.search_keywords != '':
# keywords = urllib.parse.urlencode(props.search_keywords)
keywords = props.search_keywords.replace('&','%26')
keywords = props.search_keywords.replace('&', '%26')
query_common["query"] = keywords
if props.search_verification_status != 'ALL' and utils.profile_is_validator():
@ -1234,7 +1248,7 @@ def mt(text):
def add_search_process(query, params):
global search_threads
global search_threads, thumb_workers_sml, thumb_workers_full, all_thumbs_loaded
while (len(search_threads) > 0):
old_thread = search_threads.pop(0)
@ -1249,6 +1263,16 @@ def add_search_process(query, params):
else:
urlquery = query_to_url(query, params)
if thumb_workers_sml == []:
for a in range(0, 8):
# worker = ThumbDownloadWorker(thumb_sml_download_threads, thumb_full_download_threads)
thread = threading.Thread(target=thumb_download_worker, args=(thumb_sml_download_threads, thumb_full_download_threads),
daemon=True)
thread.start()
thumb_workers_sml.append(thread)
all_thumbs_loaded = False
thread = Searcher(query, params, tempdir=tempdir, headers=headers, urlquery=urlquery)
thread.start()
@ -1405,22 +1429,23 @@ def search(category='', get_next=False, author_id=''):
props.report = 'BlenderKit searching....'
def update_filters():
sprops = utils.get_search_props()
ui_props = bpy.context.scene.blenderkitUI
fcommon = sprops.own_only or \
sprops.search_texture_resolution or\
sprops.search_file_size or \
sprops.search_procedural != 'BOTH' or \
sprops.free_only or \
sprops.quality_limit>0
sprops.search_texture_resolution or \
sprops.search_file_size or \
sprops.search_procedural != 'BOTH' or \
sprops.free_only or \
sprops.quality_limit > 0
if ui_props.asset_type =='MODEL':
if ui_props.asset_type == 'MODEL':
sprops.use_filters = fcommon or \
sprops.search_style != 'ANY' or \
sprops.search_condition != 'UNSPECIFIED' or \
sprops.search_design_year or \
sprops.search_polycount
sprops.search_condition != 'UNSPECIFIED' or \
sprops.search_design_year or \
sprops.search_polycount
elif ui_props.asset_type == 'MATERIAL':
sprops.use_filters = fcommon
@ -1522,7 +1547,6 @@ class SearchOperator(Operator):
# description='Try to close the window below mouse before download',
# default=False)
tooltip: bpy.props.StringProperty(default='Runs search and displays the asset bar at the same time')
@classmethod
@ -1556,6 +1580,7 @@ class SearchOperator(Operator):
# context.window.cursor_warp(event.mouse_x, event.mouse_y);
# return self. execute(context)
class UrlOperator(Operator):
""""""
bl_idname = "wm.blenderkit_url"
@ -1574,6 +1599,7 @@ class UrlOperator(Operator):
bpy.ops.wm.url_open(url=self.url)
return {'FINISHED'}
class TooltipLabelOperator(Operator):
""""""
bl_idname = "wm.blenderkit_tooltip"
@ -1590,6 +1616,7 @@ class TooltipLabelOperator(Operator):
def execute(self, context):
return {'FINISHED'}
classes = [
SearchOperator,
UrlOperator,
@ -1605,7 +1632,6 @@ 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()
@ -1619,5 +1645,3 @@ def unregister_search():
if bpy.app.timers.is_registered(search_timer):
bpy.app.timers.unregister(search_timer)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@ -40,7 +40,6 @@ import os
import logging
draw_time = 0
eval_time = 0
@ -361,7 +360,7 @@ def draw_tooltip(x, y, name='', author='', quality='-', img=None, gravatar=None)
# draw author's name
author_text_size = int(name_height * .7)
ui_bgl.draw_text(author, author_x_text, gravatar_y, author_text_size, textcol, ralign=True)
ui_bgl.draw_text(author, author_x_text, gravatar_y, author_text_size, textcol, halign='RIGHT')
# draw quality
quality_text_size = int(name_height * 1)
@ -385,7 +384,7 @@ def draw_tooltip_with_author(asset_data, x, y):
if a.get('gravatarImg') is not None:
gimg = utils.get_hidden_image(a['gravatarImg'], a['gravatarHash']).name
if len(a['firstName'])>0 or len(a['lastName'])>0:
if len(a['firstName']) > 0 or len(a['lastName']) > 0:
author_text = f"by {a['firstName']} {a['lastName']}"
aname = asset_data['displayName']
@ -398,13 +397,13 @@ def draw_tooltip_with_author(asset_data, x, y):
rcount = 0
quality = '-'
if rc:
rcount = min(rc.get('quality',0), rc.get('workingHours',0))
rcount = min(rc.get('quality', 0), rc.get('workingHours', 0))
if rcount > show_rating_threshold:
quality = round(asset_data['ratingsAverage'].get('quality'))
tooltip_data={
tooltip_data = {
'aname': aname,
'author_text': author_text,
'quality':quality,
'quality': quality,
'gimg': gimg
}
asset_data['tooltip_data'] = tooltip_data
@ -419,7 +418,6 @@ def draw_tooltip_with_author(asset_data, x, y):
gravatar=gimg)
def draw_callback_2d(self, context):
if not utils.guard_from_crash():
return
@ -699,7 +697,9 @@ def draw_asset_bar(self, context):
# w + 2*highlight_margin, h + 2*highlight_margin , highlight)
else:
ui_bgl.draw_rect(x, y, ui_props.thumb_size, ui_props.thumb_size, white)
ui_bgl.draw_rect(x, y, ui_props.thumb_size, ui_props.thumb_size, grey2)
ui_bgl.draw_text('loading', x + ui_props.thumb_size // 2, y + ui_props.thumb_size // 2,
ui_props.thumb_size // 6, white, halign='CENTER', valign='CENTER')
result = search_results[index]
# code to inform validators that the validation is waiting too long and should be done asap
@ -744,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 and len(search_results)>ui_props.active_index:
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
@ -1193,7 +1193,6 @@ class AssetBarOperator(bpy.types.Operator):
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:
@ -1256,7 +1255,6 @@ class AssetBarOperator(bpy.types.Operator):
self.area.tag_redraw()
s = context.scene
if ui_props.turn_off:
ui_props.turn_off = False
self.exit_modal()
@ -1432,7 +1430,6 @@ class AssetBarOperator(bpy.types.Operator):
ui_props.scrolloffset = max(0, ui_props.scrolloffset - ui_props.wcount * ui_props.hcount)
return {'RUNNING_MODAL'}
if ui_props.active_index == -3:
return {'RUNNING_MODAL'}
else:
@ -1529,7 +1526,6 @@ class AssetBarOperator(bpy.types.Operator):
update_ui_size(self.area, self.region)
self._handle_2d = bpy.types.SpaceView3D.draw_handler_add(draw_callback_2d, args, 'WINDOW', 'POST_PIXEL')
ui_props.assetbar_on = True
@ -1592,10 +1588,10 @@ def draw_callback_dragging(self, context):
try:
img = bpy.data.images.get(self.iname)
except:
# 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',
# bpy.types.SpaceView3D.draw_handler_remove(self._handle,
# bpy.types.SpaceView3D.draw_handler_remove(self._handle_3d, 'WINDOW')
# 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',
# bpy.types.SpaceView3D.draw_handler_remove(self._handle,
# bpy.types.SpaceView3D.draw_handler_remove(self._handle_3d, 'WINDOW')
return
linelength = 35
@ -1646,7 +1642,7 @@ class AssetDragOperator(bpy.types.Operator):
if ui_props.asset_type == 'MODEL':
if not self.drag:
self.snapped_location = scene.cursor.location
self.snapped_rotation = (0,0,0)
self.snapped_rotation = (0, 0, 0)
target_object = ''
if self.object_name is not None:
@ -1671,7 +1667,7 @@ class AssetDragOperator(bpy.types.Operator):
target_object = ''
target_slot = ''
if not self.drag:
#click interaction
# click interaction
object = bpy.context.active_object
if object is None:
ui_panels.ui_message(title='Nothing selected',
@ -1725,7 +1721,6 @@ class AssetDragOperator(bpy.types.Operator):
message=f"Can't assign materials to {object.type.lower()} object.")
return
if target_object != '':
# position is for downloader:
loc = self.snapped_location
@ -1741,16 +1736,13 @@ class AssetDragOperator(bpy.types.Operator):
target_object=target_object,
material_target_slot=target_slot)
if ui_props.asset_type == 'HDR':
bpy.ops.scene.blenderkit_download('INVOKE_DEFAULT',
asset_index=self.asset_search_index,
# replace_resolution=True,
invoke_resolution=True,
max_resolution=self.asset_data.get('max_resolution', 0)
)
asset_index=self.asset_search_index,
# replace_resolution=True,
invoke_resolution=True,
max_resolution=self.asset_data.get('max_resolution', 0)
)
if ui_props.asset_type == 'SCENE':
bpy.ops.scene.blenderkit_download('INVOKE_DEFAULT',
@ -1778,18 +1770,18 @@ class AssetDragOperator(bpy.types.Operator):
self.mouse_x = event.mouse_region_x
self.mouse_y = event.mouse_region_y
#are we dragging already?
# are we dragging already?
drag_threshold = 10
if not self.drag and \
(abs(self.start_mouse_x - self.mouse_x) > drag_threshold or \
abs(self.start_mouse_y - self.mouse_y) > drag_threshold):
abs(self.start_mouse_y - self.mouse_y) > drag_threshold):
self.drag = True
#turn off asset bar here, shout start again after finishing drag drop.
# turn off asset bar here, shout start again after finishing drag drop.
ui_props.turn_off = True
if (event.type == 'ESC' or \
not mouse_in_region(context.region, self.mouse_x, self.mouse_y))and \
(not self.drag or self.steps<5):
not mouse_in_region(context.region, self.mouse_x, self.mouse_y)) and \
(not self.drag or self.steps < 5):
# this case is for canceling from inside popup card when there's an escape attempt to close the window
return {'PASS_THROUGH'}
@ -1808,8 +1800,7 @@ class AssetDragOperator(bpy.types.Operator):
sprops.offset_rotation_amount -= sprops.offset_rotation_step
return {'RUNNING_MODAL'}
if event.type =='MOUSEMOVE':
if event.type == 'MOUSEMOVE':
#### TODO - this snapping code below is 3x in this file.... refactor it.
self.has_hit, self.snapped_location, self.snapped_normal, self.snapped_rotation, self.face_index, object, self.matrix = mouse_raycast(
@ -1831,14 +1822,14 @@ class AssetDragOperator(bpy.types.Operator):
return {'RUNNING_MODAL'}
if event.type == 'LEFTMOUSE' and event.value == 'RELEASE':
self.mouse_release()# does the main job with assets
self.mouse_release() # does the main job with assets
self.handlers_remove()
bpy.context.window.cursor_set("DEFAULT")
bpy.ops.object.run_assetbar_fix_context(keep_running = True, do_search = False)
bpy.ops.object.run_assetbar_fix_context(keep_running=True, do_search=False)
ui_props.dragging = False
return {'FINISHED'}
self.steps +=1
self.steps += 1
return {'RUNNING_MODAL'}
@ -1850,8 +1841,6 @@ class AssetDragOperator(bpy.types.Operator):
# draw in view space with 'POST_VIEW' and 'PRE_VIEW'
self.iname = utils.previmg_name(self.asset_search_index)
self.mouse_x = 0
self.mouse_y = 0
self.steps = 0

View File

@ -134,14 +134,20 @@ def draw_image(x, y, width, height, image, transparency, crop=(0, 0, 1, 1), batc
return batch
def draw_text(text, x, y, size, color=(1, 1, 1, 0.5), ralign = False):
def draw_text(text, x, y, size, color=(1, 1, 1, 0.5), halign = 'LEFT', valign = 'TOP'):
font_id = 1
# bgl.glColor4f(*color)
blf.color(font_id, color[0], color[1], color[2], color[3])
blf.size(font_id, size, 72)
if ralign:
if halign != 'LEFT':
width,height = blf.dimensions(font_id, text)
x-=width
if halign == 'RIGHT':
x-=width
elif halign == 'CENTER':
x-=width//2
if valign=='CENTER':
y-=height//2
#bottom could be here but there's no reason for it
blf.position(font_id, x, y, 0)
blf.draw(font_id, text)

View File

@ -1506,10 +1506,11 @@ class AssetPopupCard(bpy.types.Operator, ratings_utils.RatingsProperties):
def draw_properties(self, layout, width=250):
if type(self.asset_data['parameters']) == list:
mparams = utils.params_to_dict(self.asset_data['parameters'])
else:
mparams = self.asset_data['parameters']
# if type(self.asset_data['parameters']) == list:
# mparams = utils.params_to_dict(self.asset_data['parameters'])
# else:
# mparams = self.asset_data['parameters']
mparams = self.asset_data['dictParameters']
pcoll = icons.icon_collections["main"]

View File

@ -721,14 +721,16 @@ def fmt_length(prop):
def get_param(asset_data, parameter_name, default=None):
if not asset_data.get('parameters'):
if not asset_data.get('dictParameters'):
# this can appear in older version files.
return default
for p in asset_data['parameters']:
if p.get('parameterType') == parameter_name:
return p['value']
return default
return asset_data['dictParameters'].get(parameter_name, default)
# for p in asset_data['parameters']:
# if p.get('parameterType') == parameter_name:
# return p['value']
# return default
def params_to_dict(params):