I have a button created by my script. I wonder how I can display what the system console displays but inside blender's console. CoDEmanX did this by running a script inside it as shown here, but I want to display what happens in the system console inside blender's console.
3 Answers
- First redirect output from Python's stdout into your own buffer,
see: How to redirect output from 'bpy.ops.*'? - Then run
bpy.ops.console.scrollback_append
with the console space set using an override, see: https://blender.stackexchange.com/a/31398/55
Hello world example that prints text into the first console found.
import bpy
def console_get():
for area in bpy.context.screen.areas:
if area.type == 'CONSOLE':
for space in area.spaces:
if space.type == 'CONSOLE':
return area, space
return None, None
def console_write(text):
area, space = console_get()
if space is None:
return
context = bpy.context.copy()
context.update(dict(
space=space,
area=area,
))
for line in text.split("\n"):
bpy.ops.console.scrollback_append(context, text=line, type='OUTPUT')
console_write("Hello World")
Step 1: Redirect the output to the python console.
You can use the following codes written by @batFINGER (https://blender.stackexchange.com/a/93746/101378):
import bpy
from bpy import context
import builtins as __builtin__
def console_print(*args, **kwargs):
for a in context.screen.areas:
if a.type == 'CONSOLE':
c = {}
c['area'] = a
c['space_data'] = a.spaces.active
c['region'] = a.regions[-1]
c['window'] = context.window
c['screen'] = context.screen
s = " ".join([str(arg) for arg in args])
for line in s.split("\n"):
bpy.ops.console.scrollback_append(c, text=line)
def print(*args, **kwargs):
console_print(*args, **kwargs) # to Python Console
__builtin__.print(*args, **kwargs) # to System Console
The codes above will redirect the outputs in System Console to the Python Console.
Step 2: Add the script to the system path which your Blender can find it.
You can run sys.path
in the python console and then it will output a list of system paths which python in Blender can find the imported scripts.
For example, %APPDATA%\Blender Foundation\Blender\2.92\scripts\addons
.
Write the script in step 1 to a seperated file, such as pycl.py
.
Then use from pycl import print
to use the redirected print
in your scripts:
If all you want is a block of code that you can paste at the start of a script, to have all print("...")
calls within the script show up in Blender's visible Python-console panels, you can use this:
import sys
import bpy
class StdOutOverride:
def write(self, text):
sys.__stdout__.write(text) # also send to standard-output (can comment this out)
if text != '\n': # ignore extra stdout.write('\n') call at end of each print()
for line in text.replace('\t', ' ').split('\n'):
for area in bpy.context.screen.areas:
if area.type == 'CONSOLE':
with bpy.context.temp_override(area=area):
bpy.ops.console.scrollback_append(text=line, type='OUTPUT')
sys.stdout = StdOutOverride()
print("Example text which will display within Blender's Python-console panels.")
The above is not 100% robust though (it can error in certain cases, eg. during rendering, and might mess up line-breaks for large text-blobs that require buffering), so this is the longer (but more robust) version: (I put this into a "prelude" script, which I execute prior to executing the target external script)
import sys
import bpy
class StdOutOverride:
buffer = []
def write(self, text):
sys.__stdout__.write(text) # also send to standard-output (can comment this out)
# at end of each call to print(), a basic write('\n') call gets added; when received, print buffer
if text == '\n':
self.print_to_console()
# for actual-content calls, split text into lines, and print all but last (it'll be printed shortly)
else:
for line in text.replace('\t', ' ').split('\n'):
if len(self.buffer) > 0:
self.print_to_console()
self.buffer.append(line)
def print_to_console(self):
buffer_str = ''.join(map(str, self.buffer))
if hasattr(bpy.context, 'screen') and bpy.context.screen:
for area in bpy.context.screen.areas:
if area.type == 'CONSOLE':
with bpy.context.temp_override(area=area):
# need try-catch, since scrollback_append can error in certain cases (eg. when rendering)
try:
bpy.ops.console.scrollback_append(text=buffer_str, type='OUTPUT')
except Exception as ex:
pass
self.buffer = []
sys.stdout = StdOutOverride()
print("Example text which will display within Blender's Python-console panels.")