[Bf-extensions-cvs] [2769f4b5] master: development_edit_operator: update to 2.8x: T68541

meta-androcto noreply at git.blender.org
Mon Sep 2 05:04:42 CEST 2019


Commit: 2769f4b5f5f4769cf9115fa7cdd1287540a795bf
Author: meta-androcto
Date:   Mon Sep 2 13:04:25 2019 +1000
Branches: master
https://developer.blender.org/rBAC2769f4b5f5f4769cf9115fa7cdd1287540a795bf

development_edit_operator: update to 2.8x: T68541

===================================================================

M	development_edit_operator.py

===================================================================

diff --git a/development_edit_operator.py b/development_edit_operator.py
index 257e61be..08e91d87 100644
--- a/development_edit_operator.py
+++ b/development_edit_operator.py
@@ -21,9 +21,9 @@ bl_info = {
     "name": "Edit Operator Source",
     "author": "scorpion81",
     "version": (1, 2, 2),
-    "blender": (2, 78, 0),
+    "blender": (2, 80, 0),
     "location": "Text Editor > Edit > Edit Operator",
-    "description": "Opens source file of chosen operator, if it is an add-on one",
+    "description": "Opens source file of chosen operator or call locations, if source not available",
     "warning": "",
     "wiki_url": "https://wiki.blender.org/index.php/Extensions:2.6/"
                 "Py/Scripts/Development/Edit_Operator_Source",
@@ -31,46 +31,117 @@ bl_info = {
 
 import bpy
 import sys
+import os
 import inspect
 from bpy.types import (
         Operator,
         Panel,
+        Header,
+        Menu,
+        PropertyGroup
         )
-from bpy.props import EnumProperty
+from bpy.props import ( 
+        EnumProperty,
+        StringProperty,
+        IntProperty
+        )
+
+def stdlib_excludes():
+    #need a handy list of modules to avoid walking into
+    import distutils.sysconfig as sysconfig
+    excludes = []
+    std_lib = sysconfig.get_python_lib(standard_lib=True)
+    for top, dirs, files in os.walk(std_lib):
+        for nm in files:
+            if nm != '__init__.py' and nm[-3:] == '.py':
+                excludes.append(os.path.join(top, nm)[len(std_lib)+1:-3].replace('\\','.'))
+    
+    return excludes
+
+def make_loc(prefix, c):
+    #too long and not helpful... omitting for now
+    space = ""
+    #if hasattr(c, "bl_space_type"):
+    #    space = c.bl_space_type
+        
+    region = ""
+    #if hasattr(c, "bl_region_type"):
+    #   region = c.bl_region_type
+        
+    label = ""
+    if hasattr(c, "bl_label"):
+        label = c.bl_label
+    
+    return prefix+": " + space + " " + region + " " + label
 
+def walk_module(opname, mod, calls=[], exclude=[]):
+    
+    for name, m in inspect.getmembers(mod):
+        if inspect.ismodule(m):
+            if m.__name__ not in exclude:
+                #print(name, m.__name__)
+                walk_module(opname, m, calls, exclude)
+        elif inspect.isclass(m):
+            if (issubclass(m, Panel) or \
+                issubclass(m, Header) or \
+                issubclass(m, Menu)) and mod.__name__ != "bl_ui":
+                if hasattr(m, "draw"):
+                    loc = ""
+                    file = ""
+                    line = -1
+                    src, n = inspect.getsourcelines(m.draw)
+                    for i, s in enumerate(src):
+                        if opname in s:
+                            file = mod.__file__
+                            line = n + i
+                        
+                            if issubclass(m, Panel) and name != "Panel":
+                                loc = make_loc("Panel", m)
+                                calls.append([opname, loc, file, line])
+                            if issubclass(m, Header) and name != "Header":
+                                loc = make_loc("Header", m)
+                                calls.append([opname, loc, file, line])
+                            if issubclass(m, Menu) and name != "Menu":
+                                loc = make_loc("Menu", m)
+                                calls.append([opname, loc, file, line])
 
-def get_py_class_from_op(opname):
+
+def getclazz(opname):
     opid = opname.split(".")
     opmod = getattr(bpy.ops, opid[0])
     op = getattr(opmod, opid[1])
-    id = op.get_rna_type().identifier
-    # C operators won't be added
-    return getattr(bpy.types, id, None)
+    id = op.get_rna_type().bl_rna.identifier
+    try:
+        clazz = getattr(bpy.types, id)
+        return clazz
+    except AttributeError:
+        return None
 
 
 def getmodule(opname):
-    cls = get_py_class_from_op(opname)
-    if cls is None:
-        addon = False
+    addon = True
+    clazz = getclazz(opname)
+    
+    if clazz is None:
+        return  "", -1, False
+    
+    modn = clazz.__module__
+
+    try:
+        line = inspect.getsourcelines(clazz)[1]
+    except IOError:
         line = -1
+    except TypeError:
+        line = -1
+
+    if modn == 'bpy.types':
         mod = 'C operator'
+        addon = False
+    elif modn != '__main__':
+        mod = sys.modules[modn].__file__
     else:
-        addon = True
-        mod_name = cls.__module__
-        try:
-            line = inspect.getsourcelines(cls)[1]
-        except IOError:
-            line = -1
-        except TypeError:
-            line = -1
-
-        if mod_name == 'bpy.types':
-            addon = False
-        elif mod_name != '__main__':
-            mod = sys.modules[mod_name].__file__
-        else:
-            addon = False
-            mod = mod_name
+        addon = False
+        mod = modn
 
     return mod, line, addon
 
@@ -83,9 +154,9 @@ def get_ops():
         opmoddir = dir(opmod)
         for o in opmoddir:
             name = opmodname + "." + o
-            cls = get_py_class_from_op(name)
-            if cls is not None:
-                allops.append(name)
+            clazz = getclazz(name)
+            #if (clazz is not None) :# and clazz.__module__ != 'bpy.types'):
+            allops.append(name)
         del opmoddir
 
     # add own operator name too, since its not loaded yet when this is called
@@ -96,8 +167,27 @@ def get_ops():
 
     return [(y, y, "", x) for x, y in enumerate(l)]
 
+class OperatorEntry(PropertyGroup):
+    
+    label : StringProperty(
+            name="Label",
+            description="",
+            default=""
+            )
+             
+    path : StringProperty(
+            name="Path",
+            description="",
+            default=""
+            )
+            
+    line : IntProperty(
+            name="Line",
+            description="",
+            default=-1
+            ) 
 
-class EditOperator(Operator):
+class TEXT_OT_EditOperator(Operator):
     bl_idname = "text.edit_operator"
     bl_label = "Edit Operator"
     bl_description = "Opens the source file of operators chosen from Menu"
@@ -105,61 +195,142 @@ class EditOperator(Operator):
 
     items = get_ops()
 
-    op: EnumProperty(
+    op : EnumProperty(
             name="Op",
             description="",
             items=items
             )
+            
+    path : StringProperty(
+            name="Path",
+            description="",
+            default=""
+            )
+            
+    line : IntProperty(
+            name="Line",
+            description="",
+            default=-1
+            )
+            
+    def show_text(self, context, path, line):
+        found = False
+        
+        for t in bpy.data.texts:
+            if t.filepath == path:
+                #switch to the wanted text first
+                context.space_data.text = t
+                ctx = context.copy()
+                ctx['edit_text'] = t
+                bpy.ops.text.jump(ctx, line=line)
+                found = True
+                break
+
+        if (found is False):
+            self.report({'INFO'},
+                        "Opened file: " + path)
+            bpy.ops.text.open(filepath=path)
+            bpy.ops.text.jump(line=line)
+            
+    def show_calls(self, context):
+        import bl_ui
+        import addon_utils
+        
+        exclude = stdlib_excludes()
+        exclude.append("bpy")
+        exclude.append("sys")
+        
+        calls = []
+        walk_module(self.op, bl_ui, calls, exclude)
+        
+        for m in addon_utils.modules():
+            try:
+                mod = sys.modules[m.__name__]
+                walk_module(self.op, mod, calls, exclude)
+            except KeyError:
+                continue
+            
+        for c in calls:
+            cl = context.scene.calls.add()
+            cl.name = c[0]
+            cl.label = c[1]
+            cl.path = c[2]
+            cl.line = c[3]
 
     def invoke(self, context, event):
         context.window_manager.invoke_search_popup(self)
         return {'PASS_THROUGH'}
 
     def execute(self, context):
-        found = False
-        path, line, addon = getmodule(self.op)
-        if addon:
-            for t in bpy.data.texts:
-                if t.filepath == path:
-                    ctx = context.copy()
-                    ctx['edit_text'] = t
-                    bpy.ops.text.jump(ctx, line=line)
-                    found = True
-                    break
-
-            if (found is False):
-                self.report({'INFO'},
-                            "Opened file: " + path)
-                bpy.ops.text.open(filepath=path)
-                bpy.ops.text.jump(line=line)
-
-            return {'FINISHED'}
+        if self.path != "" and self.line != -1:
+            #invocation of one of the "found" locations
+            self.show_text(context, self.path, self.line)
+            return {'FINISHED'} 
         else:
-            self.report({'WARNING'},
-                        "Found no source file for " + self.op)
-
-            return {'CANCELLED'}
+            context.scene.calls.clear()
+            path, line, addon = getmodule(self.op)
+            
+            if addon:
+                self.show_text(context, path, line)
+                
+                #add convenient "source" button, to toggle back from calls to source
+                c = context.scene.calls.add()
+                c.name = self.op
+                c.label = "Source"
+                c.path = path
+                c.line = line
+                
+                self.show_calls(context)
+                context.area.tag_redraw()
+                
+                return {'FINISHED'}
+            else:
+                
+                self.report({'WARNING'},
+                            "Found no source file for " + self.op)
+                            
+                self.show_calls(context)
+                context.area.tag_redraw()
+               
+                return {'FINISHED'}
 
 
-class EditOperatorPanel(Panel):
-    bl_idname = "D

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-extensions-cvs mailing list