Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'release/ui/space_console.py')
-rw-r--r--release/ui/space_console.py417
1 files changed, 417 insertions, 0 deletions
diff --git a/release/ui/space_console.py b/release/ui/space_console.py
new file mode 100644
index 00000000000..497f34a28d0
--- /dev/null
+++ b/release/ui/space_console.py
@@ -0,0 +1,417 @@
+
+import bpy
+
+class CONSOLE_HT_header(bpy.types.Header):
+ __space_type__ = "CONSOLE"
+ __idname__ = "CONSOLE_HT_header"
+
+ def draw(self, context):
+ sc = context.space_data
+ # text = sc.text
+ layout = self.layout
+
+ layout.template_header()
+
+ if context.area.show_menus:
+ row = layout.row()
+ row.itemM("CONSOLE_MT_console")
+
+ row = layout.row()
+ row.scale_x = 0.9
+ row.itemR(sc, "type", expand=True)
+ if sc.type == 'REPORT':
+ row.itemR(sc, "show_report_debug")
+ row.itemR(sc, "show_report_info")
+ row.itemR(sc, "show_report_operator")
+ row.itemR(sc, "show_report_warn")
+ row.itemR(sc, "show_report_error")
+
+
+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_OT_clear")
+
+def add_scrollback(text, text_type):
+ for l in text.split('\n'):
+ bpy.ops.CONSOLE_OT_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, io
+
+ 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)
+
+ if sys.version.startswith('2'):
+ stdout = io.BytesIO() # Py2x support
+ stderr = io.BytesIO()
+ else:
+ 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):
+ '''
+ Operator documentatuon text, will be used for the operator tooltip and python docs.
+ '''
+ __label__ = "Console Execute"
+
+ # 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.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_OT_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_OT_history_append(text="", current_character=0)
+
+ # 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',)
+
+
+def autocomp(bcon):
+ '''
+ This function has been taken from a BGE console autocomp I wrote a while ago
+ the dictionaty bcon is not needed but it means I can copy and paste from the old func
+ which works ok for now.
+
+ could be moved into its own module.
+ '''
+
+
+ def is_delimiter(ch):
+ '''
+ For skipping words
+ '''
+ if ch == '_':
+ return False
+ if ch.isalnum():
+ return False
+
+ return True
+
+ def is_delimiter_autocomp(ch):
+ '''
+ When autocompleteing will earch back and
+ '''
+ if ch in '._[] "\'':
+ return False
+ if ch.isalnum():
+ return False
+
+ return True
+
+
+ def do_autocomp(autocomp_prefix, autocomp_members):
+ '''
+ return text to insert and a list of options
+ '''
+ autocomp_members = [v for v in autocomp_members if v.startswith(autocomp_prefix)]
+
+ print("AUTO: '%s'" % autocomp_prefix)
+ print("MEMBERS: '%s'" % str(autocomp_members))
+
+ if not autocomp_prefix:
+ return '', autocomp_members
+ elif len(autocomp_members) > 1:
+ # find a common string between all members after the prefix
+ # 'ge' [getA, getB, getC] --> 'get'
+
+ # get the shortest member
+ min_len = min([len(v) for v in autocomp_members])
+
+ autocomp_prefix_ret = ''
+
+ for i in range(len(autocomp_prefix), min_len):
+ char_soup = set()
+ for v in autocomp_members:
+ char_soup.add(v[i])
+
+ if len(char_soup) > 1:
+ break
+ else:
+ autocomp_prefix_ret += char_soup.pop()
+
+ print(autocomp_prefix_ret)
+ return autocomp_prefix_ret, autocomp_members
+ elif len(autocomp_members) == 1:
+ return autocomp_members[0][len(autocomp_prefix):], []
+ else:
+ return '', []
+
+
+ def BCon_PrevChar(bcon):
+ cursor = bcon['cursor']-1
+ if cursor<0:
+ return None
+
+ try:
+ return bcon['edit_text'][cursor]
+ except:
+ return None
+
+
+ def BCon_NextChar(bcon):
+ try:
+ return bcon['edit_text'][bcon['cursor']]
+ except:
+ return None
+
+ def BCon_cursorLeft(bcon):
+ bcon['cursor'] -= 1
+ if bcon['cursor'] < 0:
+ bcon['cursor'] = 0
+
+ def BCon_cursorRight(bcon):
+ bcon['cursor'] += 1
+ if bcon['cursor'] > len(bcon['edit_text']):
+ bcon['cursor'] = len(bcon['edit_text'])
+
+ def BCon_AddScrollback(bcon, text):
+
+ bcon['scrollback'] = bcon['scrollback'] + text
+
+
+ def BCon_cursorInsertChar(bcon, ch):
+ if bcon['cursor']==0:
+ bcon['edit_text'] = ch + bcon['edit_text']
+ elif bcon['cursor']==len(bcon['edit_text']):
+ bcon['edit_text'] = bcon['edit_text'] + ch
+ else:
+ bcon['edit_text'] = bcon['edit_text'][:bcon['cursor']] + ch + bcon['edit_text'][bcon['cursor']:]
+
+ bcon['cursor']
+ if bcon['cursor'] > len(bcon['edit_text']):
+ bcon['cursor'] = len(bcon['edit_text'])
+ BCon_cursorRight(bcon)
+
+
+ TEMP_NAME = '___tempname___'
+
+ cursor_orig = bcon['cursor']
+
+ ch = BCon_PrevChar(bcon)
+ while ch != None and (not is_delimiter(ch)):
+ ch = BCon_PrevChar(bcon)
+ BCon_cursorLeft(bcon)
+
+ if ch != None:
+ BCon_cursorRight(bcon)
+
+ #print (cursor_orig, bcon['cursor'])
+
+ cursor_base = bcon['cursor']
+
+ autocomp_prefix = bcon['edit_text'][cursor_base:cursor_orig]
+
+ print("PREFIX:'%s'" % autocomp_prefix)
+
+ # Get the previous word
+ if BCon_PrevChar(bcon)=='.':
+ BCon_cursorLeft(bcon)
+ ch = BCon_PrevChar(bcon)
+ while ch != None and is_delimiter_autocomp(ch)==False:
+ ch = BCon_PrevChar(bcon)
+ BCon_cursorLeft(bcon)
+
+ cursor_new = bcon['cursor']
+
+ if ch != None:
+ cursor_new+=1
+
+ pytxt = bcon['edit_text'][cursor_new:cursor_base-1].strip()
+ print("AUTOCOMP EVAL: '%s'" % pytxt)
+ #try:
+ if pytxt:
+ bcon['console'].runsource(TEMP_NAME + '=' + pytxt, '<input>', 'single')
+ # print val
+ else: ##except:
+ val = None
+
+ try:
+ val = bcon['namespace'][TEMP_NAME]
+ del bcon['namespace'][TEMP_NAME]
+ except:
+ val = None
+
+ if val:
+ autocomp_members = dir(val)
+
+ autocomp_prefix_ret, autocomp_members = do_autocomp(autocomp_prefix, autocomp_members)
+
+ bcon['cursor'] = cursor_orig
+ for v in autocomp_prefix_ret:
+ BCon_cursorInsertChar(bcon, v)
+ cursor_orig = bcon['cursor']
+
+ if autocomp_members:
+ BCon_AddScrollback(bcon, ', '.join(autocomp_members))
+
+ del val
+
+ else:
+ # Autocomp global namespace
+ autocomp_members = bcon['namespace'].keys()
+
+ if autocomp_prefix:
+ autocomp_members = [v for v in autocomp_members if v.startswith(autocomp_prefix)]
+
+ autocomp_prefix_ret, autocomp_members = do_autocomp(autocomp_prefix, autocomp_members)
+
+ bcon['cursor'] = cursor_orig
+ for v in autocomp_prefix_ret:
+ BCon_cursorInsertChar(bcon, v)
+ cursor_orig = bcon['cursor']
+
+ if autocomp_members:
+ BCon_AddScrollback(bcon, ', '.join(autocomp_members))
+
+ bcon['cursor'] = cursor_orig
+
+
+class CONSOLE_OT_autocomplete(bpy.types.Operator):
+ '''
+ Operator documentatuon text, will be used for the operator tooltip and python docs.
+ '''
+ __label__ = "Console Autocomplete"
+
+ def poll(self, context):
+ return context.space_data.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.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
+ autocomp(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.ops.add(CONSOLE_OT_exec)
+bpy.ops.add(CONSOLE_OT_autocomplete)
+