svn merge -r 23207:23528 https://svn.blender.org/svnroot/bf-blender/trunk/blender
[blender.git] / release / scripts / ui / space_console.py
1 import bpy
2
3 class CONSOLE_HT_header(bpy.types.Header):
4         __space_type__ = 'CONSOLE'
5
6         def draw(self, context):
7                 sc = context.space_data
8                 # text = sc.text
9                 layout = self.layout
10
11                 row= layout.row(align=True)
12                 row.template_header()
13
14                 if context.area.show_menus:
15                         sub = row.row(align=True)
16
17                         if sc.console_type == 'REPORT':
18                                 sub.itemM("CONSOLE_MT_report")
19                         else:
20                                 sub.itemM("CONSOLE_MT_console")
21
22                 layout.itemS()
23                 layout.itemR(sc, "console_type", expand=True)
24
25                 if sc.console_type == 'REPORT':
26                         row = layout.row(align=True)
27                         row.itemR(sc, "show_report_debug", text="Debug")
28                         row.itemR(sc, "show_report_info", text="Info")
29                         row.itemR(sc, "show_report_operator", text="Operators")
30                         row.itemR(sc, "show_report_warn", text="Warnings")
31                         row.itemR(sc, "show_report_error", text="Errors")
32                         
33                         row = layout.row()
34                         row.enabled = sc.show_report_operator
35                         row.itemO("console.report_replay")
36                 else:
37                         row = layout.row(align=True)
38                         row.itemO("console.autocomplete", text="Autocomplete")
39
40 class CONSOLE_MT_console(bpy.types.Menu):
41         __space_type__ = 'CONSOLE'
42         __label__ = "Console"
43
44         def draw(self, context):
45                 layout = self.layout
46                 sc = context.space_data
47
48                 layout.column()
49                 layout.itemO("console.clear")
50                 layout.itemO("console.copy")
51                 layout.itemO("console.paste")
52
53 class CONSOLE_MT_report(bpy.types.Menu):
54         __space_type__ = 'CONSOLE'
55         __label__ = "Report"
56
57         def draw(self, context):
58                 layout = self.layout
59                 sc = context.space_data
60
61                 layout.column()
62                 layout.itemO("console.select_all_toggle")
63                 layout.itemO("console.select_border")
64                 layout.itemO("console.report_delete")
65                 layout.itemO("console.report_copy")
66
67 def add_scrollback(text, text_type):
68         for l in text.split('\n'):
69                 bpy.ops.console.scrollback_append(text=l.replace('\t', '    '), type=text_type)
70
71 def get_console(console_id):
72         '''
73         helper function for console operators
74         currently each text datablock gets its own console - code.InteractiveConsole()
75         ...which is stored in this function.
76         
77         console_id can be any hashable type
78         '''
79         import sys, code
80         
81         try:    consoles = get_console.consoles
82         except:consoles = get_console.consoles = {}
83         
84         # clear all dead consoles, use text names as IDs
85         # TODO, find a way to clear IDs
86         '''
87         for console_id in list(consoles.keys()):
88                 if console_id not in bpy.data.texts:
89                         del consoles[id]
90         '''
91         
92         try:
93                 namespace, console, stdout, stderr = consoles[console_id]
94         except:
95                 namespace = {'__builtins__':__builtins__} # locals()
96                 namespace['bpy'] = bpy
97                 
98                 console = code.InteractiveConsole(namespace)
99                 
100                 import io
101                 stdout = io.StringIO()
102                 stderr = io.StringIO()
103         
104                 consoles[console_id]= namespace, console, stdout, stderr
105                 
106         return namespace, console, stdout, stderr
107
108 class CONSOLE_OT_exec(bpy.types.Operator):
109         '''Execute the current console line as a python expression.'''
110         __idname__ = "console.execute"
111         __label__ = "Console Execute"
112         __register__ = False
113         
114         # Both prompts must be the same length
115         PROMPT = '>>> ' 
116         PROMPT_MULTI = '... '
117         
118         # is this working???
119         '''
120         def poll(self, context):
121                 return (context.space_data.type == 'PYTHON')
122         ''' # its not :|
123         
124         def execute(self, context):
125                 import sys
126                 
127                 sc = context.space_data
128                 
129                 try:
130                         line = sc.history[-1].line
131                 except:
132                         return ('CANCELLED',)
133                 
134                 if sc.console_type != 'PYTHON':
135                         return ('CANCELLED',)
136                 
137                 namespace, console, stdout, stderr = get_console(hash(context.region))
138                 
139                 # redirect output
140                 sys.stdout = stdout
141                 sys.stderr = stderr
142                 
143                 # run the console
144                 if not line.strip():
145                         line_exec = '\n' # executes a multiline statement
146                 else:
147                         line_exec = line
148                 
149                 is_multiline = console.push(line_exec)
150                 
151                 stdout.seek(0)
152                 stderr.seek(0)
153                 
154                 output = stdout.read()
155                 output_err = stderr.read()
156         
157                 # cleanup
158                 sys.stdout = sys.__stdout__
159                 sys.stderr = sys.__stderr__
160                 sys.last_traceback = None
161                 
162                 # So we can reuse, clear all data
163                 stdout.truncate(0)
164                 stderr.truncate(0)
165                 
166                 bpy.ops.console.scrollback_append(text = sc.prompt+line, type='INPUT')
167                 
168                 if is_multiline:        sc.prompt = self.PROMPT_MULTI
169                 else:                           sc.prompt = self.PROMPT
170                 
171                 # insert a new blank line
172                 bpy.ops.console.history_append(text="", current_character=0, remove_duplicates= True)
173                 
174                 # Insert the output into the editor
175                 # not quite correct because the order might have changed, but ok 99% of the time.
176                 if output:                      add_scrollback(output, 'OUTPUT')
177                 if output_err:          add_scrollback(output_err, 'ERROR')
178                 
179                 
180                 return ('FINISHED',)
181
182
183 class CONSOLE_OT_autocomplete(bpy.types.Operator):
184         '''Evaluate the namespace up until the cursor and give a list of options or complete the name if there is only one.'''
185         __idname__ = "console.autocomplete"
186         __label__ = "Console Autocomplete"
187         __register__ = False
188         
189         def poll(self, context):
190                 return context.space_data.console_type == 'PYTHON'
191         
192         def execute(self, context):
193                 
194                 sc = context.space_data
195                 
196                 namespace, console, stdout, stderr = get_console(hash(context.region))
197                 
198                 current_line = sc.history[-1]
199                 line = current_line.line
200                 
201                 if not console:
202                         return ('CANCELLED',)
203                 
204                 if sc.console_type != 'PYTHON':
205                         return ('CANCELLED',)
206                 
207                 # fake cursor, use for autocomp func.
208                 bcon = {}
209                 bcon['cursor'] = current_line.current_character
210                 bcon['console'] = console
211                 bcon['edit_text'] = line
212                 bcon['namespace'] = namespace
213                 bcon['scrollback'] = '' # nor from the BGE console
214                 
215                 
216                 # This function isnt aware of the text editor or being an operator
217                 # just does the autocomp then copy its results back
218                 import autocomplete
219                 autocomplete.execute(bcon)
220                 
221                 # Now we need to copy back the line from blender back into the text editor.
222                 # This will change when we dont use the text editor anymore
223                 if bcon['scrollback']:
224                         add_scrollback(bcon['scrollback'], 'INFO')
225                 
226                 # copy back
227                 current_line.line = bcon['edit_text']
228                 current_line.current_character = bcon['cursor']
229                 
230                 context.area.tag_redraw()
231                 
232                 return ('FINISHED',)
233
234
235
236 bpy.types.register(CONSOLE_HT_header)
237 bpy.types.register(CONSOLE_MT_console)
238 bpy.types.register(CONSOLE_MT_report)
239
240 bpy.ops.add(CONSOLE_OT_exec)
241 bpy.ops.add(CONSOLE_OT_autocomplete)
242