diff options
Diffstat (limited to 'release/scripts/ui/space_console.py')
-rw-r--r-- | release/scripts/ui/space_console.py | 242 |
1 files changed, 242 insertions, 0 deletions
diff --git a/release/scripts/ui/space_console.py b/release/scripts/ui/space_console.py new file mode 100644 index 00000000000..a65d7577c7a --- /dev/null +++ b/release/scripts/ui/space_console.py @@ -0,0 +1,242 @@ +import bpy + +class CONSOLE_HT_header(bpy.types.Header): + __space_type__ = 'CONSOLE' + + def draw(self, context): + sc = context.space_data + # text = sc.text + layout = self.layout + + row= layout.row(align=True) + row.template_header() + + if context.area.show_menus: + sub = row.row(align=True) + + if sc.console_type == 'REPORT': + sub.itemM("CONSOLE_MT_report") + else: + sub.itemM("CONSOLE_MT_console") + + layout.itemS() + layout.itemR(sc, "console_type", expand=True) + + if sc.console_type == 'REPORT': + row = layout.row(align=True) + row.itemR(sc, "show_report_debug", text="Debug") + row.itemR(sc, "show_report_info", text="Info") + row.itemR(sc, "show_report_operator", text="Operators") + row.itemR(sc, "show_report_warn", text="Warnings") + row.itemR(sc, "show_report_error", text="Errors") + + row = layout.row() + row.enabled = sc.show_report_operator + row.itemO("console.report_replay") + else: + row = layout.row(align=True) + row.itemO("console.autocomplete", text="Autocomplete") + +class CONSOLE_MT_console(bpy.types.Menu): + __space_type__ = 'CONSOLE' + __label__ = "Console" + + def draw(self, context): + layout = self.layout + sc = context.space_data + + layout.column() + layout.itemO("console.clear") + layout.itemO("console.copy") + layout.itemO("console.paste") + +class CONSOLE_MT_report(bpy.types.Menu): + __space_type__ = 'CONSOLE' + __label__ = "Report" + + def draw(self, context): + layout = self.layout + sc = context.space_data + + layout.column() + layout.itemO("console.select_all_toggle") + layout.itemO("console.select_border") + layout.itemO("console.report_delete") + layout.itemO("console.report_copy") + +def add_scrollback(text, text_type): + for l in text.split('\n'): + bpy.ops.console.scrollback_append(text=l.replace('\t', ' '), type=text_type) + +def get_console(console_id): + ''' + helper function for console operators + currently each text datablock gets its own console - code.InteractiveConsole() + ...which is stored in this function. + + console_id can be any hashable type + ''' + import sys, code + + try: consoles = get_console.consoles + except:consoles = get_console.consoles = {} + + # clear all dead consoles, use text names as IDs + # TODO, find a way to clear IDs + ''' + for console_id in list(consoles.keys()): + if console_id not in bpy.data.texts: + del consoles[id] + ''' + + try: + namespace, console, stdout, stderr = consoles[console_id] + except: + namespace = {'__builtins__':__builtins__} # locals() + namespace['bpy'] = bpy + + console = code.InteractiveConsole(namespace) + + import io + stdout = io.StringIO() + stderr = io.StringIO() + + consoles[console_id]= namespace, console, stdout, stderr + + return namespace, console, stdout, stderr + +class CONSOLE_OT_exec(bpy.types.Operator): + '''Execute the current console line as a python expression.''' + __idname__ = "console.execute" + __label__ = "Console Execute" + __register__ = False + + # Both prompts must be the same length + PROMPT = '>>> ' + PROMPT_MULTI = '... ' + + # is this working??? + ''' + def poll(self, context): + return (context.space_data.type == 'PYTHON') + ''' # its not :| + + def execute(self, context): + import sys + + sc = context.space_data + + try: + line = sc.history[-1].line + except: + return ('CANCELLED',) + + if sc.console_type != 'PYTHON': + return ('CANCELLED',) + + namespace, console, stdout, stderr = get_console(hash(context.region)) + + # redirect output + sys.stdout = stdout + sys.stderr = stderr + + # run the console + if not line.strip(): + line_exec = '\n' # executes a multiline statement + else: + line_exec = line + + is_multiline = console.push(line_exec) + + stdout.seek(0) + stderr.seek(0) + + output = stdout.read() + output_err = stderr.read() + + # cleanup + sys.stdout = sys.__stdout__ + sys.stderr = sys.__stderr__ + sys.last_traceback = None + + # So we can reuse, clear all data + stdout.truncate(0) + stderr.truncate(0) + + bpy.ops.console.scrollback_append(text = sc.prompt+line, type='INPUT') + + if is_multiline: sc.prompt = self.PROMPT_MULTI + else: sc.prompt = self.PROMPT + + # insert a new blank line + bpy.ops.console.history_append(text="", current_character=0, remove_duplicates= True) + + # Insert the output into the editor + # not quite correct because the order might have changed, but ok 99% of the time. + if output: add_scrollback(output, 'OUTPUT') + if output_err: add_scrollback(output_err, 'ERROR') + + + return ('FINISHED',) + + +class CONSOLE_OT_autocomplete(bpy.types.Operator): + '''Evaluate the namespace up until the cursor and give a list of options or complete the name if there is only one.''' + __idname__ = "console.autocomplete" + __label__ = "Console Autocomplete" + __register__ = False + + def poll(self, context): + return context.space_data.console_type == 'PYTHON' + + def execute(self, context): + + sc = context.space_data + + namespace, console, stdout, stderr = get_console(hash(context.region)) + + current_line = sc.history[-1] + line = current_line.line + + if not console: + return ('CANCELLED',) + + if sc.console_type != 'PYTHON': + return ('CANCELLED',) + + # fake cursor, use for autocomp func. + bcon = {} + bcon['cursor'] = current_line.current_character + bcon['console'] = console + bcon['edit_text'] = line + bcon['namespace'] = namespace + bcon['scrollback'] = '' # nor from the BGE console + + + # This function isnt aware of the text editor or being an operator + # just does the autocomp then copy its results back + import autocomplete + autocomplete.execute(bcon) + + # Now we need to copy back the line from blender back into the text editor. + # This will change when we dont use the text editor anymore + if bcon['scrollback']: + add_scrollback(bcon['scrollback'], 'INFO') + + # copy back + current_line.line = bcon['edit_text'] + current_line.current_character = bcon['cursor'] + + context.area.tag_redraw() + + return ('FINISHED',) + + + +bpy.types.register(CONSOLE_HT_header) +bpy.types.register(CONSOLE_MT_console) +bpy.types.register(CONSOLE_MT_report) + +bpy.ops.add(CONSOLE_OT_exec) +bpy.ops.add(CONSOLE_OT_autocomplete) + |