Search code examples
pythonscreenshotblender

Blender creates screenshot using python script but not when running in background


Am I crazy or is there some bug with this feature (using python script with blender running in background) ?

1/ Copy / paste the following code (from screenshot.py) inside Blender (switch to Scripting view) and it will take a screenshot but ...

2/ Call this script from command line : (so you can automatize screenshots while you're modifying the scene with your script(s) ... for your blog, a tutorial, etc.)

blender --background --python screenshot.py

And it will crash without giving informations about what is "incorrect" in the context

screenshot.py :

import os, sys

#FIXME: Blender creates screenshot using python but not when running in background
#
# Same result from Ubuntu stock version (2.76) and from ppa (2.78)
#
# Within Blender : unable to fix this last warning but screenshot is generated !
#
#Traceback (most recent call last):
#  File "/usr/share/blender/2.78/scripts/startup/bl_ui/space_text.py", line 32, in draw
#    text = st.text
#AttributeError: 'SpaceView3D' object has no attribute 'text'
#
# From command line (crash) : blender --background --python screenshot.py
# Unable to determine what's wrong in the context
#
#Traceback (most recent call last):
#(...)
#File "/usr/share/blender/2.78/scripts/modules/bpy/ops.py", line 187, in __call__
#    ret = op_call(self.idname_py(), C_dict, kw, C_exec, C_undo)
#RuntimeError: Operator bpy.ops.screen.screenshot.poll() failed, context is incorrect

def screenshot(P_filename, P_path = None):
  import bpy

  L_saveAs = P_filename
  L_saveAs = os.path.join(P_path, L_saveAs) if P_path else os.path.join("/tmp", L_saveAs)
  print("Scene saved in " + L_saveAs)

  #XXX: switching to 3D full view = maximize scene in main window
  #bpy.context.window.screen = bpy.data.screens['3D View Full']
  for window in bpy.context.window_manager.windows:
    screen = window.screen
    print("Window : " + str(window.width) + "x" + str(window.height) + ":" + str(window.x) + "," + str(window.y))
    print("Screen : " + str(screen.name) + ", Scene : " + str(screen.scene.name))
    #for area in bpy.context.screen.areas:
    for area in screen.areas:
      print("Area   : " + str(area.type))
      if area.type == 'VIEW_3D':
        for space in area.spaces:
          print("Space  : " + str(space.type))
          if space.type == 'VIEW_3D':
            #space.viewport_shade = 'RENDERED'
            for region in area.regions:
              print("Region  : " + str(region.type))
              if region.type == 'WINDOW':
                L_altBpyCtx = {                        # defining alternative context (allowing modifications without altering real one)
                  'area'      : area                   # our 3D View (first found)
                , 'blend_data': bpy.context.blend_data # just to suppress PyContext warning, doesn't seem to have any effect
                #, 'edit_text' : bpy.context.edit_text  # just to suppress PyContext warning, doesn't seem to have any effect
                , 'region'    : None                   # just to suppress PyContext warning, doesn't seem to have any effect
                #, 'scene'     : bpy.context.scene
                , 'scene'     : screen.scene
                , 'space'     : space
                , 'screen'    : window.screen
                #, 'window'    : bpy.context.window     # current window, could also copy context
                , 'window'    : window                 # current window, could also copy context
                }
                bpy.ops.screen.screenshot(L_altBpyCtx, filepath = L_saveAs, full = False)
                break #XXX: limit to the window of the 3D View
            break #XXX: limit to the corresponding space (3D View)
        break #XXX: limit to the first 3D View (area)

screenshot("screenshot.png", ".")

3/ By the way, call it without the "--background" argument and it will generate an empty screenshot (size of your window, full of gray). In the meantime, uncomment

#bpy.context.window.screen = bpy.data.screens['3D View Full']

and/or

#space.viewport_shade = 'RENDERED'

And you'll see your Blender interface being affected (switching layout of your views from 'Default' to '3D Full View' and/or your viewport shading from 'Solid', by default, to 'Rendered') ... so the script is actually interpreted but it does not generate the screenshot !

No matter what, the problem seems coming from calling Blender with "--python somescript.py" directly from the command line

I'm probably missing something ?! Thanks for your help


Solution

  • Got the answer from a Blender guy (thanks sergey). Bottom line : it's a limitation since all the scripts passed via command line argument are executed at command line processing time, prior to any OpenGL draw is done !

    The warning is caused by script trying to trick the system and override the context, that context is likely in inconsistent state. When running screenshot from the interface you don't need to override any of the contexts.

    All the scripts passed via command line argument are executed at command line processing time, prior to any OpenGL draw is done. The screenshot operator itself is just reading OpenGL buffer,m without doing any actual drawing (it can't draw anything since that'd be unsafe).

    When running blender in background mode, there is no window created, which also means there is no OpenGL context. You can;t use screenshot in such configuration.

    So thanks for the report, but it's just limitation which can not be easily bypassed.

    Precision : (since the provided code do not show all the attempts made trying to "trick" Blender)

    Sadly, if you are also / still interested in such feature, you'll have to think outside (Blender) box ! Don't waste your time trying to "trick" Blender, like delaying script execution, playing with application handlers, faking script import in a Text editor, etc. ... I've done it for you : it doesn't work, probably for a very similar reason.