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

git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'development_api_navigator.py')
-rw-r--r--development_api_navigator.py715
1 files changed, 715 insertions, 0 deletions
diff --git a/development_api_navigator.py b/development_api_navigator.py
new file mode 100644
index 00000000..addc0d29
--- /dev/null
+++ b/development_api_navigator.py
@@ -0,0 +1,715 @@
+# development_api_navigator.py
+#
+# ***** BEGIN GPL LICENSE BLOCK *****
+#
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ***** END GPL LICENCE BLOCK *****
+
+bl_info = {
+ "name": "API Navigator",
+ "author": "Dany Lebel (Axon_D)",
+ "version": (1, 0, 2),
+ "blender": (2, 5, 7),
+ "api": 36079,
+ "location": "Text Editor > Properties > API Navigator Panel",
+ "description": "Allows exploration of the python api via the user interface",
+ "warning": "",
+ "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.5/Py/"
+ "Scripts/Text_Editor/API_Navigator",
+ "tracker_url": "http://projects.blender.org/tracker/index.php?"\
+ "func=detail&aid=24982",
+ "category": "Development"}
+
+"""
+ You can browse through the tree structure of the api. Each child object appears in a list
+that tries to be representative of its type. These lists are :
+
+ * Items (for an iterable object)
+ * Item Values (for an iterable object wich only supports index)
+ * Modules
+ * Types
+ * Properties
+ * Structs and Functions
+ * Methods and Functions
+ * Attributes
+ * Inaccessible (some objects may be listed but inaccessible)
+
+ The lists can be filtered to help searching in the tree. Just enter the text in the
+filter section. It is also possible to explore other modules. Go the the root and select
+it in the list of available modules. It will be imported dynamically.
+
+ In the text section, some informations are displayed. The type of the object,
+what it returns, and its docstring. We could hope that these docstrings will be as
+descriptive as possible. This text data block named api_doc_ can be toggled on and off
+with the Escape key. (but a bug prevent the keymap to register correctly at start)
+
+"""
+
+import bpy, sys
+from console.complete_import import get_root_modules
+
+
+############ Global Variables ############
+
+last_text = None # last text data block
+
+root_module = None # root module of the tree
+
+root_m_path = '' # root_module + path as a string
+
+current_module = None # the object itself in the tree structure
+
+
+tree_level = None # the list of objects from the current_module
+
+def init_tree_level():
+ global tree_level
+ tree_level = [[],[],[],[],[],[],[], [], []]
+
+init_tree_level()
+
+
+api_doc_ = '' # the documentation formated for the API Navigator
+
+module_type = None # the type of current_module
+
+return_report = '' # what current_module returns
+
+filter_mem = {} # remember last filters entered for each path
+
+too_long = False # is tree_level list too long to display in a panel?
+
+
+############ Functions ############
+
+def get_root_module(path):
+ #print('get_root_module')
+ global root_module
+ if '.' in path:
+ root = path[:path.find('.')]
+ else :
+ root = path
+ try :
+ root_module = __import__(root)
+ except :
+ root_module = None
+
+
+def evaluate(module):
+ #print('evaluate')
+ global root_module, tree_level, root_m_path
+
+ path = bpy.context.window_manager.api_nav_props.path
+ try :
+ len_name = root_module.__name__.__len__()
+ root_m_path = 'root_module' + module[len_name:]
+ current_module = eval(root_m_path)
+ return current_module
+ except :
+ init_tree_level
+ return None
+
+
+def get_tree_level():
+ #print('get_tree_level')
+
+ path = bpy.context.window_manager.api_nav_props.path
+
+ def object_list():
+ #print('object_list')
+ global current_module, root_m_path
+
+ itm, val, mod, typ, props, struct, met, att, bug = [], [], [], [], [], [], [], [], []
+ iterable = isiterable(current_module)
+ if iterable:
+ iter(current_module)
+ current_type = str(module_type)
+ if current_type != "<class 'str'>":
+ if iterable == 'a':
+ #if iterable == 'a':
+ #current_type.__iter__()
+ itm = list(current_module.keys())
+ if not itm:
+ val = list(current_module)
+ else :
+ val = list(current_module)
+
+ for i in dir(current_module):
+ try :
+ t = str(type(eval(root_m_path + '.' + i)))
+ except (AttributeError, SyntaxError):
+ bug += [i]
+ continue
+
+
+ if t == "<class 'module'>":
+ mod += [i]
+ elif t[0:16] == "<class 'bpy_prop":
+ props += [i]
+ elif t[8:11] == 'bpy':
+ struct += [i]
+ elif t == "<class 'builtin_function_or_method'>":
+ met += [i]
+ elif t == "<class 'type'>":
+ typ += [i]
+ else :
+ att += [i]
+
+ return [itm, val, mod, typ, props, struct, met, att, bug]
+
+ if not path:
+ return [[], [], [i for i in get_root_modules()], [], [], [], [], [], []]
+ return object_list()
+
+
+def parent(path):
+ """Returns the parent path"""
+ #print('parent')
+
+ parent = path
+ if parent[-1] == ']' and '[' in parent:
+ while parent[-1] != '[':
+ parent = parent[:-1]
+ elif '.' in parent:
+ while parent[-1] != '.':
+ parent = parent[:-1]
+ else :
+ return ''
+ parent = parent[:-1]
+ return parent
+
+
+def update_filter():
+ """Update the filter according to the current path"""
+ global filter_mem
+
+ try :
+ bpy.context.window_manager.api_nav_props.filter = filter_mem[bpy.context.window_manager.api_nav_props.path]
+ except :
+ bpy.context.window_manager.api_nav_props.filter = ''
+
+
+def isiterable(mod):
+
+ try :
+ iter(mod)
+ except :
+ return False
+ try :
+ mod['']
+ return 'a'
+ except KeyError:
+ return 'a'
+ except (AttributeError, TypeError):
+ return 'b'
+
+
+def fill_filter_mem():
+ global filter_mem
+
+ filter = bpy.context.window_manager.api_nav_props.filter
+ if filter:
+ filter_mem[bpy.context.window_manager.api_nav_props.old_path] = bpy.context.window_manager.api_nav_props.filter
+ else :
+ filter_mem.pop(bpy.context.window_manager.api_nav_props.old_path, None)
+
+
+###### API Navigator parent class #######
+
+class ApiNavigator():
+ """Parent class for API Navigator"""
+
+ @staticmethod
+ def generate_global_values():
+ """Populate the level attributes to display the panel buttons and the documentation"""
+ global tree_level, current_module, module_type, return_report, last_text
+
+ text = bpy.context.space_data.text
+ if text:
+ if text.name != 'api_doc_':
+ last_text = bpy.context.space_data.text.name
+ elif bpy.data.texts.__len__() < 2:
+ last_text = None
+ else :
+ last_text = None
+ bpy.context.window_manager.api_nav_props.pages = 0
+ get_root_module(bpy.context.window_manager.api_nav_props.path)
+ current_module = evaluate(bpy.context.window_manager.api_nav_props.path)
+ module_type = str(type(current_module))
+ return_report = str(current_module)
+ tree_level = get_tree_level()
+
+ if tree_level.__len__() > 30:
+ global too_long
+ too_long = True
+ else :
+ too_long = False
+
+ __class__.generate_api_doc()
+ return {'FINISHED'}
+
+ @staticmethod
+ def generate_api_doc():
+ """Format the doc string for API Navigator"""
+ global current_module, api_doc_, return_report, module_type
+
+ path = bpy.context.window_manager.api_nav_props.path
+ line = "-" * (path.__len__()+2)
+ header = """\n\n\n\t\t%s\n\t %s\n\
+_____________________________________________\n\
+\n\
+Type : %s\n\
+\n\
+\n\
+Return : %s\n\
+_____________________________________________\n\
+\n\
+Doc:
+\n\
+""" % (path, line, module_type, return_report)
+ footer = "\n\
+_____________________________________________\n\
+\n\
+\n\
+\n\
+#############################################\n\
+# api_doc_ #\n\
+# Escape to toggle text #\n\
+# (F8 to reload modules if doesn't work) #\n\
+#############################################"
+ doc = current_module.__doc__
+ api_doc_ = header + str(doc) + footer
+ return {'FINISHED'}
+
+ @staticmethod
+ def doc_text_datablock():
+ """Create the text databloc or overwrite it if it already exist"""
+ global api_doc_
+
+ space_data = bpy.context.space_data
+
+ try :
+ doc_text = bpy.data.texts['api_doc_']
+ space_data.text = doc_text
+ doc_text.clear()
+ except :
+ bpy.data.texts.new(name='api_doc_')
+ doc_text = bpy.data.texts['api_doc_']
+ space_data.text = doc_text
+
+ doc_text.write(text=api_doc_)
+ return {'FINISHED'}
+
+
+
+############ Operators ############
+def api_update(context):
+ if bpy.context.window_manager.api_nav_props.path != bpy.context.window_manager.api_nav_props.old_path:
+ fill_filter_mem()
+ bpy.context.window_manager.api_nav_props.old_path = bpy.context.window_manager.api_nav_props.path
+ update_filter()
+ ApiNavigator.generate_global_values()
+ ApiNavigator.doc_text_datablock()
+
+
+class Update(ApiNavigator, bpy.types.Operator):
+ """Update the tree structure"""
+ bl_idname = "api_navigator.update"
+ bl_label = "API Navigator Update"
+
+ def execute(self, context):
+ api_update()
+ return {'FINISHED'}
+
+
+class BackToBpy(ApiNavigator, bpy.types.Operator):
+ """go back to module bpy"""
+ bl_idname = "api_navigator.back_to_bpy"
+ bl_label = "Back to bpy"
+
+ def execute(self, context):
+ fill_filter_mem()
+ if not bpy.context.window_manager.api_nav_props.path:
+ bpy.context.window_manager.api_nav_props.old_path = bpy.context.window_manager.api_nav_props.path = 'bpy'
+ else :
+ bpy.context.window_manager.api_nav_props.old_path = bpy.context.window_manager.api_nav_props.path = 'bpy'
+ update_filter()
+ self.generate_global_values()
+ self.doc_text_datablock()
+ return {'FINISHED'}
+
+
+class Down(ApiNavigator, bpy.types.Operator):
+ """go to this Module"""
+ bl_idname = "api_navigator.down"
+ bl_label = "API Navigator Down"
+ pointed_module = bpy.props.StringProperty(name='Current Module', default='')
+
+
+ def execute(self, context):
+ fill_filter_mem()
+
+ if not bpy.context.window_manager.api_nav_props.path:
+ bpy.context.window_manager.api_nav_props.old_path = bpy.context.window_manager.api_nav_props.path = bpy.context.window_manager.api_nav_props.path + self.pointed_module
+ else :
+ bpy.context.window_manager.api_nav_props.old_path = bpy.context.window_manager.api_nav_props.path = bpy.context.window_manager.api_nav_props.path + '.' + self.pointed_module
+
+ update_filter()
+ self.generate_global_values()
+ self.doc_text_datablock()
+ return {'FINISHED'}
+
+
+class Parent(ApiNavigator, bpy.types.Operator):
+ """go to Parent Module"""
+ bl_idname = "api_navigator.parent"
+ bl_label = "API Navigator Parent"
+
+
+ def execute(self, context):
+ path = bpy.context.window_manager.api_nav_props.path
+
+ if path:
+ fill_filter_mem()
+ bpy.context.window_manager.api_nav_props.old_path = bpy.context.window_manager.api_nav_props.path = parent(bpy.context.window_manager.api_nav_props.path)
+ update_filter()
+ self.generate_global_values()
+ self.doc_text_datablock()
+
+ return {'FINISHED'}
+
+
+class ClearFilter(ApiNavigator, bpy.types.Operator):
+ """Clear the filter"""
+ bl_idname = 'api_navigator.clear_filter'
+ bl_label = 'API Nav clear filter'
+
+ def execute(self, context):
+ bpy.context.window_manager.api_nav_props.filter = ''
+ return {'FINISHED'}
+
+
+class FakeButton(ApiNavigator, bpy.types.Operator):
+ """The list is not displayed completely""" # only serve as an indicator
+ bl_idname = 'api_navigator.fake_button'
+ bl_label = ''
+
+
+class Subscript(ApiNavigator, bpy.types.Operator):
+ """Subscript to this Item"""
+ bl_idname = "api_navigator.subscript"
+ bl_label = "API Navigator Subscript"
+ subscription = bpy.props.StringProperty(name='', default='')
+
+ def execute(self, context):
+ fill_filter_mem()
+ bpy.context.window_manager.api_nav_props.old_path = bpy.context.window_manager.api_nav_props.path = bpy.context.window_manager.api_nav_props.path + '[' + self.subscription + ']'
+ update_filter()
+ self.generate_global_values()
+ self.doc_text_datablock()
+ return {'FINISHED'}
+
+
+class Toggle_doc(ApiNavigator, bpy.types.Operator):
+ """Toggle on or off api_doc_ Text"""
+ bl_idname = 'api_navigator.toggle_doc'
+ bl_label = 'Toggle api_doc_'
+
+
+ def execute(self, context):
+ global last_text
+
+ try :
+ if bpy.context.space_data.text.name != "api_doc_":
+ last_text = bpy.context.space_data.text.name
+ except : pass
+
+ try :
+ text = bpy.data.texts["api_doc_"]
+ bpy.data.texts["api_doc_"].clear()
+ bpy.data.texts.remove(text)
+ except KeyError:
+ self.doc_text_datablock()
+ return {'FINISHED'}
+
+ try :
+ text = bpy.data.texts[last_text]
+ bpy.context.space_data.text = text
+ #line = bpy.ops.text.line_number() # operator doesn't seems to work ???
+ #bpy.ops.text.jump(line=line)
+ return {'FINISHED'}
+ except : pass
+
+ bpy.context.space_data.text = None
+ return {'FINISHED'}
+
+############ UI Panels ############
+
+class OBJECT_PT_api_navigator(ApiNavigator, bpy.types.Panel):
+ bl_idname = 'api_navigator'
+ bl_space_type = "TEXT_EDITOR"
+ bl_region_type = "UI"
+ bl_label = "API Navigator"
+ bl_options = "DEFAULT_CLOSED"
+
+
+ columns = 3
+
+
+ def iterable_draw(self):
+ global tree_level, current_module
+
+ iterable = isiterable(current_module)
+
+ if iterable:
+ iter(current_module)
+ current_type = str(module_type)
+
+ if current_type == "<class 'str'>":
+ return {'FINISHED'}
+
+ col = self.layout
+ filter = bpy.context.window_manager.api_nav_props.filter
+ reduce_to = bpy.context.window_manager.api_nav_props.reduce_to * self.columns
+ pages = bpy.context.window_manager.api_nav_props.pages
+ page_index = reduce_to*pages
+ rank = 0
+ count = 0
+ i = 0
+ filtered = 0
+
+ if iterable == 'a':
+ current_type.__iter__()
+ collection = list(current_module.keys())
+ end = collection.__len__()
+ box = self.layout.box()
+ row = box.row()
+ row.label(text="Items", icon="DOTSDOWN")
+ box = box.box()
+ col = box.column(align=True)
+
+ while count < reduce_to and i < end:
+ mod = collection[i]
+ if filtered < page_index:
+ filtered += 1
+ i += 1
+ continue
+
+ if not (i % self.columns):
+ row = col.row()
+ row.operator('api_navigator.subscript', text=mod, emboss=False).subscription = '"' + mod + '"'
+ filtered += 1
+ i += 1
+ count += 1
+
+ elif iterable == 'b':
+ box = self.layout.box()
+ row = box.row()
+ row.label(text="Item Values", icon="OOPS")
+ box = box.box()
+ col = box.column(align=True)
+ collection = list(current_module)
+ end = collection.__len__()
+
+ while count < reduce_to and i < end:
+ mod = str(collection[i])
+ if filtered < page_index:
+ filtered += 1
+ i += 1
+ continue
+
+ if not (i % self.columns):
+ row = col.row()
+ row.operator('api_navigator.subscript', text=mod, emboss=False).subscription = str(i)
+ filtered += 1
+ i += 1
+ count += 1
+
+ too_long = end > 30
+
+ if too_long:
+ row = col.row()
+ row.prop(bpy.context.window_manager.api_nav_props, 'reduce_to')
+ row.operator('api_navigator.fake_button', text='', emboss=False, icon="DOTSDOWN")
+ row.prop(bpy.context.window_manager.api_nav_props, 'pages', text='Pages')
+
+ return {'FINISHED'}
+
+
+
+
+ def list_draw(self, t, pages, icon, label=None, emboss=False):
+ global tree_level, current_module
+
+ def reduced(too_long):
+
+ if too_long:
+ row = col.row()
+ row.prop(bpy.context.window_manager.api_nav_props, 'reduce_to')
+ row.operator('api_navigator.fake_button', text='', emboss=False, icon="DOTSDOWN")
+ row.prop(bpy.context.window_manager.api_nav_props, 'pages', text='Pages')
+
+ layout = self.layout
+
+ filter = bpy.context.window_manager.api_nav_props.filter
+
+ reduce_to = bpy.context.window_manager.api_nav_props.reduce_to * self.columns
+
+ page_index = reduce_to*pages
+
+
+ len = tree_level[t].__len__()
+ too_long = len > reduce_to
+
+ if len:
+ col = layout.column()
+ box = col.box()
+
+ row = box.row()
+ row.label(text=label, icon=icon)
+
+ if t < 2:
+ box = box.box()
+ row = box.row()
+ col = row.column(align=True)
+ i = 0
+ objects = 0
+ count = 0
+ filtered = 0
+
+ while count < reduce_to and i < len:
+ obj = tree_level[t][i]
+
+ if filter and filter not in obj:
+ i += 1
+ continue
+ elif filtered < page_index:
+ filtered += 1
+ i += 1
+ continue
+
+ if not (objects % self.columns):
+ row = col.row()
+ if t > 1:
+ row.operator("api_navigator.down", text=obj, emboss=emboss).pointed_module = obj
+ elif t == 0:
+ row.operator('api_navigator.subscript', text=str(obj), emboss=False).subscription = '"' + obj + '"'
+ else :
+ row.operator('api_navigator.subscript', text=str(obj), emboss=False).subscription = str(i)
+ filtered += 1
+ i += 1
+ objects += 1
+ count += 1
+
+ reduced(too_long)
+
+ return {'FINISHED'}
+
+
+ def draw(self, context):
+ global tree_level, current_module, module_type, return_report
+
+ api_update(context)
+
+ st = bpy.context.space_data
+
+ ###### layout ######
+ layout = self.layout
+ col = layout.column()
+ layout.label(text='Tree Structure')
+ col = layout.column(align=True)
+ col.prop(bpy.context.window_manager.api_nav_props, 'path', text='')
+ row = col.row()
+ row.operator("api_navigator.parent", text="Parent", icon="BACK")
+ row.operator("api_navigator.back_to_bpy", text='', emboss=True, icon="FILE_PARENT")
+
+ col = layout.column()
+ row = col.row(align=True)
+ row.prop(bpy.context.window_manager.api_nav_props, 'filter')
+ row.operator('api_navigator.clear_filter', text='', icon='PANEL_CLOSE')
+
+ col = layout.column()
+
+ pages = bpy.context.window_manager.api_nav_props.pages
+ self.list_draw(0, pages, "DOTSDOWN", label="Items")
+ self.list_draw(1, pages, "DOTSDOWN", label="Item Values")
+ self.list_draw(2, pages, "PACKAGE", label="Modules", emboss=True)
+ self.list_draw(3, pages, "WORDWRAP_ON", label="Types", emboss=False)
+ self.list_draw(4, pages, "BUTS", label="Properties", emboss=False)
+ self.list_draw(5, pages, "OOPS", label="Structs and Functions")
+ self.list_draw(6, pages, "SCRIPTWIN", label="Methods and Functions")
+ self.list_draw(7, pages, "INFO", label="Attributes")
+ self.list_draw(8, pages, "ERROR", label="Inaccessible")
+
+
+########### Menu functions ###############
+
+
+def register_keymaps():
+ kc = bpy.context.window_manager.keyconfigs['Blender']
+ km = kc.keymaps.get("Text")
+ if km is None:
+ km = kc.keymaps.new(name="Text")
+ kmi = km.keymap_items.new('api_navigator.toggle_doc', 'ESC', 'PRESS')
+
+
+def unregister_keymaps():
+ km = bpy.data.window_managers["WinMan"].keyconfigs.default.keymaps["Text"]
+ kmi = km.keymap_items["api_navigator.toggle_doc"]
+ km.keymap_items.remove(kmi)
+
+
+def register():
+ from bpy.props import StringProperty, IntProperty, PointerProperty
+
+ class ApiNavProps(bpy.types.PropertyGroup):
+ """
+ Fake module like class.
+
+ bpy.context.window_manager.api_nav_props
+
+ """
+ path = StringProperty(name='path',
+ description='Enter bpy.ops.api_navigator to see the documentation',
+ default='bpy')
+ old_path = StringProperty(name='old_path', default='')
+ filter = StringProperty(name='filter',
+ description='Filter the resulting modules', default='')
+ reduce_to = IntProperty(name='Reduce to ',
+ description='Display a maximum number of x entries by pages',
+ default=10, min=1)
+ pages = IntProperty(name='Pages',
+ description='Display a Page', default=0, min=0)
+
+ bpy.utils.register_module(__name__)
+
+ bpy.types.WindowManager.api_nav_props = PointerProperty(
+ type=ApiNavProps, name='API Nav Props', description='')
+
+ register_keymaps()
+ #print(get_tree_level())
+
+
+def unregister():
+ unregister_keymaps()
+ del bpy.types.WindowManager.api_nav_props
+
+ bpy.utils.unregister_module(__name__)
+
+
+if __name__ == '__main__':
+ register()