主要参考:http://m.blog.csdn.net/blog/u011017966/40984473
安装路径选择D:\Program Files (x86)\Microsoft Visual Studio 10.0,
安装路径选择:D:\Qt
安装结束后添加系统路径:
安装路径为:D:\Program Files (x86)\Digia\Qt5VSAddin
安装完成后就可以在vs2010中看到QT的菜单项了,
这时还不能打开QT的Open QT Project File,需要向VS中添加qt的make路径,如下所示:
QT->QT Options->Add
至此,QT与vs2010完成整合。
注意此方法是在VS2010环境下使用QT,QT Creater环境下的使用还需要调节,方法没有找到合适的,主要估计是因为一些环境变量的设置不对,比较烦。
上面的主要参考资料主要来源于,在此表示感谢:
http://blog.sina.com.cn/s/blog_a6fb6cc90101gynd.html
在上述画面中点击Advanced,选择以下:(下面的选项在常规选项中可以选择,不用点击Advance)
a.BUILD_SHARED_LIBIRARES勾选,此为是否编译动态链接库,选择编译。
b.BUILD_EXAMPLES:此为编译例子,可以选择编译或者不编译,若编译,会在编译生成的bin文件夹下生成例子的 exe可执行文件,方便理解源码的例子是什么运行结果,我选择了不编译。
c.点击上述add Entry,添加路径:qt的安装路径,如下所示:
d.VTK_GROUP_QT,因为选择用QT,所以勾选此项。
a.relase版本编译:如下,选择relase版本,点击菜单栏build->build solution,等待。
b.等待release版本完成后,点击debug版本进行编译,等待。
上面的步骤只是将下载下来的VTK源码在windows的平台上生成了相当于setup文件,就是install工程,还需要在编译 install工程才能在cmake时候的变量CMAKE_INSTALL_PREFIX下生成安装文件,(在前面编译的时候INSTALL工程都是skip的,没有编译),在visual studio 中的solution中找打INSTALL中,点击BUILD ONLY PROJECT,build安装,如下图
编译没有错误的话会在C:\Program Files (x86)\VTK文件(如果cmake时没有更改CMAKE_INSTALL_PREFIX)下生成如下文件:
表示vtk安装成功。(注:INSTALL工程只能在管理员权限内才能成功编译安装,因此必须以管理员权限打开visual studio进行编译)
a.PATH=C:\Program Files(x86)\VTK\bin; 如下图:
b.QTDIR=D:\Qt\Qt5.3.2\5.3\msvc2010_opengl,如下图:
图11
a.http://www.vtk.org/Wiki/VTK/Configure_and_Build#Qt5..2A,截图:
b.http://www.vtk.org/Wiki/VTK/Tutorials/QtSetup#Windows,截图
本篇博文的主要想说的内容是两个部分,一部分是如何用cmake来讲vtk和qt结合起来构建vs2010内的工程的具体方法(学会这个以后如果以后还想在这个里面添加itk就可以直接类比vtk的方法,会简单很多),另一部分主要介绍cmakelists.txt的写法。然后博文的组织上是分为单独vtk的测试vtk+qt的测试两部分。
在自己工作的目录下新建文件夹Cylinder,该文件夹下新建一个cpp文件Cylinder.cpp,该文件为工程源文件,一个CMakeLists.txt文件,该文件为该工程的cmake配置文件。
打开Cylinder.cpp,拷贝下述代码进去保存:
// // This simple example shows how to do basic rendering and pipeline // creation using C++. // #include "vtkCylinderSource.h" #include "vtkPolyDataMapper.h" #include "vtkActor.h" #include "vtkRenderer.h" #include "vtkRenderWindow.h" #include "vtkRenderWindowInteractor.h" #include "vtkProperty.h" #include "vtkCamera.h" int main() { // This creates a polygonal cylinder model with eight circumferential facets. // vtkCylinderSource *cylinder = vtkCylinderSource::New(); cylinder->SetResolution(8); // The mapper is responsible for pushing the geometry into the graphics // library. It may also do color mapping, if scalars or other attributes // are defined. // vtkPolyDataMapper *cylinderMapper = vtkPolyDataMapper::New(); cylinderMapper->SetInputConnection(cylinder->GetOutputPort()); // The actor is a grouping mechanism: besides the geometry (mapper), it // also has a property, transformation matrix, and/or texture map. // Here we set its color and rotate it -22.5 degrees. vtkActor *cylinderActor = vtkActor::New(); cylinderActor->SetMapper(cylinderMapper); cylinderActor->GetProperty()->SetColor(1.0000, 0.3882, 0.2784); cylinderActor->RotateX(30.0); cylinderActor->RotateY(-45.0); // Create the graphics structure. The renderer renders into the // render window. The render window interactor captures mouse events // and will perform appropriate camera or actor manipulation // depending on the nature of the events. // vtkRenderer *ren1 = vtkRenderer::New(); vtkRenderWindow *renWin = vtkRenderWindow::New(); renWin->AddRenderer(ren1); vtkRenderWindowInteractor *iren = vtkRenderWindowInteractor::New(); iren->SetRenderWindow(renWin); // Add the actors to the renderer, set the background and size // ren1->AddActor(cylinderActor); ren1->SetBackground(0.1, 0.2, 0.4); renWin->SetSize(200, 200); // We'll zoom in a little by accessing the camera and invoking a "Zoom" // method on it. ren1->ResetCamera(); ren1->GetActiveCamera()->Zoom(1.5); renWin->Render(); // This starts the event loop and as a side effect causes an initial render. iren->Start(); // Exiting from here, we have to delete all the instances that // have been created. cylinder->Delete(); cylinderMapper->Delete(); cylinderActor->Delete(); ren1->Delete(); renWin->Delete(); iren->Delete(); return 0; } 打开cmakelists.txt,拷贝下述代码进去保存: cmake_minimum_required(VERSION 2.8.5 FATAL_ERROR) project(Cylinder) find_package(VTK REQUIRED) include(${VTK_USE_FILE}) add_executable(${PROJECT_NAME} Cylinder.cpp) target_link_libraries(${PROJECT_NAME} ${VTK_LIBRARIES})
关于上面的代码的讲解,可以参考博文http://blog.csdn.net/www_doling_net/article/details/8532742中的介绍。
点击confige配置,默认选择visual studio 10(32编译版本),配置完成后没有错误如图2选择generate生成工程文件如图3,在我的目录的build文件夹下会生成如下图4:
图2
图3
图4
打开cylinder.sln文件,鼠标右键选择properties,然后选择Linker内的input,在附加依赖项Additional Dependencies 中找到Qt5::Widgets.lib删除,确定。这块的原因是因为我们在编译安装VTK的时候选择了使用QT5,所以每回新建的工程用cmake管理都会添加Qt5::Widgets.lib头文件,但是在cmakeLists.txt文件中又没有配置QT的选项,所以如果不删除Qt5::Widgets.lib就会出现“LINK : fatal error LNK1104: cannot open file'Qt5::Widgets.lib'”的错误。操作如下图5所示:
Cylinder右键--》Properties-》Linker-》Input-》Additional Dependencies-》Edit,找到Qt5::Widgets.lib删除:
图5
完成后,右击cylinder工程,选择SetUp As A StratUp Project,将Cylinder工程设为运行工程。否则编译生成的文件找不到运行启动项。
点击build运行,运行结果如下图6:
然后好多像我这样的新手虽然看了很多也不是很了解到底Cmake这个工具在在这个过程中起到的是什么作用。我在这边说一下我的理解,希望可以对大家有所帮助,有不对的地方也希望大家指出来。
首先我们都知道CMake是个跨平台的工程构建工具,这句话怎么理解呢,在我们安装VTK的时候,我们如果点开看源码,可以发现源码的c++文件的后缀名都是.cxx,这个后缀名是Linux平台的c++源文件,我们常使用的windows系统的c++的源文件后缀名应该是cpp才对,也就是说vtk的源码并不是针对windows平台开发的,那为什么我们可以编译安装呢?这就是cmake程序的作用。Cmake可以将VTK的源代码根据我们的平台(例如vs2010)生成针对这个平台的工程文件,就相当于我们直接在vs2010中新建的工程文件是针对vs2010这个平台的,也就是说,我们下载的VTK的源代码里面只有源代码和CmakeLists.txt文件,它是不针对平台和编译器的,当我们用cmake配置VTK的源码选择编译器位为vs2010后并generate生成后,生成的build文件夹下的内容就是针对vs2010的32位编译器的工程文件了,这个文件夹下面有源文件、有告诉编译器需要连接哪些库文件、需要根据什么顺序编译源文件的工程等的配置文件,而这些配置文件就是cmake针对vs2010生成的。
所以在这个示例中,我们如果用cmake管理我们新建的工程,在我们写的CMakeList.txt内我们就已经写明我们这个工程需要链接哪些头文件库文件等,generate生成的工程文件中就已经有需要添加哪些头文件库文件的配置文件了,所以这也是我们用cmake生成的工程不用手动添加头文件库文件而用vs2010新建的工程需要手动添加的原因。
在这个过程中我们一定要注意通过编译源代码生成二进制的可执行文件的过程是由编译器完成的,跟CMake没有关系,CMake做的只是告诉编译器怎么编译。
当然,如果我们在cmake进行configure时选择Linux平台的Mingw编译器,那生成的工程文件就是针对Linux平台的Mingw编译器的,就实现了我们编写的代码的跨平台。
另外我想说一下后面的开发的步骤。
其实上面给出的CMakeList.txt代码中,我们只需要将project(Cylinder)中的工程名改成我们以后自己的工程名,find_package(VTK REQUIRED),include(${VTK_USE_FILE})每次都不需要改动,add_executable(${PROJECT_NAME} Cylinder.cpp)是由源文件配置编译选项生成目标代码的语句,只需要将后面的源文件Cylinder.cpp改成我们自己的cpp文件即可,然后因为我们默认生成的目标代码的名称就为工程名,而${PROJECT_NAME}就是取工程文件名,所以也是不需要改动的。然后对于语句target_link_libraries(${PROJECT_NAME} ${VTK_LIBRARIES})是连接库文件的语句,对于初学者并不知道该添加哪些库文件的情况下最简单的方法就是将编译生成的VTK所有的库文件全部添加,即${VTK_LIBRARIES},所以就会出现我们上面需要删除Qt5::Widgets.lib的情况。这样确实简单,但是缺点也是显而易见的就是编译的工程中有很多没用的库文件,会拖慢编译速度,增加工程文件体积,所以在弄清楚你用哪些库文件后(这块的内容还是可以参考上面那篇博文内容,里面有讲解如何根据自己所使用的内确定需要的库文件),只添加你需要的是明智的选择的。
然后在我们需要新建一个工程时,我们只需要将我们的以前做的工程原封不动的复制过来,然后更改工程名、cpp文件的名字(当然如果你工程简单可以直接将cpp命名为main.cpp以后也就不用改了,但这不是个好习惯),然后在CMakeList.txt中更改工程名和源文件名,就可以编译工程了,最后在打开的工程中修改cpp文件,编写新的代码即可,这样做的好处是可以用vs的自动代码提示功能了。
这节的最后给大家推荐下http://www.vtk.org/Wiki/VTK/Examples/Cxx就是vtk官网上的例子,从这边进去的例子都是带有CMakeList.txt代码的,大家学习cmake的关于VTK的写法可以上这个网站去看。而且经常不知道怎么实现的功能上面都有例子,非常实用。
在讲这边的测试程序前,我们需要先了解QT的工程的编译步骤和原理,这边的过程请大家参考博文http://blog.csdn.net/www_doling_net/article/details/8668870的1.1节和1.2节,讲的很清楚,特别是那张图,引用出来粘贴在这里图7:
要理解下面贴出来的CmakeLists代码的内容大家一定要返回去看看这篇博文的内容,他讲了QT新建工程的.ui(用QT Designer设计的界面文件),.qrc(资源文件),使用槽函数的头文件(在头文件中包含QObject)等如何通过qt的工具如qmake等转变成vs编译器可以识别的c++代码的,这也是用cmake管理qt工程的基础。
<span style="background-color: rgb(255, 255, 255);">project(testproject) # Find includes in corresponding build directories #这句必须加上,所以标记下这句我们需要复制到我们的工程</span> set(CMAKE_INCLUDE_CURRENT_DIR ON) # Instruct CMake to run moc automatically when needed. #这句是设置自动MOC为开,MOC就是我们用来将qt的ui文件,包含槽函数的头文件等翻译成c++文件的工具, #这个不好控制,所以我们还是按照东灵博客讲的方法自己翻译,所以这句不要 set(CMAKE_AUTOMOC ON) # Find the QtWidgets library #这句是找到QT5组建,因为测试程序中只用到Qt5Widgets这个组件,所以只添加这一个 #这句必须有,标记下等会添加</span> find_package(Qt5Widgets) # Tell CMake to create the helloworld executable #这个和前面VTK的一样,没什么说的 add_executable(helloworld WIN32 main.cpp) # Use the Widgets module from Qt 5. #最后一句是连接库函数,这句话是使用新版的好处,这句话可以自动添加目录、编译定义、和标志符等, target_link_libraries(helloworld Qt5::Widgets) </span>而我们可以在老版的官方的写法下面发现这样的语句:
<span style="background-color: rgb(255, 255, 255);">include_directories(${Qt5Widgets_INCLUDE_DIRS}) add_definitions(${Qt5Widgets_DEFINITIONS}) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${Qt5Widgets_EXECUTABLE_COMPILE_FLAGS}")</span>这句话而这些话就是新版的最后一句话的可以完成的工作,所以我们直接用新版的说法,所以结合东灵博客的写法,VTK的写法和我们这块的讨论,我们可以得到我们测试程序的CMakeLists.txt的写法:
<span style="background-color: rgb(255, 255, 255);">#因为我们使用的新版本,所以最低要求是2.8.11 cmake_minimum_required( VERSION 2.8.11 ) project( CombineQtAndVTK ) #这句话是我们刚才标记添加的内容 set(CMAKE_INCLUDE_CURRENT_DIR ON) #这两句是VTK的内容,没什么说的 find_package( VTK REQUIRED ) include( ${VTK_USE_FILE} ) #这句话是刚才标价添加的 find_package(Qt5Widgets) #这个include是根据东灵博客的内容 include_directories(${PROJECT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ${VTK_DIR}) #这片的内容是设置MOC翻译的内容,我们只需要将东灵博客的QT4改成QT5就可以 SET( PROJECT_SRCS main.cpp testqtwithvtk.cpp testqtwithvtk.h ) SET( PROJECT_UIS testqtwithvtk.ui ) SET( PROJECT_MOC_HDRS testqtwithvtk.h ) QT5_WRAP_UI( PROJECT_UIS_H ${PROJECT_UIS} ) QT5_WRAP_CPP( PROJECT_MOC_SRCS ${PROJECT_MOC_HDRS} ) #这句话就是添加可执行文件了 ADD_EXECUTABLE( CombineQtAndVTK ${PROJECT_SRCS} ${PROJECT_UIS_H} ${PROJECT_MOC_SRCS} ) #这边我们要包含的东西就是vtk和QT5各自需要包含的头文件 TARGET_LINK_LIBRARIES ( CombineQtAndVTK ${VTK_LIBRARIES} Qt5::Widgets )</span>
至此,我们CMakeLists.txt就写完了,虽然我们的源文件内基本没有程序文件,但这并不影响cmake进行配置,如图8所示:
图8
2.2编写源文件
打开cmake generate生成的CombineQtAndVTK.sln,打开工程,我们可以看到下面图9所示的文档结构:
图9
需要说明的是:ui_testqtwithvtk.h是testqtwithvtk.ui生成的文件,所有在testqtwithvtk.ui中所做的更改都会重新生成ui_testqtwithvtk,注意ui_testqtwithvtk我们以后会经常打开研究。moc_testqtwithvtk.cpp是testqtwithvtk.h MOC后生成文件,这个我现在还觉得不需要看,所以不需要太关注。然后我们打开testqtwithvtk.ui,进行UI设计,我们拖一个QVTKWidgets进去,然后再拖一个Label进去,然后新建菜单项New Open Save,并更改对象名称如下图10所示:
图10
然后点击工程进行build,使testqtwithvtk.ui所做的更改可以保存到ui_testqtwithvtk.h文件中。然后打开testqtwithvtk.h,其内容为:
<span style="background-color: rgb(255, 255, 255);">#ifndef TESTQTWITHVTK_H #define TESTQTWITHVTK_H #include <QtWidgets/QMainWindow> #include "ui_testqtwithvtk.h" #include "vtkSmartPointer.h" class vtkImageViewer2; class vtkRenderer; class vtkEventQtSlotConnect; class TestQTWithVTK : public QMainWindow { Q_OBJECT public: TestQTWithVTK(QWidget *parent = 0); ~TestQTWithVTK(); private: Ui::TestQTWithVTKClass ui; private slots: //响应打开图像文件的槽函数 void onOpenSlot(); private: vtkSmartPointer< vtkImageViewer2 > m_pImageViewer; vtkSmartPointer< vtkRenderer > m_pRenderder; private slots: //响应鼠标移动的消息,实时输出鼠标的当前位置 void updateCoords(vtkObject* obj); private: vtkEventQtSlotConnect* m_Connections; }; #endif // TESTQTWITHVTK_H</span>
<span style="background-color: rgb(255, 255, 255);">#include "testqtwithvtk.h" #include <QFileDialog> #include <QDir> #include <vtkRenderWindow.h> #include <vtkRenderer.h> #include <vtkImageViewer2.h> #include <QVTKWidget.h> #include <vtkBMPReader.h> #include <vtkImageActor.h> #include "vtkEventQtSlotConnect.h" #include "vtkCommand.h" TestQTWithVTK::TestQTWithVTK(QWidget *parent) : QMainWindow(parent) { ui.setupUi(this); m_pImageViewer = vtkSmartPointer< vtkImageViewer2 >::New(); m_pRenderder = vtkSmartPointer< vtkRenderer >::New(); m_Connections = vtkEventQtSlotConnect::New(); // 设置m_QVTKWidget的渲染器 ui.qvtkWidget->GetRenderWindow()->AddRenderer(m_pRenderder); //连接打开的信号与相应的槽 connect(ui.actionOpen, SIGNAL( triggered() ), this, SLOT( onOpenSlot() ) ); m_Connections->Connect(ui.qvtkWidget->GetRenderWindow()->GetInteractor(), vtkCommand::MouseMoveEvent, this, SLOT(updateCoords(vtkObject*))); } TestQTWithVTK::~TestQTWithVTK() { m_Connections->Delete(); } void TestQTWithVTK::onOpenSlot() { QString filter; filter = "BMP image file (*.bmp)"; QDir dir; QString fileName = QFileDialog::getOpenFileName( this, QString(tr("打开图像")), dir.absolutePath() , filter ); if ( fileName.isEmpty() == true ) return; // 支持带中文路径的读取 QByteArray ba = fileName.toLocal8Bit(); const char *fileName_str = ba.data(); // 用vtkJPEGReader读取JPG图像 vtkSmartPointer<vtkBMPReader> reader = vtkSmartPointer<vtkBMPReader>::New(); reader->SetFileName(fileName_str); // 将reader的输出作为m_pImageViewer的输入,并设置m_pImageViewer与渲染器m_pRenderer的关联 m_pImageViewer->SetInputConnection(reader->GetOutputPort()); //m_pImageViewer->UpdateDisplayExtent(); m_pImageViewer->SetRenderWindow(ui.qvtkWidget->GetRenderWindow()); m_pImageViewer->SetRenderer(m_pRenderder); m_pImageViewer->SetupInteractor(ui.qvtkWidget->GetRenderWindow()->GetInteractor()); m_pImageViewer->SetSliceOrientationToXY(); //默认就是这个方向的 m_pImageViewer->GetImageActor()->InterpolateOff(); m_pRenderder->ResetCamera(); m_pRenderder->DrawOn(); ui.qvtkWidget->GetRenderWindow()->Render(); } void TestQTWithVTK::updateCoords(vtkObject* obj) { // 获取交互器 vtkRenderWindowInteractor* iren = vtkRenderWindowInteractor::SafeDownCast(obj); // 获取鼠标的当前位置 int event_pos[2]; iren->GetEventPosition(event_pos); QString str; str.sprintf("x=%d : y=%d", event_pos[0], event_pos[1]); ui.statusBar->showMessage(str); } </span>main.cpp的程序内容一般不用更改。