diff options
author | Vilém Duha <vilda.novak@gmail.com> | 2021-04-29 13:13:40 +0300 |
---|---|---|
committer | Vilém Duha <vilda.novak@gmail.com> | 2021-04-29 13:13:59 +0300 |
commit | e670cee07525af65395a13f30f57ca5e656771a1 (patch) | |
tree | 6b5433dd9521bb63770ea82849db190be9f1f1d6 | |
parent | 452996ae951478853af3c1bcac3e0e1a26a6cab4 (diff) |
BlenderKit: improve right click menu
-basically a complete revamp of the code, enables to display asset information in a much cleaner way with more tooltips e.t.c.
-simplified floating asset preview - name needs to be fixed still, and tooltip generation cleaned
-added several new icons, deleted one unused
-improve a lot of tooltips
-fix rerender of thumbnails in unsaved files (would save assets into addon directory)
-reorganize some rating functions into ratings_utils.py
-new 'wrap' operator for web links, allows to have custom tooltips for each link
-rw-r--r-- | blenderkit/__init__.py | 10 | ||||
-rw-r--r-- | blenderkit/asset_bar_op.py | 11 | ||||
-rw-r--r-- | blenderkit/autothumb.py | 15 | ||||
-rw-r--r-- | blenderkit/autothumb_material_bg.py | 6 | ||||
-rw-r--r-- | blenderkit/autothumb_model_bg.py | 8 | ||||
-rw-r--r-- | blenderkit/icons.py | 16 | ||||
-rw-r--r-- | blenderkit/ratings.py | 217 | ||||
-rw-r--r-- | blenderkit/ratings_utils.py | 121 | ||||
-rw-r--r-- | blenderkit/search.py | 212 | ||||
-rw-r--r-- | blenderkit/thumbnails/cc0.png | bin | 0 -> 5419 bytes | |||
-rw-r--r-- | blenderkit/thumbnails/locked_large.png | bin | 3170 -> 0 bytes | |||
-rw-r--r-- | blenderkit/thumbnails/private.png | bin | 0 -> 540 bytes | |||
-rw-r--r-- | blenderkit/thumbnails/royalty_free.png | bin | 0 -> 4755 bytes | |||
-rw-r--r-- | blenderkit/thumbnails/trophy.png | bin | 0 -> 3078 bytes | |||
-rw-r--r-- | blenderkit/thumbnails/vs_validated.png | bin | 0 -> 2393 bytes | |||
-rw-r--r-- | blenderkit/ui.py | 32 | ||||
-rw-r--r-- | blenderkit/ui_panels.py | 483 | ||||
-rw-r--r-- | blenderkit/upload.py | 7 | ||||
-rw-r--r-- | blenderkit/utils.py | 13 |
19 files changed, 859 insertions, 292 deletions
diff --git a/blenderkit/__init__.py b/blenderkit/__init__.py index 9633ae50..02d17f0b 100644 --- a/blenderkit/__init__.py +++ b/blenderkit/__init__.py @@ -49,6 +49,7 @@ if "bpy" in locals(): overrides = reload(overrides) paths = reload(paths) ratings = reload(ratings) + ratings_utils = reload(ratings_utils) resolutions = reload(resolutions) search = reload(search) tasks_queue = reload(tasks_queue) @@ -84,6 +85,7 @@ else: from blenderkit import overrides from blenderkit import paths from blenderkit import ratings + from blenderkit import ratings_utils from blenderkit import resolutions from blenderkit import search from blenderkit import tasks_queue @@ -731,20 +733,20 @@ class BlenderKitRatingProps(PropertyGroup): description="quality of the material", default=0, min=-1, max=10, - update=ratings.update_ratings_quality) + update=ratings_utils.update_ratings_quality) # the following enum is only to ease interaction - enums support 'drag over' and enable to draw the stars easily. rating_quality_ui: EnumProperty(name='rating_quality_ui', - items=ratings.stars_enum_callback, + items=ratings_utils.stars_enum_callback, description='Rating stars 0 - 10', default=None, - update=ratings.update_quality_ui, + update=ratings_utils.update_quality_ui, ) rating_work_hours: FloatProperty(name="Work Hours", description="How many hours did this work take?", default=0.00, - min=0.0, max=150, update=ratings.update_ratings_work_hours + min=0.0, max=150, update=ratings_utils.update_ratings_work_hours ) # rating_complexity: IntProperty(name="Complexity", diff --git a/blenderkit/asset_bar_op.py b/blenderkit/asset_bar_op.py index acb9cf94..30d94e27 100644 --- a/blenderkit/asset_bar_op.py +++ b/blenderkit/asset_bar_op.py @@ -225,9 +225,9 @@ class BlenderKitAssetBarOperator(BL_UI_OT_draw_operator): self.asset_name = name_label self.tooltip_widgets.append(name_label) offset_y = 16 + self.margin - label = self.new_text('Left click or drag to append/link. Right click for more options.', self.assetbar_margin*2, labels_start + offset_y, - text_size=14) - self.tooltip_widgets.append(label) + # label = self.new_text('Left click or drag to append/link. Right click for more options.', self.assetbar_margin*2, labels_start + offset_y, + # text_size=14) + # self.tooltip_widgets.append(label) self.hide_tooltip() @@ -505,6 +505,9 @@ class BlenderKitAssetBarOperator(BL_UI_OT_draw_operator): # handlers def enter_button(self, widget): + # context.window.cursor_warp(event.mouse_x, event.mouse_y - 20); + + self.show_tooltip() if self.active_index != widget.search_index: @@ -532,6 +535,8 @@ class BlenderKitAssetBarOperator(BL_UI_OT_draw_operator): self.tooltip_panel.update(tooltip_x, widget.y_screen + widget.height) self.tooltip_panel.layout_widgets() + # bpy.ops.wm.blenderkit_asset_popup('INVOKE_DEFAULT') + def exit_button(self, widget): # this condition checks if there wasn't another button already entered, which can happen with small button gaps diff --git a/blenderkit/autothumb.py b/blenderkit/autothumb.py index 26697d74..baf1a2ca 100644 --- a/blenderkit/autothumb.py +++ b/blenderkit/autothumb.py @@ -156,7 +156,7 @@ def start_thumbnailer(self=None, json_args=None, props=None, wait=False, add_bg_ eval_path_state = "bpy.data.objects['%s'].blenderkit.thumbnail_generating_state" % json_args['asset_name'] eval_path = "bpy.data.objects['%s']" % json_args['asset_name'] - bg_blender.add_bg_process(eval_path_computing=eval_path_computing, eval_path_state=eval_path_state, + bg_blender.add_bg_process(name = f"{json_args['asset_name']} thumbnailer" ,eval_path_computing=eval_path_computing, eval_path_state=eval_path_state, eval_path=eval_path, process_type='THUMBNAILER', process=proc) @@ -206,7 +206,7 @@ def start_material_thumbnailer(self=None, json_args=None, props=None, wait=False eval_path_state = "bpy.data.materials['%s'].blenderkit.thumbnail_generating_state" % json_args['asset_name'] eval_path = "bpy.data.materials['%s']" % json_args['asset_name'] - bg_blender.add_bg_process(name=json_args['asset_name'], eval_path_computing=eval_path_computing, + bg_blender.add_bg_process(name=f"{json_args['asset_name']} thumbnailer", eval_path_computing=eval_path_computing, eval_path_state=eval_path_state, eval_path=eval_path, process_type='THUMBNAILER', process=proc) if props: @@ -328,7 +328,10 @@ class GenerateThumbnailOperator(bpy.types.Operator): class ReGenerateThumbnailOperator(bpy.types.Operator): - """Generate Cycles thumbnail for model assets""" + """ + Generate default thumbnail with Cycles renderer and upload it. + Works also for assets from search results, without being downloaded before. + """ bl_idname = "object.blenderkit_regenerate_thumbnail" bl_label = "BlenderKit Thumbnail Re-generate" bl_options = {'REGISTER', 'INTERNAL'} @@ -371,11 +374,9 @@ class ReGenerateThumbnailOperator(bpy.types.Operator): return True # bpy.context.view_layer.objects.active is not None def draw(self, context): - ob = bpy.context.active_object - while ob.parent is not None: - ob = ob.parent props = self layout = self.layout + # layout.label('This will re-generate thumbnail and directly upload it to server. You should see your updated thumbnail online depending ') layout.label(text='thumbnailer settings') layout.prop(props, 'thumbnail_background_lightness') layout.prop(props, 'thumbnail_angle') @@ -521,7 +522,7 @@ class GenerateMaterialThumbnailOperator(bpy.types.Operator): class ReGenerateMaterialThumbnailOperator(bpy.types.Operator): """ - Generate default thumbnail with Cycles renderer. + Generate default thumbnail with Cycles renderer and upload it. Works also for assets from search results, without being downloaded before. """ bl_idname = "object.blenderkit_regenerate_material_thumbnail" diff --git a/blenderkit/autothumb_material_bg.py b/blenderkit/autothumb_material_bg.py index 0a0ce5db..3ae0d5dd 100644 --- a/blenderkit/autothumb_material_bg.py +++ b/blenderkit/autothumb_material_bg.py @@ -20,7 +20,7 @@ from blenderkit import utils, append_link, bg_blender, upload_bg, download -import sys, json, math +import sys, json, math, os import bpy from pathlib import Path @@ -48,6 +48,10 @@ if __name__ == "__main__": data = json.load(s) # append_material(file_name, matname = None, link = False, fake_user = True) if data.get('do_download'): + #need to save the file, so that asset doesn't get downloaded into addon directory + temp_blend_path = os.path.join(data['tempdir'], 'temp.blend') + bpy.ops.wm.save_as_mainfile(filepath=temp_blend_path) + asset_data = data['asset_data'] has_url = download.get_download_url(asset_data, download.get_scene_id(), user_preferences.api_key, tcom=None, resolution='blend') diff --git a/blenderkit/autothumb_model_bg.py b/blenderkit/autothumb_model_bg.py index 9be56d9f..d8e529fd 100644 --- a/blenderkit/autothumb_model_bg.py +++ b/blenderkit/autothumb_model_bg.py @@ -20,8 +20,7 @@ from blenderkit import utils, append_link, bg_blender, download, upload_bg -import sys, json, math -from pathlib import Path +import sys, json, math, os import bpy import mathutils @@ -86,8 +85,11 @@ if __name__ == "__main__": if data.get('do_download'): - bg_blender.progress('Downloading asset') + #need to save the file, so that asset doesn't get downloaded into addon directory + temp_blend_path = os.path.join(data['tempdir'], 'temp.blend') + bpy.ops.wm.save_as_mainfile(filepath = temp_blend_path) + bg_blender.progress('Downloading asset') asset_data = data['asset_data'] has_url = download.get_download_url(asset_data, download.get_scene_id(), user_preferences.api_key, tcom=None, resolution='blend') diff --git a/blenderkit/icons.py b/blenderkit/icons.py index 45c729df..ab17efcf 100644 --- a/blenderkit/icons.py +++ b/blenderkit/icons.py @@ -27,9 +27,23 @@ icon_collections = {} icons_read = { 'fp.png': 'free', 'flp.png': 'full', - 'test.jpg': 'test', + 'trophy.png': 'trophy', + 'cc0.png': 'cc0', + 'royalty_free.png': 'royalty_free', } +verification_icons = { + 'vs_ready.png':'ready', + 'vs_deleted.png':'deleted' , + 'vs_uploaded.png': 'uploaded', + 'vs_uploading.png': 'uploading', + 'vs_on_hold.png': 'on_hold', + 'vs_validated.png': 'validated', + 'vs_rejected.png': 'rejected' + +} + +icons_read.update(verification_icons) def register_icons(): # Note that preview collections returned by bpy.utils.previews diff --git a/blenderkit/ratings.py b/blenderkit/ratings.py index c7055905..118cc3ae 100644 --- a/blenderkit/ratings.py +++ b/blenderkit/ratings.py @@ -16,7 +16,7 @@ # # ##### END GPL LICENSE BLOCK ##### -from blenderkit import paths, utils, rerequests, tasks_queue +from blenderkit import paths, utils, rerequests, tasks_queue, ratings_utils import bpy import requests, threading @@ -103,42 +103,6 @@ def get_rating(asset_id): print(r.text) -def update_ratings_quality(self, context): - user_preferences = bpy.context.preferences.addons['blenderkit'].preferences - api_key = user_preferences.api_key - - headers = utils.get_headers(api_key) - asset = self.id_data - if asset: - bkit_ratings = asset.bkit_ratings - url = paths.get_api_url() + 'assets/' + asset['asset_data']['id'] + '/rating/' - else: - # this part is for operator rating: - bkit_ratings = self - url = paths.get_api_url() + f'assets/{self.asset_id}/rating/' - - if bkit_ratings.rating_quality > 0.1: - ratings = [('quality', bkit_ratings.rating_quality)] - tasks_queue.add_task((send_rating_to_thread_quality, (url, ratings, headers)), wait=2.5, only_last=True) - - -def update_ratings_work_hours(self, context): - user_preferences = bpy.context.preferences.addons['blenderkit'].preferences - api_key = user_preferences.api_key - headers = utils.get_headers(api_key) - asset = self.id_data - if asset: - bkit_ratings = asset.bkit_ratings - url = paths.get_api_url() + 'assets/' + asset['asset_data']['id'] + '/rating/' - else: - # this part is for operator rating: - bkit_ratings = self - url = paths.get_api_url() + f'assets/{self.asset_id}/rating/' - - if bkit_ratings.rating_work_hours > 0.45: - ratings = [('working_hours', round(bkit_ratings.rating_work_hours, 1))] - tasks_queue.add_task((send_rating_to_thread_work_hours, (url, ratings, headers)), wait=2.5, only_last=True) - def upload_rating(asset): user_preferences = bpy.context.preferences.addons['blenderkit'].preferences @@ -242,69 +206,40 @@ class UploadRatingOperator(bpy.types.Operator): return wm.invoke_props_dialog(self) -def stars_enum_callback(self, context): - '''regenerates the enum property used to display rating stars, so that there are filled/empty stars correctly.''' - items = [] - for a in range(0, 10): - if self.rating_quality < a + 1: - icon = 'SOLO_OFF' - else: - icon = 'SOLO_ON' - # has to have something before the number in the value, otherwise fails on registration. - items.append((f'{a + 1}', f'{a + 1}', '', icon, a + 1)) - return items - - -def update_quality_ui(self, context): - '''Converts the _ui the enum into actual quality number.''' - user_preferences = bpy.context.preferences.addons['blenderkit'].preferences - if user_preferences.api_key == '': - # ui_panels.draw_not_logged_in(self, message='Please login/signup to rate assets.') - # bpy.ops.wm.call_menu(name='OBJECT_MT_blenderkit_login_menu') - # return - bpy.ops.wm.blenderkit_login('INVOKE_DEFAULT', - message='Please login/signup to rate assets. Clicking OK takes you to web login.') - # self.rating_quality_ui = '0' - self.rating_quality = int(self.rating_quality_ui) - -def update_ratings_work_hours_ui(self, context): - user_preferences = bpy.context.preferences.addons['blenderkit'].preferences - if user_preferences.api_key == '': - # ui_panels.draw_not_logged_in(self, message='Please login/signup to rate assets.') - # bpy.ops.wm.call_menu(name='OBJECT_MT_blenderkit_login_menu') - # return - bpy.ops.wm.blenderkit_login('INVOKE_DEFAULT', - message='Please login/signup to rate assets. Clicking OK takes you to web login.') - # self.rating_work_hours_ui = '0' - self.rating_work_hours = float(self.rating_work_hours_ui) +def draw_ratings_menu(self, context, layout): + col = layout.column() + # layout.template_icon_view(bkit_ratings, property, show_labels=False, scale=6.0, scale_popup=5.0) + row = col.row() + row.prop(self, 'rating_quality_ui', expand=True, icon_only=True, emboss=False) + # row.label(text=str(self.rating_quality)) + col.separator() + row = layout.row() + row.label(text=f"How many hours did this {self.asset_type} save you?") -def update_ratings_work_hours_ui_1_5(self, context): - user_preferences = bpy.context.preferences.addons['blenderkit'].preferences - if user_preferences.api_key == '': - # ui_panels.draw_not_logged_in(self, message='Please login/signup to rate assets.') - # bpy.ops.wm.call_menu(name='OBJECT_MT_blenderkit_login_menu') - # return - bpy.ops.wm.blenderkit_login('INVOKE_DEFAULT', - message='Please login/signup to rate assets. Clicking OK takes you to web login.') - # self.rating_work_hours_ui_1_5 = '0' - # print('updating 1-5') - # print(float(self.rating_work_hours_ui_1_5)) - self.rating_work_hours = float(self.rating_work_hours_ui_1_5) - -def update_ratings_work_hours_ui_1_10(self, context): - user_preferences = bpy.context.preferences.addons['blenderkit'].preferences - if user_preferences.api_key == '': - # ui_panels.draw_not_logged_in(self, message='Please login/signup to rate assets.') - # bpy.ops.wm.call_menu(name='OBJECT_MT_blenderkit_login_menu') - # return - bpy.ops.wm.blenderkit_login('INVOKE_DEFAULT', - message='Please login/signup to rate assets. Clicking OK takes you to web login.') - # self.rating_work_hours_ui_1_5 = '0' - # print('updating 1-5') - # print(float(self.rating_work_hours_ui_1_5)) - self.rating_work_hours = float(self.rating_work_hours_ui_1_10) + if self.asset_type in ('model', 'scene'): + row = layout.row() + if utils.profile_is_validator(): + col.prop(self, 'rating_work_hours') + row.prop(self, 'rating_work_hours_ui', expand=True, icon_only=False, emboss=True) + if float(self.rating_work_hours_ui) > 100: + utils.label_multiline(layout, + text=f"\nThat's huge! please be sure to give such rating only to godly {self.asset_type}s.\n", + width=500) + elif float(self.rating_work_hours_ui) > 18: + layout.separator() + + utils.label_multiline(layout, + text=f"\nThat's a lot! please be sure to give such rating only to amazing {self.asset_type}s.\n", + width=500) + + elif self.asset_type == 'hdr': + row = layout.row() + row.prop(self, 'rating_work_hours_ui_1_10', expand=True, icon_only=False, emboss=True) + else: + row = layout.row() + row.prop(self, 'rating_work_hours_ui_1_5', expand=True, icon_only=False, emboss=True) class FastRateMenu(Operator): @@ -341,22 +276,22 @@ class FastRateMenu(Operator): description="quality of the material", default=0, min=-1, max=10, - # update=update_ratings_quality, + # update=ratings_utils.update_ratings_quality, options={'SKIP_SAVE'}) # the following enum is only to ease interaction - enums support 'drag over' and enable to draw the stars easily. rating_quality_ui: EnumProperty(name='rating_quality_ui', - items=stars_enum_callback, + items=ratings_utils.stars_enum_callback, description='Rating stars 0 - 10', default=0, - update=update_quality_ui, + update=ratings_utils.update_quality_ui, options={'SKIP_SAVE'}) rating_work_hours: FloatProperty(name="Work Hours", description="How many hours did this work take?", default=0.00, min=0.0, max=300, - # update=update_ratings_work_hours, + # update=ratings_utils.update_ratings_work_hours, options={'SKIP_SAVE'} ) @@ -383,8 +318,8 @@ class FastRateMenu(Operator): ('200', '200', high_rating_warning), ('250', '250', high_rating_warning), ], - default='0', update=update_ratings_work_hours_ui, - options = {'SKIP_SAVE'} + default='0', update=ratings_utils.update_ratings_work_hours_ui, + options={'SKIP_SAVE'} ) rating_work_hours_ui_1_5: EnumProperty(name="Work Hours", @@ -399,28 +334,28 @@ class FastRateMenu(Operator): ('5', '5', '') ], default='0', - update=update_ratings_work_hours_ui_1_5, - options = {'SKIP_SAVE'} + update=ratings_utils.update_ratings_work_hours_ui_1_5, + options={'SKIP_SAVE'} ) rating_work_hours_ui_1_10: EnumProperty(name="Work Hours", - description="How many hours did this work take?", - items=[('0', '0', ''), - ('1', '1', ''), - ('2', '2', ''), - ('3', '3', ''), - ('4', '4', ''), - ('5', '5', ''), - ('6', '6', ''), - ('7', '7', ''), - ('8', '8', ''), - ('9', '9', ''), - ('10', '10', '') - ], - default='0', - update=update_ratings_work_hours_ui_1_10, - options={'SKIP_SAVE'} - ) + description="How many hours did this work take?", + items=[('0', '0', ''), + ('1', '1', ''), + ('2', '2', ''), + ('3', '3', ''), + ('4', '4', ''), + ('5', '5', ''), + ('6', '6', ''), + ('7', '7', ''), + ('8', '8', ''), + ('9', '9', ''), + ('10', '10', '') + ], + default='0', + update=ratings_utils.update_ratings_work_hours_ui_1_10, + options={'SKIP_SAVE'} + ) @classmethod def poll(cls, context): @@ -430,41 +365,9 @@ class FastRateMenu(Operator): def draw(self, context): layout = self.layout - col = layout.column() - - # layout.template_icon_view(bkit_ratings, property, show_labels=False, scale=6.0, scale_popup=5.0) - col.label(text=self.message) - row = col.row() - row.prop(self, 'rating_quality_ui', expand=True, icon_only=True, emboss=False) - # row.label(text=str(self.rating_quality)) - col.separator() - - row = layout.row() - row.label(text=f"How many hours did this {self.asset_type} save you?") - - if self.asset_type in ('model', 'scene'): - row = layout.row() - if utils.profile_is_validator(): - col.prop(self, 'rating_work_hours') - row.prop(self, 'rating_work_hours_ui', expand=True, icon_only=False, emboss=True) - if float(self.rating_work_hours_ui) > 100: - utils.label_multiline(layout, - text=f"\nThat's huge! please be sure to give such rating only to godly {self.asset_type}s.\n", - width=500) - elif float(self.rating_work_hours_ui) > 18: - layout.separator() - - utils.label_multiline(layout, - text=f"\nThat's a lot! please be sure to give such rating only to amazing {self.asset_type}s.\n", - width=500) - - elif self.asset_type == 'hdr': - row = layout.row() - row.prop(self, 'rating_work_hours_ui_1_10', expand=True, icon_only=False, emboss=True) - else: - row = layout.row() - row.prop(self, 'rating_work_hours_ui_1_5', expand=True, icon_only=False, emboss=True) + layout.label(text=self.message) + draw_ratings_menu(self, context, layout) def execute(self, context): user_preferences = bpy.context.preferences.addons['blenderkit'].preferences @@ -505,7 +408,7 @@ class FastRateMenu(Operator): self.message = f"Rate asset {self.asset_name}" wm = context.window_manager - if self.asset_type in ('model','scene'): + if self.asset_type in ('model', 'scene'): # spawn a wider one for validators for the enum buttons return wm.invoke_props_dialog(self, width=500) else: diff --git a/blenderkit/ratings_utils.py b/blenderkit/ratings_utils.py new file mode 100644 index 00000000..9dc02e55 --- /dev/null +++ b/blenderkit/ratings_utils.py @@ -0,0 +1,121 @@ +# ##### 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 ##### + +#mainly update functions and callbacks for ratings properties, here to avoid circular imports. +import bpy + +def update_ratings_quality(self, context): + user_preferences = bpy.context.preferences.addons['blenderkit'].preferences + api_key = user_preferences.api_key + + headers = utils.get_headers(api_key) + asset = self.id_data + if asset: + bkit_ratings = asset.bkit_ratings + url = paths.get_api_url() + 'assets/' + asset['asset_data']['id'] + '/rating/' + else: + # this part is for operator rating: + bkit_ratings = self + url = paths.get_api_url() + f'assets/{self.asset_id}/rating/' + + if bkit_ratings.rating_quality > 0.1: + ratings = [('quality', bkit_ratings.rating_quality)] + tasks_queue.add_task((send_rating_to_thread_quality, (url, ratings, headers)), wait=2.5, only_last=True) + + +def update_ratings_work_hours(self, context): + user_preferences = bpy.context.preferences.addons['blenderkit'].preferences + api_key = user_preferences.api_key + headers = utils.get_headers(api_key) + asset = self.id_data + if asset: + bkit_ratings = asset.bkit_ratings + url = paths.get_api_url() + 'assets/' + asset['asset_data']['id'] + '/rating/' + else: + # this part is for operator rating: + bkit_ratings = self + url = paths.get_api_url() + f'assets/{self.asset_id}/rating/' + + if bkit_ratings.rating_work_hours > 0.45: + ratings = [('working_hours', round(bkit_ratings.rating_work_hours, 1))] + tasks_queue.add_task((send_rating_to_thread_work_hours, (url, ratings, headers)), wait=2.5, only_last=True) + +def update_quality_ui(self, context): + '''Converts the _ui the enum into actual quality number.''' + user_preferences = bpy.context.preferences.addons['blenderkit'].preferences + if user_preferences.api_key == '': + # ui_panels.draw_not_logged_in(self, message='Please login/signup to rate assets.') + # bpy.ops.wm.call_menu(name='OBJECT_MT_blenderkit_login_menu') + # return + bpy.ops.wm.blenderkit_login('INVOKE_DEFAULT', + message='Please login/signup to rate assets. Clicking OK takes you to web login.') + # self.rating_quality_ui = '0' + self.rating_quality = int(self.rating_quality_ui) + + +def update_ratings_work_hours_ui(self, context): + user_preferences = bpy.context.preferences.addons['blenderkit'].preferences + if user_preferences.api_key == '': + # ui_panels.draw_not_logged_in(self, message='Please login/signup to rate assets.') + # bpy.ops.wm.call_menu(name='OBJECT_MT_blenderkit_login_menu') + # return + bpy.ops.wm.blenderkit_login('INVOKE_DEFAULT', + message='Please login/signup to rate assets. Clicking OK takes you to web login.') + # self.rating_work_hours_ui = '0' + self.rating_work_hours = float(self.rating_work_hours_ui) + + +def update_ratings_work_hours_ui_1_5(self, context): + user_preferences = bpy.context.preferences.addons['blenderkit'].preferences + if user_preferences.api_key == '': + # ui_panels.draw_not_logged_in(self, message='Please login/signup to rate assets.') + # bpy.ops.wm.call_menu(name='OBJECT_MT_blenderkit_login_menu') + # return + bpy.ops.wm.blenderkit_login('INVOKE_DEFAULT', + message='Please login/signup to rate assets. Clicking OK takes you to web login.') + # self.rating_work_hours_ui_1_5 = '0' + # print('updating 1-5') + # print(float(self.rating_work_hours_ui_1_5)) + self.rating_work_hours = float(self.rating_work_hours_ui_1_5) + + +def update_ratings_work_hours_ui_1_10(self, context): + user_preferences = bpy.context.preferences.addons['blenderkit'].preferences + if user_preferences.api_key == '': + # ui_panels.draw_not_logged_in(self, message='Please login/signup to rate assets.') + # bpy.ops.wm.call_menu(name='OBJECT_MT_blenderkit_login_menu') + # return + bpy.ops.wm.blenderkit_login('INVOKE_DEFAULT', + message='Please login/signup to rate assets. Clicking OK takes you to web login.') + # self.rating_work_hours_ui_1_5 = '0' + # print('updating 1-5') + # print(float(self.rating_work_hours_ui_1_5)) + self.rating_work_hours = float(self.rating_work_hours_ui_1_10) + + +def stars_enum_callback(self, context): + '''regenerates the enum property used to display rating stars, so that there are filled/empty stars correctly.''' + items = [] + for a in range(0, 10): + if self.rating_quality < a + 1: + icon = 'SOLO_OFF' + else: + icon = 'SOLO_ON' + # has to have something before the number in the value, otherwise fails on registration. + items.append((f'{a + 1}', f'{a + 1}', '', icon, a + 1)) + return items
\ No newline at end of file diff --git a/blenderkit/search.py b/blenderkit/search.py index 1465d103..3f0532b5 100644 --- a/blenderkit/search.py +++ b/blenderkit/search.py @@ -572,10 +572,6 @@ def writeblockm(tooltip, mdata, key='', pretext=None, width=40): # for longer t return tooltip -def fmt_length(prop): - prop = str(round(prop, 2)) - return prop - def has(mdata, prop): if mdata.get(prop) is not None and mdata[prop] is not None and mdata[prop] is not False: @@ -583,7 +579,6 @@ def has(mdata, prop): else: return False - def generate_tooltip(mdata): col_w = 40 if type(mdata['parameters']) == list: @@ -591,6 +586,151 @@ def generate_tooltip(mdata): else: mparams = mdata['parameters'] t = '' + # t = writeblock(t, mdata['displayName'], width=col_w) + # t += '\n' + + t = writeblockm(t, mdata, key='description', pretext='', width=col_w) + if mdata['description'] != '': + t += '\n' + + bools = (('rig', None), ('animated', None), ('manifold', 'non-manifold'), ('scene', None), ('simulation', None), + ('uv', None)) + for b in bools: + if mparams.get(b[0]): + mdata['tags'].append(b[0]) + elif b[1] != None: + mdata['tags'].append(b[1]) + + bools_data = ('adult',) + for b in bools_data: + if mdata.get(b) and mdata[b]: + mdata['tags'].append(b) + t = writeblockm(t, mparams, key='designer', pretext='Designer', width=col_w) + t = writeblockm(t, mparams, key='manufacturer', pretext='Manufacturer', width=col_w) + t = writeblockm(t, mparams, key='designCollection', pretext='Design collection', width=col_w) + + # t = writeblockm(t, mparams, key='engines', pretext='engine', width = col_w) + # t = writeblockm(t, mparams, key='model_style', pretext='style', width = col_w) + # t = writeblockm(t, mparams, key='material_style', pretext='style', width = col_w) + # t = writeblockm(t, mdata, key='tags', width = col_w) + # t = writeblockm(t, mparams, key='condition', pretext='condition', width = col_w) + # t = writeblockm(t, mparams, key='productionLevel', pretext='production level', width = col_w) + if has(mdata, 'purePbr'): + t = writeblockm(t, mparams, key='pbrType', pretext='Pbr', width=col_w) + + t = writeblockm(t, mparams, key='designYear', pretext='Design year', width=col_w) + + if has(mparams, 'dimensionX'): + t += 'Size: %s × %s × %s m\n' % (utils.fmt_length(mparams['dimensionX']), + utils.fmt_length(mparams['dimensionY']), + utils.fmt_length(mparams['dimensionZ'])) + if has(mparams, 'faceCount') and mdata['assetType'] == 'model': + t += 'Face count: %s\n' % (mparams['faceCount']) + # t += 'face count: %s, render: %s\n' % (mparams['faceCount'], mparams['faceCountRender']) + + # write files size - this doesn't reflect true file size, since files size is computed from all asset files, including resolutions. + # if mdata.get('filesSize'): + # fs = utils.files_size_to_text(mdata['filesSize']) + # t += f'files size: {fs}\n' + + # t = writeblockm(t, mparams, key='meshPolyType', pretext='mesh type', width = col_w) + # t = writeblockm(t, mparams, key='objectCount', pretext='nubmber of objects', width = col_w) + + # t = writeblockm(t, mparams, key='materials', width = col_w) + # t = writeblockm(t, mparams, key='modifiers', width = col_w) + # t = writeblockm(t, mparams, key='shaders', width = col_w) + + # if has(mparams, 'textureSizeMeters'): + # t += 'Texture size: %s m\n' % utils.fmt_length(mparams['textureSizeMeters']) + + if has(mparams, 'textureResolutionMax') and mparams['textureResolutionMax'] > 0: + if not mparams.get('textureResolutionMin'): # for HDR's + t = writeblockm(t, mparams, key='textureResolutionMax', pretext='Resolution', width=col_w) + elif mparams.get('textureResolutionMin') == mparams['textureResolutionMax']: + t = writeblockm(t, mparams, key='textureResolutionMin', pretext='Texture resolution', width=col_w) + else: + t += 'Tex resolution: %i - %i\n' % (mparams.get('textureResolutionMin'), mparams['textureResolutionMax']) + + if has(mparams, 'thumbnailScale'): + t = writeblockm(t, mparams, key='thumbnailScale', pretext='Preview scale', width=col_w) + + # t += 'uv: %s\n' % mdata['uv'] + # t += '\n' + if mdata.get('license') == 'cc_zero': + t+= 'license: CC Zero\n' + else: + t+= 'license: Royalty free\n' + # t = writeblockm(t, mdata, key='license', width=col_w) + + fs = mdata.get('files') + + if utils.profile_is_validator(): + if fs and len(fs) > 2: + resolutions = 'Resolutions:' + list.sort(fs, key=lambda f: f['fileType']) + for f in fs: + if f['fileType'].find('resolution') > -1: + resolutions += f['fileType'][11:] + ' ' + resolutions += '\n' + t += resolutions.replace('_', '.') + + # if mdata['isFree']: + # t += 'Free plan\n' + # else: + # t += 'Full plan\n' + else: + if fs: + for f in fs: + if f['fileType'].find('resolution') > -1: + t += 'Asset has lower resolutions available\n' + break; + + # generator is for both upload preview and search, this is only after search + # if mdata.get('versionNumber'): + # # t = writeblockm(t, mdata, key='versionNumber', pretext='version', width = col_w) + # a_id = mdata['author'].get('id') + # if a_id != None: + # adata = bpy.context.window_manager['bkit authors'].get(str(a_id)) + # if adata != None: + # t += generate_author_textblock(adata) + + + # t += '\n' + # rc = mdata.get('ratingsCount') + # if rc: + # t+='\n' + # if rc: + # rcount = min(rc['quality'], rc['workingHours']) + # else: + # rcount = 0 + # + # show_rating_threshold = 5 + # + # if rcount < show_rating_threshold and mdata['assetType'] != 'hdr': + # t += f"Only assets with enough ratings \nshow the rating value. Please rate.\n" + # if rc['quality'] >= show_rating_threshold: + # # t += f"{int(mdata['ratingsAverage']['quality']) * '*'}\n" + # t += f"* {round(mdata['ratingsAverage']['quality'],1)}\n" + # if rc['workingHours'] >= show_rating_threshold: + # t += f"Hours saved: {int(mdata['ratingsAverage']['workingHours'])}\n" + # if utils.profile_is_validator(): + # t += f"Score: {int(mdata['score'])}\n" + # + # t += f"Ratings count {rc['quality']}*/{rc['workingHours']}wh value " \ + # f"{(mdata['ratingsAverage']['quality'],1)}*/{(mdata['ratingsAverage']['workingHours'],1)}wh\n" + # if len(t.split('\n')) < 11: + # t += '\n' + # t += get_random_tip(mdata) + # t += '\n' + return t + +def generate_tooltip_old(mdata): + col_w = 40 + if type(mdata['parameters']) == list: + mparams = utils.params_to_dict(mdata['parameters']) + else: + mparams = mdata['parameters'] + t = '' t = writeblock(t, mdata['displayName'], width=col_w) t += '\n' @@ -626,9 +766,9 @@ def generate_tooltip(mdata): t = writeblockm(t, mparams, key='designYear', pretext='Design year', width=col_w) if has(mparams, 'dimensionX'): - t += 'Size: %s x %s x %sm\n' % (fmt_length(mparams['dimensionX']), - fmt_length(mparams['dimensionY']), - fmt_length(mparams['dimensionZ'])) + t += 'Size: %s x %s x %sm\n' % (utils.fmt_length(mparams['dimensionX']), + utils.fmt_length(mparams['dimensionY']), + utils.fmt_length(mparams['dimensionZ'])) if has(mparams, 'faceCount') and mdata['assetType'] == 'model': t += 'Face count: %s\n' % (mparams['faceCount']) # t += 'face count: %s, render: %s\n' % (mparams['faceCount'], mparams['faceCountRender']) @@ -646,7 +786,7 @@ def generate_tooltip(mdata): # t = writeblockm(t, mparams, key='shaders', width = col_w) # if has(mparams, 'textureSizeMeters'): - # t += 'Texture size: %s m\n' % fmt_length(mparams['textureSizeMeters']) + # t += 'Texture size: %s m\n' % utils.fmt_length(mparams['textureSizeMeters']) if has(mparams, 'textureResolutionMax') and mparams['textureResolutionMax'] > 0: if not mparams.get('textureResolutionMin'): # for HDR's @@ -729,37 +869,24 @@ def generate_tooltip(mdata): return t -def get_random_tip(mdata): +def get_random_tip(): t = '' - tip = 'Tip: ' + random.choice(rtips) t = writeblock(t, tip) return t - # at = mdata['assetType'] - # if at == 'brush' or at == 'texture': - # t += 'click to link %s' % mdata['assetType'] - # if at == 'model' or at == 'material': - # tips = ['Click or drag in scene to link/append %s' % mdata['assetType'], - # "'A' key to search assets by same author", - # "'W' key to open Authors webpage", - # ] - # tip = 'Tip: ' + random.choice(tips) - # t = writeblock(t, tip) - return t def generate_author_textblock(adata): - t = '\n\n\n' + t = '' if adata not in (None, ''): - col_w = 40 + col_w = 2000 if len(adata['firstName'] + adata['lastName']) > 0: - t = 'Author:\n' - t += '%s %s\n' % (adata['firstName'], adata['lastName']) + t = 'Author: %s %s\n' % (adata['firstName'], adata['lastName']) t += '\n' - if adata.get('aboutMeUrl') is not None: - t = writeblockm(t, adata, key='aboutMeUrl', pretext='', width=col_w) - t += '\n' + # if adata.get('aboutMeUrl') is not None: + # t = writeblockm(t, adata, key='aboutMeUrl', pretext='', width=col_w) + # t += '\n' if adata.get('aboutMe') is not None: t = writeblockm(t, adata, key='aboutMe', pretext='', width=col_w) t += '\n' @@ -1528,6 +1655,7 @@ class SearchOperator(Operator): bl_label = "BlenderKit asset search" bl_description = "Search online for assets" bl_options = {'REGISTER', 'UNDO', 'INTERNAL'} + own: BoolProperty(name="own assets only", description="Find all own assets", default=False) @@ -1559,6 +1687,12 @@ class SearchOperator(Operator): options={'SKIP_SAVE'} ) + tooltip: bpy.props.StringProperty(default='Runs search and displays the asset bar at the same time') + + @classmethod + def description(cls, context, properties): + return properties.tooltip + @classmethod def poll(cls, context): return True @@ -1577,9 +1711,27 @@ class SearchOperator(Operator): return {'FINISHED'} +class UrlOperator(Operator): + """""" + bl_idname = "wm.blenderkit_url" + bl_label = "" + bl_description = "Search online for assets" + bl_options = {'REGISTER', 'UNDO', 'INTERNAL'} + + tooltip: bpy.props.StringProperty(default='Open a web page') + url: bpy.props.StringProperty(default='Runs search and displays the asset bar at the same time') + + @classmethod + def description(cls, context, properties): + return properties.tooltip + + def execute(self,context): + bpy.ops.wm.url_open(url=self.url) + classes = [ - SearchOperator + SearchOperator, + UrlOperator ] diff --git a/blenderkit/thumbnails/cc0.png b/blenderkit/thumbnails/cc0.png Binary files differnew file mode 100644 index 00000000..3f5450f8 --- /dev/null +++ b/blenderkit/thumbnails/cc0.png diff --git a/blenderkit/thumbnails/locked_large.png b/blenderkit/thumbnails/locked_large.png Binary files differdeleted file mode 100644 index eb2deb65..00000000 --- a/blenderkit/thumbnails/locked_large.png +++ /dev/null diff --git a/blenderkit/thumbnails/private.png b/blenderkit/thumbnails/private.png Binary files differnew file mode 100644 index 00000000..7ac3c3d7 --- /dev/null +++ b/blenderkit/thumbnails/private.png diff --git a/blenderkit/thumbnails/royalty_free.png b/blenderkit/thumbnails/royalty_free.png Binary files differnew file mode 100644 index 00000000..b85fa910 --- /dev/null +++ b/blenderkit/thumbnails/royalty_free.png diff --git a/blenderkit/thumbnails/trophy.png b/blenderkit/thumbnails/trophy.png Binary files differnew file mode 100644 index 00000000..01d77a22 --- /dev/null +++ b/blenderkit/thumbnails/trophy.png diff --git a/blenderkit/thumbnails/vs_validated.png b/blenderkit/thumbnails/vs_validated.png Binary files differnew file mode 100644 index 00000000..b2d8fdd2 --- /dev/null +++ b/blenderkit/thumbnails/vs_validated.png diff --git a/blenderkit/ui.py b/blenderkit/ui.py index 7117055f..7ef5092a 100644 --- a/blenderkit/ui.py +++ b/blenderkit/ui.py @@ -360,8 +360,10 @@ def draw_tooltip(x, y, text='', author='', img=None, gravatar=None): if gravatar is not None: # ui_bgl.draw_image(x + isizex - gsize - textmargin, y - isizey + texth - gsize - nameline_height - textmargin, # gsize, gsize, gravatar, 1) - ui_bgl.draw_image(x + isizex / 2 + textmargin, y - isizey + texth - gsize - nameline_height - textmargin, - gsize, gsize, gravatar, 1) + # ui_bgl.draw_image(x + isizex / 2 + textmargin, y - isizey + texth - gsize - nameline_height - textmargin, + # gsize, gsize, gravatar, 1) + ui_bgl.draw_image(x + isizex / 2 + textmargin, y - isizey + texth - gsize - textmargin, + gsize, gsize, gravatar, 1) i = 0 column_lines = -1 # start minus one for the name @@ -399,13 +401,14 @@ def draw_tooltip(x, y, text='', author='', img=None, gravatar=None): xtext -= gsize + textmargin ytext = y - column_lines * line_height - nameline_height - ttipmargin - textmargin - isizey + texth - if False: # i == 0: + if i == 0: + fsize = name_height-4 ytext = y - name_height + 5 - isizey + texth - textmargin elif i == len(lines) - 1: ytext = y - (nlines - 1) * line_height - nameline_height - ttipmargin * 2 - isizey + texth tcol = textcol tsize = font_height - if (i > 0 and alines[i - 1][:7] == 'Author:'): + elif (i > 0 and alines[i - 1][:7] == 'Author:'): tcol = textcol_strong fsize = font_height + 2 else: @@ -455,9 +458,16 @@ def draw_tooltip_with_author(asset_data, x, y): gimg = utils.get_hidden_image(a['gravatarImg'], a['gravatarHash']) atip = a['tooltip'] + + tooltip = f"{asset_data['displayName']}\n\n" \ + f"Left click to drag to append/link.\nRight click for more." + # scene = bpy.context.scene # ui_props = scene.blenderkitUI - draw_tooltip(x, y, text=asset_data['tooltip'], author=atip, img=img, + author_s = '' + if not gimg: + author_s = 'Author: ' + draw_tooltip(x, y, text=asset_data['displayName']+'\n\n', author=f"{author_s}{a['firstName']} {a['lastName']}", img=img, gravatar=gimg) @@ -851,6 +861,8 @@ def draw_asset_bar(self, context): img = utils.get_thumbnail('locked.png') ui_bgl.draw_image(x + 2, y + 2, 24, 24, img, 1) + # pcoll = icons.icon_collections["main"] + # v_icon = pcoll['rejected'] v_icon = verification_icons[result.get('verificationStatus', 'validated')] if v_icon is not None: img = utils.get_thumbnail(v_icon) @@ -1575,8 +1587,12 @@ class AssetBarOperator(bpy.types.Operator): my = event.mouse_y - r.y if event.value == 'PRESS' and mouse_in_asset_bar(mx, my): - # bpy.ops.wm.blenderkit_asset_popup('INVOKE_DEFAULT') - bpy.ops.wm.call_menu(name='OBJECT_MT_blenderkit_asset_menu') + context.window.cursor_warp(event.mouse_x-500, event.mouse_y-45); + + bpy.ops.wm.blenderkit_asset_popup('INVOKE_DEFAULT') + context.window.cursor_warp(event.mouse_x, event.mouse_y); + + # bpy.ops.wm.call_menu(name='OBJECT_MT_blenderkit_asset_menu') return {'RUNNING_MODAL'} if event.type == 'LEFTMOUSE': @@ -1960,7 +1976,7 @@ def find_and_activate_instancers(object): class AssetDragOperator(bpy.types.Operator): - """Draw a line with the mouse""" + """Drag & drop assets into scene.""" bl_idname = "view3d.asset_drag_drop" bl_label = "BlenderKit asset drag drop" diff --git a/blenderkit/ui_panels.py b/blenderkit/ui_panels.py index 6bacc895..4f703ba3 100644 --- a/blenderkit/ui_panels.py +++ b/blenderkit/ui_panels.py @@ -17,8 +17,9 @@ # ##### END GPL LICENSE BLOCK ##### -from blenderkit import paths, ratings, utils, download, categories, icons, search, resolutions, ui, tasks_queue, \ - autothumb +from blenderkit import paths, ratings, ratings_utils, utils, download, categories, icons, search, resolutions, ui, \ + tasks_queue, \ + autothumb, upload from bpy.types import ( Panel @@ -35,9 +36,7 @@ from bpy.props import ( import bpy import os -import random import logging -import blenderkit bk_logger = logging.getLogger('blenderkit') @@ -207,7 +206,6 @@ def draw_panel_hdr_search(self, context): utils.label_multiline(layout, text=props.report) - def draw_thumbnail_upload_panel(layout, props): update = False tex = autothumb.get_texture_ui(props.thumbnail, '.upload_preview') @@ -216,6 +214,7 @@ def draw_thumbnail_upload_panel(layout, props): box = layout.box() box.template_icon(icon_value=tex.image.preview.icon_id, scale=6.0) + def draw_panel_model_upload(self, context): ob = bpy.context.active_object while ob.parent is not None: @@ -646,7 +645,8 @@ def draw_panel_material_upload(self, context): prop_needed(row, props, 'thumbnail', props.has_thumbnail, False) if bpy.context.scene.render.engine in ('CYCLES', 'BLENDER_EEVEE'): - layout.operator("object.blenderkit_generate_material_thumbnail", text='Render thumbnail with Cycles', icon='EXPORT') + layout.operator("object.blenderkit_generate_material_thumbnail", text='Render thumbnail with Cycles', + icon='EXPORT') if props.is_generating_thumbnail: row = layout.row(align=True) row.label(text=props.thumbnail_generating_state, icon='RENDER_STILL') @@ -1153,12 +1153,13 @@ def draw_asset_context_menu(layout, context, asset_data, from_panel=False): layout.operator_context = 'INVOKE_DEFAULT' - op = layout.operator('wm.blenderkit_menu_rating_upload', text='Rate') - op.asset_name = asset_data['name'] - op.asset_id = asset_data['id'] - op.asset_type = asset_data['assetType'] + if from_panel: + op = layout.operator('wm.blenderkit_menu_rating_upload', text='Rate') + op.asset_name = asset_data['name'] + op.asset_id = asset_data['id'] + op.asset_type = asset_data['assetType'] - if wm.get('bkit authors') is not None and author_id is not None: + if from_panel and wm.get('bkit authors') is not None and author_id is not None: a = bpy.context.window_manager['bkit authors'].get(author_id) if a is not None: # utils.p('author:', a) @@ -1172,6 +1173,7 @@ def draw_asset_context_menu(layout, context, asset_data, from_panel=False): op.author_id = author_id op = layout.operator('view3d.blenderkit_search', text='Search Similar') + op.tooltip = 'Search for similar assets in the library' # build search string from description and tags: op.keywords = asset_data['name'] if asset_data.get('description'): @@ -1263,7 +1265,6 @@ def draw_asset_context_menu(layout, context, asset_data, from_panel=False): op.model_rotation = (0, 0, 0) op.max_resolution = asset_data.get('max_resolution', 0) # str(utils.get_param(asset_data, 'textureResolutionMax')) - print('should be drawn!') # print('operator res ', resolution) # op.resolution = resolution @@ -1297,7 +1298,7 @@ def draw_asset_context_menu(layout, context, asset_data, from_panel=False): row = layout.row() row.operator_context = 'INVOKE_DEFAULT' - op = layout.operator('wm.blenderkit_fast_metadata', text='Fast Edit Metadata') + op = layout.operator('wm.blenderkit_fast_metadata', text='Edit Metadata') op.asset_id = asset_data['id'] op.asset_type = asset_data['assetType'] @@ -1377,97 +1378,429 @@ class OBJECT_MT_blenderkit_asset_menu(bpy.types.Menu): asset_data = sr[ui_props.active_index] draw_asset_context_menu(self.layout, context, asset_data, from_panel=False) - # ui_props = context.scene.blenderkitUI - # - # sr = bpy.context.window_manager['search results'] - # asset_data = sr[ui_props.active_index] - # layout = self.layout - # row = layout.row() - # split = row.split(factor=0.2) - # col = split.column() - # op = col.operator('view3d.asset_drag_drop') - # op.asset_search_index=ui_props.active_index - # - # draw_asset_context_menu(col, context, asset_data, from_panel=False) - # split = split.split(factor=0.3) - # col1 = split.column() - # box = col1.box() - # utils.label_multiline(box, asset_data['tooltip']) - # col2 = split.column() - # - # pcoll = icons.icon_collections["main"] - # my_icon = pcoll['test'] - # row = col2.row() - # row.scale_y = 4 - # row.template_icon(icon_value=my_icon.icon_id, scale=2.0) - # # col2.template_icon(icon_value=self.img.preview.icon_id, scale=10.0) - # box2 = col2.box() - # - # box2.label(text='and heere goes the rating') - # box2.label(text='************') - # box2.label(text='dadydadadada') - class AssetPopupCard(bpy.types.Operator): """Generate Cycles thumbnail for model assets""" bl_idname = "wm.blenderkit_asset_popup" bl_label = "BlenderKit asset popup" - # bl_options = {'REGISTER', 'INTERNAL'} - bl_options = {'REGISTER', } + + width = 700 + + message: StringProperty( + name="message", + description="message", + default="Rating asset", + options={'SKIP_SAVE'}) + + asset_id: StringProperty( + name="Asset Base Id", + description="Unique id of the asset (hidden)", + default="", + options={'SKIP_SAVE'}) + + asset_name: StringProperty( + name="Asset Name", + description="Name of the asset (hidden)", + default="", + options={'SKIP_SAVE'}) + + asset_type: StringProperty( + name="Asset type", + description="asset type", + default="", + options={'SKIP_SAVE'}) + + rating_quality: IntProperty(name="Quality", + description="quality of the material", + default=0, + min=-1, max=10, + # update=update_ratings_quality, + options={'SKIP_SAVE'}) + + # the following enum is only to ease interaction - enums support 'drag over' and enable to draw the stars easily. + rating_quality_ui: EnumProperty(name='rating_quality_ui', + items=ratings_utils.stars_enum_callback, + description='Rating stars 0 - 10', + default=0, + update=ratings_utils.update_quality_ui, + options={'SKIP_SAVE'}) + + rating_work_hours: FloatProperty(name="Work Hours", + description="How many hours did this work take?", + default=0.00, + min=0.0, max=300, + # update=update_ratings_work_hours, + options={'SKIP_SAVE'} + ) + + high_rating_warning = "This is a high rating, please be sure to give such rating only to amazing assets" + + rating_work_hours_ui: EnumProperty(name="Work Hours", + description="How many hours did this work take?", + items=[('0', '0', ''), + ('.5', '0.5', ''), + ('1', '1', ''), + ('2', '2', ''), + ('3', '3', ''), + ('4', '4', ''), + ('5', '5', ''), + ('6', '6', ''), + ('8', '8', ''), + ('10', '10', ''), + ('15', '15', ''), + ('20', '20', ''), + ('30', '30', high_rating_warning), + ('50', '50', high_rating_warning), + ('100', '100', high_rating_warning), + ('150', '150', high_rating_warning), + ('200', '200', high_rating_warning), + ('250', '250', high_rating_warning), + ], + default='0', update=ratings_utils.update_ratings_work_hours_ui, + options={'SKIP_SAVE'} + ) + + rating_work_hours_ui_1_5: EnumProperty(name="Work Hours", + description="How many hours did this work take?", + items=[('0', '0', ''), + ('.2', '0.2', ''), + ('.5', '0.5', ''), + ('1', '1', ''), + ('2', '2', ''), + ('3', '3', ''), + ('4', '4', ''), + ('5', '5', '') + ], + default='0', + update=ratings_utils.update_ratings_work_hours_ui_1_5, + options={'SKIP_SAVE'} + ) + + rating_work_hours_ui_1_10: EnumProperty(name="Work Hours", + description="How many hours did this work take?", + items=[('0', '0', ''), + ('1', '1', ''), + ('2', '2', ''), + ('3', '3', ''), + ('4', '4', ''), + ('5', '5', ''), + ('6', '6', ''), + ('7', '7', ''), + ('8', '8', ''), + ('9', '9', ''), + ('10', '10', '') + ], + default='0', + update=ratings_utils.update_ratings_work_hours_ui_1_10, + options={'SKIP_SAVE'} + ) @classmethod def poll(cls, context): return True + def draw_menu(self, context, layout): + ui_props = context.scene.blenderkitUI + + col_left = layout.column() + # row_button = col_left.row() + # row_button.scale_y = 3 + # op = row_button.operator('view3d.asset_drag_drop', text='Drag & Drop') + # op.asset_search_index = ui_props.active_index + draw_asset_context_menu(col_left, context, self.asset_data, from_panel=False) + # layout = col_left + # op = layout.operator('view3d.blenderkit_search', text='Search Similar') + # # build search string from description and tags: + # op.keywords = self.asset_data['name'] + # if self.asset_data.get('description'): + # op.keywords += ' ' + self.asset_data.get('description') + ' ' + # op.keywords += ' '.join(self.asset_data.get('tags')) + + def draw_property(self, layout, left, right, icon=None, icon_value=None, url=None, tooltip=''): + right = str(right) + row = layout.row() + split = row.split(factor=0.4) + split.alignment = 'RIGHT' + split.label(text=left) + split = split.split() + # if url: + # if icon_value: + # op = split.operator('wm.url_open', text=right, icon_value=icon_value) + # elif icon: + # op = split.operator('wm.url_open', text=right, icon=icon) + # else: + # op = split.operator('wm.url_open', text=right) + # op.url = url + # return + if url: + split = split.split(factor=0.9) + if icon_value: + split.label(text=right, icon_value=icon_value) + elif icon: + split.label(text=right, icon=icon) + + else: + split.label(text=right) + if url: + split = split.split() + op = split.operator('wm.blenderkit_url', text='', icon='QUESTION') + op.url = url + op.tooltip = tooltip + + def draw_asset_parameter(self, layout, key='', pretext=''): + parameter = utils.get_param(self.asset_data, key) + if parameter == None: + return + self.draw_property(layout, pretext, parameter) + + def draw_tooltip(self, layout): + if type(self.asset_data['parameters']) == list: + mparams = utils.params_to_dict(self.asset_data['parameters']) + else: + mparams = self.asset_data['parameters'] + + layout = layout.column() + if len(self.asset_data['description']) > 0: + box = layout.box() + box.scale_y = 0.8 + box.label(text='Description:') + utils.label_multiline(box, self.asset_data['description'], width=200) + + pcoll = icons.icon_collections["main"] + + box = layout.box() + box.scale_y = 0.8 + + if self.asset_data.get('license') == 'cc_zero': + t = 'CC Zero' + icon = pcoll['cc0'] + + else: + t = 'Royalty free' + icon = pcoll['royalty_free'] + + self.draw_property(box, + 'license:', t, + icon_value=icon.icon_id, + url="https://www.blenderkit.com/docs/licenses/", + tooltip='All BlenderKit assets are available for commercial use. ' + 'Click to read more about BlenderKit licenses online' + ) + + if upload.can_edit_asset(asset_data=self.asset_data): + icon = pcoll[self.asset_data['verificationStatus']] + verification_status_tooltips = { + 'uploading': "Your asset got stuck during upload. Probably, your file was too large " + "or your connection too slow or interrupting. If you have repeated issues, " + "please contact us and let us know, it might be a bug", + 'uploaded': "Your asset uploaded successfully. Yay! If it's public, " + "it's awaiting validation. If it's private, use it", + 'on_hold': "Your asset needs some (usually smaller) fixes, " + "so we can make it public for everybody." + " Please check your email to see the feedback " + "that we send to every creator personally", + 'rejected': "The asset has serious quality issues, " \ + "and it's probable that it might be good to start " \ + "all over again or try with something simpler. " \ + "You also get personal feedback into your e-mail, " \ + "since we believe that together, we can all learn " \ + "to become awesome 3D artists", + 'deleted': "You deleted this asset", + 'validated': "Your asset passed our validation process, " + "and is now available to BlenderKit users" + + } + self.draw_property(box, + 'Verification:', + self.asset_data['verificationStatus'], + icon_value=icon.icon_id, + url="https://www.blenderkit.com/docs/validation-status/", + tooltip=verification_status_tooltips[self.asset_data['verificationStatus']] + + ) + + self.draw_asset_parameter(box, key='textureResolutionMax', pretext='Resolution:') + + self.draw_asset_parameter(box, key='designer', pretext='Designer:') + self.draw_asset_parameter(box, key='manufacturer', pretext='Manufacturer:') + self.draw_asset_parameter(box, key='collection', pretext='Collection:') + self.draw_asset_parameter(box, key='designYear', pretext='Design year:') + self.draw_asset_parameter(box, key='faceCount', pretext='Face count:') + self.draw_asset_parameter(box, key='thumbnailScale', pretext='Preview scale:') + + if utils.get_param(self.asset_data, 'dimensionX'): + t = '%s × %s × %s m' % (utils.fmt_length(mparams['dimensionX']), + utils.fmt_length(mparams['dimensionY']), + utils.fmt_length(mparams['dimensionZ'])) + self.draw_property(box, 'Size:', t) + + #Free/Full plan or private Access + if self.asset_data['isPrivate']: + t = 'Private' + self.draw_property(box, 'Access:', t, icon='LOCKED') + elif self.asset_data['isFree']: + t = 'Free plan' + icon = pcoll['free'] + self.draw_property(box, 'Access:', t, icon_value=icon.icon_id) + else: + t = 'Full plan' + icon = pcoll['full'] + self.draw_property(box, 'Access:', t, icon_value=icon.icon_id) + + def draw_author(self, layout, width=330): + image_split = 0.25 + text_width = width + authors = bpy.context.window_manager['bkit authors'] + a = authors.get(self.asset_data['author']['id']) + if a is not None: # or a is '' or (a.get('gravatarHash') is not None and a.get('gravatarImg') is None): + row = layout.row() + author_box = row.box() + author_box.scale_y = 0.6 # get text lines closer to each other + if hasattr(self, 'gimg'): + + author_left = author_box.split(factor=0.25) + author_left.template_icon(icon_value=self.gimg.preview.icon_id, scale=6.0) + text_area = author_left.split() + text_width = int(text_width * (1 - image_split)) + else: + text_area = author_box + + author_right = text_area.column() + row = author_right.row() + col = row.column() + + utils.label_multiline(col, text=a['tooltip'], width=text_width) + if upload.can_edit_asset(asset_data=self.asset_data) and a.get('aboutMe') is not None and len( + a.get('aboutMe', '')) == 0: + col.label(text='Please write something about yourself!') + op = col.operator('wm.blenderkit_url', text='Edit your profile') + op.url = 'https://www.blenderkit.com/profile' + op.tooltip = 'Edit your profile on BlenderKit webpage' + + button_row = author_box.row() + button_row.scale_y = 2.0 + + if a.get('aboutMeUrl') is not None: + url = a['aboutMeUrl'] + text = url + if len(url) > 25: + text = url[:25] + '...' + else: + url = paths.get_author_gallery_url(a['id']) + text = "Open Author's Profile" + + op = button_row.operator('wm.url_open', text=text) + op.url = url + + op = button_row.operator('view3d.blenderkit_search', text="Show Assets By Author") + op.keywords = '' + op.author_id = self.asset_data['author']['id'] + + def draw_thumbnail_box(self, layout): + layout.emboss = 'NORMAL' + + box_thumbnail = layout.box() + # row = split_right.row() + # column_right = row.column() + + box_thumbnail.scale_y = 0.5 + # row = box_thumbnail.row() + # row.scale_y = 20 + + box_thumbnail.template_icon(icon_value=self.img.preview.icon_id, scale=34.0) + # row = box_thumbnail.row() + # row.scale_y = 4 + # op = row.operator('view3d.asset_drag_drop', text='Drag & Drop from here', depress=True) + + row = box_thumbnail.row() + row.alignment = 'EXPAND' + rc = self.asset_data.get('ratingsCount') + show_rating_threshold = 3 + + if rc: + rcount = min(rc['quality'], rc['workingHours']) + else: + rcount = 0 + if rcount >= show_rating_threshold or upload.can_edit_asset(asset_data=self.asset_data): + pcoll = icons.icon_collections["main"] + my_icon = pcoll['trophy'] + s = self.asset_data['score'] + if s: + row.label(text=str(round(s)), icon_value=my_icon.icon_id) + q = self.asset_data['ratingsAverage'].get('quality') + if q: + row.label(text=str(round(q)), icon='SOLO_ON') + c = self.asset_data['ratingsAverage'].get('workingHours') + if c: + row.label(text=str(round(c)), icon='SORTTIME') + else: + box_thumbnail.label(text=f"This asset needs more ratings ( {rcount} of {show_rating_threshold} ).") + # box_thumbnail.label(text=f"Please rate this asset.") + + def draw_menu_desc_author(self, context, layout): + box = layout.column() + + box.emboss = 'NORMAL' + # left - tooltip & params + row = box.row() + split_left_left = row.split(factor=0.7) + self.draw_tooltip(split_left_left) + + # right - menu + col1 = split_left_left.split() + self.draw_menu(context, col1) + + # author + self.draw_author(box) + def draw(self, context): ui_props = context.scene.blenderkitUI sr = bpy.context.window_manager['search results'] asset_data = sr[ui_props.active_index] + self.asset_data = asset_data layout = self.layout - row = layout.row() - split = row.split(factor=0.2) - col = split.column() - op = col.operator('view3d.asset_drag_drop') - op.asset_search_index = ui_props.active_index - draw_asset_context_menu(col, context, asset_data, from_panel=False) - split = split.split(factor=0.5) - col1 = split.column() - box = col1.box() - utils.label_multiline(box, asset_data['tooltip'], width=300) - - col2 = split.column() + # top draggabe bar with name of the asset + top_row = layout.row() + top_drag_bar = top_row.box() + top_drag_bar.alignment = 'CENTER' - pcoll = icons.icon_collections["main"] - my_icon = pcoll['test'] - col2.template_icon(icon_value=my_icon.icon_id, scale=20.0) - # col2.template_icon(icon_value=self.img.preview.icon_id, scale=10.0) - box2 = col2.box() - - # draw_ratings(box2, context, asset_data) - box2.label(text='Ratings') - # print(tp, dir(tp)) - # if not hasattr(self, 'first_draw'):# try to redraw because of template preview which needs update - # for region in context.area.regions: - # region.tag_redraw() - # self.first_draw = True + top_drag_bar.label(text=asset_data['displayName']) + # left side + row = layout.row(align=True) + split_left = row.split(factor=0.5) + self.draw_thumbnail_box(split_left) - def execute(self, context): - print('execute') - return {'FINISHED'} + # right split + split_right = split_left.split() + self.draw_menu_desc_author(context, split_right) - def invoke(self, context, event): + ratings_box = layout.box() + ratings_box.label(text='Rate asset quality:') + ratings.draw_ratings_menu(self, context, ratings_box) + tip_box = layout.box() + tip_box.label(text=self.tip) + + def execute(self, context): wm = context.window_manager ui_props = context.scene.blenderkitUI ui_props.draw_tooltip = False sr = bpy.context.window_manager['search results'] asset_data = sr[ui_props.active_index] self.img = ui.get_large_thumbnail_image(asset_data) + self.asset_type = asset_data['assetType'] # self.tex = utils.get_hidden_texture(self.img) # self.tex.update_tag() + authors = bpy.context.window_manager['bkit authors'] + a = authors.get(asset_data['author']['id']) + if a.get('gravatarImg') is not None: + self.gimg = utils.get_hidden_image(a['gravatarImg'], a['gravatarHash']) + bl_label = asset_data['name'] - return wm.invoke_props_dialog(self, width=700) + self.tip = search.get_random_tip() + self.tip = self.tip.replace('\n', '') + return wm.invoke_popup(self, width=self.width) class OBJECT_MT_blenderkit_login_menu(bpy.types.Menu): diff --git a/blenderkit/upload.py b/blenderkit/upload.py index cc72b92a..b1d20f94 100644 --- a/blenderkit/upload.py +++ b/blenderkit/upload.py @@ -580,13 +580,13 @@ def can_edit_asset(active_index=-1, asset_data=None): sr = bpy.context.window_manager['search results'] asset_data = dict(sr[active_index]) # print(profile, asset_data) - if asset_data['author']['id'] == profile['user']['id']: + if int(asset_data['author']['id']) == int(profile['user']['id']): return True return False class FastMetadata(bpy.types.Operator): - """Fast change of the category of object directly in asset bar.""" + """Edit metadata of the asset""" bl_idname = "wm.blenderkit_fast_metadata" bl_label = "Update metadata" bl_options = {'REGISTER', 'UNDO', 'INTERNAL'} @@ -731,6 +731,7 @@ class FastMetadata(bpy.types.Operator): active_asset = utils.get_active_asset_by_type(asset_type = self.asset_type) asset_data = active_asset.get('asset_data') + print('can edit asset?', can_edit_asset(asset_data=asset_data)) if not can_edit_asset(asset_data=asset_data): return {'CANCELLED'} self.asset_id = asset_data['id'] @@ -1298,7 +1299,7 @@ class AssetDebugPrint(Operator): class AssetVerificationStatusChange(Operator): """Change verification status""" bl_idname = "object.blenderkit_change_status" - bl_description = "Change asset ststus" + bl_description = "Change asset status" bl_label = "Change verification status" bl_options = {'REGISTER', 'UNDO', 'INTERNAL'} diff --git a/blenderkit/utils.py b/blenderkit/utils.py index 953ded5e..f4302ac0 100644 --- a/blenderkit/utils.py +++ b/blenderkit/utils.py @@ -337,6 +337,12 @@ def get_hidden_texture(name, force_reload=False): return t +def img_to_preview(img): + img.preview.image_size = (img.size[0], img.size[1]) + img.preview.image_pixels_float = img.pixels[:] + # img.preview.icon_size = (img.size[0], img.size[1]) + # img.preview.icon_pixels_float = img.pixels[:] + def get_hidden_image(tpath, bdata_name, force_reload=False, colorspace='sRGB'): if bdata_name[0] == '.': hidden_name = bdata_name @@ -355,6 +361,7 @@ def get_hidden_image(tpath, bdata_name, force_reload=False, colorspace='sRGB'): if img is None: img = bpy.data.images.load(tpath) + img_to_preview(img) img.name = hidden_name else: if img.filepath != tpath: @@ -363,13 +370,16 @@ def get_hidden_image(tpath, bdata_name, force_reload=False, colorspace='sRGB'): img.filepath = tpath img.reload() + img_to_preview(img) image_utils.set_colorspace(img, colorspace) elif force_reload: if img.packed_file is not None: img.unpack(method='USE_ORIGINAL') img.reload() + img_to_preview(img) image_utils.set_colorspace(img, colorspace) + return img @@ -690,6 +700,9 @@ def name_update(props): # Here we actually rename assets datablocks, but don't do that with HDR's and possibly with others asset.name = fname +def fmt_length(prop): + prop = str(round(prop, 2)) + return prop def get_param(asset_data, parameter_name, default = None): if not asset_data.get('parameters'): |