[Maya - python] Super weird behaviour of M3dView

I’m having this problem with Maya M3dView class. I’m creating a super simple widget with this code:

def get_preview_window():
    win = cmds.window()
    layout = cmds.paneLayout()
    model_editor = cmds.modelPanel()

Now I’m trying to grab a screenshot from this with this code:

import ctypes
import maya.OpenMaya as om
import maya.OpenMayaUI as omui
from PySide.QtGui import QImage

view = omui.M3dView.active3dView()
width = view.portWidth()
height = view.portHeight()
img = om.MImage()
view.readColorBuffer(img, False)
ptr = ctypes.cast(img.pixels().__long__(), ctypes.POINTER(ctypes.c_char))
ptrAsStr = ctypes.string_at(ptr, width * height * 4)
qimg = QImage(ptrAsStr, width, height, QImage.Format_ARGB32)
qimg = qimg.mirrored(horizontal=False, vertical=True)

Now - the thing is that the grabbed image has a proper size of an embedded viewport (the one created with mel) but the actual image comes from the main viewport (“modelPanel4”) so it’s cropped and it’s completely not what I want.
Can anyone help me with that? :confused:

PS. One IMPORTANT thing - it stops to work when you open the viewport window, navigate it and then try to grab it. If you grab it right after creation everything works just fine.

active3dView() returns a class, but is it a static object, or does it change when the active viewport changes? Does the active viewport change while the script is operating?

Ok - I’ve forgot to post a reply here. It’s a Maya 2016 viewport 2.0 bug. In previous Maya versions it’s working properly, and in 2016 it works fine with legacy viewport. Already reported to Autodesk. Took me 2 days to figure out that it’s a bug :confused:

Btw - active3dView() is a static method that returns you an instance of the M3dView class. However you can change your active screen after obtaining it, it still points to the viewport gathered with active3dView().

Ok - the problem was actually with the floating/fixed point pixel buffers. MImage by default is kByte and the viewport 2.0 is kFloat.
Now, whenever I’ve passed a kByte MImage to M3dView::readColorBuffer() the image buffer was actually filled with some random data that by some strange accident made some sense (output image contained a part of a different viewport so it was hard to determine that it was in fact a trash, I’ve figured it out because calling MImage::create gave the same results as readColorBuffer). Passing the MImage created as kFloat to the readColorBuffer fixes this issue so the code below works just fine.

# grab active Model editor view to QImage
import ctypes
import maya.OpenMaya as om
import maya.OpenMayaUI as omui
from PySide.QtGui import QImage, QWidget
from shiboken import wrapInstance
import uuid

view = omui.M3dView.active3dView()
model_editor_qwidget = wrapInstance(long(view.widget()), QWidget)

view.refresh(True, True)
img = om.MImage()
if view.getRendererName() == view.kViewport2Renderer:
    print "Saving float point viewport"
    img.create(view.portWidth(), view.portHeight(), 4, om.MImage.kFloat)
    img.convertPixelFormat(om.MImage.kByte )
    print "Saving fixed point viewport"

pixels_size = view.portWidth() * view.portHeight() * bg_image.depth()
pixels_pointer = bg_image.pixels()
# --------------------------------------- convert the frame buffer to str
pp = uuid.ctypes.cast(pixels_pointer.__long__(), uuid.ctypes.POINTER(uuid.ctypes.c_char))
pixels_string = uuid.ctypes.string_at(pp, pixels_size)

# ------------------------------------------ make the QImage from the str
image = QImage(pixels_string, width, height, QImage.Format_RGB32).mirrored(horizontal=False, vertical=True)
image.save("c:/qimage.png", "PNG")

I am aware that this whole viewport capture method is currently obsolete. I think that before the release of the next Maya version I’ll have to invent “the proper way” of capturing viewports with python. Meanwhile a great “thank you” to the Autodesk API team for the tip about the MImage bit depth, I would NEVER figure it on myself.

PS. Even if I’ve had Floating Point Render Target disabled in the hardwareRenderGlobals the VP2.0 readColorBuffer still needs a floating point MImage.

I am currently bumping my head on a similar issue, and im wondering if you (or anyone) ever managed to
capture the viewport using the new “proper” way ?
any snippet would help

  • capture a specific viewport , possibly with specific resolution, and save an image file (png, jpg, exr)
  • capture the image even when the viewport is not refreshing (off screen render)