Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJulian Eisel <eiseljulian@gmail.com>2019-09-02 16:57:41 +0300
committerJulian Eisel <eiseljulian@gmail.com>2019-09-02 16:57:41 +0300
commitef8196788d448367610151d49f7bc069497570ae (patch)
tree9a87867ee284e8102d6f2cf242828cc0bb01d75b
parent8f976022ffec50a595c147f83e7c31b1b922266a (diff)
parentdb4f7e2dc66d437b205f8097c2bdf9097b5c8834 (diff)
Merge branch 'master' into filebrowser_redesign
-rw-r--r--blenderkit/bkit_oauth.py4
-rw-r--r--blenderkit/categories.py5
-rw-r--r--blenderkit/download.py14
-rw-r--r--blenderkit/ratings.py7
-rw-r--r--blenderkit/rerequests.py95
-rw-r--r--blenderkit/search.py41
-rw-r--r--blenderkit/upload.py9
-rw-r--r--blenderkit/upload_bg.py18
-rw-r--r--blenderkit/utils.py2
-rw-r--r--development_edit_operator.py337
-rw-r--r--space_view3d_brush_menus/brush_menu.py19
-rw-r--r--space_view3d_spacebar_menu/__init__.py4
12 files changed, 495 insertions, 60 deletions
diff --git a/blenderkit/bkit_oauth.py b/blenderkit/bkit_oauth.py
index f435d95d..57d3389f 100644
--- a/blenderkit/bkit_oauth.py
+++ b/blenderkit/bkit_oauth.py
@@ -74,7 +74,7 @@ def refresh_token(api_key_refresh, url):
auth_token, refresh_token = authenticator.get_refreshed_token(api_key_refresh)
if auth_token is not None and refresh_token is not None:
tasks_queue.add_task((write_tokens, (auth_token, refresh_token)))
-
+ return auth_token, refresh_token
def write_tokens(auth_token, refresh_token):
utils.p('writing tokens')
@@ -85,7 +85,7 @@ def write_tokens(auth_token, refresh_token):
props = utils.get_search_props()
if props is not None:
props.report = ''
- ui.add_report('BlenderKit Login success')
+ ui.add_report('BlenderKit Re-Login success')
search.get_profile()
categories.fetch_categories_thread(auth_token)
diff --git a/blenderkit/categories.py b/blenderkit/categories.py
index 6407b050..1d411499 100644
--- a/blenderkit/categories.py
+++ b/blenderkit/categories.py
@@ -22,8 +22,9 @@ if "bpy" in locals():
paths = reload(paths)
utils = reload(utils)
tasks_queue = reload(tasks_queue)
+ rerequests = reload(rerequests)
else:
- from blenderkit import paths, utils, tasks_queue
+ from blenderkit import paths, utils, tasks_queue, rerequests
import requests
import json
@@ -114,7 +115,7 @@ def fetch_categories(API_key):
categories_filepath = os.path.join(tempdir, 'categories.json')
try:
- r = requests.get(url, headers=headers)
+ r = rerequests.get(url, headers=headers)
rdata = r.json()
categories = rdata['results']
fix_category_counts(categories)
diff --git a/blenderkit/download.py b/blenderkit/download.py
index 366081ca..9f5f0314 100644
--- a/blenderkit/download.py
+++ b/blenderkit/download.py
@@ -25,8 +25,9 @@ if "bpy" in locals():
ui = reload(ui)
colors = reload(colors)
tasks_queue = reload(tasks_queue)
+ rerequests = reload(rerequests)
else:
- from blenderkit import paths, append_link, utils, ui, colors, tasks_queue
+ from blenderkit import paths, append_link, utils, ui, colors, tasks_queue, rerequests
import threading
import time
@@ -266,7 +267,7 @@ def report_usages():
scene['assets rated'][k] = scene['assets rated'].get(k, False)
thread = threading.Thread(target=utils.requests_post_thread, args=(url, usage_report, headers))
thread.start()
- # r = requests.post(url, headers=headers, json=usage_report)
+ # r = rerequests.post(url, headers=headers, json=usage_report)
mt = time.time() - mt
print('report generation: ', mt)
@@ -551,6 +552,7 @@ class Downloader(threading.Thread):
tcom.downloaded = 100
utils.p('not downloading, trying to append again')
return;
+
file_name = paths.get_download_filenames(asset_data)[0] # prefer global dir if possible.
# for k in asset_data:
# print(asset_data[k])
@@ -617,7 +619,6 @@ def download(asset_data, **kwargs):
asset_data = copy.deepcopy(asset_data)
else:
asset_data = asset_data.to_dict()
-
readthread = Downloader(asset_data, tcom, scene_id, api_key)
readthread.start()
@@ -730,7 +731,7 @@ def get_download_url(asset_data, scene_id, api_key, tcom=None):
}
r = None
try:
- r = requests.get(asset_data['download_url'], params=data, headers=headers)
+ r = rerequests.get(asset_data['download_url'], params=data, headers=headers)
except Exception as e:
print(e)
if tcom is not None:
@@ -755,10 +756,6 @@ def get_download_url(asset_data, scene_id, api_key, tcom=None):
tasks_queue.add_task((ui.add_report, (r1, 5, colors.RED)))
tcom.error = True
- if r.status_code == 401:
- tcom.report = 'Invalid API key'
- tcom.error = True
- return 'Invalid API key'
elif r.status_code >= 500:
tcom.report = 'Server error'
tcom.error = True
@@ -833,7 +830,6 @@ class BlenderkitDownloadOperator(bpy.types.Operator):
bl_label = "BlenderKit Asset Download"
bl_options = {'REGISTER', 'UNDO', 'INTERNAL'}
-
asset_type: EnumProperty(
name="Type",
items=asset_types,
diff --git a/blenderkit/ratings.py b/blenderkit/ratings.py
index ddf01e6c..7684d017 100644
--- a/blenderkit/ratings.py
+++ b/blenderkit/ratings.py
@@ -21,8 +21,9 @@ if "bpy" in locals():
paths = reload(paths)
utils = reload(utils)
+ rerequests = reload(rerequests)
else:
- from blenderkit import paths, utils
+ from blenderkit import paths, utils, rerequests
import bpy
import requests, threading
@@ -67,14 +68,14 @@ def uplaod_rating_thread(url, ratings, headers):
}
try:
- r = requests.put(rating_url, data=data, verify=True, headers=headers)
+ r = rerequests.put(rating_url, data=data, verify=True, headers=headers)
except requests.exceptions.RequestException as e:
print('ratings upload failed: %s' % str(e))
def uplaod_review_thread(url, reviews, headers):
- r = requests.put(url, data=reviews, verify=True, headers=headers)
+ r = rerequests.put(url, data=reviews, verify=True, headers=headers)
# except requests.exceptions.RequestException as e:
# print('reviews upload failed: %s' % str(e))
diff --git a/blenderkit/rerequests.py b/blenderkit/rerequests.py
new file mode 100644
index 00000000..d58214bc
--- /dev/null
+++ b/blenderkit/rerequests.py
@@ -0,0 +1,95 @@
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+if "bpy" in locals():
+ from importlib import reload
+
+ ui = reload(ui)
+ utils = reload(utils)
+ paths = reload(paths)
+ tasks_queue = reload(tasks_queue)
+ bkit_oauth = reload(bkit_oauth)
+else:
+ from blenderkit import ui, utils, paths, tasks_queue, bkit_oauth
+
+import requests
+import bpy
+
+
+def rerequest(method, url, **kwargs):
+ # first get any additional args from kwargs
+ immediate = False
+ if kwargs.get('immediate'):
+ immediate = kwargs['immediate']
+ kwargs.pop('immediate')
+ # first normal attempt
+ response = requests.request(method, url, **kwargs)
+
+ utils.p(url)
+ utils.p(response.status_code)
+
+ if response.status_code == 401:
+ try:
+ rdata = response.json()
+ except:
+ rdata = {}
+
+ tasks_queue.add_task((ui.add_report, (method + ' request Failed.' + str(rdata.get('detail')),)))
+
+ if rdata.get('detail') == 'Invalid token.':
+ user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
+ if user_preferences.api_key != '':
+ if user_preferences.enable_oauth:
+ tasks_queue.add_task((ui.add_report, ('refreshing token.',)))
+ refresh_url = paths.get_bkit_url()
+ auth_token, refresh_token = bkit_oauth.refresh_token(user_preferences.api_key_refresh, refresh_url)
+
+ # utils.p(auth_token, refresh_token)
+ if auth_token is not None:
+ if immediate == True:
+ # this can write tokens occasionally into prefs. used e.g. in upload. Only possible
+ # in non-threaded tasks
+ bpy.context.preferences.addons['blenderkit'].preferences.api_key = auth_token
+ bpy.context.preferences.addons['blenderkit'].preferences.api_key_refresh = refresh_token
+
+ kwargs['headers'] = utils.get_headers(auth_token)
+ response = requests.request(method, url, **kwargs)
+ utils.p('reresult', response.status_code)
+ if response.status_code >= 400:
+ utils.p('reresult', response.text)
+ return response
+
+
+def get(url, **kwargs):
+ response = rerequest('get', url, **kwargs)
+ return response
+
+
+def post(url, **kwargs):
+ response = rerequest('post', url, **kwargs)
+ return response
+
+
+def put(url, **kwargs):
+ response = rerequest('put', url, **kwargs)
+ return response
+
+
+def patch(url, **kwargs):
+ response = rerequest('patch', url, **kwargs)
+ return response
diff --git a/blenderkit/search.py b/blenderkit/search.py
index f19a019e..96c50a0e 100644
--- a/blenderkit/search.py
+++ b/blenderkit/search.py
@@ -27,8 +27,9 @@ if "bpy" in locals():
bkit_oauth = reload(bkit_oauth)
version_checker = reload(version_checker)
tasks_queue = reload(tasks_queue)
+ rerequests = reload(rerequests)
else:
- from blenderkit import paths, utils, categories, ui, bkit_oauth, version_checker, tasks_queue
+ from blenderkit import paths, utils, categories, ui, bkit_oauth, version_checker, tasks_queue, rerequests
import blenderkit
from bpy.app.handlers import persistent
@@ -121,9 +122,11 @@ def timer_update(): # TODO might get moved to handle all blenderkit stuff.
# causing a lot of throuble literally.
if len(search_threads) == 0 or bpy.context.scene.blenderkitUI.dragging:
return 1
- for thread in search_threads: # TODO this doesn't check all processess when removal... mostly 1 process will be running however.
+ for thread in search_threads: # TODO this doesn't check all processess when one gets removed, but most time only
+ # one is running anyway
if not thread[0].is_alive():
+ print('parsing')
search_threads.remove(thread) #
icons_dir = thread[1]
scene = bpy.context.scene
@@ -558,7 +561,7 @@ class ThumbDownloader(threading.Thread):
return self._stop_event.is_set()
def run(self):
- r = requests.get(self.url, stream=False)
+ r = rerequests.get(self.url, stream=False)
if r.status_code == 200:
with open(self.path, 'wb') as f:
f.write(r.content)
@@ -581,7 +584,7 @@ def fetch_author(a_id, api_key):
try:
a_url = paths.get_api_url() + 'accounts/' + a_id + '/'
headers = utils.get_headers(api_key)
- r = requests.get(a_url, headers=headers)
+ r = rerequests.get(a_url, headers=headers)
if r.status_code == 200:
adata = r.json()
if not hasattr(adata, 'id'):
@@ -591,13 +594,14 @@ def fetch_author(a_id, api_key):
if adata.get('gravatarHash') is not None:
gravatar_path = paths.get_temp_dir(subdir='g/') + adata['gravatarHash'] + '.jpg'
url = "https://www.gravatar.com/avatar/" + adata['gravatarHash'] + '?d=404'
- r = requests.get(url, stream=False)
+ r = rerequests.get(url, stream=False)
if r.status_code == 200:
with open(gravatar_path, 'wb') as f:
f.write(r.content)
adata['gravatarImg'] = gravatar_path
elif r.status_code == '404':
adata['gravatarHash'] = None
+ utils.p('gravatar for author not available.')
except Exception as e:
utils.p(e)
utils.p('finish fetch')
@@ -635,7 +639,7 @@ def write_profile(adata):
def request_profile(api_key):
a_url = paths.get_api_url() + 'me/'
headers = utils.get_headers(api_key)
- r = requests.get(a_url, headers=headers)
+ r = rerequests.get(a_url, headers=headers)
adata = r.json()
if adata.get('user') is None:
utils.p(adata)
@@ -678,8 +682,7 @@ class Searcher(threading.Thread):
return self._stop_event.is_set()
def run(self):
- maxthreads = 300
- maximages = 50
+ maxthreads = 50
query = self.query
params = self.params
global reports
@@ -731,7 +734,7 @@ class Searcher(threading.Thread):
try:
utils.p(urlquery)
- r = requests.get(urlquery, headers=headers)
+ r = rerequests.get(urlquery, headers=headers)
reports = ''
# utils.p(r.text)
except requests.exceptions.RequestException as e:
@@ -1115,7 +1118,7 @@ class SearchOperator(Operator):
description="get next page from previous search",
default=False)
- keywords = StringProperty(
+ keywords: StringProperty(
name="Keywords",
description="Keywords",
default="")
@@ -1132,9 +1135,8 @@ class SearchOperator(Operator):
if self.keywords != '':
sprops.search_keywords = self.keywords
-
search(category=self.category, get_next=self.get_next, author_id=self.author_id)
- #bpy.ops.view3d.blenderkit_asset_bar()
+ # bpy.ops.view3d.blenderkit_asset_bar()
return {'FINISHED'}
@@ -1163,18 +1165,3 @@ def unregister_search():
# bpy.app.timers.unregister(timer_update)
-
-'''
-search -
-build query
-START THREAD
-send query (bg already)
-get result - metadata, small thumbnails, big thumbnails paths (now generate this?)
-write metadata, possibly to
-download small thumbnails first
-start big thumbnails download. these don't have to be there on updates, if they aren't the Image in image editor doesn't get updated.
-parse metadata, save it in json in the temp dir which gets read on each update of the search.
-END THREAD
-when download is triggered, get also this metadata from json. E
-pass this single - item metadata in the download functions, threads.
-'''
diff --git a/blenderkit/upload.py b/blenderkit/upload.py
index c9e767a4..75f203df 100644
--- a/blenderkit/upload.py
+++ b/blenderkit/upload.py
@@ -31,9 +31,10 @@ if "bpy" in locals():
ui = reload(ui)
overrides = reload(overrides)
colors = reload(colors)
+ rerequests = reload(rerequests)
else:
from blenderkit import asset_inspector, paths, utils, bg_blender, autothumb, version_checker, search, ui_panels, ui, \
- overrides, colors
+ overrides, colors, rerequests
import tempfile, os, subprocess, json, re
@@ -473,7 +474,7 @@ def verification_status_change(self, context, asset_id, state):
url = paths.get_api_url() + 'assets/' + str(asset_id) + '/'
headers = utils.get_headers(user_preferences.api_key)
try:
- r = requests.patch(url, json=upload_data, headers=headers, verify=True) # files = files,
+ r = rerequests.patch(url, json=upload_data, headers=headers, verify=True) # files = files,
#print('changed status ')
#print(r.text)
except requests.exceptions.RequestException as e:
@@ -609,7 +610,7 @@ def start_upload(self, context, asset_type, reupload, upload_set):
global reports
if props.asset_base_id == '':
try:
- r = requests.post(url, json=json_metadata, headers=headers, verify=True) # files = files,
+ r = rerequests.post(url, json=json_metadata, headers=headers, verify=True, immediate = True) # files = files,
ui.add_report('uploaded metadata')
utils.p(r.text)
except requests.exceptions.RequestException as e:
@@ -623,7 +624,7 @@ def start_upload(self, context, asset_type, reupload, upload_set):
try:
if upload_set != ['METADATA']:
json_metadata["verificationStatus"] = "uploading"
- r = requests.put(url, json=json_metadata, headers=headers, verify=True) # files = files,
+ r = rerequests.put(url, json=json_metadata, headers=headers, verify=True , immediate = True) # files = files,
ui.add_report('uploaded metadata')
# parse the request
# print('uploaded metadata')
diff --git a/blenderkit/upload_bg.py b/blenderkit/upload_bg.py
index d951eac2..a9c9f19b 100644
--- a/blenderkit/upload_bg.py
+++ b/blenderkit/upload_bg.py
@@ -24,8 +24,9 @@ if "bpy" in locals():
append_link = reload(append_link)
bg_blender = reload(bg_blender)
utils = reload(utils)
+ rerequests = reload(rerequests)
else:
- from blenderkit import paths, append_link, bg_blender, utils
+ from blenderkit import paths, append_link, bg_blender, utils, rerequests
import sys, json, os, time
import requests
@@ -84,7 +85,7 @@ def upload_file(upload_data, f):
'originalFilename': os.path.basename(f['file_path'])
}
upload_create_url = paths.get_api_url() + 'uploads/'
- upload = requests.post(upload_create_url, json=upload_info, headers=headers, verify=True)
+ upload = rerequests.post(upload_create_url, json=upload_info, headers=headers, verify=True)
upload = upload.json()
chunk_size = 1024 * 256
@@ -109,7 +110,7 @@ def upload_file(upload_data, f):
# confirm single file upload to bkit server
upload_done_url = paths.get_api_url() + 'uploads_s3/' + upload['id'] + '/upload-file/'
- upload_response = requests.post(upload_done_url, headers=headers, verify=True)
+ upload_response = rerequests.post(upload_done_url, headers=headers, verify=True)
bg_blender.progress('finished uploading')
@@ -117,7 +118,6 @@ def upload_file(upload_data, f):
def upload_files(upload_data, files):
-
uploaded_all = True
for f in files:
uploaded = upload_file(upload_data, f)
@@ -180,10 +180,10 @@ if __name__ == "__main__":
files = []
if 'THUMBNAIL' in upload_set:
files.append({
- "type": "thumbnail",
- "index": 0,
- "file_path": export_data["thumbnail_path"]
- })
+ "type": "thumbnail",
+ "index": 0,
+ "file_path": export_data["thumbnail_path"]
+ })
if 'MAINFILE' in upload_set:
files.append({
"type": "blend",
@@ -207,7 +207,7 @@ if __name__ == "__main__":
url += upload_data["id"] + '/'
- r = requests.patch(url, json=confirm_data, headers=headers, verify=True) # files = files,
+ r = rerequests.patch(url, json=confirm_data, headers=headers, verify=True) # files = files,
bg_blender.progress('upload finished successfully')
else:
diff --git a/blenderkit/utils.py b/blenderkit/utils.py
index 7c56ce8a..a6bb407d 100644
--- a/blenderkit/utils.py
+++ b/blenderkit/utils.py
@@ -385,7 +385,7 @@ def get_dimensions(obs):
def requests_post_thread(url, json, headers):
- r = requests.post(url, json=json, verify=True, headers=headers)
+ r = rerequests.post(url, json=json, verify=True, headers=headers)
def get_headers(api_key):
diff --git a/development_edit_operator.py b/development_edit_operator.py
new file mode 100644
index 00000000..08e91d87
--- /dev/null
+++ b/development_edit_operator.py
@@ -0,0 +1,337 @@
+# ##### 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 #####
+
+
+bl_info = {
+ "name": "Edit Operator Source",
+ "author": "scorpion81",
+ "version": (1, 2, 2),
+ "blender": (2, 80, 0),
+ "location": "Text Editor > Edit > Edit Operator",
+ "description": "Opens source file of chosen operator or call locations, if source not available",
+ "warning": "",
+ "wiki_url": "https://wiki.blender.org/index.php/Extensions:2.6/"
+ "Py/Scripts/Development/Edit_Operator_Source",
+ "category": "Development"}
+
+import bpy
+import sys
+import os
+import inspect
+from bpy.types import (
+ Operator,
+ Panel,
+ Header,
+ Menu,
+ PropertyGroup
+ )
+from bpy.props import (
+ EnumProperty,
+ StringProperty,
+ IntProperty
+ )
+
+def stdlib_excludes():
+ #need a handy list of modules to avoid walking into
+ import distutils.sysconfig as sysconfig
+ excludes = []
+ std_lib = sysconfig.get_python_lib(standard_lib=True)
+ for top, dirs, files in os.walk(std_lib):
+ for nm in files:
+ if nm != '__init__.py' and nm[-3:] == '.py':
+ excludes.append(os.path.join(top, nm)[len(std_lib)+1:-3].replace('\\','.'))
+
+ return excludes
+
+def make_loc(prefix, c):
+ #too long and not helpful... omitting for now
+ space = ""
+ #if hasattr(c, "bl_space_type"):
+ # space = c.bl_space_type
+
+ region = ""
+ #if hasattr(c, "bl_region_type"):
+ # region = c.bl_region_type
+
+ label = ""
+ if hasattr(c, "bl_label"):
+ label = c.bl_label
+
+ return prefix+": " + space + " " + region + " " + label
+
+def walk_module(opname, mod, calls=[], exclude=[]):
+
+ for name, m in inspect.getmembers(mod):
+ if inspect.ismodule(m):
+ if m.__name__ not in exclude:
+ #print(name, m.__name__)
+ walk_module(opname, m, calls, exclude)
+ elif inspect.isclass(m):
+ if (issubclass(m, Panel) or \
+ issubclass(m, Header) or \
+ issubclass(m, Menu)) and mod.__name__ != "bl_ui":
+ if hasattr(m, "draw"):
+ loc = ""
+ file = ""
+ line = -1
+ src, n = inspect.getsourcelines(m.draw)
+ for i, s in enumerate(src):
+ if opname in s:
+ file = mod.__file__
+ line = n + i
+
+ if issubclass(m, Panel) and name != "Panel":
+ loc = make_loc("Panel", m)
+ calls.append([opname, loc, file, line])
+ if issubclass(m, Header) and name != "Header":
+ loc = make_loc("Header", m)
+ calls.append([opname, loc, file, line])
+ if issubclass(m, Menu) and name != "Menu":
+ loc = make_loc("Menu", m)
+ calls.append([opname, loc, file, line])
+
+
+def getclazz(opname):
+ opid = opname.split(".")
+ opmod = getattr(bpy.ops, opid[0])
+ op = getattr(opmod, opid[1])
+ id = op.get_rna_type().bl_rna.identifier
+ try:
+ clazz = getattr(bpy.types, id)
+ return clazz
+ except AttributeError:
+ return None
+
+
+def getmodule(opname):
+ addon = True
+ clazz = getclazz(opname)
+
+ if clazz is None:
+ return "", -1, False
+
+ modn = clazz.__module__
+
+ try:
+ line = inspect.getsourcelines(clazz)[1]
+ except IOError:
+ line = -1
+ except TypeError:
+ line = -1
+
+ if modn == 'bpy.types':
+ mod = 'C operator'
+ addon = False
+ elif modn != '__main__':
+ mod = sys.modules[modn].__file__
+ else:
+ addon = False
+ mod = modn
+
+ return mod, line, addon
+
+
+def get_ops():
+ allops = []
+ opsdir = dir(bpy.ops)
+ for opmodname in opsdir:
+ opmod = getattr(bpy.ops, opmodname)
+ opmoddir = dir(opmod)
+ for o in opmoddir:
+ name = opmodname + "." + o
+ clazz = getclazz(name)
+ #if (clazz is not None) :# and clazz.__module__ != 'bpy.types'):
+ allops.append(name)
+ del opmoddir
+
+ # add own operator name too, since its not loaded yet when this is called
+ allops.append("text.edit_operator")
+ l = sorted(allops)
+ del allops
+ del opsdir
+
+ return [(y, y, "", x) for x, y in enumerate(l)]
+
+class OperatorEntry(PropertyGroup):
+
+ label : StringProperty(
+ name="Label",
+ description="",
+ default=""
+ )
+
+ path : StringProperty(
+ name="Path",
+ description="",
+ default=""
+ )
+
+ line : IntProperty(
+ name="Line",
+ description="",
+ default=-1
+ )
+
+class TEXT_OT_EditOperator(Operator):
+ bl_idname = "text.edit_operator"
+ bl_label = "Edit Operator"
+ bl_description = "Opens the source file of operators chosen from Menu"
+ bl_property = "op"
+
+ items = get_ops()
+
+ op : EnumProperty(
+ name="Op",
+ description="",
+ items=items
+ )
+
+ path : StringProperty(
+ name="Path",
+ description="",
+ default=""
+ )
+
+ line : IntProperty(
+ name="Line",
+ description="",
+ default=-1
+ )
+
+ def show_text(self, context, path, line):
+ found = False
+
+ for t in bpy.data.texts:
+ if t.filepath == path:
+ #switch to the wanted text first
+ context.space_data.text = t
+ ctx = context.copy()
+ ctx['edit_text'] = t
+ bpy.ops.text.jump(ctx, line=line)
+ found = True
+ break
+
+ if (found is False):
+ self.report({'INFO'},
+ "Opened file: " + path)
+ bpy.ops.text.open(filepath=path)
+ bpy.ops.text.jump(line=line)
+
+ def show_calls(self, context):
+ import bl_ui
+ import addon_utils
+
+ exclude = stdlib_excludes()
+ exclude.append("bpy")
+ exclude.append("sys")
+
+ calls = []
+ walk_module(self.op, bl_ui, calls, exclude)
+
+ for m in addon_utils.modules():
+ try:
+ mod = sys.modules[m.__name__]
+ walk_module(self.op, mod, calls, exclude)
+ except KeyError:
+ continue
+
+ for c in calls:
+ cl = context.scene.calls.add()
+ cl.name = c[0]
+ cl.label = c[1]
+ cl.path = c[2]
+ cl.line = c[3]
+
+ def invoke(self, context, event):
+ context.window_manager.invoke_search_popup(self)
+ return {'PASS_THROUGH'}
+
+ def execute(self, context):
+ if self.path != "" and self.line != -1:
+ #invocation of one of the "found" locations
+ self.show_text(context, self.path, self.line)
+ return {'FINISHED'}
+ else:
+ context.scene.calls.clear()
+ path, line, addon = getmodule(self.op)
+
+ if addon:
+ self.show_text(context, path, line)
+
+ #add convenient "source" button, to toggle back from calls to source
+ c = context.scene.calls.add()
+ c.name = self.op
+ c.label = "Source"
+ c.path = path
+ c.line = line
+
+ self.show_calls(context)
+ context.area.tag_redraw()
+
+ return {'FINISHED'}
+ else:
+
+ self.report({'WARNING'},
+ "Found no source file for " + self.op)
+
+ self.show_calls(context)
+ context.area.tag_redraw()
+
+ return {'FINISHED'}
+
+
+class TEXT_PT_EditOperatorPanel(Panel):
+ bl_space_type = 'TEXT_EDITOR'
+ bl_region_type = 'UI'
+ bl_label = "Edit Operator"
+ bl_category = "Text"
+
+ def draw(self, context):
+ layout = self.layout
+ op = layout.operator("text.edit_operator")
+ op.path = ""
+ op.line = -1
+
+ if len(context.scene.calls) > 0:
+ box = layout.box()
+ box.label(text="Calls of: " + context.scene.calls[0].name)
+ box.operator_context = 'EXEC_DEFAULT'
+ for c in context.scene.calls:
+ op = box.operator("text.edit_operator", text=c.label)
+ op.path = c.path
+ op.line = c.line
+ op.op = c.name
+
+
+def register():
+ bpy.utils.register_class(OperatorEntry)
+ bpy.types.Scene.calls = bpy.props.CollectionProperty(name="Calls",
+ type=OperatorEntry)
+ bpy.utils.register_class(TEXT_OT_EditOperator)
+ bpy.utils.register_class(TEXT_PT_EditOperatorPanel)
+
+
+def unregister():
+ bpy.utils.unregister_class(TEXT_PT_EditOperatorPanel)
+ bpy.utils.unregister_class(TEXT_OT_EditOperator)
+ del bpy.types.Scene.calls
+ bpy.utils.unregister_class(OperatorEntry)
+
+
+if __name__ == "__main__":
+ register()
diff --git a/space_view3d_brush_menus/brush_menu.py b/space_view3d_brush_menus/brush_menu.py
index 28caf747..85d77bcd 100644
--- a/space_view3d_brush_menus/brush_menu.py
+++ b/space_view3d_brush_menus/brush_menu.py
@@ -42,7 +42,11 @@ class BrushOptionsMenu(Menu):
has_brush = utils_core.get_brush_link(context, types="brush")
icons = brushes.brush_icon[mode][has_brush.sculpt_tool] if \
has_brush else "BRUSH_DATA"
+ layout.operator_context = 'INVOKE_REGION_WIN'
+ layout.operator("wm.search_menu", text="Search", icon='VIEWZOOM')
layout.operator("wm.toolbar", text="Tools", icon='TOOL_SETTINGS')
+ layout.menu("SCREEN_MT_user_menu", text="Quick Favorites", icon='HEART')
+ layout.separator()
layout.row().menu("VIEW3D_MT_sv3_brushes_menu",
icon=icons)
@@ -65,7 +69,11 @@ class BrushOptionsMenu(Menu):
icons = brushes.brush_icon[mode][has_brush.vertex_tool] if \
has_brush else "BRUSH_DATA"
+ layout.operator_context = 'INVOKE_REGION_WIN'
+ layout.operator("wm.search_menu", text="Search", icon='VIEWZOOM')
layout.operator("wm.toolbar", text="Tools", icon='TOOL_SETTINGS')
+ layout.menu("SCREEN_MT_user_menu", text="Quick Favorites", icon='HEART')
+ layout.separator()
if mode == 'VERTEX_PAINT':
layout.row().operator(ColorPickerPopup.bl_idname, icon="COLOR")
@@ -118,7 +126,10 @@ class BrushOptionsMenu(Menu):
layout.row().label(text="Missing Data", icon='ERROR')
layout.row().label(text="See Mask Properties", icon='FORWARD')
layout.row().separator()
+ layout.operator("wm.search_menu", text="Search", icon='VIEWZOOM')
layout.operator("wm.toolbar", text="Tools", icon='TOOL_SETTINGS')
+ layout.menu("SCREEN_MT_user_menu", text="Quick Favorites", icon='HEART')
+ layout.row().separator()
layout.row().menu("VIEW3D_MT_sv3_brushes_menu",
icon=icons)
@@ -128,7 +139,11 @@ class BrushOptionsMenu(Menu):
layout.row().label(text="Missing Data", icon="INFO")
else:
+ layout.operator_context = 'INVOKE_REGION_WIN'
+ layout.operator("wm.search_menu", text="Search", icon='VIEWZOOM')
layout.operator("wm.toolbar", text="Tools", icon='TOOL_SETTINGS')
+ layout.menu("SCREEN_MT_user_menu", text="Quick Favorites", icon='HEART')
+ layout.separator()
if has_brush and has_brush.image_tool in {'DRAW', 'FILL'} and \
has_brush.blend not in {'ERASE_ALPHA', 'ADD_ALPHA'}:
@@ -160,7 +175,11 @@ class BrushOptionsMenu(Menu):
def particle(self, layout, context):
particle_edit = context.tool_settings.particle_edit
+ layout.operator_context = 'INVOKE_REGION_WIN'
+ layout.operator("wm.search_menu", text="Search", icon='VIEWZOOM')
layout.operator("wm.toolbar", text="Tools", icon='TOOL_SETTINGS')
+ layout.menu("SCREEN_MT_user_menu", text="Quick Favorites", icon='HEART')
+ layout.separator()
layout.row().menu("VIEW3D_MT_sv3_brushes_menu",
icon="BRUSH_DATA")
diff --git a/space_view3d_spacebar_menu/__init__.py b/space_view3d_spacebar_menu/__init__.py
index 201b9506..467eaa62 100644
--- a/space_view3d_spacebar_menu/__init__.py
+++ b/space_view3d_spacebar_menu/__init__.py
@@ -645,10 +645,8 @@ class VIEW3D_MT_Space_Dynamic_Menu(Menu):
layout.menu("VIEW3D_MT_InteractiveMode", icon='VIEW3D')
UseSeparator(self, context)
layout.menu("VIEW3D_MT_View_Menu", icon='ZOOM_ALL')
- layout.menu("VIEW3D_MT_Select_Particle",
- icon='RESTRICT_SELECT_OFF')
layout.menu("VIEW3D_MT_select_particle",
- text="Select and Display Mode", icon='PARTICLE_PATH')
+ text="Select", icon='PARTICLE_PATH')
UseSeparator(self, context)
layout.menu("VIEW3D_MT_TransformMenu", icon='EMPTY_ARROWS')
layout.menu("VIEW3D_MT_mirror", icon='MOD_MIRROR')