Amaranth: Update to 1.0.5, Refactor the scene debug script, fixes
authorlijenstina <lijenstina@gmail.com>
Mon, 25 Dec 2017 03:09:57 +0000 (04:09 +0100)
committerlijenstina <lijenstina@gmail.com>
Mon, 25 Dec 2017 03:09:57 +0000 (04:09 +0100)
Bumped version to 1.0.5
Pep 8 cleanup
Imports as tuples

Apply the patch bellow. Thanks to the author
Differential Revision: https://developer.blender.org/D1288

Refactor the scene\debug.py script:
- Use a separate class for storage (AMTH_store_data) instead in Operators
  # Don't store object data in it (can cause issues with undo)
  # AMTH_SCENE_OT_list_users_debug_clear is now one operator for all data

- Add some printing helper functions

- Use UI lists for Images and Lighter's corner panel:
  # As they need collections, fill the collections with data
  # Performance difference is noticable in scene with 2000 lights
  # The only downside is that they need to be refreshed manually
  # Remove the poll and the toggle in preferences

- Change the logic of the AMTH_SCENE_OT_list_users_for_x_type:
  # Always have a default entry USER_X_NAME_EMPTY
  # Add check around it, deduplicate code
  # add an update call to the amth_datablock_types prop (weak)

To do:
  # Maybe use collections and UI Lists for everything
  # Refactor the AMTH_SCENE_OT_list_users_for_x

Fix check for add-on preferences props calls (crashes on unregister)
Fix  button_camera_passepartout with camera missing, object as camera

Notes:
- The UnicodeDecodeError should be solved (garbage strings in enum)
- T48042 crash still happens in 2.76.10 version
  # The problem arises from nested groups code search in that version
  !!if nd and nd.type == 'GROUP'!! in AMTH_SCENE_OT_list_users_for_x_type
  # Could not be recreated in current master nor 2.79, consider it solved

12 files changed:
amaranth/__init__.py
amaranth/animation/frame_current.py
amaranth/animation/jump_frames.py
amaranth/animation/timeline_extra_info.py
amaranth/node_editor/display_image.py
amaranth/prefs.py
amaranth/render/passepartout.py
amaranth/render/remember_layers.py
amaranth/scene/debug.py
amaranth/scene/refresh.py
amaranth/scene/save_reload.py
amaranth/scene/stats.py

