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