3.2 创建应用程序
本节介绍使用四种编程语言Tcl,C++,Java和Python开发VTK应用程序所需的基本信息。阅读完此介绍之后,您应该跳到讨论您感兴趣的语言使用的小节。除了为您提供有关如何创建和运行简单应用程序的说明之外,每节还将向您展示如何利用该语言的回调。
用户方法、观察者和命令
回调(或用户方法)是使用主题/观察者和命令设计模式在VTK中实现的。这意味着VTK中几乎每个类(vtkObject的每个子类)都有一个AddObserver()方法,该方法可用于从VTK设置回调。观察者查看在对象上调用的每个事件,如果它与观察者正在观察的事件之一匹配,则将调用关联的命令(即回调)。例如,所有VTK筛选器都在开始执行之前立即调用StartEvent。如果您添加了一个监视StartEvent的观察器,则每次过滤器开始执行时都会调用该观察器。考虑以下Tcl脚本,该脚本创建vtkElevationFilter的实例,并为StartEvent添加观察者以调用过程PrintStatus。
proc PrintStatus {} {
puts "Starting to execute the elevation filter"
}
vtkElevationFilter foo
foo AddObserver StartEvent PrintStatus
VTK支持的所有语言均提供这种类型的功能(即回调)。接下来的每一节将简要说明如何使用它。第421页的“与窗口系统集成”中提供了有关用户方法的进一步讨论。(本节还讨论了用户界面集成问题。)
要创建自己的应用程序,建议从VTK附带的示例之一开始。可以在源代码发布的VTK/Examples中找到它们。在源代码分发中,示例首先按主题进行组织,然后按语言进行组织。在“VTK/Examples”下,您将找到不同主题的目录,并且在目录下将存在不同语言(例如Tcl)的子目录。
Tcl
Tcl是开始创建VTK应用程序的最简单的语言之一。一旦安装了VTK,就应该能够运行发行版随附的Tcl示例。在UNIX下,您必须按照第14页“在Unix系统上安装VTK”中所述使用Tcl支持编译VTK。在Windows下,您可以按照第10页“在Windows XP,Vista或更高版本上安装VTK”中的说明安装自解压存档。
Windows。 在Windows下,只需双击文件(在此示例中为Cone.tcl)即可运行Tcl脚本。如果没有任何反应,则可能是脚本错误或将Tcl文件与vtk.exe可执行文件相关联的问题。要检测到这一点,您需要先运行vtk.exe。vtk.exe可以在VTK下的开始菜单中找到。执行开始后,将出现一个控制台窗口,其中带有提示。在此提示符下,键入cd命令以更改为Cone.tcl所在的目录。以下是两个示例:
% cd "c:/VTK/Examples/Tutorial/Step1/Tcl"
然后,您需要使用以下命令来获取示例脚本:
% source Cone.tcl
Tcl将尝试执行Cone.tcl,您将能够看到错误或警告消息,否则它们将不会出现。
Unix。 在UNIX下,可以通过运行在二进制目录(例如,VTK-bin/bin/vtk,VTKSolaris/bin/vtk等)中找到的VTK可执行文件(在编译源代码之后)来完成Tcl开发。然后提供Tcl脚本作为第一个参数,如下所示。
unix machine> cd VTK/Examples/Tutorial/Step1/Tcl
unix machine> /home/VTK-Solaris/bin/vtk Cone.tcl
可以按照本节简介中所示设置用户方法。可以在Examples/Tutorial/Step2/Tcl/Cone2.tcl中找到一个示例。关键更改如下所示。
proc myCallback {} {
puts "Starting to render"
}
vtkRenderer ren1
ren1 AddObserver StartEvent myCallback
您可以改为直接将proc的主体直接提供给AddObserver()。
vtkRenderer ren1
ren1 AddObserver StartEvent {puts "Starting to render"}
C++
与大多数其他语言相比,使用C++作为开发语言通常会导致更小,更快和更容易部署应用程序。C++开发还具有以下优点:您无需编译对Tcl,Java或Python的任何其他支持。本节将向您展示如何使用适当的编译器为带有Microsoft Visual C++的PC和UNIX创建简单的VTK C++应用程序。我们将从一个简单的示例Cone.cxx开始,该示例可以在Examples/Tutorial/Step1/Cxx中找到。对于Windows和UNIX,您都可以使用VTK的源代码安装或已安装的二进制文件。这些示例将同时适用于两者。
构建C++程序的第一步是使用CMake生成makefile或工作区文件,具体取决于您的编译器。Cone.cxx附带的CMakeList.txt文件(如下所示)使用FindVTK和UseVTK CMake模块。这些模块尝试找到VTK,然后设置用于构建C++程序的包含路径和链接线。如果他们没有成功找到VTK,则必须手动指定适当的CMake参数,并根据需要重新运行CMake。
PROJECT (Step1)
FIND_PACKAGE(VTK REQUIRED)
IF(NOT VTK_USE_RENDERING)
MESSAGE(FATAL_ERROR
"Example ${PROJECT_NAME} requires VTK_USE_RENDERING.")
ENDIF(NOT VTK_USE_RENDERING)
INCLUDE(${VTK_USE_FILE})
ADD_EXECUTABLE(Cone Cone.cxx)
TARGET_LINK_LIBRARIES(Cone vtkRendering)
Microsoft Visual C++。为Cone示例运行CMake之后,就可以启动Microsoft Visual C++并加载生成的解决方案文件了。对于当前的.NET版本的编译器,其名称为Cone.sln。您现在可以选择一个生成类型(例如Release或Debug)并生成您的应用程序。如果要将VTK集成到不使用CMake的现有项目中,可以将此简单示例中的设置复制到现有工作区中。
现在考虑一个真正的Windows应用程序的示例。该过程与上面的过程非常相似,除了我们创建Windows应用程序而不是控制台应用程序,如下所示。许多代码是标准的Windows代码,任何Windows开发人员都会熟悉。可以在VTK/Examples/GUI/Win32/SimpleCxx/Win32Cone.cxx中找到此示例。请注意,对CMakeLists.txt文件的唯一重要更改是在ADD_EXECUTABLE命令中添加了WIN32参数。
#include "windows.h"
#include "vtkConeSource.h"
#include "vtkPolyDataMapper.h"
#include "vtkRenderWindow.h"
#include "vtkRenderWindowInteractor.h"
#include "vtkRenderer.h"
static HANDLE hinst;
long FAR PASCAL WndProc(HWND, UINT, UINT, LONG);
// define the vtk part as a simple c++ class
class myVTKApp
{
public:
myVTKApp(HWND parent);
~myVTKApp();
private:
vtkRenderWindow *renWin;
vtkRenderer *renderer;
vtkRenderWindowInteractor *iren;
vtkConeSource *cone;
vtkPolyDataMapper *coneMapper;
vtkActor *coneActor;
};
我们首先包括所需的VTK包含文件。接下来,我们有两个标准的Windows原型,然后是一个名为myVTKApp的小类定义。使用C++开发时,应尝试使用面向对象的方法,而不是许多Tcl示例中的脚本编程风格。在这里,我们将应用程序的VTK组件封装为一个小类。
这是myVTKApp的构造函数。如您所见,它分配了所需的VTK对象,设置了它们的实例变量,然后将它们连接起来以形成可视化管道。除vtkRenderWindow之外,大多数代码都是简单的VTK代码。 此构造函数将HWND句柄带到应包含VTK渲染窗口的父窗口。然后,我们在vtkRenderWindow的SetParentId()方法中使用此方法,以便它将创建其窗口作为传递给构造函数的窗口的子级。
myVTKApp::myVTKApp(HWND hwnd)
{
// Similar to Examples/Tutorial/Step1/Cxx/Cone.cxx
// We create the basic parts of a pipeline and connect them
this->renderer = vtkRenderer::New();
this->renWin = vtkRenderWindow::New();
this->renWin->AddRenderer(this->renderer);
// setup the parent window
this->renWin->SetParentId(hwnd);
this->iren = vtkRenderWindowInteractor::New();
this->iren->SetRenderWindow(this->renWin);
this->cone = vtkConeSource::New();
this->cone->SetHeight( 3.0 );
this->cone->SetRadius( 1.0 );
this->cone->SetResolution( 10 );
this->coneMapper = vtkPolyDataMapper::New();
this->coneMapper->SetInputConnection(this->cone->GetOutputPort());
this->coneActor = vtkActor::New();
this->coneActor->SetMapper(this->coneMapper);
this->renderer->AddActor(this->coneActor);
this->renderer->SetBackground(0.2,0.4,0.3);
this->renWin->SetSize(400,400);
// Finally we start the interactor so that event will be handled
this->renWin->Render();
}
析构函数只是释放在构造函数中分配的所有VTK对象。
myVTKApp::~myVTKApp()
{
renWin->Delete();
renderer->Delete();
iren->Delete();
cone->Delete();
coneMapper->Delete();
coneActor->Delete();
}
这里的WinMain代码是所有标准的Windows代码,并且其中没有VTK引用。如您所见,应用程序可以控制事件循环。事件由本节稍后介绍的WndProc处理。
int PASCAL WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdParam, int nCmdShow)
{
static char szAppName[] = "Win32Cone";
HWND hwnd ;
MSG msg ;
WNDCLASS wndclass ;
if (!hPrevInstance)
{
wndclass.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
wndclass.lpfnWndProc = WndProc ;
wndclass.cbClsExtra = 0 ;
wndclass.cbWndExtra = 0 ;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(NULL,IDI_APPLICATION);
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW);
wndclass.lpszMenuName = NULL;
wndclass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
wndclass.lpszClassName = szAppName;
RegisterClass (&wndclass);
}
hinst = hInstance;
hwnd = CreateWindow ( szAppName, "Draw Window", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 400, 480, NULL, NULL, hInstance, NULL);
ShowWindow (hwnd, nCmdShow);
UpdateWindow (hwnd);
while (GetMessage (&msg, NULL, 0, 0))
{
TranslateMessage (&msg);
DispatchMessage (&msg);
}
return msg.wParam;
}
该WndProc是一个非常简单的事件处理程序。对于完整的应用程序,它要复杂得多,但是关键的集成问题是相同的。在此函数的顶部,我们声明对myVTKApp实例的静态引用。在处理WM_CREATE方法时,我们创建一个Exit按钮,然后构造一个myVTKApp实例,将其句柄传递给当前窗口。vtkRenderWindowInteractor将处理vtkRenderWindow的所有事件,因此您无需在此处处理它们。 可能需要添加代码以处理事件大小调整事件,以便相对于整个用户界面适当调整渲染窗口的大小。如果您未设置vtkRenderWindow的ParentId,它将显示为顶层独立窗口。其他所有内容的行为都应与以前相同。
long FAR PASCAL WndProc (HWND hwnd, UINT message, UINT wParam, LONG lParam)
{
static HWND ewin;
static myVTKApp *theVTKApp;
switch (message)
{
case WM_CREATE:
{
ewin = CreateWindow("button","Exit", WS_CHILD | WS_VISIBLE | SS_CENTER, 0,400,400,60, hwnd,(HMENU)2, (HINSTANCE)GetWindowLong(hwnd,GWL_HINSTANCE), NULL);
theVTKApp = new myVTKApp(hwnd);
return 0;
}
case WM_COMMAND:
switch (wParam)
{
case 2:
PostQuitMessage (0);
if (theVTKApp)
{
delete theVTKApp;
theVTKApp = NULL;
}
break;
}
return 0;
case WM_DESTROY:
PostQuitMessage (0);
if (theVTKApp)
{
delete theVTKApp;
theVTKApp = NULL;
}
return 0;
}
return DefWindowProc (hwnd, message, wParam, lParam);
}
UNIX。 通过运行CMake然后执行make,可以在UNIX上创建C ++应用程序。CMake创建一个makefile,该makefile指定包含路径,链接行和依赖项。然后,make程序使用此makefile编译应用程序。 这将导致您可以运行一个Cone可执行文件。如果Cone.cxx无法编译,请检查make错误并进行更正。确保CMakeCache.txt顶部的值有效。如果它可以编译,但是在尝试运行它时收到错误,则可能需要按照第二章“C++中的用户方法”中所述设置LD_LIBRARY_PATH。通过创建覆盖Execute()方法的vtkCommand的子类,可以在C++中添加用户方法(使用观察者/命令设计模式)。请考虑以下示例,该示例取自VTK/Examples/Tutorial/Step2/Cxx/Cone2.cxx。
class vtkMyCallback : public vtkCommand {
static myCallback *New() {return new vtkMyCallback;}
virtual void Execute(vtkObject *caller, unsigned long, void *)
{
vtkRenderer *renderer = reinterpret_cast<vtkRenderer*>(caller);
cout << renderer->GetActiveCamera()->GetPosition()[0] << " "
<< renderer->GetActiveCamera()->GetPosition()[1] << " "
<< renderer->GetActiveCamera()->GetPosition()[2] << "\n";
}
};
尽管始终将Execute()方法传递给调用对象(调用者),但您无需使用它。如果您确实使用了调用方,则通常需要对实际类型执行SafeDownCast()。例如:
virtual void Execute(vtkObject *caller, unsigned long, void *callData)
{
vtkRenderer *ren = vtkRenderer::SafeDownCast(caller);
if (ren) { ren->SetBackground(0.2,0.3,0.4); }
}
一旦创建了vtkCommand的子类,就可以添加一个观察者,该观察者将在某些事件上调用您的命令。这可以如下进行。
// Here is where we setup the observer,
//we do a new and ren1 will eventually free the observer
vtkMyCallback *mo1 = vtkMyCallback::New();
ren1->AddObserver(vtkCommand::StartEvent,mo1);
mo1->Delete();
上面的代码创建了myCallback的实例,然后在ren1上为StartEvent添加了一个观察者。每当ren1开始渲染时,将调用vtkMyCallback的Execute()方法。删除ren1后,回调也将被删除。
Java
要创建Java应用程序,您必须首先具有一个正常的Java开发环境。 本节提供了有关在Windows或UNIX上使用Sun的JDK 1.3或更高版本的说明。一旦安装了JDK并安装了VTK,就需要设置CLASSPATH环境变量以包含VTK类。在Microsoft Windows下,可以通过右键单击“我的电脑”图标,选择属性选项,然后选择“高级”选项卡,然后单击“环境变量”按钮来进行设置。 然后添加CLASSPATH环境变量,并将其设置为包括您的vtk.jar文件的路径,Wrapping/ ava目录和当前目录。对于Windows构建,它将类似于“ C:\vtk-bin\bin\vtk.jar; C:\vtkbin\Wrapping\ ava;”。在UNIX下,应该将CLASSPATH环境变量设置为类似于“ /yourdisk/vtk-bin/bin/vtk.jar;/yourdisk/vtk-bin/Wrapping/Java ;.”的名称。
下一步是对Java程序进行字节编译。对于初学者,请尝试使用VTK/Examples/Tutorial/Step1/Java在VTK附带的Cone.java示例中进行字节编译(使用javac)。然后,您应该能够使用java命令运行生成的应用程序。它应该显示一个旋转360度的圆锥,然后退出。 下一步是使用提供的示例作为起点来创建自己的应用程序。
public void myCallback()
{
System.out.println("Starting a render");
}
您可以通过传递三个参数来设置回调。第一个是您感兴趣的事件的名称,第二个是类的实例,第三个是您要调用的方法的名称。在此示例中,我们将StartEvent设置为在我身上调用myCallback方法(这是Cone2的实例)。myCallback方法当然必须是Cone2的有效方法,以避免发生错误。(此代码段来自VTK/Examples/Tutorial/Step2/Java/cone2.java。)
Cone2 me = new Cone2();
ren1.AddObserver("StartEvent",me,"myCallback");
Python
如果您已经建立了具有Python支持的VTK,将创建一个vtkpython可执行文件。使用此可执行文件,您应该能够按照以下方式运行Examples/Tutorial/Step1/Python/Cone.py。
vtkpython Cone.py
使用我们的一些示例脚本作为起点,创建您自己的Python脚本很简单。可以通过定义一个函数,然后将其作为参数传递给AddObserver来设置用户方法,如下所示。
def myCallback(obj,event):
print "Starting to render"
ren1.AddObserver("StartEvent",myCallback)
上面显示的示例的完整源代码在VTK/Examples/Tutorial/Step2/Python/Cone2.py中。