1 # ##### BEGIN GPL LICENSE BLOCK #####
3 # This program is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU General Public License
5 # as published by the Free Software Foundation; either version 2
6 # of the License, or (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software Foundation,
15 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 # ##### END GPL LICENCE BLOCK #####
20 "name": "API Navigator",
21 "author": "Dany Lebel (Axon_D)",
23 "blender": (2, 57, 0),
24 "location": "Text Editor > Properties > API Navigator Panel",
25 "description": "Allows exploration of the python api via the user interface",
27 "wiki_url": "https://wiki.blender.org/index.php/Extensions:2.6/Py/"
28 "Scripts/Text_Editor/API_Navigator",
29 "category": "Development",
33 You can browse through the tree structure of the api. Each child object appears in a list
34 that tries to be representative of its type. These lists are :
36 * Items (for an iterable object)
37 * Item Values (for an iterable object wich only supports index)
41 * Structs and Functions
42 * Methods and Functions
44 * Inaccessible (some objects may be listed but inaccessible)
46 The lists can be filtered to help searching in the tree. Just enter the text in the
47 filter section. It is also possible to explore other modules. Go the the root and select
48 it in the list of available modules. It will be imported dynamically.
50 In the text section, some informations are displayed. The type of the object,
51 what it returns, and its docstring. We could hope that these docstrings will be as
52 descriptive as possible. This text data block named api_doc_ can be toggled on and off
53 with the Escape key. (but a bug prevent the keymap to register correctly at start)
58 from bpy
.types
import (
63 from bpy
.props
import (
69 from console
.complete_import
import get_root_modules
72 # ########## Global Variables ##########
74 last_text
= None # last text data block
75 root_module
= None # root module of the tree
76 root_m_path
= '' # root_module + path as a string
77 current_module
= None # the object itself in the tree structure
78 tree_level
= None # the list of objects from the current_module
81 def init_tree_level():
83 tree_level
= [[], [], [], [], [], [], [], [], []]
88 api_doc_
= '' # the documentation formated for the API Navigator
89 module_type
= None # the type of current_module
90 return_report
= '' # what current_module returns
91 filter_mem
= {} # remember last filters entered for each path
92 too_long
= False # is tree_level list too long to display in a panel?
95 # ########## Functions ############
96 def get_root_module(path
):
99 root
= path
[:path
.find('.')]
103 root_module
= __import__(root
)
108 def evaluate(module
):
109 global root_module
, tree_level
, root_m_path
112 len_name
= root_module
.__name
__.__len
__()
113 root_m_path
= 'root_module' + module
[len_name
:]
114 current_module
= eval(root_m_path
)
115 return current_module
121 def get_tree_level():
123 path
= bpy
.context
.window_manager
.api_nav_props
.path
126 global current_module
, root_m_path
128 itm
, val
, mod
, typ
, props
, struct
, met
, att
, bug
= [], [], [], [], [], [], [], [], []
129 iterable
= isiterable(current_module
)
132 current_type
= str(module_type
)
133 if current_type
!= "<class 'str'>":
135 itm
= list(current_module
.keys())
137 val
= list(current_module
)
139 val
= list(current_module
)
141 for i
in dir(current_module
):
143 t
= str(type(eval(root_m_path
+ '.' + i
)))
144 except (AttributeError, SyntaxError):
148 if t
== "<class 'module'>":
150 elif t
[0:16] == "<class 'bpy_prop":
152 elif t
[8:11] == 'bpy':
154 elif t
== "<class 'builtin_function_or_method'>":
156 elif t
== "<class 'type'>":
161 return [itm
, val
, mod
, typ
, props
, struct
, met
, att
, bug
]
164 return [[], [], [i
for i
in get_root_modules()], [], [], [], [], [], []]
169 """Returns the parent path"""
171 if parent
[-1] == ']' and '[' in parent
:
172 while parent
[-1] != '[':
175 while parent
[-1] != '.':
184 """Update the filter according to the current path"""
188 bpy
.context
.window_manager
.api_nav_props
.filters
= filter_mem
[
189 bpy
.context
.window_manager
.api_nav_props
.path
192 bpy
.context
.window_manager
.api_nav_props
.filters
= ''
205 except (AttributeError, TypeError):
209 def fill_filter_mem():
212 filters
= bpy
.context
.window_manager
.api_nav_props
.filters
214 filter_mem
[bpy
.context
.window_manager
.api_nav_props
.old_path
] = \
215 bpy
.context
.window_manager
.api_nav_props
.filters
217 filter_mem
.pop(bpy
.context
.window_manager
.api_nav_props
.old_path
, None)
220 # #### API Navigator parent class ######
221 class ApiNavigator():
222 """Parent class for API Navigator"""
225 def generate_global_values():
226 """Populate the level attributes to display the panel buttons and the documentation"""
227 global tree_level
, current_module
, module_type
, return_report
, last_text
229 text
= bpy
.context
.space_data
.text
231 if text
.name
!= 'api_doc_':
232 last_text
= bpy
.context
.space_data
.text
.name
233 elif bpy
.data
.texts
.__len
__() < 2:
237 bpy
.context
.window_manager
.api_nav_props
.pages
= 0
238 get_root_module(bpy
.context
.window_manager
.api_nav_props
.path
)
239 current_module
= evaluate(bpy
.context
.window_manager
.api_nav_props
.path
)
240 module_type
= str(type(current_module
))
241 return_report
= str(current_module
)
242 tree_level
= get_tree_level()
244 if tree_level
.__len
__() > 30:
250 ApiNavigator
.generate_api_doc()
254 def generate_api_doc():
255 """Format the doc string for API Navigator"""
256 global current_module
, api_doc_
, return_report
, module_type
258 path
= bpy
.context
.window_manager
.api_nav_props
.path
259 line
= "-" * (path
.__len
__() + 2)
260 header
= """\n\n\n\t\t%s\n\t %s\n\
261 _____________________________________________\n\
267 _____________________________________________\n\
271 """ % (path
, line
, module_type
, return_report
)
273 _____________________________________________\n\
277 #############################################\n\
279 # Escape to toggle text #\n\
280 # (F8 to reload modules if doesn't work) #\n\
281 #############################################"
282 doc
= current_module
.__doc
__
283 api_doc_
= header
+ str(doc
) + footer
287 def doc_text_datablock():
288 """Create the text databloc or overwrite it if it already exist"""
291 space_data
= bpy
.context
.space_data
294 doc_text
= bpy
.data
.texts
['api_doc_']
295 space_data
.text
= doc_text
298 bpy
.data
.texts
.new(name
='api_doc_')
299 doc_text
= bpy
.data
.texts
['api_doc_']
300 space_data
.text
= doc_text
302 doc_text
.write(text
=api_doc_
)
306 # ######### Operators ###########
307 def api_update(context
):
308 if bpy
.context
.window_manager
.api_nav_props
.path
!= bpy
.context
.window_manager
.api_nav_props
.old_path
:
310 bpy
.context
.window_manager
.api_nav_props
.old_path
= bpy
.context
.window_manager
.api_nav_props
.path
312 ApiNavigator
.generate_global_values()
313 ApiNavigator
.doc_text_datablock()
316 class Update(ApiNavigator
, Operator
):
317 """Update the tree structure"""
318 bl_idname
= "api_navigator.update"
319 bl_label
= "API Navigator Update"
321 def execute(self
, context
):
326 class BackToBpy(ApiNavigator
, Operator
):
327 """Go back to module bpy"""
328 bl_idname
= "api_navigator.back_to_bpy"
329 bl_label
= "Back to bpy"
331 def execute(self
, context
):
333 if not bpy
.context
.window_manager
.api_nav_props
.path
:
334 bpy
.context
.window_manager
.api_nav_props
.old_path
= \
335 bpy
.context
.window_manager
.api_nav_props
.path
= 'bpy'
337 bpy
.context
.window_manager
.api_nav_props
.old_path
= \
338 bpy
.context
.window_manager
.api_nav_props
.path
= 'bpy'
340 self
.generate_global_values()
341 self
.doc_text_datablock()
345 class Down(ApiNavigator
, Operator
):
346 """Go to this Module"""
347 bl_idname
= "api_navigator.down"
348 bl_label
= "API Navigator Down"
350 pointed_module
= StringProperty(
351 name
="Current Module",
355 def execute(self
, context
):
358 if not bpy
.context
.window_manager
.api_nav_props
.path
:
359 bpy
.context
.window_manager
.api_nav_props
.old_path
= \
360 bpy
.context
.window_manager
.api_nav_props
.path
= \
361 bpy
.context
.window_manager
.api_nav_props
.path
+ self
.pointed_module
363 bpy
.context
.window_manager
.api_nav_props
.old_path
= \
364 bpy
.context
.window_manager
.api_nav_props
.path
= \
365 bpy
.context
.window_manager
.api_nav_props
.path
+ '.' + self
.pointed_module
368 self
.generate_global_values()
369 self
.doc_text_datablock()
373 class Parent(ApiNavigator
, Operator
):
374 """Go to Parent Module"""
375 bl_idname
= "api_navigator.parent"
376 bl_label
= "API Navigator Parent"
378 def execute(self
, context
):
379 path
= bpy
.context
.window_manager
.api_nav_props
.path
383 bpy
.context
.window_manager
.api_nav_props
.old_path
= \
384 bpy
.context
.window_manager
.api_nav_props
.path
= \
385 parent(bpy
.context
.window_manager
.api_nav_props
.path
)
387 self
.generate_global_values()
388 self
.doc_text_datablock()
393 class ClearFilter(ApiNavigator
, Operator
):
394 """Clear the filter"""
395 bl_idname
= "api_navigator.clear_filter"
396 bl_label
= "API Nav clear filter"
398 def execute(self
, context
):
399 bpy
.context
.window_manager
.api_nav_props
.filters
= ''
403 class Subscript(ApiNavigator
, Operator
):
404 """Subscript to this Item"""
405 bl_idname
= "api_navigator.subscript"
406 bl_label
= "API Navigator Subscript"
408 subscription
= StringProperty(
413 def execute(self
, context
):
415 bpy
.context
.window_manager
.api_nav_props
.old_path
= \
416 bpy
.context
.window_manager
.api_nav_props
.path
= \
417 bpy
.context
.window_manager
.api_nav_props
.path
+ '[' + self
.subscription
+ ']'
419 self
.generate_global_values()
420 self
.doc_text_datablock()
424 class Toggle_doc(ApiNavigator
, Operator
):
425 """Toggle on or off api_doc_ Text"""
426 bl_idname
= "api_navigator.toggle_doc"
427 bl_label
= "Toggle api_doc_"
429 def execute(self
, context
):
433 if bpy
.context
.space_data
.text
.name
!= "api_doc_":
434 last_text
= bpy
.context
.space_data
.text
.name
439 text
= bpy
.data
.texts
["api_doc_"]
440 bpy
.data
.texts
["api_doc_"].clear()
441 bpy
.data
.texts
.remove(text
)
443 self
.doc_text_datablock()
447 text
= bpy
.data
.texts
[last_text
]
448 bpy
.context
.space_data
.text
= text
453 bpy
.context
.space_data
.text
= None
458 # ######### UI Panels ############
459 class OBJECT_PT_api_navigator(ApiNavigator
, Panel
):
460 bl_idname
= 'OBJECT_PT_api_navigator'
461 bl_space_type
= "TEXT_EDITOR"
462 bl_region_type
= "UI"
463 bl_label
= "API Navigator"
464 bl_options
= {'DEFAULT_CLOSED'}
468 def iterable_draw(self
):
469 # Note: Currently unused method
470 global tree_level
, current_module
472 iterable
= isiterable(current_module
)
476 current_type
= str(module_type
)
478 if current_type
== "<class 'str'>":
482 reduce_to
= bpy
.context
.window_manager
.api_nav_props
.reduce_to
* self
.columns
483 pages
= bpy
.context
.window_manager
.api_nav_props
.pages
484 page_index
= reduce_to
* pages
490 current_type
.__iter
__()
491 collection
= list(current_module
.keys())
492 end
= collection
.__len
__()
493 box
= self
.layout
.box()
495 row
.label(text
="Items", icon
="DOTSDOWN")
497 col
= box
.column(align
=True)
499 while count
< reduce_to
and i
< end
:
501 if filtered
< page_index
:
506 if not (i
% self
.columns
):
508 row
.operator("api_navigator.subscript",
509 text
=mod
, emboss
=True).subscription
= '"' + mod
+ '"'
514 elif iterable
== 'b':
515 box
= self
.layout
.box()
517 row
.label(text
="Item Values", icon
="OOPS")
519 col
= box
.column(align
=True)
520 collection
= list(current_module
)
521 end
= collection
.__len
__()
523 while count
< reduce_to
and i
< end
:
524 mod
= str(collection
[i
])
525 if filtered
< page_index
:
530 if not (i
% self
.columns
):
532 row
.operator("api_navigator.subscript",
533 text
=mod
, emboss
=True).subscription
= str(i
)
542 row
.prop(bpy
.context
.window_manager
.api_nav_props
, "reduce_to")
543 row
.label(text
="", icon
="DOTSDOWN")
544 row
.prop(bpy
.context
.window_manager
.api_nav_props
, "pages", text
="Pages")
548 def list_draw(self
, t
, pages
, icon
, label
=None, emboss
=True):
549 global tree_level
, current_module
551 def reduced(row
, too_long
):
553 sub_row
= row
.row(align
=True)
554 sub_row
.prop(bpy
.context
.window_manager
.api_nav_props
, "reduce_to")
555 sub_row
.prop(bpy
.context
.window_manager
.api_nav_props
, "pages", text
="Pages")
558 filters
= bpy
.context
.window_manager
.api_nav_props
.filters
559 reduce_to
= bpy
.context
.window_manager
.api_nav_props
.reduce_to
* self
.columns
560 page_index
= reduce_to
* pages
561 show_panel_elements
= bpy
.context
.window_manager
.api_nav_props
.panel_toggle
[t
] if \
562 0 <= t
< 10 else True
564 lenght
= tree_level
[t
].__len
__()
565 too_long
= lenght
> reduce_to
568 col
= layout
.column()
571 title_box
= box
.row(align
=True)
572 title_box
.prop(bpy
.context
.window_manager
.api_nav_props
,
573 "panel_toggle", text
="", index
=t
if 0 <= t
< 10 else 0)
574 title_box
.label(text
=label
, icon
=icon
)
575 reduced(box
, too_long
)
577 if show_panel_elements
:
581 col
= row
.column(align
=True)
583 objects
, count
, filtered
= 0, 0, 0
585 while count
< reduce_to
and i
< lenght
:
586 obj
= tree_level
[t
][i
]
588 if filters
and filters
not in obj
:
591 elif filtered
< page_index
:
596 if not (objects
% self
.columns
):
597 row
= col
.row(align
=True)
599 row
.operator("api_navigator.down",
600 text
=obj
, emboss
=emboss
).pointed_module
= obj
602 row
.operator("api_navigator.subscript",
603 text
=str(obj
), emboss
=emboss
).subscription
= '"' + obj
+ '"'
605 row
.operator("api_navigator.subscript",
606 text
=str(obj
), emboss
=emboss
).subscription
= str(i
)
614 def draw(self
, context
):
615 global tree_level
, current_module
, module_type
, return_report
620 layout
.label(text
="Tree Structure:")
622 col
= layout
.column(align
=True)
623 col
.prop(bpy
.context
.window_manager
.api_nav_props
, 'path', text
='')
625 row
= col
.row(align
=True)
626 row
.operator("api_navigator.parent", text
="Parent", icon
="BACK")
627 row
.operator("api_navigator.back_to_bpy", text
='', emboss
=True, icon
="FILE_PARENT")
629 col
= layout
.column()
630 row
= col
.row(align
=True)
631 row
.prop(bpy
.context
.window_manager
.api_nav_props
, "filters", text
="Filter")
632 row
.operator("api_navigator.clear_filter", text
="", icon
="PANEL_CLOSE")
634 col
= layout
.column()
636 pages
= bpy
.context
.window_manager
.api_nav_props
.pages
637 self
.list_draw(0, pages
, "DOTSDOWN", label
="Items")
638 self
.list_draw(1, pages
, "DOTSDOWN", label
="Item Values")
639 self
.list_draw(2, pages
, "PACKAGE", label
="Modules", emboss
=True)
640 self
.list_draw(3, pages
, "WORDWRAP_ON", label
="Types", emboss
=True)
641 self
.list_draw(4, pages
, "BUTS", label
="Properties", emboss
=True)
642 self
.list_draw(5, pages
, "OOPS", label
="Structs and Functions")
643 self
.list_draw(6, pages
, "SCRIPTWIN", label
="Methods and Functions")
644 self
.list_draw(7, pages
, "INFO", label
="Attributes")
645 self
.list_draw(8, pages
, "ERROR", label
="Inaccessible")
648 # ###### Properties #######
649 class ApiNavProps(PropertyGroup
):
651 Fake module like class.
653 bpy.context.window_manager.api_nav_props
655 path
= StringProperty(
657 description
="Enter bpy.ops.api_navigator to see the documentation",
660 old_path
= StringProperty(
664 filters
= StringProperty(
666 description
="Filter the resulting modules",
669 reduce_to
= IntProperty(
671 description
="Display a maximum number of x entries by pages",
677 description
="Display a Page",
681 panel_toggle
= BoolVectorProperty(
683 description
="Expand/Collapse UI elements",
689 # ######## Register #########
690 def register_keymaps():
691 kc
= bpy
.context
.window_manager
.keyconfigs
.addon
693 km
= kc
.keymaps
.new(name
="Text", space_type
='TEXT_EDITOR')
694 km
.keymap_items
.new('api_navigator.toggle_doc', 'ESC', 'PRESS')
697 def unregister_keymaps():
698 kc
= bpy
.context
.window_manager
.keyconfigs
.addon
700 km
= kc
.keymaps
["Text"]
701 kmi
= km
.keymap_items
["api_navigator.toggle_doc"]
702 km
.keymap_items
.remove(kmi
)
706 bpy
.utils
.register_module(__name__
)
707 bpy
.types
.WindowManager
.api_nav_props
= PointerProperty(
709 name
="API Nav Props",
717 del bpy
.types
.WindowManager
.api_nav_props
719 bpy
.utils
.unregister_module(__name__
)
722 if __name__
== '__main__':