forked from bartvdbraak/blender
- access console languages as modules rather then having the py operator call an operator
- workaround for __getattr__ existing for types that dont support it
This commit is contained in:
parent
7f8a24b614
commit
51f2dcd08c
@ -66,160 +66,130 @@ def get_console(console_id):
|
|||||||
return console, stdout, stderr
|
return console, stdout, stderr
|
||||||
|
|
||||||
|
|
||||||
class PyConsoleExec(bpy.types.Operator):
|
# Both prompts must be the same length
|
||||||
'''Execute the current console line as a python expression.'''
|
PROMPT = '>>> '
|
||||||
bl_idname = "console.execute_" + language_id
|
PROMPT_MULTI = '... '
|
||||||
bl_label = "Console Execute"
|
|
||||||
bl_register = False
|
|
||||||
|
|
||||||
# Both prompts must be the same length
|
def execute(context):
|
||||||
PROMPT = '>>> '
|
sc = context.space_data
|
||||||
PROMPT_MULTI = '... '
|
|
||||||
|
|
||||||
# is this working???
|
try:
|
||||||
'''
|
line = sc.history[-1].line
|
||||||
def poll(self, context):
|
except:
|
||||||
return (context.space_data.type == 'PYTHON')
|
return ('CANCELLED',)
|
||||||
'''
|
|
||||||
# its not :|
|
|
||||||
|
|
||||||
def execute(self, context):
|
if sc.console_type != 'PYTHON':
|
||||||
sc = context.space_data
|
return ('CANCELLED',)
|
||||||
|
|
||||||
try:
|
console, stdout, stderr = get_console(hash(context.region))
|
||||||
line = sc.history[-1].line
|
|
||||||
except:
|
|
||||||
return ('CANCELLED',)
|
|
||||||
|
|
||||||
if sc.console_type != 'PYTHON':
|
# Hack, useful but must add some other way to access
|
||||||
return ('CANCELLED',)
|
#if "C" not in console.locals:
|
||||||
|
console.locals["C"] = context
|
||||||
|
|
||||||
console, stdout, stderr = get_console(hash(context.region))
|
# redirect output
|
||||||
|
sys.stdout = stdout
|
||||||
|
sys.stderr = stderr
|
||||||
|
|
||||||
# Hack, useful but must add some other way to access
|
# run the console
|
||||||
#if "C" not in console.locals:
|
if not line.strip():
|
||||||
console.locals["C"] = context
|
line_exec = '\n' # executes a multiline statement
|
||||||
|
else:
|
||||||
|
line_exec = line
|
||||||
|
|
||||||
# redirect output
|
is_multiline = console.push(line_exec)
|
||||||
sys.stdout = stdout
|
|
||||||
sys.stderr = stderr
|
|
||||||
|
|
||||||
# run the console
|
stdout.seek(0)
|
||||||
if not line.strip():
|
stderr.seek(0)
|
||||||
line_exec = '\n' # executes a multiline statement
|
|
||||||
else:
|
|
||||||
line_exec = line
|
|
||||||
|
|
||||||
is_multiline = console.push(line_exec)
|
output = stdout.read()
|
||||||
|
output_err = stderr.read()
|
||||||
|
|
||||||
stdout.seek(0)
|
# cleanup
|
||||||
stderr.seek(0)
|
sys.stdout = sys.__stdout__
|
||||||
|
sys.stderr = sys.__stderr__
|
||||||
|
sys.last_traceback = None
|
||||||
|
|
||||||
output = stdout.read()
|
# So we can reuse, clear all data
|
||||||
output_err = stderr.read()
|
stdout.truncate(0)
|
||||||
|
stderr.truncate(0)
|
||||||
|
|
||||||
# cleanup
|
bpy.ops.console.scrollback_append(text=sc.prompt + line, type='INPUT')
|
||||||
sys.stdout = sys.__stdout__
|
|
||||||
sys.stderr = sys.__stderr__
|
|
||||||
sys.last_traceback = None
|
|
||||||
|
|
||||||
# So we can reuse, clear all data
|
if is_multiline:
|
||||||
stdout.truncate(0)
|
sc.prompt = PROMPT_MULTI
|
||||||
stderr.truncate(0)
|
else:
|
||||||
|
sc.prompt = PROMPT
|
||||||
|
|
||||||
bpy.ops.console.scrollback_append(text=sc.prompt + line, type='INPUT')
|
# insert a new blank line
|
||||||
|
bpy.ops.console.history_append(text="", current_character=0,
|
||||||
|
remove_duplicates=True)
|
||||||
|
|
||||||
if is_multiline:
|
# Insert the output into the editor
|
||||||
sc.prompt = self.PROMPT_MULTI
|
# not quite correct because the order might have changed,
|
||||||
else:
|
# but ok 99% of the time.
|
||||||
sc.prompt = self.PROMPT
|
if output:
|
||||||
|
add_scrollback(output, 'OUTPUT')
|
||||||
|
if output_err:
|
||||||
|
add_scrollback(output_err, 'ERROR')
|
||||||
|
|
||||||
# insert a new blank line
|
return ('FINISHED',)
|
||||||
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 PyConsoleAutocomplete(bpy.types.Operator):
|
def autocomplete(context):
|
||||||
'''Evaluate the namespace up until the cursor and give a list of
|
from console import intellisense
|
||||||
options or complete the name if there is only one.'''
|
|
||||||
bl_idname = "console.autocomplete_" + language_id
|
|
||||||
bl_label = "Python Console Autocomplete"
|
|
||||||
bl_register = False
|
|
||||||
|
|
||||||
def poll(self, context):
|
sc = context.space_data
|
||||||
return context.space_data.console_type == 'PYTHON'
|
|
||||||
|
|
||||||
def execute(self, context):
|
console = get_console(hash(context.region))[0]
|
||||||
from console import intellisense
|
|
||||||
|
current_line = sc.history[-1]
|
||||||
|
line = current_line.line
|
||||||
|
|
||||||
sc = context.space_data
|
if not console:
|
||||||
|
return ('CANCELLED',)
|
||||||
|
|
||||||
console = get_console(hash(context.region))[0]
|
if sc.console_type != 'PYTHON':
|
||||||
|
return ('CANCELLED',)
|
||||||
current_line = sc.history[-1]
|
|
||||||
line = current_line.line
|
|
||||||
|
|
||||||
if not console:
|
# This function isnt aware of the text editor or being an operator
|
||||||
return ('CANCELLED',)
|
# 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)
|
||||||
|
|
||||||
if sc.console_type != 'PYTHON':
|
# Now we need to copy back the line from blender back into the
|
||||||
return ('CANCELLED',)
|
# text editor. This will change when we dont use the text editor
|
||||||
|
# anymore
|
||||||
|
if scrollback:
|
||||||
|
add_scrollback(scrollback, 'INFO')
|
||||||
|
|
||||||
# This function isnt aware of the text editor or being an operator
|
context.area.tag_redraw()
|
||||||
# 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
|
return ('FINISHED',)
|
||||||
# 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',)
|
|
||||||
|
|
||||||
|
|
||||||
class PyConsoleBanner(bpy.types.Operator):
|
def banner(context):
|
||||||
bl_idname = "console.banner_" + language_id
|
sc = context.space_data
|
||||||
|
version_string = sys.version.strip().replace('\n', ' ')
|
||||||
|
|
||||||
def execute(self, context):
|
add_scrollback(" * Python Interactive Console %s *" % version_string, 'OUTPUT')
|
||||||
sc = context.space_data
|
add_scrollback("Command History: Up/Down Arrow", 'OUTPUT')
|
||||||
version_string = sys.version.strip().replace('\n', ' ')
|
add_scrollback("Cursor: Left/Right Home/End", 'OUTPUT')
|
||||||
|
add_scrollback("Remove: Backspace/Delete", 'OUTPUT')
|
||||||
|
add_scrollback("Execute: Enter", 'OUTPUT')
|
||||||
|
add_scrollback("Autocomplete: Ctrl+Space", 'OUTPUT')
|
||||||
|
add_scrollback("Ctrl +/- Wheel: Zoom", 'OUTPUT')
|
||||||
|
add_scrollback("Builtin Modules: bpy, bpy.data, bpy.ops, bpy.props, bpy.types, bpy.context, Mathutils, Geometry, BGL", 'OUTPUT')
|
||||||
|
add_scrollback("", 'OUTPUT')
|
||||||
|
add_scrollback("", 'OUTPUT')
|
||||||
|
sc.prompt = PROMPT
|
||||||
|
|
||||||
add_scrollback(" * Python Interactive Console %s *" % version_string, 'OUTPUT')
|
# Add context into the namespace for quick access
|
||||||
add_scrollback("Command History: Up/Down Arrow", 'OUTPUT')
|
console = get_console(hash(context.region))[0]
|
||||||
add_scrollback("Cursor: Left/Right Home/End", 'OUTPUT')
|
console.locals["C"] = bpy.context
|
||||||
add_scrollback("Remove: Backspace/Delete", 'OUTPUT')
|
|
||||||
add_scrollback("Execute: Enter", 'OUTPUT')
|
|
||||||
add_scrollback("Autocomplete: Ctrl+Space", 'OUTPUT')
|
|
||||||
add_scrollback("Ctrl +/- Wheel: Zoom", 'OUTPUT')
|
|
||||||
add_scrollback("Builtin Modules: bpy, bpy.data, bpy.ops, bpy.props, bpy.types, bpy.context, Mathutils, Geometry, BGL", 'OUTPUT')
|
|
||||||
add_scrollback("", 'OUTPUT')
|
|
||||||
add_scrollback("", 'OUTPUT')
|
|
||||||
sc.prompt = PyConsoleExec.PROMPT
|
|
||||||
|
|
||||||
# Add context into the namespace for quick access
|
return ('FINISHED',)
|
||||||
console = get_console(hash(context.region))[0]
|
|
||||||
console.locals["C"] = bpy.context
|
|
||||||
|
|
||||||
return ('FINISHED',)
|
|
||||||
|
|
||||||
bpy.ops.add(PyConsoleExec)
|
|
||||||
bpy.ops.add(PyConsoleAutocomplete)
|
|
||||||
bpy.ops.add(PyConsoleBanner)
|
|
||||||
|
@ -41,73 +41,40 @@ def shell_run(text):
|
|||||||
|
|
||||||
add_scrollback(output, style)
|
add_scrollback(output, style)
|
||||||
|
|
||||||
|
PROMPT = '$ '
|
||||||
class ShellConsoleExec(bpy.types.Operator):
|
|
||||||
'''Execute the current console line as a python expression.'''
|
|
||||||
bl_idname = "console.execute_" + language_id
|
|
||||||
bl_label = "Console Execute"
|
|
||||||
bl_register = False
|
|
||||||
|
|
||||||
# Both prompts must be the same length
|
|
||||||
PROMPT = '$ '
|
|
||||||
|
|
||||||
# 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',)
|
|
||||||
|
|
||||||
bpy.ops.console.scrollback_append(text=sc.prompt + line, type='INPUT')
|
|
||||||
|
|
||||||
shell_run(line)
|
|
||||||
|
|
||||||
# insert a new blank line
|
|
||||||
bpy.ops.console.history_append(text="", current_character=0,
|
|
||||||
remove_duplicates=True)
|
|
||||||
|
|
||||||
sc.prompt = os.getcwd()+ShellConsoleExec.PROMPT
|
|
||||||
return ('FINISHED',)
|
|
||||||
|
|
||||||
|
|
||||||
class ShellConsoleAutocomplete(bpy.types.Operator):
|
def execute(context):
|
||||||
'''Evaluate the namespace up until the cursor and give a list of
|
sc = context.space_data
|
||||||
options or complete the name if there is only one.'''
|
|
||||||
bl_idname = "console.autocomplete_" + language_id
|
|
||||||
bl_label = "Python Console Autocomplete"
|
|
||||||
bl_register = False
|
|
||||||
|
|
||||||
def poll(self, context):
|
try:
|
||||||
return context.space_data.console_type == 'PYTHON'
|
line = sc.history[-1].line
|
||||||
|
except:
|
||||||
def execute(self, context):
|
|
||||||
from console import intellisense
|
|
||||||
|
|
||||||
sc = context.space_data
|
|
||||||
|
|
||||||
# TODO
|
|
||||||
return ('CANCELLED',)
|
return ('CANCELLED',)
|
||||||
|
|
||||||
|
|
||||||
class ShellConsoleBanner(bpy.types.Operator):
|
|
||||||
bl_idname = "console.banner_" + language_id
|
|
||||||
|
|
||||||
def execute(self, context):
|
|
||||||
sc = context.space_data
|
|
||||||
|
|
||||||
shell_run("bash --version")
|
bpy.ops.console.scrollback_append(text=sc.prompt + line, type='INPUT')
|
||||||
sc.prompt = os.getcwd()+ShellConsoleExec.PROMPT
|
|
||||||
|
shell_run(line)
|
||||||
|
|
||||||
|
# insert a new blank line
|
||||||
|
bpy.ops.console.history_append(text="", current_character=0,
|
||||||
|
remove_duplicates=True)
|
||||||
|
|
||||||
return ('FINISHED',)
|
sc.prompt = os.getcwd()+PROMPT
|
||||||
|
return ('FINISHED',)
|
||||||
|
|
||||||
|
|
||||||
|
def autocomplete(context):
|
||||||
|
# sc = context.space_data
|
||||||
|
# TODO
|
||||||
|
return ('CANCELLED',)
|
||||||
|
|
||||||
|
|
||||||
|
def banner(context):
|
||||||
|
sc = context.space_data
|
||||||
|
|
||||||
|
shell_run("bash --version")
|
||||||
|
sc.prompt = os.getcwd()+PROMPT
|
||||||
|
|
||||||
|
return ('FINISHED',)
|
||||||
|
|
||||||
bpy.ops.add(ShellConsoleExec)
|
|
||||||
bpy.ops.add(ShellConsoleAutocomplete)
|
|
||||||
bpy.ops.add(ShellConsoleBanner)
|
|
||||||
|
@ -87,15 +87,16 @@ class CONSOLE_MT_language(bpy.types.Menu):
|
|||||||
bl_label = "Languages..."
|
bl_label = "Languages..."
|
||||||
|
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
|
import sys
|
||||||
|
|
||||||
layout = self.layout
|
layout = self.layout
|
||||||
layout.column()
|
layout.column()
|
||||||
|
|
||||||
mod = bpy.ops.console
|
# Collect modules with 'console_*.execute'
|
||||||
languages = []
|
languages = []
|
||||||
for opname in dir(mod):
|
for modname, mod in sys.modules.items():
|
||||||
# execute_python, execute_shell etc.
|
if modname.startswith("console_") and hasattr(mod, "execute"):
|
||||||
if opname.startswith("execute_"):
|
languages.append(modname.split('_', 1)[-1])
|
||||||
languages.append(opname.split('_', 1)[-1])
|
|
||||||
|
|
||||||
languages.sort()
|
languages.sort()
|
||||||
|
|
||||||
@ -118,14 +119,14 @@ class ConsoleExec(bpy.types.Operator):
|
|||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
sc = context.space_data
|
sc = context.space_data
|
||||||
|
|
||||||
execute = getattr(bpy.ops.console, "execute_" + sc.language, None)
|
module = __import__("console_" + sc.language)
|
||||||
|
execute = getattr(module, "execute", None)
|
||||||
|
|
||||||
if execute:
|
if execute:
|
||||||
execute()
|
return execute(context)
|
||||||
else:
|
else:
|
||||||
print("Error: bpy.ops.console.execute_" + sc.language + " - not found")
|
print("Error: bpy.ops.console.execute_" + sc.language + " - not found")
|
||||||
|
return ('FINISHED',)
|
||||||
return ('FINISHED',)
|
|
||||||
|
|
||||||
|
|
||||||
class ConsoleAutocomplete(bpy.types.Operator):
|
class ConsoleAutocomplete(bpy.types.Operator):
|
||||||
@ -140,15 +141,14 @@ class ConsoleAutocomplete(bpy.types.Operator):
|
|||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
sc = context.space_data
|
sc = context.space_data
|
||||||
|
module = __import__("console_" + sc.language)
|
||||||
autocomplete = getattr(bpy.ops.console, "autocomplete_" + sc.language, None)
|
autocomplete = getattr(module, "autocomplete", None)
|
||||||
|
|
||||||
if autocomplete:
|
if autocomplete:
|
||||||
autocomplete()
|
return autocomplete(context)
|
||||||
else:
|
else:
|
||||||
print("Error: bpy.ops.console.autocomplete_" + sc.language + " - not found")
|
print("Error: bpy.ops.console.autocomplete_" + sc.language + " - not found")
|
||||||
|
return ('FINISHED',)
|
||||||
return ('FINISHED',)
|
|
||||||
|
|
||||||
|
|
||||||
class ConsoleBanner(bpy.types.Operator):
|
class ConsoleBanner(bpy.types.Operator):
|
||||||
@ -161,14 +161,14 @@ class ConsoleBanner(bpy.types.Operator):
|
|||||||
if not sc.language:
|
if not sc.language:
|
||||||
sc.language = 'python'
|
sc.language = 'python'
|
||||||
|
|
||||||
banner = getattr(bpy.ops.console, "banner_" + sc.language, None)
|
module = __import__("console_" + sc.language)
|
||||||
|
banner = getattr(module, "banner", None)
|
||||||
|
|
||||||
if banner:
|
if banner:
|
||||||
banner()
|
return banner(context)
|
||||||
else:
|
else:
|
||||||
print("Error: bpy.ops.console.banner_" + sc.language + " - not found")
|
print("Error: bpy.ops.console.banner_" + sc.language + " - not found")
|
||||||
|
return ('FINISHED',)
|
||||||
return ('FINISHED',)
|
|
||||||
|
|
||||||
|
|
||||||
class ConsoleLanguage(bpy.types.Operator):
|
class ConsoleLanguage(bpy.types.Operator):
|
||||||
|
@ -258,7 +258,7 @@ void RNA_def_main(BlenderRNA *brna)
|
|||||||
{"brushes", "Brush", "rna_Main_brush_begin", "Brushes", "Brush datablocks."},
|
{"brushes", "Brush", "rna_Main_brush_begin", "Brushes", "Brush datablocks."},
|
||||||
{"worlds", "World", "rna_Main_world_begin", "Worlds", "World datablocks."},
|
{"worlds", "World", "rna_Main_world_begin", "Worlds", "World datablocks."},
|
||||||
{"groups", "Group", "rna_Main_group_begin", "Groups", "Group datablocks."},
|
{"groups", "Group", "rna_Main_group_begin", "Groups", "Group datablocks."},
|
||||||
{"keys", "Key", "rna_Main_key_begin", "Keys", "Key datablocks."},
|
/* {"keys", "Key", "rna_Main_key_begin", "Keys", "Key datablocks."}, */
|
||||||
{"scripts", "ID", "rna_Main_script_begin", "Scripts", "Script datablocks (DEPRECATED)."},
|
{"scripts", "ID", "rna_Main_script_begin", "Scripts", "Script datablocks (DEPRECATED)."},
|
||||||
{"texts", "Text", "rna_Main_text_begin", "Texts", "Text datablocks."},
|
{"texts", "Text", "rna_Main_text_begin", "Texts", "Text datablocks."},
|
||||||
{"sounds", "Sound", "rna_Main_sound_begin", "Sounds", "Sound datablocks."},
|
{"sounds", "Sound", "rna_Main_sound_begin", "Sounds", "Sound datablocks."},
|
||||||
|
@ -1205,6 +1205,11 @@ static PyObject *pyrna_struct_subscript( BPy_StructRNA *self, PyObject *key )
|
|||||||
IDProperty *group, *idprop;
|
IDProperty *group, *idprop;
|
||||||
char *name= _PyUnicode_AsString(key);
|
char *name= _PyUnicode_AsString(key);
|
||||||
|
|
||||||
|
if(RNA_struct_idproperties_check(&self->ptr)==0) {
|
||||||
|
PyErr_SetString( PyExc_TypeError, "this type doesn't support IDProperties");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
if(name==NULL) {
|
if(name==NULL) {
|
||||||
PyErr_SetString( PyExc_TypeError, "only strings are allowed as keys of ID properties");
|
PyErr_SetString( PyExc_TypeError, "only strings are allowed as keys of ID properties");
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -1512,7 +1517,14 @@ static PyObject *pyrna_struct_getattro( BPy_StructRNA *self, PyObject *pyname )
|
|||||||
FunctionRNA *func;
|
FunctionRNA *func;
|
||||||
|
|
||||||
if(name[0]=='_') { // rna can't start with a "_", so for __dict__ and similar we can skip using rna lookups
|
if(name[0]=='_') { // rna can't start with a "_", so for __dict__ and similar we can skip using rna lookups
|
||||||
ret = PyObject_GenericGetAttr((PyObject *)self, pyname);
|
/* annoying exception, maybe we need to have different types for this... */
|
||||||
|
if((strcmp(name, "__getitem__")==0 || strcmp(name, "__setitem__")==0) && !RNA_struct_idproperties_check(&self->ptr)) {
|
||||||
|
PyErr_SetString(PyExc_AttributeError, "StructRNA - no __getitem__ support for this type");
|
||||||
|
ret = NULL;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ret = PyObject_GenericGetAttr((PyObject *)self, pyname);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if ((prop = RNA_struct_find_property(&self->ptr, name))) {
|
else if ((prop = RNA_struct_find_property(&self->ptr, name))) {
|
||||||
ret = pyrna_prop_to_py(&self->ptr, prop);
|
ret = pyrna_prop_to_py(&self->ptr, prop);
|
||||||
|
Loading…
Reference in New Issue
Block a user