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 "category": "Development",
36 You can browse through the tree structure of the api. Each child object appears in a list
37 that tries to be representative of its type. These lists are :
39 * Items (for an iterable object)
40 * Item Values (for an iterable object wich only supports index)
44 * Structs and Functions
45 * Methods and Functions
47 * Inaccessible (some objects may be listed but inaccessible)
49 The lists can be filtered to help searching in the tree. Just enter the text in the
50 filter section. It is also possible to explore other modules. Go the the root and select
51 it in the list of available modules. It will be imported dynamically.
53 In the text section, some informations are displayed. The type of the object,
54 what it returns, and its docstring. We could hope that these docstrings will be as
55 descriptive as possible. This text data block named api_doc_ can be toggled on and off
56 with the Escape key. (but a bug prevent the keymap to register correctly at start)
61 from console
.complete_import
import get_root_modules
64 ############ Global Variables ############
66 last_text
= None # last text data block
68 root_module
= None # root module of the tree
70 root_m_path
= '' # root_module + path as a string
72 current_module
= None # the object itself in the tree structure
75 tree_level
= None # the list of objects from the current_module
77 def init_tree_level():
79 tree_level
= [[],[],[],[],[],[],[], [], []]
84 api_doc_
= '' # the documentation formated for the API Navigator
86 module_type
= None # the type of current_module
88 return_report
= '' # what current_module returns
90 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 ############
97 def get_root_module(path
):
98 #print('get_root_module')
101 root
= path
[:path
.find('.')]
105 root_module
= __import__(root
)
110 def evaluate(module
):
112 global root_module
, tree_level
, root_m_path
114 # path = bpy.context.window_manager.api_nav_props.path
116 len_name
= root_module
.__name
__.__len
__()
117 root_m_path
= 'root_module' + module
[len_name
:]
118 current_module
= eval(root_m_path
)
119 return current_module
125 def get_tree_level():
126 #print('get_tree_level')
128 path
= bpy
.context
.window_manager
.api_nav_props
.path
131 #print('object_list')
132 global current_module
, root_m_path
134 itm
, val
, mod
, typ
, props
, struct
, met
, att
, bug
= [], [], [], [], [], [], [], [], []
135 iterable
= isiterable(current_module
)
138 current_type
= str(module_type
)
139 if current_type
!= "<class 'str'>":
142 #current_type.__iter__()
143 itm
= list(current_module
.keys())
145 val
= list(current_module
)
147 val
= list(current_module
)
149 for i
in dir(current_module
):
151 t
= str(type(eval(root_m_path
+ '.' + i
)))
152 except (AttributeError, SyntaxError):
157 if t
== "<class 'module'>":
159 elif t
[0:16] == "<class 'bpy_prop":
161 elif t
[8:11] == 'bpy':
163 elif t
== "<class 'builtin_function_or_method'>":
165 elif t
== "<class 'type'>":
170 return [itm
, val
, mod
, typ
, props
, struct
, met
, att
, bug
]
173 return [[], [], [i
for i
in get_root_modules()], [], [], [], [], [], []]
178 """Returns the parent path"""
182 if parent
[-1] == ']' and '[' in parent
:
183 while parent
[-1] != '[':
186 while parent
[-1] != '.':
195 """Update the filter according to the current path"""
199 bpy
.context
.window_manager
.api_nav_props
.filter = filter_mem
[bpy
.context
.window_manager
.api_nav_props
.path
]
201 bpy
.context
.window_manager
.api_nav_props
.filter = ''
215 except (AttributeError, TypeError):
219 def fill_filter_mem():
222 filter = bpy
.context
.window_manager
.api_nav_props
.filter
224 filter_mem
[bpy
.context
.window_manager
.api_nav_props
.old_path
] = bpy
.context
.window_manager
.api_nav_props
.filter
226 filter_mem
.pop(bpy
.context
.window_manager
.api_nav_props
.old_path
, None)
229 ###### API Navigator parent class #######
231 class ApiNavigator():
232 """Parent class for API Navigator"""
235 def generate_global_values():
236 """Populate the level attributes to display the panel buttons and the documentation"""
237 global tree_level
, current_module
, module_type
, return_report
, last_text
239 text
= bpy
.context
.space_data
.text
241 if text
.name
!= 'api_doc_':
242 last_text
= bpy
.context
.space_data
.text
.name
243 elif bpy
.data
.texts
.__len
__() < 2:
247 bpy
.context
.window_manager
.api_nav_props
.pages
= 0
248 get_root_module(bpy
.context
.window_manager
.api_nav_props
.path
)
249 current_module
= evaluate(bpy
.context
.window_manager
.api_nav_props
.path
)
250 module_type
= str(type(current_module
))
251 return_report
= str(current_module
)
252 tree_level
= get_tree_level()
254 if tree_level
.__len
__() > 30:
260 ApiNavigator
.generate_api_doc()
264 def generate_api_doc():
265 """Format the doc string for API Navigator"""
266 global current_module
, api_doc_
, return_report
, module_type
268 path
= bpy
.context
.window_manager
.api_nav_props
.path
269 line
= "-" * (path
.__len
__()+2)
270 header
= """\n\n\n\t\t%s\n\t %s\n\
271 _____________________________________________\n\
277 _____________________________________________\n\
281 """ % (path
, line
, module_type
, return_report
)
283 _____________________________________________\n\
287 #############################################\n\
289 # Escape to toggle text #\n\
290 # (F8 to reload modules if doesn't work) #\n\
291 #############################################"
292 doc
= current_module
.__doc
__
293 api_doc_
= header
+ str(doc
) + footer
297 def doc_text_datablock():
298 """Create the text databloc or overwrite it if it already exist"""
301 space_data
= bpy
.context
.space_data
304 doc_text
= bpy
.data
.texts
['api_doc_']
305 space_data
.text
= doc_text
308 bpy
.data
.texts
.new(name
='api_doc_')
309 doc_text
= bpy
.data
.texts
['api_doc_']
310 space_data
.text
= doc_text
312 doc_text
.write(text
=api_doc_
)
317 ############ Operators ############
318 def api_update(context
):
319 if bpy
.context
.window_manager
.api_nav_props
.path
!= bpy
.context
.window_manager
.api_nav_props
.old_path
:
321 bpy
.context
.window_manager
.api_nav_props
.old_path
= bpy
.context
.window_manager
.api_nav_props
.path
323 ApiNavigator
.generate_global_values()
324 ApiNavigator
.doc_text_datablock()
327 class Update(ApiNavigator
, bpy
.types
.Operator
):
328 """Update the tree structure"""
329 bl_idname
= "api_navigator.update"
330 bl_label
= "API Navigator Update"
332 def execute(self
, context
):
337 class BackToBpy(ApiNavigator
, bpy
.types
.Operator
):
338 """go back to module bpy"""
339 bl_idname
= "api_navigator.back_to_bpy"
340 bl_label
= "Back to bpy"
342 def execute(self
, context
):
344 if not bpy
.context
.window_manager
.api_nav_props
.path
:
345 bpy
.context
.window_manager
.api_nav_props
.old_path
= bpy
.context
.window_manager
.api_nav_props
.path
= 'bpy'
347 bpy
.context
.window_manager
.api_nav_props
.old_path
= bpy
.context
.window_manager
.api_nav_props
.path
= 'bpy'
349 self
.generate_global_values()
350 self
.doc_text_datablock()
354 class Down(ApiNavigator
, bpy
.types
.Operator
):
355 """go to this Module"""
356 bl_idname
= "api_navigator.down"
357 bl_label
= "API Navigator Down"
358 pointed_module
= bpy
.props
.StringProperty(name
='Current Module', default
='')
361 def execute(self
, context
):
364 if not bpy
.context
.window_manager
.api_nav_props
.path
:
365 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
367 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
370 self
.generate_global_values()
371 self
.doc_text_datablock()
375 class Parent(ApiNavigator
, bpy
.types
.Operator
):
376 """go to Parent Module"""
377 bl_idname
= "api_navigator.parent"
378 bl_label
= "API Navigator Parent"
381 def execute(self
, context
):
382 path
= bpy
.context
.window_manager
.api_nav_props
.path
386 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
)
388 self
.generate_global_values()
389 self
.doc_text_datablock()
394 class ClearFilter(ApiNavigator
, bpy
.types
.Operator
):
395 """Clear the filter"""
396 bl_idname
= 'api_navigator.clear_filter'
397 bl_label
= 'API Nav clear filter'
399 def execute(self
, context
):
400 bpy
.context
.window_manager
.api_nav_props
.filter = ''
404 class FakeButton(ApiNavigator
, bpy
.types
.Operator
):
405 """The list is not displayed completely""" # only serve as an indicator
406 bl_idname
= 'api_navigator.fake_button'
410 class Subscript(ApiNavigator
, bpy
.types
.Operator
):
411 """Subscript to this Item"""
412 bl_idname
= "api_navigator.subscript"
413 bl_label
= "API Navigator Subscript"
414 subscription
= bpy
.props
.StringProperty(name
='', default
='')
416 def execute(self
, context
):
418 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
+ ']'
420 self
.generate_global_values()
421 self
.doc_text_datablock()
425 class Toggle_doc(ApiNavigator
, bpy
.types
.Operator
):
426 """Toggle on or off api_doc_ Text"""
427 bl_idname
= 'api_navigator.toggle_doc'
428 bl_label
= 'Toggle api_doc_'
431 def execute(self
, context
):
435 if bpy
.context
.space_data
.text
.name
!= "api_doc_":
436 last_text
= bpy
.context
.space_data
.text
.name
440 text
= bpy
.data
.texts
["api_doc_"]
441 bpy
.data
.texts
["api_doc_"].clear()
442 bpy
.data
.texts
.remove(text
)
444 self
.doc_text_datablock()
448 text
= bpy
.data
.texts
[last_text
]
449 bpy
.context
.space_data
.text
= text
450 #line = bpy.ops.text.line_number() # operator doesn't seems to work ???
451 #bpy.ops.text.jump(line=line)
455 bpy
.context
.space_data
.text
= None
458 ############ UI Panels ############
460 class OBJECT_PT_api_navigator(ApiNavigator
, bpy
.types
.Panel
):
461 bl_idname
= 'api_navigator'
462 bl_space_type
= "TEXT_EDITOR"
463 bl_region_type
= "UI"
464 bl_label
= "API Navigator"
465 bl_options
= {'DEFAULT_CLOSED'}
471 def iterable_draw(self
):
472 global tree_level
, current_module
474 iterable
= isiterable(current_module
)
478 current_type
= str(module_type
)
480 if current_type
== "<class 'str'>":
484 # filter = bpy.context.window_manager.api_nav_props.filter # UNUSED
485 reduce_to
= bpy
.context
.window_manager
.api_nav_props
.reduce_to
* self
.columns
486 pages
= bpy
.context
.window_manager
.api_nav_props
.pages
487 page_index
= reduce_to
*pages
494 current_type
.__iter
__()
495 collection
= list(current_module
.keys())
496 end
= collection
.__len
__()
497 box
= self
.layout
.box()
499 row
.label(text
="Items", icon
="DOTSDOWN")
501 col
= box
.column(align
=True)
503 while count
< reduce_to
and i
< end
:
505 if filtered
< page_index
:
510 if not (i
% self
.columns
):
512 row
.operator('api_navigator.subscript', text
=mod
, emboss
=False).subscription
= '"' + mod
+ '"'
517 elif iterable
== 'b':
518 box
= self
.layout
.box()
520 row
.label(text
="Item Values", icon
="OOPS")
522 col
= box
.column(align
=True)
523 collection
= list(current_module
)
524 end
= collection
.__len
__()
526 while count
< reduce_to
and i
< end
:
527 mod
= str(collection
[i
])
528 if filtered
< page_index
:
533 if not (i
% self
.columns
):
535 row
.operator('api_navigator.subscript', text
=mod
, emboss
=False).subscription
= str(i
)
544 row
.prop(bpy
.context
.window_manager
.api_nav_props
, 'reduce_to')
545 row
.operator('api_navigator.fake_button', text
='', emboss
=False, icon
="DOTSDOWN")
546 row
.prop(bpy
.context
.window_manager
.api_nav_props
, 'pages', text
='Pages')
553 def list_draw(self
, t
, pages
, icon
, label
=None, emboss
=False):
554 global tree_level
, current_module
556 def reduced(too_long
):
560 row
.prop(bpy
.context
.window_manager
.api_nav_props
, 'reduce_to')
561 row
.operator('api_navigator.fake_button', text
='', emboss
=False, icon
="DOTSDOWN")
562 row
.prop(bpy
.context
.window_manager
.api_nav_props
, 'pages', text
='Pages')
566 filter = bpy
.context
.window_manager
.api_nav_props
.filter
568 reduce_to
= bpy
.context
.window_manager
.api_nav_props
.reduce_to
* self
.columns
570 page_index
= reduce_to
*pages
573 len = tree_level
[t
].__len
__()
574 too_long
= len > reduce_to
577 col
= layout
.column()
581 row
.label(text
=label
, icon
=icon
)
586 col
= row
.column(align
=True)
592 while count
< reduce_to
and i
< len:
593 obj
= tree_level
[t
][i
]
595 if filter and filter not in obj
:
598 elif filtered
< page_index
:
603 if not (objects
% self
.columns
):
606 row
.operator("api_navigator.down", text
=obj
, emboss
=emboss
).pointed_module
= obj
608 row
.operator('api_navigator.subscript', text
=str(obj
), emboss
=False).subscription
= '"' + obj
+ '"'
610 row
.operator('api_navigator.subscript', text
=str(obj
), emboss
=False).subscription
= str(i
)
621 def draw(self
, context
):
622 global tree_level
, current_module
, module_type
, return_report
629 layout
.label(text
="Tree Structure:")
630 col
= layout
.column(align
=True)
632 col
.prop(bpy
.context
.window_manager
.api_nav_props
, 'path', text
='')
634 row
.operator("api_navigator.parent", text
="Parent", icon
="BACK")
635 row
.operator("api_navigator.back_to_bpy", text
='', emboss
=True, icon
="FILE_PARENT")
637 col
= layout
.column()
638 row
= col
.row(align
=True)
639 row
.prop(bpy
.context
.window_manager
.api_nav_props
, 'filter')
640 row
.operator('api_navigator.clear_filter', text
='', icon
='PANEL_CLOSE')
642 col
= layout
.column()
644 pages
= bpy
.context
.window_manager
.api_nav_props
.pages
645 self
.list_draw(0, pages
, "DOTSDOWN", label
="Items")
646 self
.list_draw(1, pages
, "DOTSDOWN", label
="Item Values")
647 self
.list_draw(2, pages
, "PACKAGE", label
="Modules", emboss
=True)
648 self
.list_draw(3, pages
, "WORDWRAP_ON", label
="Types", emboss
=False)
649 self
.list_draw(4, pages
, "BUTS", label
="Properties", emboss
=False)
650 self
.list_draw(5, pages
, "OOPS", label
="Structs and Functions")
651 self
.list_draw(6, pages
, "SCRIPTWIN", label
="Methods and Functions")
652 self
.list_draw(7, pages
, "INFO", label
="Attributes")
653 self
.list_draw(8, pages
, "ERROR", label
="Inaccessible")
656 ########### Menu functions ###############
659 def register_keymaps():
660 kc
= bpy
.context
.window_manager
.keyconfigs
.addon
662 km
= kc
.keymaps
.new(name
="Text", space_type
='TEXT_EDITOR')
663 km
.keymap_items
.new('api_navigator.toggle_doc', 'ESC', 'PRESS')
666 def unregister_keymaps():
667 kc
= bpy
.context
.window_manager
.keyconfigs
.addon
669 km
= kc
.keymaps
["Text"]
670 kmi
= km
.keymap_items
["api_navigator.toggle_doc"]
671 km
.keymap_items
.remove(kmi
)
675 from bpy
.props
import StringProperty
, IntProperty
, PointerProperty
677 class ApiNavProps(bpy
.types
.PropertyGroup
):
679 Fake module like class.
681 bpy.context.window_manager.api_nav_props
684 path
= StringProperty(name
='path',
685 description
='Enter bpy.ops.api_navigator to see the documentation',
687 old_path
= StringProperty(name
='old_path', default
='')
688 filter = StringProperty(name
='filter',
689 description
='Filter the resulting modules', default
='')
690 reduce_to
= IntProperty(name
='Reduce to ',
691 description
='Display a maximum number of x entries by pages',
693 pages
= IntProperty(name
='Pages',
694 description
='Display a Page', default
=0, min=0)
696 bpy
.utils
.register_module(__name__
)
698 bpy
.types
.WindowManager
.api_nav_props
= PointerProperty(
699 type=ApiNavProps
, name
='API Nav Props', description
='')
702 #print(get_tree_level())
707 del bpy
.types
.WindowManager
.api_nav_props
709 bpy
.utils
.unregister_module(__name__
)
712 if __name__
== '__main__':