Skip to content

Instantly share code, notes, and snippets.

@Andrej730
Forked from tin2tin/log.py
Created October 5, 2023 11:36
Show Gist options
  • Save Andrej730/073a92ec0e30328dcf5f510047084c1f to your computer and use it in GitHub Desktop.
Save Andrej730/073a92ec0e30328dcf5f510047084c1f to your computer and use it in GitHub Desktop.
Route system output (stdout/stderr) of Blender to the app console (install as an addon)
bl_info = {
"name": "Reroute System Console Ouput to Python Console",
"author": "@tamask, @tin2tin, @Andrej730",
"version": (1, 0),
"blender": (3, 0, 0),
"location": "Python Console header",
"description": "",
"warning": "",
"category": "Development"
}
import os
import sys
import bpy
output = None
input = None
info = None
error = None
write = None
def reset():
global output
global input
global info
global error
global write
output = Stream('OUTPUT')
input = Stream('INPUT')
info = Stream('INFO')
error = Stream('ERROR')
write = output.write
class Stream:
def __init__(self, enum, context=None):
self.text = ''
self.enum = enum
self.line = None
self.newline = False
self.context = context
if not self.context:
self.context = get_console_context()
def write(self, text):
try:
if not self.context:
self.context = get_console_context()
if self.context:
self.console = getattr(
self.context, 'space_data',
self.context['space_data'])
self.scrollback = self.console.scrollback
else:
if self.enum == 'ERROR':
return sys.__stderr__.write(text)
else:
return sys.__stdout__.write(text)
line = self.line
sb = self.scrollback
if len(sb) == 0:
return
text = str(text)
lines = text.replace('\r\n', '\n').split('\n')
if ((line and not
line == sb[len(sb) - 1]) or self.newline):
self.newline = False
self.line = line = None
if line:
line.body += lines[0]
lines = lines[1:]
if lines and lines[len(lines) - 1] == '':
self.newline = True
lines = lines[:-1]
# NOTE: will break after Blender 4.0
# https://projects.blender.org/blender/blender/issues/108763#issuecomment-1037842
for l in lines:
bpy.ops.console.scrollback_append(
self.context, text=l, type=self.enum)
self.line = sb[len(sb) - 1]
except:
import traceback
traceback.print_exc(file=sys.__stderr__)
# no-op interface
def flush(self):
pass
def tell(self):
return 0
def read(self, size=-1):
return ''
def seek(self, offset, whence=0):
pass
def truncate(self, size=None):
pass
@property
def name(self):
return self.enum
def get_console_context():
# do nothing while _RestrictContext
if not hasattr(bpy.context, 'window'):
return {}
# hack to prioritize Scripting tab
for screen in list(bpy.data.screens)[::-1]:
for area in screen.areas:
if area.type == 'CONSOLE':
context = {}
context['area'] = area
for space in area.spaces:
if space.type == 'CONSOLE':
context['space_data'] = space
return context
return {}
def capture_streams():
sys.stdout = info
sys.stderr = error
def release_streams():
sys.stdout = sys.__stdout__
sys.stderr = sys.__stderr__
_console_draw_handle = None
@bpy.app.handlers.persistent
def install(*args, **kwargs):
global _console_draw_handle
wm = bpy.context.window_manager
if wm.capture_console_output:
reset()
context = get_console_context()
if context:
space = context['space_data']
if _console_draw_handle:
space.draw_handler_remove(_console_draw_handle, 'WINDOW')
_console_draw_handle = space.draw_handler_add(capture_streams, tuple(), 'WINDOW', 'POST_PIXEL')
capture_streams()
def uninstall(*args, **kwargs):
global _console_draw_handle
context = get_console_context()
if context:
space = context['space_data']
if _console_draw_handle:
space.draw_handler_remove(_console_draw_handle, 'WINDOW')
_console_draw_handle = None
release_streams()
def toggle_capture(self, context):
if self.capture_console_output:
install()
else:
uninstall()
def console_header_draw(self, context):
layout = self.layout.row()
layout.template_header()
if context.area.show_menus:
layout.menu("CONSOLE_MT_console")
layout.operator("console.autocomplete", text="Autocomplete")
layout.prop(context.window_manager, 'capture_console_output')
def register():
bpy.types.CONSOLE_HT_header.draw = console_header_draw
bpy.types.WindowManager.capture_console_output = bpy.props.BoolProperty(
name='Capture Standard Output', default=False, update=toggle_capture,
description='Route system output (stdout/stderr) to this console',
)
reset()
def unregister():
del bpy.types.WindowManager.capture_console_output
@hannesdelbeke
Copy link

didn't work for me.
ran this in script editor.
then enabled the addon
then ran gderegrer in script editor, error still printed in sys console

@Andrej730
Copy link
Author

Andrej730 commented Oct 23, 2023

@hannesdelbeke are you sure you checked "Capture Standart Output" in python console? Any console errors?
I'm actively using it on Blender 3.6, not sure about other Blender versions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment