4 class CONSOLE_HT_header(bpy.types.Header):
5 __space_type__ = "CONSOLE"
6 __idname__ = "CONSOLE_HT_header"
8 def draw(self, context):
9 sc = context.space_data
13 layout.template_header()
15 if context.area.show_menus:
17 row.itemM("CONSOLE_MT_console")
21 row.itemR(sc, "type", expand=True)
22 if sc.type == 'REPORT':
23 row.itemR(sc, "show_report_debug")
24 row.itemR(sc, "show_report_info")
25 row.itemR(sc, "show_report_operator")
26 row.itemR(sc, "show_report_warn")
27 row.itemR(sc, "show_report_error")
30 class CONSOLE_MT_console(bpy.types.Menu):
31 __space_type__ = "CONSOLE"
34 def draw(self, context):
36 sc = context.space_data
39 layout.itemO("CONSOLE_OT_clear")
41 def add_scrollback(text, text_type):
42 for l in text.split('\n'):
43 bpy.ops.CONSOLE_OT_scrollback_append(text=l.replace('\t', ' '), type=text_type)
45 def get_console(console_id):
47 helper function for console operators
48 currently each text datablock gets its own console - code.InteractiveConsole()
49 ...which is stored in this function.
51 console_id can be any hashable type
55 try: consoles = get_console.consoles
56 except:consoles = get_console.consoles = {}
58 # clear all dead consoles, use text names as IDs
59 # TODO, find a way to clear IDs
61 for console_id in list(consoles.keys()):
62 if console_id not in bpy.data.texts:
67 namespace, console, stdout, stderr = consoles[console_id]
69 namespace = {'__builtins__':__builtins__} # locals()
70 namespace['bpy'] = bpy
72 console = code.InteractiveConsole(namespace)
74 if sys.version.startswith('2'):
76 stdout = cStringIO.BytesIO() # Py2x support
77 stderr = cStringIO.BytesIO()
80 stdout = io.StringIO()
81 stderr = io.StringIO()
83 consoles[console_id]= namespace, console, stdout, stderr
85 return namespace, console, stdout, stderr
87 class CONSOLE_OT_exec(bpy.types.Operator):
89 Operator documentatuon text, will be used for the operator tooltip and python docs.
91 __label__ = "Console Execute"
94 # Both prompts must be the same length
100 def poll(self, context):
101 return (context.space_data.type == 'PYTHON')
104 def execute(self, context):
107 sc = context.space_data
110 line = sc.history[-1].line
112 return ('CANCELLED',)
114 if sc.type != 'PYTHON':
115 return ('CANCELLED',)
117 namespace, console, stdout, stderr = get_console(hash(context.region))
125 line_exec = '\n' # executes a multiline statement
129 is_multiline = console.push(line_exec)
134 output = stdout.read()
135 output_err = stderr.read()
138 sys.stdout = sys.__stdout__
139 sys.stderr = sys.__stderr__
140 sys.last_traceback = None
142 # So we can reuse, clear all data
146 bpy.ops.CONSOLE_OT_scrollback_append(text = sc.prompt+line, type='INPUT')
148 if is_multiline: sc.prompt = self.PROMPT_MULTI
149 else: sc.prompt = self.PROMPT
151 # insert a new blank line
152 bpy.ops.CONSOLE_OT_history_append(text="", current_character=0)
154 # Insert the output into the editor
155 # not quite correct because the order might have changed, but ok 99% of the time.
156 if output: add_scrollback(output, 'OUTPUT')
157 if output_err: add_scrollback(output_err, 'ERROR')
165 This function has been taken from a BGE console autocomp I wrote a while ago
166 the dictionaty bcon is not needed but it means I can copy and paste from the old func
167 which works ok for now.
169 could be moved into its own module.
173 def is_delimiter(ch):
184 def is_delimiter_autocomp(ch):
186 When autocompleteing will earch back and
196 def do_autocomp(autocomp_prefix, autocomp_members):
198 return text to insert and a list of options
200 autocomp_members = [v for v in autocomp_members if v.startswith(autocomp_prefix)]
202 print("AUTO: '%s'" % autocomp_prefix)
203 print("MEMBERS: '%s'" % str(autocomp_members))
205 if not autocomp_prefix:
206 return '', autocomp_members
207 elif len(autocomp_members) > 1:
208 # find a common string between all members after the prefix
209 # 'ge' [getA, getB, getC] --> 'get'
211 # get the shortest member
212 min_len = min([len(v) for v in autocomp_members])
214 autocomp_prefix_ret = ''
216 for i in range(len(autocomp_prefix), min_len):
218 for v in autocomp_members:
221 if len(char_soup) > 1:
224 autocomp_prefix_ret += char_soup.pop()
226 print(autocomp_prefix_ret)
227 return autocomp_prefix_ret, autocomp_members
228 elif len(autocomp_members) == 1:
229 return autocomp_members[0][len(autocomp_prefix):], []
234 def BCon_PrevChar(bcon):
235 cursor = bcon['cursor']-1
240 return bcon['edit_text'][cursor]
245 def BCon_NextChar(bcon):
247 return bcon['edit_text'][bcon['cursor']]
251 def BCon_cursorLeft(bcon):
253 if bcon['cursor'] < 0:
256 def BCon_cursorRight(bcon):
258 if bcon['cursor'] > len(bcon['edit_text']):
259 bcon['cursor'] = len(bcon['edit_text'])
261 def BCon_AddScrollback(bcon, text):
263 bcon['scrollback'] = bcon['scrollback'] + text
266 def BCon_cursorInsertChar(bcon, ch):
267 if bcon['cursor']==0:
268 bcon['edit_text'] = ch + bcon['edit_text']
269 elif bcon['cursor']==len(bcon['edit_text']):
270 bcon['edit_text'] = bcon['edit_text'] + ch
272 bcon['edit_text'] = bcon['edit_text'][:bcon['cursor']] + ch + bcon['edit_text'][bcon['cursor']:]
275 if bcon['cursor'] > len(bcon['edit_text']):
276 bcon['cursor'] = len(bcon['edit_text'])
277 BCon_cursorRight(bcon)
280 TEMP_NAME = '___tempname___'
282 cursor_orig = bcon['cursor']
284 ch = BCon_PrevChar(bcon)
285 while ch != None and (not is_delimiter(ch)):
286 ch = BCon_PrevChar(bcon)
287 BCon_cursorLeft(bcon)
290 BCon_cursorRight(bcon)
292 #print (cursor_orig, bcon['cursor'])
294 cursor_base = bcon['cursor']
296 autocomp_prefix = bcon['edit_text'][cursor_base:cursor_orig]
298 print("PREFIX:'%s'" % autocomp_prefix)
300 # Get the previous word
301 if BCon_PrevChar(bcon)=='.':
302 BCon_cursorLeft(bcon)
303 ch = BCon_PrevChar(bcon)
304 while ch != None and is_delimiter_autocomp(ch)==False:
305 ch = BCon_PrevChar(bcon)
306 BCon_cursorLeft(bcon)
308 cursor_new = bcon['cursor']
313 pytxt = bcon['edit_text'][cursor_new:cursor_base-1].strip()
314 print("AUTOCOMP EVAL: '%s'" % pytxt)
317 bcon['console'].runsource(TEMP_NAME + '=' + pytxt, '<input>', 'single')
323 val = bcon['namespace'][TEMP_NAME]
324 del bcon['namespace'][TEMP_NAME]
329 autocomp_members = dir(val)
331 autocomp_prefix_ret, autocomp_members = do_autocomp(autocomp_prefix, autocomp_members)
333 bcon['cursor'] = cursor_orig
334 for v in autocomp_prefix_ret:
335 BCon_cursorInsertChar(bcon, v)
336 cursor_orig = bcon['cursor']
339 BCon_AddScrollback(bcon, ', '.join(autocomp_members))
344 # Autocomp global namespace
345 autocomp_members = bcon['namespace'].keys()
348 autocomp_members = [v for v in autocomp_members if v.startswith(autocomp_prefix)]
350 autocomp_prefix_ret, autocomp_members = do_autocomp(autocomp_prefix, autocomp_members)
352 bcon['cursor'] = cursor_orig
353 for v in autocomp_prefix_ret:
354 BCon_cursorInsertChar(bcon, v)
355 cursor_orig = bcon['cursor']
358 BCon_AddScrollback(bcon, ', '.join(autocomp_members))
360 bcon['cursor'] = cursor_orig
363 class CONSOLE_OT_autocomplete(bpy.types.Operator):
365 Operator documentatuon text, will be used for the operator tooltip and python docs.
367 __label__ = "Console Autocomplete"
370 def poll(self, context):
371 return context.space_data.type == 'PYTHON'
373 def execute(self, context):
375 sc = context.space_data
377 namespace, console, stdout, stderr = get_console(hash(context.region))
379 current_line = sc.history[-1]
380 line = current_line.line
383 return ('CANCELLED',)
385 if sc.type != 'PYTHON':
386 return ('CANCELLED',)
388 # fake cursor, use for autocomp func.
390 bcon['cursor'] = current_line.current_character
391 bcon['console'] = console
392 bcon['edit_text'] = line
393 bcon['namespace'] = namespace
394 bcon['scrollback'] = '' # nor from the BGE console
397 # This function isnt aware of the text editor or being an operator
398 # just does the autocomp then copy its results back
401 # Now we need to copy back the line from blender back into the text editor.
402 # This will change when we dont use the text editor anymore
403 if bcon['scrollback']:
404 add_scrollback(bcon['scrollback'], 'INFO')
407 current_line.line = bcon['edit_text']
408 current_line.current_character = bcon['cursor']
410 context.area.tag_redraw()
416 bpy.types.register(CONSOLE_HT_header)
417 bpy.types.register(CONSOLE_MT_console)
419 bpy.ops.add(CONSOLE_OT_exec)
420 bpy.ops.add(CONSOLE_OT_autocomplete)