index a40a105..8a579f2 100644 (file)
@@ -90,7 +90,7 @@ from amaranth.misc import (
 bl_info = {
     "name": "Amaranth Toolset",
     "author": "Pablo Vazquez, Bassam Kurdali, Sergey Sharybin, Lukas Tönne, Cesar Saez",
-    "version": (1, 0, 4),
+    "version": (1, 0, 5),
     "blender": (2, 74),
     "location": "Everywhere!",
     "description": "A collection of tools and settings to improve productivity",
index 538931c..24bd395 100644 (file)
@@ -25,9 +25,12 @@ import bpy
 
 
 def button_frame_current(self, context):
-    preferences = context.user_preferences.addons["amaranth"].preferences
+    get_addon = "amaranth" in context.user_preferences.addons.keys()
+    if not get_addon:
+        return
+
     scene = context.scene
-    if preferences.use_frame_current:
+    if context.user_preferences.addons["amaranth"].preferences.use_frame_current:
         self.layout.separator()
         self.layout.prop(scene, "frame_current", text="Set Current Frame")
 
index 5f4ca0c..2d93e80 100644 (file)
@@ -28,6 +28,8 @@ Find it on the User Preferences, Editing.
 """
 
 import bpy
+from bpy.types import Operator
+from bpy.props import BoolProperty
 
 KEYMAPS = list()
 
@@ -40,17 +42,18 @@ def is_keyframe(ob, frame):
                 return True
     return False
 
+
 # monkey path is_keyframe function
 bpy.types.Object.is_keyframe = is_keyframe
 
 
 # FEATURE: Jump to frame in-between next and previous keyframe
-class AMTH_SCREEN_OT_keyframe_jump_inbetween(bpy.types.Operator):
-
+class AMTH_SCREEN_OT_keyframe_jump_inbetween(Operator):
     """Jump to half in-between keyframes"""
     bl_idname = "screen.amth_keyframe_jump_inbetween"
     bl_label = "Jump to Keyframe In-between"
-    backwards = bpy.props.BoolProperty()
+
+    backwards = BoolProperty()
 
     def execute(self, context):
         back = self.backwards
@@ -110,16 +113,20 @@ class AMTH_SCREEN_OT_keyframe_jump_inbetween(bpy.types.Operator):
 
 
 # FEATURE: Jump forward/backward every N frames
-class AMTH_SCREEN_OT_frame_jump(bpy.types.Operator):
-
+class AMTH_SCREEN_OT_frame_jump(Operator):
     """Jump a number of frames forward/backwards"""
     bl_idname = "screen.amaranth_frame_jump"
     bl_label = "Jump Frames"
 
-    forward = bpy.props.BoolProperty(default=True)
+    forward = BoolProperty(default=True)
 
     def execute(self, context):
         scene = context.scene
+
+        get_addon = "amaranth" in context.user_preferences.addons.keys()
+        if not get_addon:
+            return {"CANCELLED"}
+
         preferences = context.user_preferences.addons["amaranth"].preferences
 
         if preferences.use_framerate:
@@ -135,6 +142,10 @@ class AMTH_SCREEN_OT_frame_jump(bpy.types.Operator):
 
 
 def ui_userpreferences_edit(self, context):
+    get_addon = "amaranth" in context.user_preferences.addons.keys()
+    if not get_addon:
+        return
+
     preferences = context.user_preferences.addons["amaranth"].preferences
 
     col = self.layout.column()
@@ -144,11 +155,13 @@ def ui_userpreferences_edit(self, context):
 
 
 def label(self, context):
+    get_addon = "amaranth" in context.user_preferences.addons.keys()
+    if not get_addon:
+        return
 
-    preferences = context.user_preferences.addons["amaranth"].preferences
     layout = self.layout
 
-    if preferences.use_timeline_extra_info:
+    if context.user_preferences.addons["amaranth"].preferences.use_timeline_extra_info:
         row = layout.row(align=True)
 
         row.operator(AMTH_SCREEN_OT_keyframe_jump_inbetween.bl_idname,
index c1a6449..7e494ee 100644 (file)
@@ -23,12 +23,14 @@ import bpy
 
 
 def label_timeline_extra_info(self, context):
+    get_addon = "amaranth" in context.user_preferences.addons.keys()
+    if not get_addon:
+        return
 
-    preferences = context.user_preferences.addons["amaranth"].preferences
     layout = self.layout
     scene = context.scene
 
-    if preferences.use_timeline_extra_info:
+    if context.user_preferences.addons["amaranth"].preferences.use_timeline_extra_info:
         row = layout.row(align=True)
 
         # Check for preview range
index 322e4d1..5cc2e94 100644 (file)
@@ -36,7 +36,6 @@ image_nodes = ("CompositorNodeRLayers",
 
 
 class AMTH_NODE_OT_show_active_node_image(bpy.types.Operator):
-
     """Show active image node image in the image editor"""
     bl_idname = "node.show_active_node_image"
     bl_label = "Show Active Node Node"
@@ -51,6 +50,10 @@ class AMTH_NODE_OT_show_active_node_image(bpy.types.Operator):
         select_node = bpy.ops.node.select(mouse_x=mlocx, mouse_y=mlocy, extend=False)
 
         if 'FINISHED' in select_node:  # Only run if we're clicking on a node
+            get_addon = "amaranth" in context.user_preferences.addons.keys()
+            if not get_addon:
+                return {"CANCELLED"}
+
             preferences = context.user_preferences.addons["amaranth"].preferences
             if preferences.use_image_node_display:
                 if context.active_node:
index 8162521..59281f4 100644 (file)
 #  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 
 import bpy
+from bpy.props import (
+        BoolProperty,
+        IntProperty,
+        )
 
 
 class AmaranthToolsetPreferences(bpy.types.AddonPreferences):
     bl_idname = "amaranth"
-    use_frame_current = bpy.props.BoolProperty(
+    use_frame_current = BoolProperty(
         name="Current Frame Slider",
         description="Set the current frame from the Specials menu in the 3D View",
         default=True,
     )
-    use_file_save_reload = bpy.props.BoolProperty(
+    use_file_save_reload = BoolProperty(
         name="Save & Reload File",
         description="File menu > Save & Reload, or Ctrl + Shift + W",
         default=True,
     )
-
-    use_scene_refresh = bpy.props.BoolProperty(
+    use_scene_refresh = BoolProperty(
         name="Refresh Scene",
         description="Specials Menu [W], or hit F5",
         default=True,
     )
-    use_timeline_extra_info = bpy.props.BoolProperty(
+    use_timeline_extra_info = BoolProperty(
         name="Timeline Extra Info",
         description="Timeline Header",
         default=True,
     )
-    use_image_node_display = bpy.props.BoolProperty(
+    use_image_node_display = BoolProperty(
         name="Active Image Node in Editor",
         description="Display active node image in image editor",
         default=True,
     )
-    use_scene_stats = bpy.props.BoolProperty(
+    use_scene_stats = BoolProperty(
         name="Extra Scene Statistics",
         description="Display extra scene statistics in Info header (may be slow in heavy scenes)",
         default=False,
     )
-
-    frames_jump = bpy.props.IntProperty(
+    frames_jump = IntProperty(
         name="Frames",
         description="Number of frames to jump forward/backward",
         default=10,
-        min=1)
-
-    use_framerate = bpy.props.BoolProperty(
+        min=1
+    )
+    use_framerate = BoolProperty(
         name="Framerate Jump",
         description="Jump the amount of frames forward/backward that you have set as your framerate",
         default=False,
     )
-
-    use_layers_for_render = bpy.props.BoolProperty(
+    use_layers_for_render = BoolProperty(
         name="Current Layers for Render",
         description="Save the layers that should be enabled for render",
         default=True,
     )
 
-    use_lighters_corner = bpy.props.BoolProperty(
-        name="Lighter's Corner",
-        description="Display the Lighter's Corner panel on Scene properties",
-        default=False,
-    )
-
     def draw(self, context):
         layout = self.layout
 
@@ -96,7 +91,6 @@ class AmaranthToolsetPreferences(bpy.types.AddonPreferences):
         sub.prop(self, "use_scene_stats")
         sub.prop(self, "use_layers_for_render")
         sub.prop(self, "use_framerate")
-        sub.prop(self, "use_lighters_corner")
 
         sub.separator()
 
@@ -124,8 +118,6 @@ class AmaranthToolsetPreferences(bpy.types.AddonPreferences):
             text="Save the set of layers that should be activated for a final render")
         sub.label(
             text="Jump the amount of frames forward/backward that you've set as your framerate")
-        sub.label(
-            text="Display the Lighter's Corner panel on Scene properties")
 
         sub.separator()
         sub.label(text="")  # Nodes
index 525cfe0..dfc4205 100644 (file)
@@ -23,16 +23,18 @@ import bpy
 
 
 def button_camera_passepartout(self, context):
-
     view3d = context.space_data.region_3d
-    cam = context.scene.camera.data
+    cam = context.scene.camera
 
     if view3d.view_perspective == "CAMERA":
+        if cam is None or not hasattr(cam, "data") or cam.type != "CAMERA":
+            return
+
         layout = self.layout
-        if cam.show_passepartout:
-            layout.prop(cam, "passepartout_alpha", text="Passepartout")
+        if cam.data.show_passepartout:
+            layout.prop(cam.data, "passepartout_alpha", text="Passepartout")
         else:
-            layout.prop(cam, "show_passepartout")
+            layout.prop(cam.data, "show_passepartout")
 
 
 def register():
index 97e878b..a9fcbb6 100644 (file)
@@ -30,11 +30,15 @@ Now all you need to do before saving your file for rendering is press the
 """
 
 import bpy
+from bpy.types import Operator
+from bpy.props import (
+        BoolProperty,
+        IntProperty,
+        )
 
 
 # FEATURE: Set Layers to Render
-class AMTH_SCENE_OT_layers_render_save(bpy.types.Operator):
-
+class AMTH_SCENE_OT_layers_render_save(Operator):
     """Save the current scene layers as those that should be enabled for final renders"""
     bl_idname = "scene.amaranth_layers_render_save"
     bl_label = "Save as Layers for Render"
@@ -54,8 +58,7 @@ class AMTH_SCENE_OT_layers_render_save(bpy.types.Operator):
         return {"FINISHED"}
 
 
-class AMTH_SCENE_OT_layers_render_view(bpy.types.Operator):
-
+class AMTH_SCENE_OT_layers_render_view(Operator):
     """Enable the scene layers that should be active for final renders"""
     bl_idname = "scene.amaranth_layers_render_view"
     bl_label = "View Layers for Render"
@@ -80,7 +83,7 @@ class AMTH_SCENE_OT_layers_render_view(bpy.types.Operator):
                     if layers_render:
                         bpy.ops.view3d.layers(
                             override,
-                            nr=layers_render[0]+1,
+                            nr=layers_render[0] + 1,
                             extend=False,
                             toggle=False)
 
@@ -99,14 +102,13 @@ class AMTH_SCENE_OT_layers_render_view(bpy.types.Operator):
         return {"FINISHED"}
 
 
-class AMTH_SCENE_OT_layers_render_set_individual(bpy.types.Operator):
-
+class AMTH_SCENE_OT_layers_render_set_individual(Operator):
     """Whether this layer should be enabled or not for final renders"""
     bl_idname = "scene.amaranth_layers_render_set_individual"
     bl_label = "Set This Layer for Render"
 
-    toggle = bpy.props.BoolProperty()
-    number = bpy.props.IntProperty()
+    toggle = BoolProperty()
+    number = IntProperty()
 
     def execute(self, context):
         number = self.number
@@ -130,8 +132,7 @@ class AMTH_SCENE_OT_layers_render_set_individual(bpy.types.Operator):
         return {"FINISHED"}
 
 
-class AMTH_SCENE_OT_layers_render_clear(bpy.types.Operator):
-
+class AMTH_SCENE_OT_layers_render_clear(Operator):
     """Clear layers for render"""
     bl_idname = "scene.amaranth_layers_render_clear"
     bl_label = "Clear Layers for Render"
@@ -145,10 +146,11 @@ class AMTH_SCENE_OT_layers_render_clear(bpy.types.Operator):
 
 
 def ui_layers_for_render(self, context):
+    get_addon = "amaranth" in context.user_preferences.addons.keys()
+    if not get_addon:
+        return
 
-    preferences = context.user_preferences.addons["amaranth"].preferences
-
-    if preferences.use_layers_for_render:
+    if context.user_preferences.addons["amaranth"].preferences.use_layers_for_render:
         lfr_available = context.scene.get("amth_layers_for_render")
         if lfr_available:
             lfr = context.scene["amth_layers_for_render"]
@@ -207,10 +209,11 @@ def ui_layers_for_render(self, context):
 
 
 def ui_layers_for_render_header(self, context):
+    get_addon = "amaranth" in context.user_preferences.addons.keys()
+    if not get_addon:
+        return
 
-    preferences = context.user_preferences.addons["amaranth"].preferences
-
-    if preferences.use_layers_for_render:
+    if context.user_preferences.addons["amaranth"].preferences.use_layers_for_render:
         if context.scene.get("amth_layers_for_render"):
             self.layout.operator(
                 AMTH_SCENE_OT_layers_render_view.bl_idname,
index 801d849..69c2e46 100755 (executable)
@@ -28,52 +28,102 @@ or if there are many glossy shaders making things noisy.
 A current limitation is that it doesn't look inside node groups (yet,
 working on it!). It works since 0.8.8!
 
-* Lamps List
-This is a collapsable list of Lamps in the scene(s).
+Under the "Scene Debug" panel in Scene properties.
+
+* Lighter's Corner
+This is an UI List of Lamps in the scene(s).
 It allows you to quickly see how many lamps you have, select them by
 clicking on their name, see their type (icon), samples number (if using
 Branched Path Tracing), size, and change their visibility.
-The active lamp is indicated by a triangle on the right.
 
-Under the "Scene Debug" panel in Scene properties.
 """
 
 # TODO: module cleanup! maybe break it up in a package
-#     dicts instead of if, elif,else all over the place.
+#     dicts instead of if, elif, else all over the place.
 #     helper functions instead of everything on the execute method.
 #     str.format() + dicts instead of inline % op all over the place.
 #     remove/manage debug print calls.
-#     self.__class__.attr? use type(self).attr or self.attr instead.
 #     avoid duplicate code/patterns through helper functions.
 
 import os
 import bpy
 from amaranth import utils
+from bpy.types import (
+        Operator,
+        Panel,
+        UIList,
+        PropertyGroup,
+        )
+from bpy.props import (
+        BoolProperty,
+        CollectionProperty,
+        EnumProperty,
+        IntProperty,
+        PointerProperty,
+        StringProperty,
+        )
+
+# default string used in the List Users for Datablock section menus
+USER_X_NAME_EMPTY = "Data Block not selected/existing"
+
+
+class AMTH_store_data():
+    # used by: AMTH_SCENE_OT_list_users_for_x operator
+    users = {
+        'OBJECT_DATA': [],         # Store Objects with Material
+        'MATERIAL': [],            # Materials (Node tree)
+        'LAMP': [],                # Lamps
+        'WORLD': [],               # World
+        'TEXTURE': [],             # Textures (Psys, Brushes)
+        'MODIFIER': [],            # Modifiers
+        'MESH_DATA': [],           # Vertex Colors
+        'VIEW3D': [],              # Background Images
+        'NODETREE': [],            # Compositor
+        }
+    libraries = []                 # Libraries x type
+
+    # used by: AMTH_SCENE_OT_list_missing_material_slots operator
+    obj_mat_slots = []             # Missing material slots
+    obj_mat_slots_lib = []         # Libraries with missing material slots
+
+    # used by: AMTH_SCENE_OT_cycles_shader_list_nodes operator
+    mat_shaders = []               # Materials that use a specific shader
+
+    # used by : AMTH_SCENE_OT_list_missing_node_links operator
+    count_groups = 0               # Missing node groups count
+    count_images = 0               # Missing node images
+    count_image_node_unlinked = 0  # Unlinked Image nodes
+
+
+def call_update_datablock_type(self, context):
+    try:
+        # Note: this is pretty weak, but updates the operator enum selection
+        bpy.ops.scene.amth_list_users_for_x_type(list_type_select='0')
+    except:
+        pass
 
 
 def init():
     scene = bpy.types.Scene
 
-    scene.amaranth_debug_scene_list_missing_images = bpy.props.BoolProperty(
-        default=False,
-        name="List Missing Images",
-        description="Display a list of all the missing images")
-
-    scene.amaranth_lighterscorner_list_meshlights = bpy.props.BoolProperty(
+    scene.amaranth_lighterscorner_list_meshlights = BoolProperty(
         default=False,
         name="List Meshlights",
-        description="Include light emitting meshes on the list")
-
+        description="Include light emitting meshes on the list"
+    )
     amth_datablock_types = (
         ("IMAGE_DATA", "Image", "Image Datablocks", 0),
         ("MATERIAL", "Material", "Material Datablocks", 1),
         ("GROUP_VCOL", "Vertex Colors", "Vertex Color Layers", 2),
     )
-    scene.amth_datablock_types = bpy.props.EnumProperty(
+    scene.amth_datablock_types = EnumProperty(
         items=amth_datablock_types,
         name="Type",
-        description="Datablock Type")
-
+        description="Datablock Type",
+        default="MATERIAL",
+        update=call_update_datablock_type,
+        options={"SKIP_SAVE"}
+    )
     if utils.cycles_exists():
         cycles_shader_node_types = (
             ("BSDF_DIFFUSE", "Diffuse BSDF", "", 0),
@@ -95,14 +145,16 @@ def init():
             ("VOLUME_SCATTER", "Volume Scatter", "", 16),
             ("MIX_SHADER", "Mix Shader", "", 17),
             ("ADD_SHADER", "Add Shader", "", 18),
+            ('BSDF_PRINCIPLED', 'Principled BSDF', "", 19),
+        )
+        scene.amaranth_cycles_node_types = EnumProperty(
+            items=cycles_shader_node_types,
+            name="Shader"
         )
-        scene.amaranth_cycles_node_types = bpy.props.EnumProperty(
-            items=cycles_shader_node_types, name="Shader")
 
 
 def clear():
     props = (
-        "amaranth_debug_scene_list_missing_images",
         "amaranth_cycles_node_types",
         "amaranth_lighterscorner_list_meshlights",
     )
@@ -112,12 +164,50 @@ def clear():
             del wm[p]
 
 
-class AMTH_SCENE_OT_cycles_shader_list_nodes(bpy.types.Operator):
+def print_with_count_list(text="", send_list=[]):
+    if text:
+        print("\n* {}\n".format(text))
+    if not send_list:
+        print("List is empty, no items to display")
+        return
+
+    for i, entry in enumerate(send_list):
+        print('{:02d}. {}'.format(i + 1, send_list[i]))
+    print("\n")
+
+
+def print_grammar(line="", single="", multi="", cond=[]):
+    phrase = single if len(cond) == 1 else multi
+    print("\n* {} {}:\n".format(line, phrase))
+
+
+def reset_global_storage(what="NONE"):
+    if what == "NONE":
+        return
+
+    if what == "XTYPE":
+        for user in AMTH_store_data.users:
+            AMTH_store_data.users[user] = []
+        AMTH_store_data.libraries = []
+
+    elif what == "MAT_SLOTS":
+        AMTH_store_data.obj_mat_slots[:] = []
+        AMTH_store_data.obj_mat_slots_lib[:] = []
+
+    elif what == "NODE_LINK":
+        AMTH_store_data.obj_mat_slots[:] = []
+        AMTH_store_data.count_groups = 0
+        AMTH_store_data.count_images = 0
+        AMTH_store_data.count_image_node_unlinked = 0
 
+    elif what == "SHADER":
+        AMTH_store_data.mat_shaders[:] = []
+
+
+class AMTH_SCENE_OT_cycles_shader_list_nodes(Operator):
     """List Cycles materials containing a specific shader"""
     bl_idname = "scene.cycles_list_nodes"
     bl_label = "List Materials"
-    materials = []
 
     @classmethod
     def poll(cls, context):
@@ -126,171 +216,213 @@ class AMTH_SCENE_OT_cycles_shader_list_nodes(bpy.types.Operator):
     def execute(self, context):
         node_type = context.scene.amaranth_cycles_node_types
         roughness = False
-        self.__class__.materials = []
         shaders_roughness = ("BSDF_GLOSSY", "BSDF_DIFFUSE", "BSDF_GLASS")
 
-        print("\n=== Cycles Shader Type: %s === \n" % node_type)
+        reset_global_storage("SHADER")
+
+        print("\n=== Cycles Shader Type: {} === \n".format(node_type))
 
         for ma in bpy.data.materials:
-            if ma.node_tree:
-                nodes = ma.node_tree.nodes
-
-                print_unconnected = (
-                    "Note: \nOutput from \"%s\" node" % node_type,
-                    "in material \"%s\"" % ma.name, "not connected\n")
-
-                for no in nodes:
-                    if no.type == node_type:
-                        for ou in no.outputs:
-                            if ou.links:
-                                connected = True
-                                if no.type in shaders_roughness:
-                                    roughness = "R: %.4f" % no.inputs[
-                                        "Roughness"].default_value
-                                else:
-                                    roughness = False
+            if not ma.node_tree:
+                continue
+
+            nodes = ma.node_tree.nodes
+            print_unconnected = (
+                "Note: \nOutput from \"{}\" node in material \"{}\" "
+                "not connected\n".format(node_type, ma.name)
+                )
+
+            for no in nodes:
+                if no.type == node_type:
+                    for ou in no.outputs:
+                        if ou.links:
+                            connected = True
+                            if no.type in shaders_roughness:
+                                roughness = "R: {:.4f}".format(
+                                    no.inputs["Roughness"].default_value
+                                    )
                             else:
-                                connected = False
-                                print(print_unconnected)
-
-                            if ma.name not in self.__class__.materials:
-                                self.__class__.materials.append(
-                                    "%s%s [%s] %s%s%s" %
-                                    ("[L] " if ma.library else "",
-                                     ma.name,
-                                     ma.users,
-                                     "[F]" if ma.use_fake_user else "",
-                                     " - [%s]" %
-                                     roughness if roughness else "",
-                                     " * Output not connected" if not connected else ""))
-
-                    elif no.type == "GROUP":
-                        if no.node_tree:
-                            for nog in no.node_tree.nodes:
-                                if nog.type == node_type:
-                                    for ou in nog.outputs:
-                                        if ou.links:
-                                            connected = True
-                                            if nog.type in shaders_roughness:
-                                                roughness = "R: %.4f" % nog.inputs[
-                                                    "Roughness"].default_value
-                                            else:
-                                                roughness = False
+                                roughness = False
+                        else:
+                            connected = False
+                            print(print_unconnected)
+
+                        if ma.name not in AMTH_store_data.mat_shaders:
+                            AMTH_store_data.mat_shaders.append(
+                                "%s%s [%s] %s%s%s" %
+                                ("[L] " if ma.library else "",
+                                 ma.name,
+                                 ma.users,
+                                 "[F]" if ma.use_fake_user else "",
+                                 " - [%s]" %
+                                 roughness if roughness else "",
+                                 " * Output not connected" if not connected else "")
+                                )
+                elif no.type == "GROUP":
+                    if no.node_tree:
+                        for nog in no.node_tree.nodes:
+                            if nog.type == node_type:
+                                for ou in nog.outputs:
+                                    if ou.links:
+                                        connected = True
+                                        if nog.type in shaders_roughness:
+                                            roughness = "R: {:.4f}".format(
+                                                nog.inputs["Roughness"].default_value
+                                                )
                                         else:
-                                            connected = False
-                                            print(print_unconnected)
-
-                                        if ma.name not in self.__class__.materials:
-                                            self.__class__.materials.append(
-                                                '%s%s%s [%s] %s%s%s' %
-                                                ("[L] " if ma.library else "",
-                                                 "Node Group:  %s%s  ->  " %
-                                                 ("[L] " if no.node_tree.library else "",
-                                                  no.node_tree.name),
-                                                    ma.name,
-                                                    ma.users,
-                                                    "[F]" if ma.use_fake_user else "",
-                                                    " - [%s]" %
-                                                    roughness if roughness else "",
-                                                    " * Output not connected" if not connected else ""))
-
-                    self.__class__.materials = sorted(
-                        list(set(self.__class__.materials)))
-
-        if len(self.__class__.materials) == 0:
-            self.report({"INFO"},
-                        "No materials with nodes type %s found" % node_type)
-        else:
-            print("* A total of %d %s using %s was found \n" % (
-                len(self.__class__.materials),
-                "material" if len(
-                    self.__class__.materials) == 1 else "materials",
-                node_type))
-
-            count = 0
-
-            for mat in self.__class__.materials:
-                print('%02d. %s' %
-                      (count + 1, self.__class__.materials[count]))
-                count += 1
-            print("\n")
-
-        self.__class__.materials = sorted(list(set(self.__class__.materials)))
-
-        return {"FINISHED"}
-
-
-class AMTH_SCENE_OT_cycles_shader_list_nodes_clear(bpy.types.Operator):
-
-    """Clear the list below"""
-    bl_idname = "scene.cycles_list_nodes_clear"
-    bl_label = "Clear Materials List"
-
-    @classmethod
-    def poll(cls, context):
-        return utils.cycles_exists()
+                                            roughness = False
+                                    else:
+                                        connected = False
+                                        print(print_unconnected)
+
+                                    if ma.name not in AMTH_store_data.mat_shaders:
+                                        AMTH_store_data.mat_shaders.append(
+                                            '%s%s%s [%s] %s%s%s' %
+                                            ("[L] " if ma.library else "",
+                                             "Node Group:  %s%s  ->  " %
+                                             ("[L] " if no.node_tree.library else "",
+                                              no.node_tree.name),
+                                                ma.name,
+                                                ma.users,
+                                                "[F]" if ma.use_fake_user else "",
+                                                " - [%s]" %
+                                                roughness if roughness else "",
+                                                " * Output not connected" if not connected else "")
+                                            )
+                AMTH_store_data.mat_shaders = sorted(list(set(AMTH_store_data.mat_shaders)))
+
+        message = "No materials with nodes type {} found".format(node_type)
+        if len(AMTH_store_data.mat_shaders) > 0:
+            message = "A total of {} {} using {} found".format(
+                    len(AMTH_store_data.mat_shaders),
+                    "material" if len(AMTH_store_data.mat_shaders) == 1 else "materials",
+                    node_type)
+            print_with_count_list(send_list=AMTH_store_data.mat_shaders)
+
+        self.report({'INFO'}, message)
+        AMTH_store_data.mat_shaders = sorted(list(set(AMTH_store_data.mat_shaders)))
 
-    def execute(self, context):
-        AMTH_SCENE_OT_cycles_shader_list_nodes.materials[:] = []
-        print("* Cleared Cycles Materials List")
         return {"FINISHED"}
 
 
-class AMTH_SCENE_OT_amaranth_object_select(bpy.types.Operator):
-
+class AMTH_SCENE_OT_amaranth_object_select(Operator):
     """Select object"""
     bl_idname = "scene.amaranth_object_select"
     bl_label = "Select Object"
-    object = bpy.props.StringProperty()
+
+    object_name = StringProperty()
 
     def execute(self, context):
-        if self.object:
-            object = bpy.data.objects[self.object]
+        if not (self.object_name and self.object_name in bpy.data.objects):
+            self.report({'WARNING'},
+                        "Object with the given name could not be found. Operation Cancelled")
+            return {"CANCELLED"}
 
-            bpy.ops.object.select_all(action="DESELECT")
-            object.select = True
-            context.scene.objects.active = object
+        obj = bpy.data.objects[self.object_name]
 
-        return {"FINISHED"}
+        bpy.ops.object.select_all(action="DESELECT")
+        obj.select = True
+        context.scene.objects.active = obj
 
+        return {"FINISHED"}
 
-class AMTH_SCENE_OT_list_missing_node_links(bpy.types.Operator):
 
+class AMTH_SCENE_OT_list_missing_node_links(Operator):
     """Print a list of missing node links"""
     bl_idname = "scene.list_missing_node_links"
     bl_label = "List Missing Node Links"
 
-    count_groups = 0
-    count_images = 0
-    count_image_node_unlinked = 0
-
     def execute(self, context):
         missing_groups = []
         missing_images = []
         image_nodes_unlinked = []
         libraries = []
-        self.__class__.count_groups = 0
-        self.__class__.count_images = 0
-        self.__class__.count_image_node_unlinked = 0
+
+        reset_global_storage(what="NODE_LINK")
 
         for ma in bpy.data.materials:
-            if ma.node_tree:
-                for no in ma.node_tree.nodes:
-                    if no.type == "GROUP":
-                        if not no.node_tree:
-                            self.__class__.count_groups += 1
-
-                            users_ngroup = []
-
-                            for ob in bpy.data.objects:
-                                if ob.material_slots and ma.name in ob.material_slots:
-                                    users_ngroup.append("%s%s%s" % (
-                                        "[L] " if ob.library else "",
-                                        "[F] " if ob.use_fake_user else "",
-                                        ob.name))
-
-                            missing_groups.append(
-                                "MA: %s%s%s [%s]%s%s%s\n" %
+            if not ma.node_tree:
+                continue
+
+            for no in ma.node_tree.nodes:
+                if no.type == "GROUP":
+                    if not no.node_tree:
+                        AMTH_store_data.count_groups += 1
+
+                        users_ngroup = []
+
+                        for ob in bpy.data.objects:
+                            if ob.material_slots and ma.name in ob.material_slots:
+                                users_ngroup.append("%s%s%s" % (
+                                    "[L] " if ob.library else "",
+                                    "[F] " if ob.use_fake_user else "",
+                                    ob.name))
+
+                        missing_groups.append(
+                            "MA: %s%s%s [%s]%s%s%s\n" %
+                            ("[L] " if ma.library else "",
+                             "[F] " if ma.use_fake_user else "",
+                             ma.name,
+                             ma.users,
+                             " *** No users *** " if ma.users == 0 else "",
+                             "\nLI: %s" %
+                             ma.library.filepath if ma.library else "",
+                             "\nOB: %s" %
+                             ",  ".join(users_ngroup) if users_ngroup else "")
+                            )
+                        if ma.library:
+                            libraries.append(ma.library.filepath)
+
+                if no.type == "TEX_IMAGE":
+
+                    outputs_empty = not no.outputs["Color"].is_linked and \
+                                    not no.outputs["Alpha"].is_linked
+
+                    if no.image:
+                        image_path_exists = os.path.exists(
+                                bpy.path.abspath(
+                                no.image.filepath,
+                                library=no.image.library)
+                                )
+
+                    if outputs_empty or not no.image or not image_path_exists:
+
+                        users_images = []
+
+                        for ob in bpy.data.objects:
+                            if ob.material_slots and ma.name in ob.material_slots:
+                                users_images.append("%s%s%s" % (
+                                    "[L] " if ob.library else "",
+                                    "[F] " if ob.use_fake_user else "",
+                                    ob.name))
+
+                        if outputs_empty:
+                            AMTH_store_data.count_image_node_unlinked += 1
+
+                            image_nodes_unlinked.append(
+                                "%s%s%s%s%s [%s]%s%s%s%s%s\n" %
+                                ("NO: %s" %
+                                 no.name,
+                                 "\nMA: ",
+                                 "[L] " if ma.library else "",
+                                 "[F] " if ma.use_fake_user else "",
+                                 ma.name,
+                                 ma.users,
+                                 " *** No users *** " if ma.users == 0 else "",
+                                 "\nLI: %s" %
+                                 ma.library.filepath if ma.library else "",
+                                 "\nIM: %s" %
+                                 no.image.name if no.image else "",
+                                 "\nLI: %s" %
+                                 no.image.filepath if no.image and no.image.filepath else "",
+                                 "\nOB: %s" %
+                                 ',  '.join(users_images) if users_images else ""))
+
+                        if not no.image or not image_path_exists:
+                            AMTH_store_data.count_images += 1
+
+                            missing_images.append(
+                                "MA: %s%s%s [%s]%s%s%s%s%s\n" %
                                 ("[L] " if ma.library else "",
                                  "[F] " if ma.use_fake_user else "",
                                  ma.name,
@@ -298,78 +430,15 @@ class AMTH_SCENE_OT_list_missing_node_links(bpy.types.Operator):
                                  " *** No users *** " if ma.users == 0 else "",
                                  "\nLI: %s" %
                                  ma.library.filepath if ma.library else "",
+                                 "\nIM: %s" %
+                                 no.image.name if no.image else "",
+                                 "\nLI: %s" %
+                                 no.image.filepath if no.image and no.image.filepath else "",
                                  "\nOB: %s" %
-                                 ",  ".join(users_ngroup) if users_ngroup else ""))
+                                 ',  '.join(users_images) if users_images else ""))
 
                             if ma.library:
                                 libraries.append(ma.library.filepath)
-                    if no.type == "TEX_IMAGE":
-
-                        outputs_empty = not no.outputs[
-                            "Color"].is_linked and not no.outputs["Alpha"].is_linked
-
-                        if no.image:
-                            image_path_exists = os.path.exists(
-                                bpy.path.abspath(
-                                    no.image.filepath,
-                                    library=no.image.library))
-
-                        if outputs_empty or not \
-                           no.image or not \
-                           image_path_exists:
-
-                            users_images = []
-
-                            for ob in bpy.data.objects:
-                                if ob.material_slots and ma.name in ob.material_slots:
-                                    users_images.append("%s%s%s" % (
-                                        "[L] " if ob.library else "",
-                                        "[F] " if ob.use_fake_user else "",
-                                        ob.name))
-
-                            if outputs_empty:
-                                self.__class__.count_image_node_unlinked += 1
-
-                                image_nodes_unlinked.append(
-                                    "%s%s%s%s%s [%s]%s%s%s%s%s\n" %
-                                    ("NO: %s" %
-                                     no.name,
-                                     "\nMA: ",
-                                     "[L] " if ma.library else "",
-                                     "[F] " if ma.use_fake_user else "",
-                                     ma.name,
-                                     ma.users,
-                                     " *** No users *** " if ma.users == 0 else "",
-                                     "\nLI: %s" %
-                                     ma.library.filepath if ma.library else "",
-                                     "\nIM: %s" %
-                                     no.image.name if no.image else "",
-                                     "\nLI: %s" %
-                                     no.image.filepath if no.image and no.image.filepath else "",
-                                     "\nOB: %s" %
-                                     ',  '.join(users_images) if users_images else ""))
-
-                            if not no.image or not image_path_exists:
-                                self.__class__.count_images += 1
-
-                                missing_images.append(
-                                    "MA: %s%s%s [%s]%s%s%s%s%s\n" %
-                                    ("[L] " if ma.library else "",
-                                     "[F] " if ma.use_fake_user else "",
-                                     ma.name,
-                                     ma.users,
-                                     " *** No users *** " if ma.users == 0 else "",
-                                     "\nLI: %s" %
-                                     ma.library.filepath if ma.library else "",
-                                     "\nIM: %s" %
-                                     no.image.name if no.image else "",
-                                     "\nLI: %s" %
-                                     no.image.filepath if no.image and no.image.filepath else "",
-                                     "\nOB: %s" %
-                                     ',  '.join(users_images) if users_images else ""))
-
-                                if ma.library:
-                                    libraries.append(ma.library.filepath)
 
         # Remove duplicates and sort
         missing_groups = sorted(list(set(missing_groups)))
@@ -379,150 +448,107 @@ class AMTH_SCENE_OT_list_missing_node_links(bpy.types.Operator):
 
         print(
             "\n\n== %s missing image %s, %s missing node %s and %s image %s unlinked ==" %
-            ("No" if self.__class__.count_images == 0 else str(
-                self.__class__.count_images),
-                "node" if self.__class__.count_images == 1 else "nodes",
-                "no" if self.__class__.count_groups == 0 else str(
-                    self.__class__.count_groups),
-                "group" if self.__class__.count_groups == 1 else "groups",
-                "no" if self.__class__.count_image_node_unlinked == 0 else str(
-                    self.__class__.count_image_node_unlinked),
-                "node" if self.__class__.count_groups == 1 else "nodes"))
-
+            ("No" if AMTH_store_data.count_images == 0 else str(
+                AMTH_store_data.count_images),
+                "node" if AMTH_store_data.count_images == 1 else "nodes",
+                "no" if AMTH_store_data.count_groups == 0 else str(
+                    AMTH_store_data.count_groups),
+                "group" if AMTH_store_data.count_groups == 1 else "groups",
+                "no" if AMTH_store_data.count_image_node_unlinked == 0 else str(
+                    AMTH_store_data.count_image_node_unlinked),
+                "node" if AMTH_store_data.count_groups == 1 else "nodes")
+            )
         # List Missing Node Groups
         if missing_groups:
-            print("\n* Missing Node Group Links\n")
-            for mig in missing_groups:
-                print(mig)
+            print_with_count_list("Missing Node Group Links", missing_groups)
 
         # List Missing Image Nodes
         if missing_images:
-            print("\n* Missing Image Nodes Link\n")
-
-            for mii in missing_images:
-                print(mii)
+            print_with_count_list("Missing Image Nodes Link", missing_images)
 
         # List Image Nodes with its outputs unlinked
         if image_nodes_unlinked:
-            print("\n* Image Nodes Unlinked\n")
-
-            for nou in image_nodes_unlinked:
-                print(nou)
+            print_with_count_list("Image Nodes Unlinked", image_nodes_unlinked)
 
-        if missing_groups or \
-           missing_images or \
-           image_nodes_unlinked:
+        if missing_groups or missing_images or image_nodes_unlinked:
             if libraries:
-                print(
-                    "\nThat's bad, run check on %s:" %
-                    ("this library" if len(libraries) == 1 else "these libraries"))
-                for li in libraries:
-                    print(li)
+                print_grammar("That's bad, run check", "this library", "these libraries", libraries)
+                print_with_count_list(send_list=libraries)
         else:
             self.report({"INFO"}, "Yay! No missing node links")
 
-        print("\n")
-
         if missing_groups and missing_images:
             self.report(
                 {"WARNING"},
                 "%d missing image %s and %d missing node %s found" %
-                (self.__class__.count_images,
-                 "node" if self.__class__.count_images == 1 else "nodes",
-                 self.__class__.count_groups,
-                 "group" if self.__class__.count_groups == 1 else "groups"))
+                (AMTH_store_data.count_images,
+                 "node" if AMTH_store_data.count_images == 1 else "nodes",
+                 AMTH_store_data.count_groups,
+                 "group" if AMTH_store_data.count_groups == 1 else "groups")
+                )
 
         return {"FINISHED"}
 
 
-class AMTH_SCENE_OT_list_missing_material_slots(bpy.types.Operator):
-
+class AMTH_SCENE_OT_list_missing_material_slots(Operator):
     """List objects with empty material slots"""
     bl_idname = "scene.list_missing_material_slots"
     bl_label = "List Empty Material Slots"
 
-    objects = []
-    libraries = []
-
     def execute(self, context):
-        self.__class__.objects = []
-        self.__class__.libraries = []
+        reset_global_storage("MAT_SLOTS")
 
         for ob in bpy.data.objects:
             for ma in ob.material_slots:
                 if not ma.material:
-                    self.__class__.objects.append('%s%s' % (
-                        '[L] ' if ob.library else '',
-                        ob.name))
+                    AMTH_store_data.obj_mat_slots.append('{}{}'.format(
+                        '[L] ' if ob.library else '', ob.name))
                     if ob.library:
-                        self.__class__.libraries.append(ob.library.filepath)
+                        AMTH_store_data.obj_mat_slots_lib.append(ob.library.filepath)
 
-        self.__class__.objects = sorted(list(set(self.__class__.objects)))
-        self.__class__.libraries = sorted(list(set(self.__class__.libraries)))
+        AMTH_store_data.obj_mat_slots = sorted(list(set(AMTH_store_data.obj_mat_slots)))
+        AMTH_store_data.obj_mat_slots_lib = sorted(list(set(AMTH_store_data.obj_mat_slots_lib)))
 
-        if len(self.__class__.objects) == 0:
+        if len(AMTH_store_data.obj_mat_slots) == 0:
             self.report({"INFO"},
                         "No objects with empty material slots found")
-        else:
-            print(
-                "\n* A total of %d %s with empty material slots was found \n" %
-                (len(
-                    self.__class__.objects), "object" if len(
-                    self.__class__.objects) == 1 else "objects"))
-
-            count = 0
-            count_lib = 0
-
-            for obs in self.__class__.objects:
-                print('%02d. %s' % (
-                    count + 1, self.__class__.objects[count]))
-                count += 1
-
-            if self.__class__.libraries:
-                print("\n\n* Check %s:\n" %
-                     ("this library" if len(self.__class__.libraries) == 1
-                      else "these libraries"))
-
-                for libs in self.__class__.libraries:
-                    print('%02d. %s' % (
-                        count_lib + 1, self.__class__.libraries[count_lib]))
-                    count_lib += 1
-            print("\n")
+            return {"FINISHED"}
 
-        return {"FINISHED"}
-
-
-class AMTH_SCENE_OT_list_missing_material_slots_clear(bpy.types.Operator):
-
-    """Clear the list below"""
-    bl_idname = "scene.list_missing_material_slots_clear"
-    bl_label = "Clear Empty Material Slots List"
+        print(
+            "\n* A total of {} {} with empty material slots was found \n".format(
+            len(AMTH_store_data.obj_mat_slots),
+            "object" if len(AMTH_store_data.obj_mat_slots) == 1 else "objects")
+            )
+        print_with_count_list(send_list=AMTH_store_data.obj_mat_slots)
+
+        if AMTH_store_data.obj_mat_slots_lib:
+            print_grammar("Check", "this library", "these libraries",
+                          AMTH_store_data.obj_mat_slots_lib
+            )
+            print_with_count_list(send_list=AMTH_store_data.obj_mat_slots_lib)
 
-    def execute(self, context):
-        AMTH_SCENE_OT_list_missing_material_slots.objects[:] = []
-        print("* Cleared Empty Material Slots List")
         return {"FINISHED"}
 
 
-class AMTH_SCENE_OT_list_users_for_x_type(bpy.types.Operator):
+class AMTH_SCENE_OT_list_users_for_x_type(Operator):
     bl_idname = "scene.amth_list_users_for_x_type"
     bl_label = "Select"
     bl_description = "Select Datablock Name"
 
-    def avail(self,context):
-        datablock_type = bpy.context.scene.amth_datablock_types
+    @staticmethod
+    def fill_where():
+        where = []
+        data_block = bpy.context.scene.amth_datablock_types
 
-        if datablock_type == 'IMAGE_DATA':
-            where = []
+        if data_block == 'IMAGE_DATA':
             for im in bpy.data.images:
                 if im.name not in {'Render Result', 'Viewer Node'}:
                     where.append(im)
 
-        elif datablock_type == 'MATERIAL':
+        elif data_block == 'MATERIAL':
             where = bpy.data.materials
 
-        elif datablock_type == 'GROUP_VCOL':
-            where = []
+        elif data_block == 'GROUP_VCOL':
             for ob in bpy.data.objects:
                 if ob.type == 'MESH':
                     for v in ob.data.vertex_colors:
@@ -530,74 +556,54 @@ class AMTH_SCENE_OT_list_users_for_x_type(bpy.types.Operator):
                             where.append(v)
             where = list(set(where))
 
-        items = [(str(i),x.name,x.name, datablock_type, i) for i,x in enumerate(where)]
+        return where
+
+    def avail(self, context):
+        datablock_type = bpy.context.scene.amth_datablock_types
+        where = AMTH_SCENE_OT_list_users_for_x_type.fill_where()
+        items = [(str(i), x.name, x.name, datablock_type, i) for i, x in enumerate(where)]
         items = sorted(list(set(items)))
+        if not items:
+            items = [('0', USER_X_NAME_EMPTY, USER_X_NAME_EMPTY, "INFO", 0)]
         return items
 
-    list_type_select = bpy.props.EnumProperty(items = avail, name = "Available")
+    list_type_select = EnumProperty(
+            items=avail,
+            name="Available",
+            options={"SKIP_SAVE"}
+            )
 
     @classmethod
     def poll(cls, context):
         return bpy.context.scene.amth_datablock_types
 
-    def execute(self,context):
-        datablock_type = bpy.context.scene.amth_datablock_types
-
-        if datablock_type == 'IMAGE_DATA':
-            where = []
-            for im in bpy.data.images:
-                if im.name not in {'Render Result', 'Viewer Node'}:
-                    where.append(im)
-
-        elif datablock_type == 'MATERIAL':
-            where = bpy.data.materials
-
-        elif datablock_type == 'GROUP_VCOL':
-            where = []
-            for ob in bpy.data.objects:
-                if ob.type == 'MESH':
-                    for v in ob.data.vertex_colors:
-                        if v and v not in where:
-                            where.append(v)
-            where = list(set(where))
+    def execute(self, context):
+        where = self.fill_where()
+        bpy.context.scene.amth_list_users_for_x_name = \
+                where[int(self.list_type_select)].name if where else USER_X_NAME_EMPTY
 
-        bpy.context.scene.amth_list_users_for_x_name = where[int(self.list_type_select)].name
         return {'FINISHED'}
 
 
-class AMTH_SCENE_OT_list_users_for_x(bpy.types.Operator):
-
+class AMTH_SCENE_OT_list_users_for_x(Operator):
     """List users for a particular datablock"""
     bl_idname = "scene.amth_list_users_for_x"
     bl_label = "List Users for Datablock"
 
-    name = bpy.props.StringProperty()
-    users = {}
-    libraries = []
+    name = StringProperty()
 
     def execute(self, context):
-
-        datablock_type = context.scene.amth_datablock_types
         d = bpy.data
+        x = self.name if self.name else context.scene.amth_list_users_for_x_name
 
-        if self.name:
-            x = self.name
-        else:
-            x = context.scene.amth_list_users_for_x_name
+        if USER_X_NAME_EMPTY in x:
+            self.report({'INFO'},
+                        "Please select a DataBlock name first. Operation Cancelled")
+            return {"CANCELLED"}
 
         dtype = context.scene.amth_datablock_types
 
-        self.__class__.users = {
-            'OBJECT_DATA' : [], # Store Objects with Material
-            'MATERIAL' : [], # Materials (Node tree)
-            'LAMP' : [], # Lamps
-            'WORLD' : [], # World
-            'TEXTURE' : [], # Textures (Psys, Brushes)
-            'MODIFIER' : [], # Modifiers
-            'MESH_DATA' : [], # Vertex Colors
-            'VIEW3D' : [], # Background Images
-            'NODETREE' : [], # Compositor
-        }
+        reset_global_storage("XTYPE")
 
         # IMAGE TYPE
         if dtype == 'IMAGE_DATA':
@@ -609,12 +615,13 @@ class AMTH_SCENE_OT_list_users_for_x(bpy.types.Operator):
                         materials = []
 
                         for nd in ma.node_tree.nodes:
-                            if nd and nd.type in {'TEX_IMAGE','TEX_ENVIRONMENT'}:
+                            if nd and nd.type in {'TEX_IMAGE', 'TEX_ENVIRONMENT'}:
                                 materials.append(nd)
+
                             if nd and nd.type == 'GROUP':
                                 if nd.node_tree and nd.node_tree.nodes:
                                     for ng in nd.node_tree.nodes:
-                                        if ng.type in {'TEX_IMAGE','TEX_ENVIRONMENT'}:
+                                        if ng.type in {'TEX_IMAGE', 'TEX_ENVIRONMENT'}:
                                             materials.append(ng)
 
                             for no in materials:
@@ -624,7 +631,6 @@ class AMTH_SCENE_OT_list_users_for_x(bpy.types.Operator):
                                     for ob in d.objects:
                                         if ma.name in ob.material_slots:
                                             objects.append(ob.name)
-
                                     links = False
 
                                     for o in no.outputs:
@@ -636,9 +642,8 @@ class AMTH_SCENE_OT_list_users_for_x(bpy.types.Operator):
                                             'in object: {0}'.format(objects) if objects else ' (unassigned)',
                                             '' if links else ' (unconnected)')
 
-                                    if name not in self.__class__.users['MATERIAL']:
-                                        self.__class__.users['MATERIAL'].append(name)
-
+                                    if name not in AMTH_store_data.users['MATERIAL']:
+                                        AMTH_store_data.users['MATERIAL'].append(name)
             # Check Lamps
             for la in d.lamps:
                 # Cycles
@@ -646,11 +651,10 @@ class AMTH_SCENE_OT_list_users_for_x(bpy.types.Operator):
                     if la and la.node_tree and la.node_tree.nodes:
                         for no in la.node_tree.nodes:
                             if no and \
-                               no.type in {'TEX_IMAGE','TEX_ENVIRONMENT'} and \
-                               no.image and no.image.name == x:
-                                    if la.name not in self.__class__.users['LAMP']:
-                                        self.__class__.users['LAMP'].append(la.name)
-
+                                   no.type in {'TEX_IMAGE', 'TEX_ENVIRONMENT'} and \
+                                   no.image and no.image.name == x:
+                                if la.name not in AMTH_store_data.users['LAMP']:
+                                    AMTH_store_data.users['LAMP'].append(la.name)
             # Check World
             for wo in d.worlds:
                 # Cycles
@@ -658,20 +662,18 @@ class AMTH_SCENE_OT_list_users_for_x(bpy.types.Operator):
                     if wo and wo.node_tree and wo.node_tree.nodes:
                         for no in wo.node_tree.nodes:
                             if no and \
-                               no.type in {'TEX_IMAGE','TEX_ENVIRONMENT'} and \
-                               no.image and no.image.name == x:
-                                if wo.name not in self.__class__.users['WORLD']:
-                                    self.__class__.users['WORLD'].append(wo.name)
-
+                                   no.type in {'TEX_IMAGE', 'TEX_ENVIRONMENT'} and \
+                                   no.image and no.image.name == x:
+                                if wo.name not in AMTH_store_data.users['WORLD']:
+                                    AMTH_store_data.users['WORLD'].append(wo.name)
             # Check Textures
             for te in d.textures:
-                if te and te.type =='IMAGE' and te.image:
+                if te and te.type == 'IMAGE' and te.image:
                     name = te.image.name
 
                     if name == x and \
-                       name not in self.__class__.users['TEXTURE'] :
-                        self.__class__.users['TEXTURE'].append(te.name)
-
+                            name not in AMTH_store_data.users['TEXTURE']:
+                        AMTH_store_data.users['TEXTURE'].append(te.name)
             # Check Modifiers in Objects
             for ob in d.objects:
                 for mo in ob.modifiers:
@@ -680,30 +682,27 @@ class AMTH_SCENE_OT_list_users_for_x(bpy.types.Operator):
 
                         if mo and image and image.name == x:
                             name = '"{0}" modifier in {1}'.format(mo.name, ob.name)
-                            if name not in self.__class__.users['MODIFIER']:
-                                self.__class__.users['MODIFIER'].append(name)
-
+                            if name not in AMTH_store_data.users['MODIFIER']:
+                                AMTH_store_data.users['MODIFIER'].append(name)
             # Check Background Images in Viewports
             for scr in d.screens:
                 for ar in scr.areas:
                     if ar.type == 'VIEW_3D':
                         if ar.spaces and \
-                           ar.spaces.active and \
-                           ar.spaces.active.background_images:
+                               ar.spaces.active and \
+                               ar.spaces.active.background_images:
                             for bg in ar.spaces.active.background_images:
                                 image = bg.image
 
                                 if bg and image and image.name == x:
                                     name = 'Background for 3D Viewport in Screen "{0}"'\
                                             .format(scr.name)
-                                    if name not in self.__class__.users['VIEW3D']:
-                                        self.__class__.users['VIEW3D'].append(name)
-
+                                    if name not in AMTH_store_data.users['VIEW3D']:
+                                        AMTH_store_data.users['VIEW3D'].append(name)
             # Check the Compositor
             for sce in d.scenes:
                 if sce.node_tree and sce.node_tree.nodes:
                     nodes = []
-
                     for nd in sce.node_tree.nodes:
                         if nd.type == 'IMAGE':
                             nodes.append(nd)
@@ -715,7 +714,6 @@ class AMTH_SCENE_OT_list_users_for_x(bpy.types.Operator):
 
                         for no in nodes:
                             if no.image and no.image.name == x:
-
                                 links = False
 
                                 for o in no.outputs:
@@ -727,21 +725,21 @@ class AMTH_SCENE_OT_list_users_for_x(bpy.types.Operator):
                                         sce.name,
                                         '' if links else ' (unconnected)')
 
-                                if name not in self.__class__.users['NODETREE']:
-                                    self.__class__.users['NODETREE'].append(name)
-
+                                if name not in AMTH_store_data.users['NODETREE']:
+                                    AMTH_store_data.users['NODETREE'].append(name)
         # MATERIAL TYPE
         if dtype == 'MATERIAL':
-            # Check Materials
+            # Check Materials - Note: build an object_check list as only strings are stored
+            object_check = [d.objects[names] for names in AMTH_store_data.users['OBJECT_DATA'] if
+                            names in d.objects]
             for ob in d.objects:
                 for ma in ob.material_slots:
                     if ma.name == x:
-                        if ma not in self.__class__.users['OBJECT_DATA']:
-                            self.__class__.users['OBJECT_DATA'].append(ob)
+                        if ma not in object_check:
+                            AMTH_store_data.users['OBJECT_DATA'].append(ob.name)
 
                         if ob.library:
-                            self.__class__.libraries.append(ob.library.filepath)
-
+                            AMTH_store_data.libraries.append(ob.library.filepath)
         # VERTEX COLOR TYPE
         elif dtype == 'GROUP_VCOL':
             # Check VCOL in Meshes
@@ -751,9 +749,8 @@ class AMTH_SCENE_OT_list_users_for_x(bpy.types.Operator):
                         if v.name == x:
                             name = '{0}'.format(ob.name)
 
-                            if name not in self.__class__.users['MESH_DATA']:
-                                self.__class__.users['MESH_DATA'].append(name)
-
+                            if name not in AMTH_store_data.users['MESH_DATA']:
+                                AMTH_store_data.users['MESH_DATA'].append(name)
             # Check VCOL in Materials
             for ma in d.materials:
                 # Cycles
@@ -773,66 +770,59 @@ class AMTH_SCENE_OT_list_users_for_x(bpy.types.Operator):
                                     else:
                                         name = '{0} (unassigned)'.format(ma.name)
 
-                                    if name not in self.__class__.users['MATERIAL']:
-                                        self.__class__.users['MATERIAL'].append(name)
-
+                                    if name not in AMTH_store_data.users['MATERIAL']:
+                                        AMTH_store_data.users['MATERIAL'].append(name)
 
-        self.__class__.libraries = sorted(list(set(self.__class__.libraries)))
-
-        count_lib = 0
+        AMTH_store_data.libraries = sorted(list(set(AMTH_store_data.libraries)))
 
         # Print on console
         empty = True
-
-        for t in self.__class__.users:
-            if self.__class__.users[t]:
+        for t in AMTH_store_data.users:
+            if AMTH_store_data.users[t]:
                 empty = False
                 print('\n== {0} {1} use {2} "{3}" ==\n'.format(
-                        len(self.__class__.users[t]),
+                        len(AMTH_store_data.users[t]),
                         t,
                         dtype,
                         x))
-                for p in self.__class__.users[t]:
+                for p in AMTH_store_data.users[t]:
                     print(' {0}'.format(p))
 
-        if self.__class__.libraries:
-            print("\n* Check %s:\n" %
-                 ("this library" if len(self.__class__.libraries) == 1
-                  else "these libraries"))
-
-            for libs in self.__class__.libraries:
-                print('%02d. %s' % (
-                    count_lib + 1, self.__class__.libraries[count_lib]))
-                count_lib += 1
-            print("\n")
+        if AMTH_store_data.libraries:
+            print_grammar("Check", "this library", "these libraries",
+                          AMTH_store_data.libraries
+                        )
+            print_with_count_list(send_list=AMTH_store_data.libraries)
 
         if empty:
-            print('\n== No users for {0} ==\n'.format(x))
-
-        #print('Type: {0}'.format(context.scene.amth_datablock_types))
-        #print('X: {0}'.format(x))
+            self.report({'INFO'}, "No users for {}".format(x))
 
         return {"FINISHED"}
 
 
-class AMTH_SCENE_OT_list_users_for_x_clear(bpy.types.Operator):
+class AMTH_SCENE_OT_list_users_debug_clear(Operator):
+    """Clear the list bellow"""
+    bl_idname = "scene.amth_list_users_debug_clear"
+    bl_label = "Clear Debug Panel lists"
 
-    """Clear the list below"""
-    bl_idname = "scene.amth_list_users_for_x_clear"
-    bl_label = "Clear Users Lists for X"
+    what = StringProperty(
+            name="",
+            default="NONE",
+            options={'HIDDEN'}
+            )
 
     def execute(self, context):
-        AMTH_SCENE_OT_list_users_for_x.users = {}
-        print("* Cleared Users List for Datablock")
-        return {"FINISHED"}
+        reset_global_storage(self.what)
 
+        return {"FINISHED"}
 
-class AMTH_SCENE_OT_blender_instance_open(bpy.types.Operator):
 
+class AMTH_SCENE_OT_blender_instance_open(Operator):
     """Open in a new Blender instance"""
     bl_idname = "scene.blender_instance_open"
     bl_label = "Open Blender Instance"
-    filepath = bpy.props.StringProperty()
+
+    filepath = StringProperty()
 
     def execute(self, context):
         if self.filepath:
@@ -842,118 +832,106 @@ class AMTH_SCENE_OT_blender_instance_open(bpy.types.Operator):
             try:
                 subprocess.Popen([bpy.app.binary_path, filepath])
             except:
-                print("Error on the new Blender instance")
+                print("Error opening a new Blender instance")
                 import traceback
                 traceback.print_exc()
 
         return {"FINISHED"}
 
 
-class AMTH_SCENE_PT_scene_debug(bpy.types.Panel):
+class AMTH_SCENE_OT_Collection_List_Refresh(Operator):
+    bl_idname = "scene.amaranth_lighters_corner_refresh"
+    bl_label = "Refresh"
+    bl_description = ("Generate/Refresh the Lists\n"
+                      "Use to generate/refresh the list or after changes to Data")
+    bl_options = {"REGISTER", "INTERNAL"}
+
+    what = StringProperty(default="NONE")
+
+    def execute(self, context):
+        message = "No changes applied"
+
+        if self.what == "LIGHTS":
+            fill_ligters_corner_props(context, refresh=True)
+
+            found_lights = len(context.window_manager.amth_lighters_state.keys())
+            message = "No Lights in the Data" if found_lights == 0 else \
+                    "Generated list for {} found light(s)".format(found_lights)
+
+        elif self.what == "IMAGES":
+            fill_missing_images_props(context, refresh=True)
+
+            found_images = len(context.window_manager.amth_missing_images_state.keys())
+            message = "Great! No missing Images" if found_images == 0 else \
+                    "Missing {} image(s) in the Data".format(found_images)
+
+        self.report({'INFO'}, message)
+
+        return {"FINISHED"}
+
 
+class AMTH_SCENE_PT_scene_debug(Panel):
     """Scene Debug"""
     bl_label = "Scene Debug"
     bl_space_type = "PROPERTIES"
     bl_region_type = "WINDOW"
     bl_context = "scene"
+    bl_options = {"DEFAULT_CLOSED"}
 
     def draw_header(self, context):
         layout = self.layout
         layout.label(text="", icon="RADIO")
 
+    def draw_label(self, layout, body_text, single, multi, lists, ico="BLANK1"):
+        layout.label(
+            text="{} {} {}".format(
+                str(len(lists)), body_text,
+                single if len(lists) == 1 else multi),
+            icon=ico
+            )
+
+    def draw_miss_link(self, layout, text1, single, multi, text2, count, ico="BLANK1"):
+        layout.label(
+            text="{} {} {} {}".format(
+                count, text1,
+                single if count == 1 else multi, text2),
+            icon=ico
+            )
+
     def draw(self, context):
         layout = self.layout
         scene = context.scene
-        images = bpy.data.images
-        images_missing = []
-        list_missing_images = scene.amaranth_debug_scene_list_missing_images
-        materials = AMTH_SCENE_OT_cycles_shader_list_nodes.materials
-        materials_count = len(AMTH_SCENE_OT_cycles_shader_list_nodes.materials)
-        missing_material_slots_obs = AMTH_SCENE_OT_list_missing_material_slots.objects
-        missing_material_slots_count = len(
-            AMTH_SCENE_OT_list_missing_material_slots.objects)
-        missing_material_slots_lib = AMTH_SCENE_OT_list_missing_material_slots.libraries
+
+        has_images = len(bpy.data.images)
         engine = scene.render.engine
 
         # List Missing Images
         box = layout.box()
-        row = box.row(align=True)
-        split = row.split()
-        col = split.column()
-
-        if images:
-
-            for im in images:
-                if im.type not in ("UV_TEST", "RENDER_RESULT", "COMPOSITING"):
-                    if not im.packed_file and \
-                       not os.path.exists(bpy.path.abspath(im.filepath, library=im.library)):
-                        images_missing.append(["%s%s [%s]%s" % (
-                            "[L] " if im.library else "",
-                            im.name, im.users,
-                            " [F]" if im.use_fake_user else ""),
-                            im.filepath if im.filepath else "No Filepath",
-                            im.library.filepath if im.library else ""])
-
-            if images_missing:
-                row = col.row(align=True)
-                row.alignment = "LEFT"
-                row.prop(
-                    scene,
-                    "amaranth_debug_scene_list_missing_images",
-                    icon="%s" %
-                    "TRIA_DOWN" if list_missing_images else "TRIA_RIGHT",
-                    emboss=False)
-
-                split = split.split()
-                col = split.column()
-
-                col.label(text="%s missing %s" % (
-                          str(len(images_missing)),
-                          'image' if len(images_missing) == 1 else "images"),
-                          icon="ERROR")
-
-                if list_missing_images:
-                    col = box.column(align=True)
-                    for mis in images_missing:
-                        row = col.row(align=True)
-                        row.alignment = "LEFT"
-                        row.label(
-                            text=mis[0],
-                            icon="IMAGE_DATA")
-                        # XXX TODO // make clicking on image work (needs new op to set x)
-                        # row.operator(
-                        #     AMTH_SCENE_OT_list_users_for_x.bl_idname,
-                        #     text=mis[0],
-                        #     icon="IMAGE_DATA",
-                        #     emboss=False).name = mis[0][:-4]
-
-                        row = col.row(align=True)
-                        row.label(text=mis[1], icon="LIBRARY_DATA_DIRECT")
-                        if mis[2]:
-                            row = col.row(align=True)
-                            row.alignment = "LEFT"
-                            row.operator(
-                                AMTH_SCENE_OT_blender_instance_open.bl_idname,
-                                text=mis[2],
-                                icon="LINK_BLEND",
-                                emboss=False).filepath = mis[2]
-                        col.separator()
-            else:
-                row = col.row(align=True)
-                row.alignment = "LEFT"
-                row.label(
-                    text="Great! No missing images", icon="RIGHTARROW_THIN")
-
-                split = split.split()
-                col = split.column()
-
-                col.label(text="%s %s loading correctly" % (
-                          str(len(images)),
-                          "image" if len(images) == 1 else "images"),
-                          icon="IMAGE_DATA")
+        split = box.split(percentage=0.8, align=True)
+        row = split.row()
+
+        if has_images:
+            subrow = split.row(align=True)
+            subrow.alignment = "RIGHT"
+            subrow.operator(AMTH_SCENE_OT_Collection_List_Refresh.bl_idname,
+                        text="", icon="FILE_REFRESH").what = "IMAGES"
+            image_state = context.window_manager.amth_missing_images_state
+
+            row.label(
+                "{} Image Blocks present in the Data".format(has_images),
+                icon="IMAGE_DATA"
+                )
+            if len(image_state.keys()) > 0:
+                box.template_list(
+                        'AMTH_UL_MissingImages_UI',
+                        'amth_collection_index_prop',
+                        context.window_manager,
+                        'amth_missing_images_state',
+                        context.window_manager.amth_collection_index_prop,
+                        'index_image',
+                        rows=3
+                        )
         else:
-            row = col.row(align=True)
-            row.alignment = "LEFT"
             row.label(text="No images loaded yet", icon="RIGHTARROW_THIN")
 
         # List Cycles Materials by Shader
@@ -968,33 +946,21 @@ class AMTH_SCENE_PT_scene_debug(bpy.types.Panel):
             row.operator(AMTH_SCENE_OT_cycles_shader_list_nodes.bl_idname,
                          icon="SORTSIZE",
                          text="List Materials Using Shader")
-            if materials_count != 0:
+            if len(AMTH_store_data.mat_shaders) != 0:
                 row.operator(
-                    AMTH_SCENE_OT_cycles_shader_list_nodes_clear.bl_idname,
-                    icon="X", text="")
+                    AMTH_SCENE_OT_list_users_debug_clear.bl_idname,
+                    icon="X", text="").what = "SHADER"
             col.separator()
 
-            try:
-                materials
-            except NameError:
-                pass
-            else:
-                if materials_count != 0:
-                    col = box.column(align=True)
-                    count = 0
+            if len(AMTH_store_data.mat_shaders) != 0:
+                col = box.column(align=True)
+                self.draw_label(col, "found", "material", "materials",
+                        AMTH_store_data.mat_shaders, "INFO"
+                        )
+                for i, mat in enumerate(AMTH_store_data.mat_shaders):
                     col.label(
-                        text="%s %s found" %
-                        (materials_count,
-                         "material" if materials_count == 1 else "materials"),
-                        icon="INFO")
-                    for mat in materials:
-                        count += 1
-                        col.label(
-                            text="%s" %
-                            (materials[
-                                count -
-                                1]),
-                            icon="MATERIAL")
+                        text="{}".format(AMTH_store_data.mat_shaders[i]), icon="MATERIAL"
+                        )
 
         # List Missing Node Trees
         box = layout.box()
@@ -1002,41 +968,33 @@ class AMTH_SCENE_PT_scene_debug(bpy.types.Panel):
         split = row.split()
         col = split.column(align=True)
 
-        split = col.split()
+        split = col.split(align=True)
         split.label(text="Node Links")
-        split.operator(AMTH_SCENE_OT_list_missing_node_links.bl_idname,
+        row = split.row(align=True)
+        row.operator(AMTH_SCENE_OT_list_missing_node_links.bl_idname,
                        icon="NODETREE")
 
-        if AMTH_SCENE_OT_list_missing_node_links.count_groups != 0 or \
-                AMTH_SCENE_OT_list_missing_node_links.count_images != 0 or \
-                AMTH_SCENE_OT_list_missing_node_links.count_image_node_unlinked != 0:
+        if AMTH_store_data.count_groups != 0 or \
+                AMTH_store_data.count_images != 0 or \
+                AMTH_store_data.count_image_node_unlinked != 0:
+
+            row.operator(
+                AMTH_SCENE_OT_list_users_debug_clear.bl_idname,
+                icon="X", text="").what = "NODE_LINK"
             col.label(text="Warning! Check Console", icon="ERROR")
 
-        if AMTH_SCENE_OT_list_missing_node_links.count_groups != 0:
-            col.label(
-                text="%s" %
-                ("%s node %s missing link" %
-                 (str(
-                     AMTH_SCENE_OT_list_missing_node_links.count_groups),
-                     "group" if AMTH_SCENE_OT_list_missing_node_links.count_groups == 1 else "groups")),
-                icon="NODETREE")
-        if AMTH_SCENE_OT_list_missing_node_links.count_images != 0:
-            col.label(
-                text="%s" %
-                ("%s image %s missing link" %
-                 (str(
-                     AMTH_SCENE_OT_list_missing_node_links.count_images),
-                     "node" if AMTH_SCENE_OT_list_missing_node_links.count_images == 1 else "nodes")),
-                icon="IMAGE_DATA")
-
-        if AMTH_SCENE_OT_list_missing_node_links.count_image_node_unlinked != 0:
-            col.label(
-                text="%s" %
-                ("%s image %s with no output conected" %
-                 (str(
-                     AMTH_SCENE_OT_list_missing_node_links.count_image_node_unlinked),
-                     "node" if AMTH_SCENE_OT_list_missing_node_links.count_image_node_unlinked == 1 else "nodes")),
-                icon="NODE")
+        if AMTH_store_data.count_groups != 0:
+            self.draw_miss_link(col, "node", "group", "groups", "missing link",
+                    AMTH_store_data.count_groups, "NODE_TREE"
+                    )
+        if AMTH_store_data.count_images != 0:
+            self.draw_miss_link(col, "image", "node", "nodes", "missing link",
+                    AMTH_store_data.count_images, "IMAGE_DATA"
+                    )
+        if AMTH_store_data.count_image_node_unlinked != 0:
+            self.draw_miss_link(col, "image", "node", "nodes", "with no output conected",
+                    AMTH_store_data.count_image_node_unlinked, "NODE"
+                    )
 
         # List Empty Materials Slots
         box = layout.box()
@@ -1046,61 +1004,41 @@ class AMTH_SCENE_PT_scene_debug(bpy.types.Panel):
 
         row = split.row(align=True)
         row.operator(AMTH_SCENE_OT_list_missing_material_slots.bl_idname,
-                     icon="MATERIAL",
-                     text="List Empty Materials Slots")
-        if missing_material_slots_count != 0:
+                    icon="MATERIAL",
+                    text="List Empty Materials Slots"
+                    )
+        if len(AMTH_store_data.obj_mat_slots) != 0:
             row.operator(
-                AMTH_SCENE_OT_list_missing_material_slots_clear.bl_idname,
-                icon="X", text="")
-        col.separator()
-
-        try:
-            missing_material_slots_obs
-        except NameError:
-            pass
-        else:
-            if missing_material_slots_count != 0:
-                col = box.column(align=True)
-                count = 0
-                count_lib = 0
-                col.label(
-                    text="%s %s with empty material slots found" %
-                    (missing_material_slots_count,
-                     "object" if missing_material_slots_count == 1 else "objects"),
-                    icon="INFO")
-
-                for obs in missing_material_slots_obs:
-                    count += 1
-
-                    row = col.row()
-                    row.alignment = "LEFT"
-                    row.label(
-                        text="%s" % missing_material_slots_obs[count - 1],
-                        icon="OBJECT_DATA")
-
-                if missing_material_slots_lib:
-                    col.separator()
-                    col.label("Check %s:" % (
-                        "this library" if
-                        len(missing_material_slots_lib) == 1
-                        else "these libraries"))
-
-                    for libs in missing_material_slots_lib:
-                        count_lib += 1
-                        row = col.row(align=True)
-                        row.alignment = "LEFT"
-                        row.operator(
-                            AMTH_SCENE_OT_blender_instance_open.bl_idname,
-                            text=missing_material_slots_lib[
-                                count_lib - 1],
-                            icon="LINK_BLEND",
-                            emboss=False).filepath = missing_material_slots_lib[
-                            count_lib - 1]
-
-
-        # List Users for Datablock
-        list_users = AMTH_SCENE_OT_list_users_for_x.users
-        list_users_libs = AMTH_SCENE_OT_list_users_for_x.libraries
+                AMTH_SCENE_OT_list_users_debug_clear.bl_idname,
+                icon="X", text="").what = "MAT_SLOTS"
+
+            col.separator()
+            col = box.column(align=True)
+            self.draw_label(col, "found empty material slot", "object", "objects",
+                    AMTH_store_data.obj_mat_slots, "INFO"
+                    )
+        for entry, obs in enumerate(AMTH_store_data.obj_mat_slots):
+            row = col.row()
+            row.alignment = "LEFT"
+            row.label(
+                text="{}".format(AMTH_store_data.obj_mat_slots[entry]),
+                icon="OBJECT_DATA")
+
+        if AMTH_store_data.obj_mat_slots_lib:
+            col.separator()
+            col.label("Check {}:".format(
+                "this library" if
+                len(AMTH_store_data.obj_mat_slots_lib) == 1 else
+                "these libraries")
+                )
+            for ilib, libs in enumerate(AMTH_store_data.obj_mat_slots_lib):
+                row = col.row(align=True)
+                row.alignment = "LEFT"
+                row.operator(
+                    AMTH_SCENE_OT_blender_instance_open.bl_idname,
+                    text=AMTH_store_data.obj_mat_slots_lib[ilib],
+                    icon="LINK_BLEND",
+                    emboss=False).filepath = AMTH_store_data.obj_mat_slots_lib[ilib]
 
         box = layout.box()
         row = box.row(align=True)
@@ -1109,90 +1047,75 @@ class AMTH_SCENE_PT_scene_debug(bpy.types.Panel):
         col = box.column(align=True)
         split = col.split()
         row = split.row(align=True)
-        row.prop(scene, "amth_datablock_types",
-                    icon=scene.amth_datablock_types,
-                    text="")
-
-        row.operator_menu_enum("scene.amth_list_users_for_x_type",
-                                       "list_type_select",
-                                       text=scene.amth_list_users_for_x_name)
+        row.prop(
+                scene, "amth_datablock_types",
+                icon=scene.amth_datablock_types,
+                text=""
+                )
+        row.operator_menu_enum(
+                "scene.amth_list_users_for_x_type",
+                "list_type_select",
+                text=scene.amth_list_users_for_x_name
+                )
 
         row = split.row(align=True)
-        row.enabled = True if scene.amth_list_users_for_x_name else False
-        row.operator(AMTH_SCENE_OT_list_users_for_x.bl_idname).name = scene.amth_list_users_for_x_name
-        if list_users:
+        row.enabled = True if USER_X_NAME_EMPTY not in scene.amth_list_users_for_x_name else False
+        row.operator(
+                AMTH_SCENE_OT_list_users_for_x.bl_idname,
+                icon="COLLAPSEMENU").name = scene.amth_list_users_for_x_name
+
+        if any(val for val in AMTH_store_data.users.values()):
+            col = box.column(align=True)
+
+            for t in AMTH_store_data.users:
+
+                for ma in AMTH_store_data.users[t]:
+                    subrow = col.row(align=True)
+                    subrow.alignment = "LEFT"
+
+                    if t == 'OBJECT_DATA':
+                        text_lib = " [L] " if \
+                                ma in bpy.data.objects and bpy.data.objects[ma].library else ""
+                        subrow.operator(
+                            AMTH_SCENE_OT_amaranth_object_select.bl_idname,
+                            text="{} {}{}".format(text_lib, ma,
+                             "" if ma in context.scene.objects else " [Not in Scene]"),
+                            icon=t,
+                            emboss=False).object_name = ma
+                    else:
+                        subrow.label(text=ma, icon=t)
             row.operator(
-                AMTH_SCENE_OT_list_users_for_x_clear.bl_idname,
-                icon="X", text="")
+            AMTH_SCENE_OT_list_users_debug_clear.bl_idname,
+            icon="X", text="").what = "XTYPE"
 
-        try:
-            list_users
-        except NameError:
-            pass
-        else:
-            if list_users:
-                empty = True
-                col = box.column(align=True)
-                for t in list_users:
-                    if list_users[t]:
-                        empty = False
-                        for ma in list_users[t]:
-                            row = col.row(align=True)
-                            row.alignment = "LEFT"
-                            if t == 'OBJECT_DATA':
-                                row.operator(
-                                    AMTH_SCENE_OT_amaranth_object_select.bl_idname,
-                                    text="%s %s%s" %
-                                    (" [L] " if ma.library else "",
-                                     ma.name,
-                                     "" if ma.name in context.scene.objects else " [Not in Scene]"),
-                                    icon=t,
-                                    emboss=False).object = ma.name
-                            else:
-                                row.label(text=ma,
-                                          icon=t)
-                if empty:
-                    row = col.row(align=True)
-                    row.alignment = "LEFT"
-                    row.label(text="No users for '{0}'".format(
-                              scene.amth_list_users_for_x_name),
-                              icon='INFO')
-
-                if list_users_libs:
-                    count_lib = 0
-
-                    col.separator()
-                    col.label("Check %s:" % (
-                        "this library" if
-                        len(list_users_libs) == 1
-                        else "these libraries"))
-
-                    for libs in list_users_libs:
-                        count_lib += 1
-                        row = col.row(align=True)
-                        row.alignment = "LEFT"
-                        row.operator(
-                            AMTH_SCENE_OT_blender_instance_open.bl_idname,
-                            text=list_users_libs[
-                                count_lib - 1],
-                            icon="LINK_BLEND",
-                            emboss=False).filepath = list_users_libs[
-                            count_lib - 1]
-
-class AMTH_LightersCorner(bpy.types.Panel):
+        if AMTH_store_data.libraries:
+            count_lib = 0
 
+            col.separator()
+            col.label("Check {}:".format(
+                "this library" if
+                len(AMTH_store_data.libraries) == 1 else
+                "these libraries")
+                )
+            for libs in AMTH_store_data.libraries:
+                count_lib += 1
+                row = col.row(align=True)
+                row.alignment = "LEFT"
+                row.operator(
+                    AMTH_SCENE_OT_blender_instance_open.bl_idname,
+                    text=AMTH_store_data.libraries[count_lib - 1],
+                    icon="LINK_BLEND",
+                    emboss=False).filepath = AMTH_store_data.libraries[count_lib - 1]
+
+
+class AMTH_PT_LightersCorner(Panel):
     """The Lighters Panel"""
     bl_label = "Lighter's Corner"
     bl_idname = "AMTH_SCENE_PT_lighters_corner"
     bl_space_type = 'PROPERTIES'
     bl_region_type = 'WINDOW'
     bl_context = "scene"
-
-    @classmethod
-    def poll(cls, context):
-        preferences = context.user_preferences.addons["amaranth"].preferences
-
-        return preferences.use_lighters_corner
+    bl_options = {"DEFAULT_CLOSED"}
 
     def draw_header(self, context):
         layout = self.layout
@@ -1200,172 +1123,255 @@ class AMTH_LightersCorner(bpy.types.Panel):
 
     def draw(self, context):
         layout = self.layout
-        scene = context.scene
-        objects = bpy.data.objects
-        ob_act = context.active_object
-        lamps = bpy.data.lamps
-        list_meshlights = scene.amaranth_lighterscorner_list_meshlights
-        engine = scene.render.engine
+        state_props = len(context.window_manager.amth_lighters_state)
+        engine = context.scene.render.engine
+        box = layout.box()
+        row = box.row(align=True)
 
         if utils.cycles_exists():
-            layout.prop(scene, "amaranth_lighterscorner_list_meshlights")
+            row.prop(context.scene, "amaranth_lighterscorner_list_meshlights")
 
-        box = layout.box()
-        if lamps:
-            if objects:
-                row = box.row(align=True)
-                split = row.split(percentage=0.45)
-                col = split.column()
+        subrow = row.row(align=True)
+        subrow.alignment = "RIGHT"
+        subrow.operator(AMTH_SCENE_OT_Collection_List_Refresh.bl_idname,
+                text="", icon="FILE_REFRESH").what = "LIGHTS"
 
-                col.label(text="Name")
+        if not state_props:
+            row = box.row()
+            message = "Please Refresh" if len(bpy.data.lamps) > 0 else "No Lamps in Data"
+            row.label(text=message, icon="INFO")
+        else:
+            row = box.row(align=True)
+            split = row.split(percentage=0.5, align=True)
+            col = split.column(align=True)
 
-                if engine in ["CYCLES", "BLENDER_RENDER"]:
-                    if engine == "BLENDER_RENDER":
-                        split = split.split(percentage=0.7)
-                    else:
-                        split = split.split(percentage=0.27)
-                    col = split.column()
-                    col.label(text="Samples")
+            col.label(text="Name/Library link")
+
+            if engine in ["CYCLES", "BLENDER_RENDER"]:
+                splits = 0.6 if engine == "BLENDER_RENDER" else 0.4
+                splita = split.split(percentage=splits, align=True)
+                col = splita.column(align=True)
+                col.alignment = "LEFT"
+                col.label(text="Samples")
 
                 if utils.cycles_exists() and engine == "CYCLES":
-                    split = split.split(percentage=0.2)
-                    col = split.column()
+                    col = splita.column(align=True)
                     col.label(text="Size")
 
-                split = split.split(percentage=1.0)
-                col = split.column()
-                col.label(text="%sRender Visibility" %
-                          "Rays /" if utils.cycles_exists() else "")
-
-                for ob in objects:
-                    is_lamp = ob.type == "LAMP"
-                    is_emission = True if utils.cycles_is_emission(
-                        context, ob) and list_meshlights else False
-
-                    if ob and is_lamp or is_emission:
-                        lamp = ob.data
-                        if utils.cycles_exists():
-                            clamp = ob.data.cycles
-                            visibility = ob.cycles_visibility
-
-                        row = box.row(align=True)
-                        split = row.split(percentage=1.0)
-                        col = split.column()
-                        row = col.row(align=True)
-                        col.active = ob == ob_act
-                        row.label(
-                            icon="%s" %
-                            ("LAMP_%s" %
-                             ob.data.type if is_lamp else "MESH_GRID"))
-                        split = row.split(percentage=.45)
-                        col = split.column()
-                        row = col.row(align=True)
-                        row.alignment = "LEFT"
-                        row.active = True
-                        row.operator(
-                            AMTH_SCENE_OT_amaranth_object_select.bl_idname,
-                            text="%s %s%s" %
-                            (" [L] " if ob.library else "",
-                             ob.name,
-                             "" if ob.name in context.scene.objects else " [Not in Scene]"),
-                            emboss=False).object = ob.name
-                        if ob.library:
-                            row = col.row(align=True)
-                            row.alignment = "LEFT"
-                            row.operator(
-                                AMTH_SCENE_OT_blender_instance_open.bl_idname,
-                                text=ob.library.filepath,
-                                icon="LINK_BLEND",
-                                emboss=False).filepath = ob.library.filepath
-
-                        if utils.cycles_exists() and engine == "CYCLES":
-                            split = split.split(percentage=0.25)
-                            col = split.column()
-                            if is_lamp:
-                                if scene.cycles.progressive == "BRANCHED_PATH":
-                                    col.prop(clamp, "samples", text="")
-                                if scene.cycles.progressive == "PATH":
-                                    col.label(text="N/A")
-                            else:
-                                col.label(text="N/A")
-
-                        if engine == "BLENDER_RENDER":
-                            split = split.split(percentage=0.7)
-                            col = split.column()
-                            if is_lamp:
-                                if lamp.type == "HEMI":
-                                    col.label(text="Not Available")
-                                elif lamp.type == "AREA" and lamp.shadow_method == "RAY_SHADOW":
-                                    row = col.row(align=True)
-                                    row.prop(
-                                        lamp, "shadow_ray_samples_x", text="X")
-                                    if lamp.shape == "RECTANGLE":
-                                        row.prop(
-                                            lamp,
-                                            "shadow_ray_samples_y",
-                                            text="Y")
-                                elif lamp.shadow_method == "RAY_SHADOW":
-                                    col.prop(
-                                        lamp,
-                                        "shadow_ray_samples",
-                                        text="Ray Samples")
-                                elif lamp.shadow_method == "BUFFER_SHADOW":
-                                    col.prop(
-                                        lamp,
-                                        "shadow_buffer_samples",
-                                        text="Buffer Samples")
-                                else:
-                                    col.label(text="No Shadow")
-                            else:
-                                col.label(text="N/A")
-
-                        if utils.cycles_exists() and engine == "CYCLES":
-                            split = split.split(percentage=0.2)
-                            col = split.column()
-                            if is_lamp:
-                                if lamp.type in ["POINT", "SUN", "SPOT"]:
-                                    col.label(
-                                        text="%.2f" % lamp.shadow_soft_size)
-                                elif lamp.type == "HEMI":
-                                    col.label(text="N/A")
-                                elif lamp.type == "AREA" and lamp.shape == "RECTANGLE":
-                                    col.label(
-                                        text="%.2fx%.2f" %
-                                        (lamp.size, lamp.size_y))
-                                else:
-                                    col.label(text="%.2f" % lamp.size)
-                            else:
-                                col.label(text="N/A")
-
-                        split = split.split(percentage=1.0)
-                        col = split.column()
-                        row = col.row(align=True)
-                        if utils.cycles_exists():
-                            row.prop(visibility, "camera", text="")
-                            row.prop(visibility, "diffuse", text="")
-                            row.prop(visibility, "glossy", text="")
-                            row.prop(visibility, "shadow", text="")
-                            row.separator()
-                        row.prop(ob, "hide", text="", emboss=False)
-                        row.prop(ob, "hide_render", text="", emboss=False)
+            cols = row.row(align=True)
+            cols.alignment = "RIGHT"
+            cols.label(text="{}Render Visibility/Selection".format(
+                  "Rays /" if utils.cycles_exists() else "")
+                )
+            box.template_list(
+                    'AMTH_UL_LightersCorner_UI',
+                    'amth_collection_index_prop',
+                    context.window_manager,
+                    'amth_lighters_state',
+                    context.window_manager.amth_collection_index_prop,
+                    'index',
+                    rows=5
+                    )
+
+
+class AMTH_UL_MissingImages_UI(UIList):
+
+    def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
+        text_lib = item.text_lib
+        has_filepath = item.has_filepath
+        is_library = item.is_library
+
+        split = layout.split(percentage=0.4)
+        row = split.row(align=True)
+        row.alignment = "LEFT"
+        row.label(text=text_lib, icon="IMAGE_DATA")
+        image = bpy.data.images.get(item.name, None)
+
+        subrow = split.row(align=True)
+        splitp = subrow.split(percentage=0.8, align=True).row(align=True)
+        splitp.alignment = "LEFT"
+        row_lib = subrow.row(align=True)
+        row_lib.alignment = "RIGHT"
+        if not image:
+            splitp.label(text="Image is not available", icon="ERROR")
         else:
-            box.label(text="No Lamps", icon="LAMP_DATA")
+            splitp.label(text=has_filepath, icon="LIBRARY_DATA_DIRECT")
+            if is_library:
+                row_lib.operator(
+                    AMTH_SCENE_OT_blender_instance_open.bl_idname,
+                    text="",
+                    emboss=False, icon="LINK_BLEND").filepath = is_library
 
 
-classes = {
+class AMTH_UL_LightersCorner_UI(UIList):
+
+    def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
+        icon_type = item.icon_type
+        engine = context.scene.render.engine
+        text_lib = item.text_lib
+        is_library = item.is_library
+
+        split = layout.split(percentage=0.35)
+        row = split.row(align=True)
+        row.alignment = "LEFT"
+        row.label(text=text_lib, icon=icon_type)
+        ob = bpy.data.objects.get(item.name, None)
+        if not ob:
+            row.label(text="Object is not available", icon="ERROR")
+        else:
+            if is_library:
+                row.operator(
+                    AMTH_SCENE_OT_blender_instance_open.bl_idname,
+                    text="",
+                    emboss=False, icon="LINK_BLEND").filepath = is_library
+
+            rows = split.row(align=True)
+            splits = 0.9 if engine == "BLENDER_RENDER" else 0.4
+            splitlamp = rows.split(percentage=splits, align=True)
+            splitlampb = splitlamp.row(align=True)
+            splitlampc = splitlamp.row(align=True)
+            splitlampd = rows.row(align=True)
+            splitlampd.alignment = "RIGHT"
+
+            if utils.cycles_exists() and engine == "CYCLES":
+                if "LAMP" in icon_type:
+                    clamp = ob.data.cycles
+                    if context.scene.cycles.progressive == "BRANCHED_PATH":
+                        splitlampb.prop(clamp, "samples", text="")
+                    if context.scene.cycles.progressive == "PATH":
+                        splitlampb.label(text="N/A")
+                    lamp = ob.data
+                    if lamp.type in ["POINT", "SUN", "SPOT"]:
+                        splitlampc.label(text="{:.2f}".format(lamp.shadow_soft_size))
+                    elif lamp.type == "HEMI":
+                        splitlampc.label(text="N/A")
+                    elif lamp.type == "AREA" and lamp.shape == "RECTANGLE":
+                        splitlampc.label(
+                            text="{:.2f} x {:.2f}".format(lamp.size, lamp.size_y)
+                            )
+                    else:
+                        splitlampc.label(text="{:.2f}".format(lamp.size))
+                else:
+                    splitlampb.label(text="N/A")
+            if engine == "BLENDER_RENDER":
+                if "LAMP" in icon_type:
+                    lamp = ob.data
+                    if lamp.type == "HEMI":
+                        splitlampb.label(text="Not Available")
+                    elif lamp.type == "AREA" and lamp.shadow_method == "RAY_SHADOW":
+                        splitlampb.prop(lamp, "shadow_ray_samples_x", text="X")
+                        if lamp.shape == "RECTANGLE":
+                            splitlampb.prop(lamp, "shadow_ray_samples_y", text="Y")
+                    elif lamp.shadow_method == "RAY_SHADOW":
+                        splitlampb.prop(lamp, "shadow_ray_samples", text="Ray Samples")
+                    elif lamp.shadow_method == "BUFFER_SHADOW":
+                        splitlampb.prop(lamp, "shadow_buffer_samples", text="Buffer Samples")
+                    else:
+                        splitlampb.label(text="No Shadow")
+                else:
+                    splitlampb.label(text="N/A")
+            if utils.cycles_exists():
+                visibility = ob.cycles_visibility
+                splitlampd.prop(visibility, "camera", text="")
+                splitlampd.prop(visibility, "diffuse", text="")
+                splitlampd.prop(visibility, "glossy", text="")
+                splitlampd.prop(visibility, "shadow", text="")
+                splitlampd.separator()
+            splitlampd.prop(ob, "hide", text="", emboss=False)
+            splitlampd.prop(ob, "hide_render", text="", emboss=False)
+            splitlampd.operator(
+                    AMTH_SCENE_OT_amaranth_object_select.bl_idname,
+                    text="",
+                    emboss=False, icon="RESTRICT_SELECT_OFF").object_name = item.name
+
+
+def fill_missing_images_props(context, refresh=False):
+    image_state = context.window_manager.amth_missing_images_state
+    if refresh:
+        for key in image_state.keys():
+            index = image_state.find(key)
+            if index != -1:
+                image_state.remove(index)
+
+    for im in bpy.data.images:
+        if im.type not in ("UV_TEST", "RENDER_RESULT", "COMPOSITING"):
+            if not im.packed_file and \
+                    not os.path.exists(bpy.path.abspath(im.filepath, library=im.library)):
+                text_l = "{}{} [{}]{}".format("[L] " if im.library else "", im.name,
+                    im.users, " [F]" if im.use_fake_user else "")
+                prop = image_state.add()
+                prop.name = im.name
+                prop.text_lib = text_l
+                prop.has_filepath = im.filepath if im.filepath else "No Filepath"
+                prop.is_library = im.library.filepath if im.library else ""
+
+
+def fill_ligters_corner_props(context, refresh=False):
+    light_state = context.window_manager.amth_lighters_state
+    list_meshlights = context.scene.amaranth_lighterscorner_list_meshlights
+    if refresh:
+        for key in light_state.keys():
+            index = light_state.find(key)
+            if index != -1:
+                light_state.remove(index)
+
+    for ob in bpy.data.objects:
+        if ob.name not in light_state.keys() or refresh:
+            is_lamp = ob.type == "LAMP"
+            is_emission = True if utils.cycles_is_emission(
+                    context, ob) and list_meshlights else False
+
+            if is_lamp or is_emission:
+                icons = "LAMP_%s" % ob.data.type if is_lamp else "MESH_GRID"
+                text_l = "{} {}{}".format(" [L] " if ob.library else "", ob.name,
+                        "" if ob.name in context.scene.objects else " [Not in Scene]")
+                prop = light_state.add()
+                prop.name = ob.name
+                prop.icon_type = icons
+                prop.text_lib = text_l
+                prop.is_library = ob.library.filepath if ob.library else ""
+
+
+class AMTH_LightersCornerStateProp(PropertyGroup):
+    icon_type = StringProperty()
+    text_lib = StringProperty()
+    is_library = StringProperty()
+
+
+class AMTH_MissingImagesStateProp(PropertyGroup):
+    text_lib = StringProperty()
+    has_filepath = StringProperty()
+    is_library = StringProperty()
+
+
+class AMTH_LightersCollectionIndexProp(PropertyGroup):
+    index = IntProperty(
+            name="index"
+            )
+    index_image = IntProperty(
+            name="index"
+            )
+
+
+classes = (
     AMTH_SCENE_PT_scene_debug,
+    AMTH_SCENE_OT_list_users_debug_clear,
     AMTH_SCENE_OT_blender_instance_open,
     AMTH_SCENE_OT_amaranth_object_select,
     AMTH_SCENE_OT_list_missing_node_links,
     AMTH_SCENE_OT_list_missing_material_slots,
-    AMTH_SCENE_OT_list_missing_material_slots_clear,
     AMTH_SCENE_OT_cycles_shader_list_nodes,
-    AMTH_SCENE_OT_cycles_shader_list_nodes_clear,
     AMTH_SCENE_OT_list_users_for_x,
     AMTH_SCENE_OT_list_users_for_x_type,
-    AMTH_SCENE_OT_list_users_for_x_clear,
-    AMTH_LightersCorner
-}
+    AMTH_SCENE_OT_Collection_List_Refresh,
+    AMTH_LightersCornerStateProp,
+    AMTH_LightersCollectionIndexProp,
+    AMTH_MissingImagesStateProp,
+    AMTH_PT_LightersCorner,
+    AMTH_UL_LightersCorner_UI,
+    AMTH_UL_MissingImages_UI,
+)
+
 
 def register():
     init()
@@ -1373,12 +1379,22 @@ def register():
     for cls in classes:
         bpy.utils.register_class(cls)
 
-    from bpy.types import Scene
+    bpy.types.Scene.amth_list_users_for_x_name = StringProperty(
+            default="Select DataBlock Name",
+            name="Name",
+            description=USER_X_NAME_EMPTY,
+            options={"SKIP_SAVE"}
+            )
+    bpy.types.WindowManager.amth_collection_index_prop = PointerProperty(
+            type=AMTH_LightersCollectionIndexProp
+            )
+    bpy.types.WindowManager.amth_lighters_state = CollectionProperty(
+            type=AMTH_LightersCornerStateProp
+            )
+    bpy.types.WindowManager.amth_missing_images_state = CollectionProperty(
+            type=AMTH_MissingImagesStateProp
+            )
 
-    bpy.types.Scene.amth_list_users_for_x_name = bpy.props.StringProperty(
-                                                    default="",
-                                                    name="Name",
-                                                    description="Which datablock type to look for")
 
 def unregister():
     clear()
@@ -1386,6 +1402,7 @@ def unregister():
     for cls in classes:
         bpy.utils.unregister_class(cls)
 
-    from bpy.types import Scene
-
-    del Scene.amth_list_users_for_x_name
+    del bpy.types.Scene.amth_list_users_for_x_name
+    del bpy.types.WindowManager.amth_collection_index_prop
+    del bpy.types.WindowManager.amth_lighters_state
+    del bpy.types.WindowManager.amth_missing_images_state
index 707d732..3952078 100644 (file)
@@ -26,12 +26,15 @@ KEYMAPS = list()
 
 
 class AMTH_SCENE_OT_refresh(bpy.types.Operator):
-
     """Refresh the current scene"""
     bl_idname = "scene.refresh"
     bl_label = "Refresh!"
 
     def execute(self, context):
+        get_addon = "amaranth" in context.user_preferences.addons.keys()
+        if not get_addon:
+            return {"CANCELLED"}
+
         preferences = context.user_preferences.addons["amaranth"].preferences
         scene = context.scene
 
@@ -44,9 +47,11 @@ class AMTH_SCENE_OT_refresh(bpy.types.Operator):
 
 
 def button_refresh(self, context):
-    preferences = context.user_preferences.addons["amaranth"].preferences
+    get_addon = "amaranth" in context.user_preferences.addons.keys()
+    if not get_addon:
+        return
 
-    if preferences.use_scene_refresh:
+    if context.user_preferences.addons["amaranth"].preferences.use_scene_refresh:
         self.layout.separator()
         self.layout.operator(AMTH_SCENE_OT_refresh.bl_idname,
                              text="Refresh!",
index e27285e..0698755 100644 (file)
@@ -27,7 +27,6 @@ KEYMAPS = list()
 
 
 class AMTH_WM_OT_save_reload(bpy.types.Operator):
-
     """Save and Reload the current blend file"""
     bl_idname = "wm.save_reload"
     bl_label = "Save & Reload"
@@ -43,13 +42,16 @@ class AMTH_WM_OT_save_reload(bpy.types.Operator):
     def execute(self, context):
         path = bpy.data.filepath
         self.save_reload(context, path)
+
         return {"FINISHED"}
 
 
 def button_save_reload(self, context):
-    preferences = context.user_preferences.addons["amaranth"].preferences
+    get_addon = "amaranth" in context.user_preferences.addons.keys()
+    if not get_addon:
+        return
 
-    if preferences.use_file_save_reload:
+    if context.user_preferences.addons["amaranth"].preferences.use_file_save_reload:
         self.layout.separator()
         self.layout.operator(
             AMTH_WM_OT_save_reload.bl_idname,
index 0f6f519..b073492 100644 (file)
@@ -24,10 +24,11 @@ from amaranth import utils
 
 
 def stats_scene(self, context):
+    get_addon = "amaranth" in context.user_preferences.addons.keys()
+    if not get_addon:
+        return
 
-    preferences = context.user_preferences.addons["amaranth"].preferences
-
-    if preferences.use_scene_stats:
+    if context.user_preferences.addons["amaranth"].preferences.use_scene_stats:
         scenes_count = str(len(bpy.data.scenes))
         cameras_count = str(len(bpy.data.cameras))
         cameras_selected = 0