Skip to content

MultipleRenderWindows

vtk-examples/Cxx/Visualization/MultipleRenderWindows


Description

This example creates four render windows. Three of the windows will close individually and closing one window will close all the others. Guess which one!

You can also press 'q' or 'e' to close a window.

If the parameter 's' (C++) or '-s (Python) is specified, then updating the camera position in one window will also update the position in the other windows. Pressing 'q' or 'e' closes all windows. Note that in this mode you must use 'q' or 'e' to close the windows.

Note

The image you see here is the test image. When you run this program you will see an image similar to MultipleViewports however each object will be in a separate window.

Other languages

See (Python)

Question

If you have a question about this example, please use the VTK Discourse Forum

Code

MultipleRenderWindows.cxx

#include <vtkActor.h>
#include <vtkCallbackCommand.h>
#include <vtkCamera.h>
#include <vtkConeSource.h>
#include <vtkCubeSource.h>
#include <vtkCylinderSource.h>
#include <vtkInteractorStyleTrackballCamera.h>
#include <vtkNamedColors.h>
#include <vtkNew.h>
#include <vtkObjectFactory.h>
#include <vtkPolyData.h>
#include <vtkPolyDataMapper.h>
#include <vtkProperty.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkRenderer.h>
#include <vtkSmartPointer.h>
#include <vtkSphereSource.h>

#include <array>
#include <sstream>
#include <vector>

namespace {
std::vector<vtkSmartPointer<vtkPolyDataAlgorithm>> GetSources();

// Define interaction style
class KeyPressInteractorStyle : public vtkInteractorStyleTrackballCamera
{
public:
  static KeyPressInteractorStyle* New();
  vtkTypeMacro(KeyPressInteractorStyle, vtkInteractorStyleTrackballCamera);

  virtual void OnKeyPress()
  {
    // Get the keypress
    vtkRenderWindowInteractor* rwi = this->Interactor;
    std::string key = rwi->GetKeySym();

    if (key == "e" || key == "q" || key == "E" || key == "Q")
    {
      *status = false;
    }

    // Forward events
    vtkInteractorStyleTrackballCamera::OnKeyPress();
  }

public:
  bool* status = nullptr;
};
vtkStandardNewMacro(KeyPressInteractorStyle);

} // namespace

int main(int argc, char* argv[])
{
  auto simultaneousUpdate = false;
  if ((argc > 1) &&
      (std::string(argv[1]) == "s" || std::string(argv[1]) == "S"))
  {
    simultaneousUpdate = true;
  }

  vtkNew<vtkNamedColors> colors;

  // Have some fun with colors
  std::vector<std::string> renBkg{"AliceBlue", "GhostWhite", "WhiteSmoke",
                                  "Seashell"};
  std::vector<std::string> actorColor{"Bisque", "RosyBrown", "Goldenrod",
                                      "Chocolate"};
  // Window sizes and spacing.
  auto width = 300;
  auto height = 300;
  // Add extra space around each window.
  auto dx = 20;
  auto dy = 40;
  auto w = width + dx;
  auto h = height + dy;

  std::vector<vtkSmartPointer<vtkRenderWindowInteractor>> interactors;
  std::vector<vtkSmartPointer<KeyPressInteractorStyle>> styles;
  std::array<bool, 4> running{true, true, true, true};

  vtkCamera* camera = nullptr;
  auto sources = GetSources();

  for (unsigned int i = 0; i < 4; i++)
  {
    vtkNew<vtkRenderWindow> renderWindow;
    renderWindow->SetSize(width, height);

    vtkNew<vtkRenderer> renderer;

    // Use the same initial camera for each renderer.
    if (i == 0)
    {
      camera = renderer->GetActiveCamera();
      camera->Azimuth(30);
      camera->Elevation(30);
    }
    else
    {
      renderer->SetActiveCamera(camera);
    }

    renderWindow->AddRenderer(renderer);

    vtkNew<vtkRenderWindowInteractor> renderWindowInteractor;

    interactors.push_back(renderWindowInteractor);

    renderWindowInteractor->SetRenderWindow(renderWindow);
    renderWindow->Render();
    std::stringstream ss;
    ss << "MultipleRenderWindows " << i;
    renderWindow->SetWindowName(ss.str().c_str());
    renderWindow->SetPosition((i % 2) * w, h - (i / 2) * h);

    // Create a mapper and actor.
    vtkNew<vtkPolyDataMapper> mapper;
    mapper->SetInputConnection(sources[i]->GetOutputPort());

    vtkNew<vtkActor> actor;
    actor->SetMapper(mapper);
    actor->GetProperty()->SetColor(colors->GetColor3d(actorColor[i]).GetData());

    renderer->AddActor(actor);
    renderer->SetBackground(colors->GetColor3d(renBkg[i]).GetData());

    renderer->ResetCamera();

    running[i] = true;
    vtkNew<KeyPressInteractorStyle> style;
    styles.push_back(style);
    styles[i]->status = &running[i];
    interactors[i]->SetInteractorStyle(styles[i]);
    styles[i]->SetCurrentRenderer(renderer);
  }

  // Start the interaction.
  if (simultaneousUpdate)
  {
    // Changes in any window will be simultaneously reflected in the other
    // windows.
    interactors[0]->Initialize();
    // If all are running then process the commands.
    while (std::all_of(running.begin(), running.end(),
                       [](bool i) { return i == true; }))
    {
      for (unsigned int i = 0; i < 4; i++)
      {
        if (running[i])
        {
          interactors[i]->ProcessEvents();
          interactors[i]->Render();
        }
        else
        {
          interactors[i]->TerminateApp();
          std::cout << "Window " << i << " has stopped running." << std::endl;
        }
      }
    }
  }
  else
  {
    // Changes in any window will be reflected in the other
    // windows when you click in the window.
    interactors[0]->Start();
  }

  return EXIT_SUCCESS;
}

