Fix for #36387, User Preferences "Addons" panel bogs down the whole interface.
The addons panel draw function calls addon_utils.modules() which in turn retrieves a list of fake modules from the script paths every time. This can become costly when network paths are included for addons. Solution is to put the scanning process into a dedicated "refresh" function and disable it in frequently called draw and filter functions, i.e. in these cases the cached addons_fake_modules list will be used instead. Note that this may lead to invalid addon lists if script paths are changed (which is not working 100% without restart anyway according to Campbell). For this there is now a "Refresh" operator button in the addons preferences. If necessary and feasible such forced refreshes can be added later too.
This commit is contained in:
@@ -35,7 +35,6 @@ error_duplicates = False
|
||||
error_encoding = False
|
||||
addons_fake_modules = {}
|
||||
|
||||
|
||||
def paths():
|
||||
# RELEASE SCRIPTS: official scripts distributed in Blender releases
|
||||
addon_paths = _bpy.utils.script_paths("addons")
|
||||
@@ -51,7 +50,7 @@ def paths():
|
||||
return addon_paths
|
||||
|
||||
|
||||
def modules(module_cache):
|
||||
def modules_refresh(module_cache=addons_fake_modules):
|
||||
global error_duplicates
|
||||
global error_encoding
|
||||
import os
|
||||
@@ -184,6 +183,11 @@ def modules(module_cache):
|
||||
del module_cache[mod_stale]
|
||||
del modules_stale
|
||||
|
||||
|
||||
def modules(module_cache=addons_fake_modules, refresh=True):
|
||||
if refresh:
|
||||
modules_refresh(module_cache)
|
||||
|
||||
mod_list = list(module_cache.values())
|
||||
mod_list.sort(key=lambda mod: (mod.bl_info["category"],
|
||||
mod.bl_info["name"],
|
||||
@@ -370,6 +374,9 @@ def reset_all(reload_scripts=False):
|
||||
"""
|
||||
import sys
|
||||
|
||||
# initializes addons_fake_modules
|
||||
modules_refresh()
|
||||
|
||||
# RELEASE SCRIPTS: official scripts distributed in Blender releases
|
||||
paths_list = paths()
|
||||
|
||||
|
@@ -172,7 +172,7 @@ def enable_addons(addons={}, support={}, disable=False, check_only=False):
|
||||
userpref = bpy.context.user_preferences
|
||||
used_ext = {ext.module for ext in userpref.addons}
|
||||
|
||||
ret = [mod for mod in addon_utils.modules(addon_utils.addons_fake_modules)
|
||||
ret = [mod for mod in addon_utils.modules()
|
||||
if ((addons and mod.__name__ in addons) or
|
||||
(not addons and addon_utils.module_bl_info(mod)["support"] in support))]
|
||||
|
||||
|
@@ -1693,6 +1693,19 @@ class WM_OT_theme_install(Operator):
|
||||
return {'RUNNING_MODAL'}
|
||||
|
||||
|
||||
class WM_OT_addon_refresh(Operator):
|
||||
"Scan addon directories for new modules"
|
||||
bl_idname = "wm.addon_refresh"
|
||||
bl_label = "Refresh"
|
||||
|
||||
def execute(self, context):
|
||||
import addon_utils
|
||||
|
||||
addon_utils.modules_refresh()
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class WM_OT_addon_install(Operator):
|
||||
"Install an addon"
|
||||
bl_idname = "wm.addon_install"
|
||||
@@ -1782,7 +1795,7 @@ class WM_OT_addon_install(Operator):
|
||||
del pyfile_dir
|
||||
# done checking for exceptional case
|
||||
|
||||
addons_old = {mod.__name__ for mod in addon_utils.modules(addon_utils.addons_fake_modules)}
|
||||
addons_old = {mod.__name__ for mod in addon_utils.modules()}
|
||||
|
||||
#check to see if the file is in compressed format (.zip)
|
||||
if zipfile.is_zipfile(pyfile):
|
||||
@@ -1825,7 +1838,7 @@ class WM_OT_addon_install(Operator):
|
||||
traceback.print_exc()
|
||||
return {'CANCELLED'}
|
||||
|
||||
addons_new = {mod.__name__ for mod in addon_utils.modules(addon_utils.addons_fake_modules)} - addons_old
|
||||
addons_new = {mod.__name__ for mod in addon_utils.modules()} - addons_old
|
||||
addons_new.discard("modules")
|
||||
|
||||
# disable any addons we may have enabled previously and removed.
|
||||
@@ -1835,7 +1848,7 @@ class WM_OT_addon_install(Operator):
|
||||
|
||||
# possible the zip contains multiple addons, we could disallow this
|
||||
# but for now just use the first
|
||||
for mod in addon_utils.modules(addon_utils.addons_fake_modules):
|
||||
for mod in addon_utils.modules(refresh=False):
|
||||
if mod.__name__ in addons_new:
|
||||
info = addon_utils.module_bl_info(mod)
|
||||
|
||||
@@ -1875,7 +1888,7 @@ class WM_OT_addon_remove(Operator):
|
||||
import os
|
||||
import addon_utils
|
||||
|
||||
for mod in addon_utils.modules(addon_utils.addons_fake_modules):
|
||||
for mod in addon_utils.modules():
|
||||
if mod.__name__ == module:
|
||||
filepath = mod.__file__
|
||||
if os.path.exists(filepath):
|
||||
|
@@ -105,7 +105,7 @@ def register():
|
||||
|
||||
items_unique = set()
|
||||
|
||||
for mod in addon_utils.modules(addon_utils.addons_fake_modules):
|
||||
for mod in addon_utils.modules(refresh=False):
|
||||
info = addon_utils.module_bl_info(mod)
|
||||
items_unique.add(info["category"])
|
||||
|
||||
|
@@ -1142,11 +1142,12 @@ class USERPREF_PT_addons(Panel):
|
||||
scripts_addons_folder = bpy.utils.user_resource('SCRIPTS', "addons")
|
||||
|
||||
# collect the categories that can be filtered on
|
||||
addons = [(mod, addon_utils.module_bl_info(mod)) for mod in addon_utils.modules(addon_utils.addons_fake_modules)]
|
||||
addons = [(mod, addon_utils.module_bl_info(mod)) for mod in addon_utils.modules(refresh=False)]
|
||||
|
||||
split = layout.split(percentage=0.2)
|
||||
col = split.column()
|
||||
col.prop(context.window_manager, "addon_search", text="", icon='VIEWZOOM')
|
||||
col.operator("wm.addon_refresh", icon='FILE_REFRESH')
|
||||
|
||||
col.label(text="Supported Level")
|
||||
col.prop(context.window_manager, "addon_support", expand=True)
|
||||
@@ -1156,7 +1157,7 @@ class USERPREF_PT_addons(Panel):
|
||||
|
||||
col = split.column()
|
||||
|
||||
# set in addon_utils.modules(...)
|
||||
# set in addon_utils.modules_refresh()
|
||||
if addon_utils.error_duplicates:
|
||||
self.draw_error(col,
|
||||
"Multiple addons using the same name found!\n"
|
||||
|
Reference in New Issue
Block a user