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