namespace {
std::vector<vtkSmartPointer<vtkPolyDataAlgorithm>> GetSources()
{
  std::vector<vtkSmartPointer<vtkPolyDataAlgorithm>> sources;
  for (unsigned i = 0; i < 4; i++)
  {
    // Create a sphere.
    vtkNew<vtkSphereSource> sphere;
    sphere->SetCenter(0.0, 0.0, 0.0);
    sphere->Update();
    sources.push_back(sphere);
    // Create a cone.
    vtkNew<vtkConeSource> cone;
    cone->SetCenter(0.0, 0.0, 0.0);
    cone->SetDirection(0, 1, 0);
    cone->Update();
    sources.push_back(cone);
    // Create a cube.
    vtkNew<vtkCubeSource> cube;
    cube->SetCenter(0.0, 0.0, 0.0);
    cube->Update();
    sources.push_back(cube);
    // Create a cylinder.
    vtkNew<vtkCylinderSource> cylinder;
    cylinder->SetCenter(0.0, 0.0, 0.0);
    cylinder->Update();
    sources.push_back(cylinder);
  }
  return sources;
}

} // namespace

CMakeLists.txt

cmake_minimum_required(VERSION 3.3 FATAL_ERROR)

project(MultipleRenderWindows)

find_package(VTK COMPONENTS 
  vtkCommonColor
  vtkCommonCore
  vtkCommonDataModel
  vtkFiltersSources
  vtkInteractionStyle
  vtkRenderingContextOpenGL2
  vtkRenderingCore
  vtkRenderingFreeType
  vtkRenderingGL2PSOpenGL2
  vtkRenderingOpenGL2
  QUIET
)

if (NOT VTK_FOUND)
  message("Skipping MultipleRenderWindows: ${VTK_NOT_FOUND_MESSAGE}")
  return ()
endif()
message (STATUS "VTK_VERSION: ${VTK_VERSION}")
if (VTK_VERSION VERSION_LESS "8.90.0")
  # old system
  include(${VTK_USE_FILE})
  add_executable(MultipleRenderWindows MACOSX_BUNDLE MultipleRenderWindows.cxx )
  target_link_libraries(MultipleRenderWindows PRIVATE ${VTK_LIBRARIES})
else ()
  # include all components
  add_executable(MultipleRenderWindows MACOSX_BUNDLE MultipleRenderWindows.cxx )
  target_link_libraries(MultipleRenderWindows PRIVATE ${VTK_LIBRARIES})
  # vtk_module_autoinit is needed
  vtk_module_autoinit(
    TARGETS MultipleRenderWindows
    MODULES ${VTK_LIBRARIES}
    )
endif ()

Download and Build MultipleRenderWindows

Click here to download MultipleRenderWindows and its CMakeLists.txt file. Once the tarball MultipleRenderWindows.tar has been downloaded and extracted,

cd MultipleRenderWindows/build

If VTK is installed:

cmake ..

If VTK is not installed but compiled on your system, you will need to specify the path to your VTK build:

cmake -DVTK_DIR:PATH=/home/me/vtk_build ..

Build the project:

make

and run it:

./MultipleRenderWindows

WINDOWS USERS

Be sure to add the VTK bin directory to your path. This will resolve the VTK dll's at run time.