2009-11-01 15:21:20 +00:00
|
|
|
# ##### BEGIN GPL LICENSE BLOCK #####
|
|
|
|
#
|
|
|
|
# This program is free software; you can redistribute it and/or
|
|
|
|
# modify it under the terms of the GNU General Public License
|
|
|
|
# as published by the Free Software Foundation; either version 2
|
|
|
|
# of the License, or (at your option) any later version.
|
2009-11-03 07:23:02 +00:00
|
|
|
#
|
2009-11-01 15:21:20 +00:00
|
|
|
# This program is distributed in the hope that it will be useful,
|
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
# GNU General Public License for more details.
|
2009-11-03 07:23:02 +00:00
|
|
|
#
|
2009-11-01 15:21:20 +00:00
|
|
|
# You should have received a copy of the GNU General Public License
|
|
|
|
# along with this program; if not, write to the Free Software Foundation,
|
|
|
|
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
#
|
|
|
|
# ##### END GPL LICENSE BLOCK #####
|
2009-10-31 20:16:59 +00:00
|
|
|
|
2009-10-31 23:35:56 +00:00
|
|
|
# <pep8 compliant>
|
2009-10-29 20:55:45 +00:00
|
|
|
import sys
|
2009-07-16 00:50:27 +00:00
|
|
|
import bpy
|
|
|
|
|
2009-10-29 20:55:45 +00:00
|
|
|
|
2009-07-16 00:50:27 +00:00
|
|
|
class CONSOLE_HT_header(bpy.types.Header):
|
2009-10-31 13:31:23 +00:00
|
|
|
bl_space_type = 'CONSOLE'
|
2009-10-29 20:55:45 +00:00
|
|
|
|
|
|
|
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")
|
|
|
|
|
2009-07-16 00:50:27 +00:00
|
|
|
|
|
|
|
class CONSOLE_MT_console(bpy.types.Menu):
|
2009-10-31 13:31:23 +00:00
|
|
|
bl_label = "Console"
|
2009-07-16 00:50:27 +00:00
|
|
|
|
2009-10-29 20:55:45 +00:00
|
|
|
def draw(self, context):
|
|
|
|
layout = self.layout
|
|
|
|
layout.column()
|
|
|
|
layout.itemO("console.clear")
|
|
|
|
layout.itemO("console.copy")
|
|
|
|
layout.itemO("console.paste")
|
2009-07-16 00:50:27 +00:00
|
|
|
|
|
|
|
|
2009-07-19 00:49:44 +00:00
|
|
|
class CONSOLE_MT_report(bpy.types.Menu):
|
2009-10-31 13:31:23 +00:00
|
|
|
bl_label = "Report"
|
2009-07-19 00:49:44 +00:00
|
|
|
|
2009-10-29 20:55:45 +00:00
|
|
|
def draw(self, context):
|
|
|
|
layout = self.layout
|
|
|
|
layout.column()
|
|
|
|
layout.itemO("console.select_all_toggle")
|
|
|
|
layout.itemO("console.select_border")
|
|
|
|
layout.itemO("console.report_delete")
|
|
|
|
layout.itemO("console.report_copy")
|
2009-07-19 00:49:44 +00:00
|
|
|
|
|
|
|
|
2009-07-16 00:50:27 +00:00
|
|
|
def add_scrollback(text, text_type):
|
2009-10-29 20:55:45 +00:00
|
|
|
for l in text.split('\n'):
|
|
|
|
bpy.ops.console.scrollback_append(text=l.replace('\t', ' '),
|
|
|
|
type=text_type)
|
|
|
|
|
2009-07-16 00:50:27 +00:00
|
|
|
|
|
|
|
def get_console(console_id):
|
2009-10-29 20:55:45 +00:00
|
|
|
'''
|
|
|
|
helper function for console operators
|
|
|
|
currently each text datablock gets its own
|
|
|
|
console - bpython_code.InteractiveConsole()
|
|
|
|
...which is stored in this function.
|
|
|
|
|
|
|
|
console_id can be any hashable type
|
|
|
|
'''
|
|
|
|
from code import InteractiveConsole
|
|
|
|
|
|
|
|
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:
|
|
|
|
console, stdout, stderr = consoles[console_id]
|
|
|
|
except:
|
|
|
|
namespace = {'__builtins__': __builtins__, 'bpy': bpy}
|
|
|
|
console = InteractiveConsole(namespace)
|
|
|
|
|
|
|
|
import io
|
|
|
|
stdout = io.StringIO()
|
|
|
|
stderr = io.StringIO()
|
|
|
|
|
|
|
|
consoles[console_id] = console, stdout, stderr
|
|
|
|
|
|
|
|
return console, stdout, stderr
|
|
|
|
|
2009-07-16 00:50:27 +00:00
|
|
|
|
|
|
|
class CONSOLE_OT_exec(bpy.types.Operator):
|
2009-10-29 20:55:45 +00:00
|
|
|
'''Execute the current console line as a python expression.'''
|
2009-10-31 13:31:23 +00:00
|
|
|
bl_idname = "console.execute"
|
|
|
|
bl_label = "Console Execute"
|
|
|
|
bl_register = False
|
2009-10-29 20:55:45 +00:00
|
|
|
|
|
|
|
# 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):
|
|
|
|
sc = context.space_data
|
|
|
|
|
|
|
|
try:
|
|
|
|
line = sc.history[-1].line
|
|
|
|
except:
|
|
|
|
return ('CANCELLED',)
|
|
|
|
|
|
|
|
if sc.console_type != 'PYTHON':
|
|
|
|
return ('CANCELLED',)
|
|
|
|
|
|
|
|
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',)
|
2009-07-16 00:50:27 +00:00
|
|
|
|
|
|
|
|
|
|
|
class CONSOLE_OT_autocomplete(bpy.types.Operator):
|
2009-10-29 20:55:45 +00:00
|
|
|
'''Evaluate the namespace up until the cursor and give a list of
|
|
|
|
options or complete the name if there is only one.'''
|
2009-10-31 13:31:23 +00:00
|
|
|
bl_idname = "console.autocomplete"
|
|
|
|
bl_label = "Console Autocomplete"
|
|
|
|
bl_register = False
|
2009-07-16 00:50:27 +00:00
|
|
|
|
2009-10-29 20:55:45 +00:00
|
|
|
def poll(self, context):
|
|
|
|
return context.space_data.console_type == 'PYTHON'
|
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
from console import intellisense
|
|
|
|
|
|
|
|
sc = context.space_data
|
|
|
|
|
|
|
|
console = get_console(hash(context.region))[0]
|
|
|
|
|
|
|
|
current_line = sc.history[-1]
|
|
|
|
line = current_line.line
|
|
|
|
|
|
|
|
if not console:
|
|
|
|
return ('CANCELLED',)
|
|
|
|
|
|
|
|
if sc.console_type != 'PYTHON':
|
|
|
|
return ('CANCELLED',)
|
|
|
|
|
|
|
|
# This function isnt aware of the text editor or being an operator
|
|
|
|
# just does the autocomp then copy its results back
|
|
|
|
current_line.line, current_line.current_character, scrollback = \
|
|
|
|
intellisense.expand(
|
|
|
|
line=current_line.line,
|
|
|
|
cursor=current_line.current_character,
|
|
|
|
namespace=console.locals,
|
|
|
|
private='-d' in sys.argv)
|
|
|
|
|
|
|
|
# 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 scrollback:
|
|
|
|
add_scrollback(scrollback, 'INFO')
|
|
|
|
|
|
|
|
context.area.tag_redraw()
|
|
|
|
|
|
|
|
return ('FINISHED',)
|
2009-07-16 00:50:27 +00:00
|
|
|
|
|
|
|
|
|
|
|
bpy.types.register(CONSOLE_HT_header)
|
|
|
|
bpy.types.register(CONSOLE_MT_console)
|
2009-07-19 00:49:44 +00:00
|
|
|
bpy.types.register(CONSOLE_MT_report)
|
2009-07-16 00:50:27 +00:00
|
|
|
|
|
|
|
bpy.ops.add(CONSOLE_OT_exec)
|
|
|
|
bpy.ops.add(CONSOLE_OT_autocomplete)
|