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