I'm looking for a way to effectively capture the frame buffer during interaction in pyvista
so that I can produce a video afterwards of the model moving around on the screen.
The problem I've encountered is that when I click the screen to interact with the viewer/plotter, no frames are written while the mouse button is pressed and the model is moving to its next position. This results in 'jerky' movements in the video.
Is there a way around this behavior to effectively do something like a screen capture of the plotter window, even when being manipulated? Maybe through direct access to the frame buffer or something similar?
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import pyvista as pv
import numpy as np
from pyvista import examples
import matplotlib as mpl
import matplotlib.pyplot as plt
px = int(round(1920*0.4))
py = int(round(1000*0.4))
mesh = examples.download_st_helens().warp_by_scalar()
p = pv.Plotter()
p.set_background(color='k')
cmap = mpl.cm.get_cmap('viridis')
p.add_mesh(mesh, lighting=True, texture=False, cmap=cmap, smooth_shading=True)
p.show_grid()
p.show(window_size=[px,py], auto_close=False, interactive_update=True)
p.render()
p.open_movie('anim.mp4',framerate=60)
i=0
while (i<100):
i+=1
p.write_frame()
print(i)
p.close()
I have added an example in pure VTK, where a cube is rotated and a smooth video is captured with the rotation. Afterwards, the interactor is started and the user can interact with the scene afterwards.
import os
import vtk
import numpy as np
def vtkRotationMovie(renderWindow, filename='c:/test.avi'):
global degrees
degrees = 0
windowToImageFilter = vtk.vtkWindowToImageFilter()
windowToImageFilter.SetInput(renderWindow)
windowToImageFilter.SetInputBufferTypeToRGB()
windowToImageFilter.ReadFrontBufferOff()
windowToImageFilter.Update()
if os.name == 'nt':
writer = vtk.vtkAVIWriter()
else:
writer = vtk.vtkOggTheoraWriter()
writer.SetInputConnection(windowToImageFilter.GetOutputPort())
writer.SetRate(10) # Not needed for Ogg
try:
os.remove(filename)
except OSError:
pass
writer.SetFileName(filename)
writer.Start()
timerId = renderWindow.GetInteractor().CreateRepeatingTimer(50)
def cb(interactor, event):
global degrees
step = 5
if (degrees > 359):
interactor.DestroyTimer(timerId)
writer.End()
return
interactor.GetRenderWindow().Render()
cam = interactor.GetRenderWindow().GetRenderers().GetFirstRenderer().GetActiveCamera()
cam.Azimuth(step)
cam.OrthogonalizeViewUp()
windowToImageFilter.Modified()
writer.Write()
degrees = degrees + step
renderWindow.GetInteractor().AddObserver('TimerEvent', cb)
renderWindow.GetInteractor().Start()
# create a rendering window and renderer
ren = vtk.vtkRenderer()
renWin = vtk.vtkRenderWindow()
renWin.AddRenderer(ren)
# create a renderwindowinteractor
iren = vtk.vtkRenderWindowInteractor()
iren.SetRenderWindow(renWin)
# create cube
cube = vtk.vtkCubeSource()
# mapper
cubeMapper = vtk.vtkPolyDataMapper()
cubeMapper.SetInputConnection(cube.GetOutputPort())
# actor
cubeActor = vtk.vtkActor()
cubeActor.SetMapper(cubeMapper)
# assign actor to the renderer
ren.AddActor(cubeActor)
ren.SetBackground(.3,.2,.1)
# enable user interface interactor
iren.Initialize()
renWin.Render()
vtkRotationMovie(renWin, filename='./test.avi')
iren.Start()