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 <string>
#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.12 FATAL_ERROR)

project(MultipleRenderWindows)

find_package(VTK COMPONENTS 
  CommonColor
  CommonCore
  CommonDataModel
  FiltersSources
  InteractionStyle
  RenderingContextOpenGL2
  RenderingCore
  RenderingFreeType
  RenderingGL2PSOpenGL2
  RenderingOpenGL2
)

if (NOT VTK_FOUND)
  message(FATAL_ERROR "MultipleRenderWindows: Unable to find the VTK build folder.")
endif()

# Prevent a "command line is too long" failure in Windows.
set(CMAKE_NINJA_FORCE_RESPONSE_FILE "ON" CACHE BOOL "Force Ninja to use response files.")
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}
)

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.