1 # development_api_navigator.py
3 # ***** BEGIN GPL LICENSE BLOCK *****
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; either version 2
9 # of the License, or (at your option) any later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software Foundation,
18 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 # ***** END GPL LICENCE BLOCK *****
23 "name": "API Navigator",
24 "author": "Dany Lebel (Axon_D)",
26 "blender": (2, 57, 0),
27 "location": "Text Editor > Properties > API Navigator Panel",
28 "description": "Allows exploration of the python api via the user interface",
30 "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/Py/"
31 "Scripts/Text_Editor/API_Navigator",
32 "tracker_url": "http://projects.blender.org/tracker/index.php?"\
33 "func=detail&aid=24982",
34 "category": "Development"}
37 You can browse through the tree structure of the api. Each child object appears in a list
38 that tries to be representative of its type. These lists are :
40 * Items (for an iterable object)
41 * Item Values (for an iterable object wich only supports index)
45 * Structs and Functions
46 * Methods and Functions
48 * Inaccessible (some objects may be listed but inaccessible)
50 The lists can be filtered to help searching in the tree. Just enter the text in the
51 filter section. It is also possible to explore other modules. Go the the root and select
52 it in the list of available modules. It will be imported dynamically.
54 In the text section, some informations are displayed. The type of the object,
55 what it returns, and its docstring. We could hope that these docstrings will be as
56 descriptive as possible. This text data block named api_doc_ can be toggled on and off
57 with the Escape key. (but a bug prevent the keymap to register correctly at start)
62 from console
.complete_import
import get_root_modules
65 ############ Global Variables ############
67 last_text
= None # last text data block
69 root_module
= None # root module of the tree
71 root_m_path
= '' # root_module + path as a string
73 current_module
= None # the object itself in the tree structure
76 tree_level
= None # the list of objects from the current_module
78 def init_tree_level():
80 tree_level
= [[],[],[],[],[],[],[], [], []]
85 api_doc_
= '' # the documentation formated for the API Navigator
87 module_type
= None # the type of current_module
89 return_report
= '' # what current_module returns
91 filter_mem
= {} # remember last filters entered for each path
93 too_long
= False # is tree_level list too long to display in a panel?
96 ############ Functions ############
98 def get_root_module(path
):
99 #print('get_root_module')
102 root
= path
[:path
.find('.')]
106 root_module
= __import__(root
)
111 def evaluate(module
):
113 global root_module
, tree_level
, root_m_path
115 # path = bpy.context.window_manager.api_nav_props.path
117 len_name
= root_module
.__name
__.__len
__()
118 root_m_path
= 'root_module' + module
[len_name
:]
119 current_module
= eval(root_m_path
)
120 return current_module
126 def get_tree_level():
127 #print('get_tree_level')
129 path
= bpy
.context
.window_manager
.api_nav_props
.path
132 #print('object_list')
133 global current_module
, root_m_path
135 itm
, val
, mod
, typ
, props
, struct
, met
, att
, bug
= [], [], [], [], [], [], [], [], []
136 iterable
= isiterable(current_module
)
139 current_type
= str(module_type
)
140 if current_type
!= "<class 'str'>":
143 #current_type.__iter__()
144 itm
= list(current_module
.keys())
146 val
= list(current_module
)
148 val
= list(current_module
)
150 for i
in dir(current_module
):
152 t
= str(type(eval(root_m_path
+ '.' + i
)))
153 except (AttributeError, SyntaxError):
158 if t
== "<class 'module'>":
160 elif t
[0:16] == "<class 'bpy_prop":
162 elif t
[8:11] == 'bpy':
164 elif t
== "<class 'builtin_function_or_method'>":
166 elif t
== "<class 'type'>":
171 return [itm
, val
, mod
, typ
, props
, struct
, met
, att
, bug
]
174 return [[], [], [i
for i
in get_root_modules()], [], [], [], [], [], []]
179 """Returns the parent path"""
183 if parent
[-1] == ']' and '[' in parent
:
184 while parent
[-1] != '[':
187 while parent
[-1] != '.':
196 """Update the filter according to the current path"""
200 bpy
.context
.window_manager
.api_nav_props
.filter = filter_mem
[bpy
.context
.window_manager
.api_nav_props
.path
]
202 bpy
.context
.window_manager
.api_nav_props
.filter = ''
216 except (AttributeError, TypeError):
220 def fill_filter_mem():
223 filter = bpy
.context
.window_manager
.api_nav_props
.filter
225 filter_mem
[bpy
.context
.window_manager
.api_nav_props
.old_path
] = bpy
.context
.window_manager
.api_nav_props
.filter
227 filter_mem
.pop(bpy
.context
.window_manager
.api_nav_props
.old_path
, None)
230 ###### API Navigator parent class #######
232 class ApiNavigator():
233 """Parent class for API Navigator"""
236 def generate_global_values():
237 """Populate the level attributes to display the panel buttons and the documentation"""
238 global tree_level
, current_module
, module_type
, return_report
, last_text
240 text
= bpy
.context
.space_data
.text
242 if text
.name
!= 'api_doc_':
243 last_text
= bpy
.context
.space_data
.text
.name
244 elif bpy
.data
.texts
.__len
__() < 2:
248 bpy
.context
.window_manager
.api_nav_props
.pages
= 0
249 get_root_module(bpy
.context
.window_manager
.api_nav_props
.path
)
250 current_module
= evaluate(bpy
.context
.window_manager
.api_nav_props
.path
)
251 module_type
= str(type(current_module
))
252 return_report
= str(current_module
)
253 tree_level
= get_tree_level()
255 if tree_level
.__len
__() > 30:
261 ApiNavigator
.generate_api_doc()
265 def generate_api_doc():
266 """Format the doc string for API Navigator"""
267 global current_module
, api_doc_
, return_report
, module_type
269 path
= bpy
.context
.window_manager
.api_nav_props
.path
270 line
= "-" * (path
.__len
__()+2)
271 header
= """\n\n\n\t\t%s\n\t %s\n\
272 _____________________________________________\n\
278 _____________________________________________\n\
282 """ % (path
, line
, module_type
, return_report
)
284 _____________________________________________\n\
288 #############################################\n\
290 # Escape to toggle text #\n\
291 # (F8 to reload modules if doesn't work) #\n\
292 #############################################"
293 doc
= current_module
.__doc
__
294 api_doc_
= header
+ str(doc
) + footer
298 def doc_text_datablock():
299 """Create the text databloc or overwrite it if it already exist"""
302 space_data
= bpy
.context
.space_data
305 doc_text
= bpy
.data
.texts
['api_doc_']
306 space_data
.text
= doc_text
309 bpy
.data
.texts
.new(name
='api_doc_')
310 doc_text
= bpy
.data
.texts
['api_doc_']
311 space_data
.text
= doc_text
313 doc_text
.write(text
=api_doc_
)
318 ############ Operators ############
319 def api_update(context
):
320 if bpy
.context
.window_manager
.api_nav_props
.path
!= bpy
.context
.window_manager
.api_nav_props
.old_path
:
322 bpy
.context
.window_manager
.api_nav_props
.old_path
= bpy
.context
.window_manager
.api_nav_props
.path
324 ApiNavigator
.generate_global_values()
325 ApiNavigator
.doc_text_datablock()
328 class Update(ApiNavigator
, bpy
.types
.Operator
):
329 """Update the tree structure"""
330 bl_idname
= "api_navigator.update"
331 bl_label
= "API Navigator Update"
333 def execute(self
, context
):
338 class BackToBpy(ApiNavigator
, bpy
.types
.Operator
):
339 """go back to module bpy"""
340 bl_idname
= "api_navigator.back_to_bpy"
341 bl_label
= "Back to bpy"
343 def execute(self
, context
):
345 if not bpy
.context
.window_manager
.api_nav_props
.path
:
346 bpy
.context
.window_manager
.api_nav_props
.old_path
= bpy
.context
.window_manager
.api_nav_props
.path
= 'bpy'
348 bpy
.context
.window_manager
.api_nav_props
.old_path
= bpy
.context
.window_manager
.api_nav_props
.path
= 'bpy'
350 self
.generate_global_values()
351 self
.doc_text_datablock()
355 class Down(ApiNavigator
, bpy
.types
.Operator
):
356 """go to this Module"""
357 bl_idname
= "api_navigator.down"
358 bl_label
= "API Navigator Down"
359 pointed_module
= bpy
.props
.StringProperty(name
='Current Module', default
='')
362 def execute(self
, context
):
365 if not bpy
.context
.window_manager
.api_nav_props
.path
:
366 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
368 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
371 self
.generate_global_values()
372 self
.doc_text_datablock()
376 class Parent(ApiNavigator
, bpy
.types
.Operator
):
377 """go to Parent Module"""
378 bl_idname
= "api_navigator.parent"
379 bl_label
= "API Navigator Parent"
382 def execute(self
, context
):
383 path
= bpy
.context
.window_manager
.api_nav_props
.path
387 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
)
389 self
.generate_global_values()
390 self
.doc_text_datablock()
395 class ClearFilter(ApiNavigator
, bpy
.types
.Operator
):
396 """Clear the filter"""
397 bl_idname
= 'api_navigator.clear_filter'
398 bl_label
= 'API Nav clear filter'
400 def execute(self
, context
):
401 bpy
.context
.window_manager
.api_nav_props
.filter = ''
405 class FakeButton(ApiNavigator
, bpy
.types
.Operator
):
406 """The list is not displayed completely""" # only serve as an indicator
407 bl_idname
= 'api_navigator.fake_button'
411 class Subscript(ApiNavigator
, bpy
.types
.Operator
):
412 """Subscript to this Item"""
413 bl_idname
= "api_navigator.subscript"
414 bl_label
= "API Navigator Subscript"
415 subscription
= bpy
.props
.StringProperty(name
='', default
='')
417 def execute(self
, context
):
419 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
+ ']'
421 self
.generate_global_values()
422 self
.doc_text_datablock()
426 class Toggle_doc(ApiNavigator
, bpy
.types
.Operator
):
427 """Toggle on or off api_doc_ Text"""
428 bl_idname
= 'api_navigator.toggle_doc'
429 bl_label
= 'Toggle api_doc_'
432 def execute(self
, context
):
436 if bpy
.context
.space_data
.text
.name
!= "api_doc_":
437 last_text
= bpy
.context
.space_data
.text
.name
441 text
= bpy
.data
.texts
["api_doc_"]
442 bpy
.data
.texts
["api_doc_"].clear()
443 bpy
.data
.texts
.remove(text
)
445 self
.doc_text_datablock()
449 text
= bpy
.data
.texts
[last_text
]
450 bpy
.context
.space_data
.text
= text
451 #line = bpy.ops.text.line_number() # operator doesn't seems to work ???
452 #bpy.ops.text.jump(line=line)
456 bpy
.context
.space_data
.text
= None
459 ############ UI Panels ############
461 class OBJECT_PT_api_navigator(ApiNavigator
, bpy
.types
.Panel
):
462 bl_idname
= 'api_navigator'
463 bl_space_type
= "TEXT_EDITOR"
464 bl_region_type
= "UI"
465 bl_label
= "API Navigator"
466 bl_options
= {'DEFAULT_CLOSED'}
472 def iterable_draw(self
):
473 global tree_level
, current_module
475 iterable
= isiterable(current_module
)
479 current_type
= str(module_type
)
481 if current_type
== "<class 'str'>":
485 # filter = bpy.context.window_manager.api_nav_props.filter # UNUSED
486 reduce_to
= bpy
.context
.window_manager
.api_nav_props
.reduce_to
* self
.columns
487 pages
= bpy
.context
.window_manager
.api_nav_props
.pages
488 page_index
= reduce_to
*pages
495 current_type
.__iter
__()
496 collection
= list(current_module
.keys())
497 end
= collection
.__len
__()
498 box
= self
.layout
.box()
500 row
.label(text
="Items", icon
="DOTSDOWN")
502 col
= box
.column(align
=True)
504 while count
< reduce_to
and i
< end
:
506 if filtered
< page_index
:
511 if not (i
% self
.columns
):
513 row
.operator('api_navigator.subscript', text
=mod
, emboss
=False).subscription
= '"' + mod
+ '"'
518 elif iterable
== 'b':
519 box
= self
.layout
.box()
521 row
.label(text
="Item Values", icon
="OOPS")
523 col
= box
.column(align
=True)
524 collection
= list(current_module
)
525 end
= collection
.__len
__()
527 while count
< reduce_to
and i
< end
:
528 mod
= str(collection
[i
])
529 if filtered
< page_index
:
534 if not (i
% self
.columns
):
536 row
.operator('api_navigator.subscript', text
=mod
, emboss
=False).subscription
= str(i
)
545 row
.prop(bpy
.context
.window_manager
.api_nav_props
, 'reduce_to')
546 row
.operator('api_navigator.fake_button', text
='', emboss
=False, icon
="DOTSDOWN")
547 row
.prop(bpy
.context
.window_manager
.api_nav_props
, 'pages', text
='Pages')
554 def list_draw(self
, t
, pages
, icon
, label
=None, emboss
=False):
555 global tree_level
, current_module
557 def reduced(too_long
):
561 row
.prop(bpy
.context
.window_manager
.api_nav_props
, 'reduce_to')
562 row
.operator('api_navigator.fake_button', text
='', emboss
=False, icon
="DOTSDOWN")
563 row
.prop(bpy
.context
.window_manager
.api_nav_props
, 'pages', text
='Pages')
567 filter = bpy
.context
.window_manager
.api_nav_props
.filter
569 reduce_to
= bpy
.context
.window_manager
.api_nav_props
.reduce_to
* self
.columns
571 page_index
= reduce_to
*pages
574 len = tree_level
[t
].__len
__()
575 too_long
= len > reduce_to
578 col
= layout
.column()
582 row
.label(text
=label
, icon
=icon
)
587 col
= row
.column(align
=True)
593 while count
< reduce_to
and i
< len:
594 obj
= tree_level
[t
][i
]
596 if filter and filter not in obj
:
599 elif filtered
< page_index
:
604 if not (objects
% self
.columns
):
607 row
.operator("api_navigator.down", text
=obj
, emboss
=emboss
).pointed_module
= obj
609 row
.operator('api_navigator.subscript', text
=str(obj
), emboss
=False).subscription
= '"' + obj
+ '"'
611 row
.operator('api_navigator.subscript', text
=str(obj
), emboss
=False).subscription
= str(i
)
622 def draw(self
, context
):
623 global tree_level
, current_module
, module_type
, return_report
630 layout
.label(text
="Tree Structure:")
631 col
= layout
.column(align
=True)
633 col
.prop(bpy
.context
.window_manager
.api_nav_props
, 'path', text
='')
635 row
.operator("api_navigator.parent", text
="Parent", icon
="BACK")
636 row
.operator("api_navigator.back_to_bpy", text
='', emboss
=True, icon
="FILE_PARENT")
638 col
= layout
.column()
639 row
= col
.row(align
=True)
640 row
.prop(bpy
.context
.window_manager
.api_nav_props
, 'filter')
641 row
.operator('api_navigator.clear_filter', text
='', icon
='PANEL_CLOSE')
643 col
= layout
.column()
645 pages
= bpy
.context
.window_manager
.api_nav_props
.pages
646 self
.list_draw(0, pages
, "DOTSDOWN", label
="Items")
647 self
.list_draw(1, pages
, "DOTSDOWN", label
="Item Values")
648 self
.list_draw(2, pages
, "PACKAGE", label
="Modules", emboss
=True)
649 self
.list_draw(3, pages
, "WORDWRAP_ON", label
="Types", emboss
=False)
650 self
.list_draw(4, pages
, "BUTS", label
="Properties", emboss
=False)
651 self
.list_draw(5, pages
, "OOPS", label
="Structs and Functions")
652 self
.list_draw(6, pages
, "SCRIPTWIN", label
="Methods and Functions")
653 self
.list_draw(7, pages
, "INFO", label
="Attributes")
654 self
.list_draw(8, pages
, "ERROR", label
="Inaccessible")
657 ########### Menu functions ###############
660 def register_keymaps():
661 kc
= bpy
.context
.window_manager
.keyconfigs
.addon
663 km
= kc
.keymaps
.new(name
="Text", space_type
='TEXT_EDITOR')
664 km
.keymap_items
.new('api_navigator.toggle_doc', 'ESC', 'PRESS')
667 def unregister_keymaps():
668 kc
= bpy
.context
.window_manager
.keyconfigs
.addon
670 km
= kc
.keymaps
["Text"]
671 kmi
= km
.keymap_items
["api_navigator.toggle_doc"]
672 km
.keymap_items
.remove(kmi
)
676 from bpy
.props
import StringProperty
, IntProperty
, PointerProperty
678 class ApiNavProps(bpy
.types
.PropertyGroup
):
680 Fake module like class.
682 bpy.context.window_manager.api_nav_props
685 path
= StringProperty(name
='path',
686 description
='Enter bpy.ops.api_navigator to see the documentation',
688 old_path
= StringProperty(name
='old_path', default
='')
689 filter = StringProperty(name
='filter',
690 description
='Filter the resulting modules', default
='')
691 reduce_to
= IntProperty(name
='Reduce to ',
692 description
='Display a maximum number of x entries by pages',
694 pages
= IntProperty(name
='Pages',
695 description
='Display a Page', default
=0, min=0)
697 bpy
.utils
.register_module(__name__
)
699 bpy
.types
.WindowManager
.api_nav_props
= PointerProperty(
700 type=ApiNavProps
, name
='API Nav Props', description
='')
703 #print(get_tree_level())
708 del bpy
.types
.WindowManager
.api_nav_props
710 bpy
.utils
.unregister_module(__name__
)
713 if __name__
== '__main__':