基于VTK的Qt应用程序开发

VTK附带的程序示例中大多是基于控制台的,作为可视化开发工具包,VTK也可以与很多流行的GUI开发工具整合,比如MFC、Qt(题外话:Qt已经被Digia从诺基亚手中收购了,Qt现在的链接是:
http://qt-project.org/,也有已经编译好的版本:
http://code.google.com/p/qt-msvc-installer/downloads/list直接下载安装。可能因为大学课程里会教授MFC的内容,一些非计算机专业的会偏向于采用MFC,个人觉得,对于非计算机专业而言,如果一定要选择一种GUI工具做开发的话,建议用Qt,容易上手,学习周期短)、FLTK(
http://www.fltk.org/,FLTK也是跨平台的,是一种比较轻便的GUI工具,VTK官方发布版本没有提供对FLTK的接口,但可以借助类
vtkFlRenderWindowInteractor,来实现VTK与FLTK的整合)等等,VTK的源码目录里(VTK-5.10\\Examples\\GUI)包含有VTK与Qt、MFC、Tcl等工具的整合。考虑到VTK对Qt的特殊照顾(VTK提供了很多针对Qt的类可以非常方便地与Qt整合),以及Qt自身的一些性质(如易用性、跨平台等),我们参考了VTK自带的一些例子,给出了VTK与Qt整合的详细步骤。

1.   CMakeLists.txt文件

我们已经知道了VTK工程的管理是用CMake的,而Qt自身有qmake工具,如果对于一些小工程而言,单纯的Qt程序用qmake来构建工程,确实很方便,但如果随着工程复杂度的增加以及工程依赖其他的函数库时,使用CMake来管理工程或许是一个明智的选择。而且随着你对CMake语法的了解,你会发现用CMake来管理工程是一件非常棒的事情。

我们先看看对于单纯的Qt工程,怎么来写CMakeLists.txt脚本文件。

1.1 用CMake来管理Qt工程

官方对于这个话题给出的解释在这里。我们引用一下这篇博文的图,然后给出每句CMakeLists.txt脚本的注释,结合这个图以及脚本的注释,相信你应该能明白了。

 

#----------------------------------------------
# 下面这两行,没什么好解释的
cmake_minimum_required( VERSION 2.8 )
project( YourProjectName )
 
#----------------------------------------------
# 下面这两行,也没什么好解释的
find_package( Qt4 REQUIRED )
include( ${QT_USE_FILE} )
 
#----------------------------------------------
# 程序所有源文件。<TODO:在此处添加源文件>
# 定义变量Project_SRCS,其值为所列的文件列表
SET( Project_SRCS
    main.cpp
  )
 
#----------------------------------------------
# 程序所有UI文件。<TODO:在此处添加UI文件>
# 定义变量Project_UIS,其值为所列的文件列表
SET( Project_UIS
    YourQtWindows.ui
)
 
#----------------------------------------------
# 所有包含Q_OBJECT的头文件。<TODO:在此处添加头文件>
# 定义变量Project_MOC_HDRS,其值为所列的文件列表
SET( Project_MOC_HDRS
    YourQtProjectFiles.h
)
 
#-----------------------------------------------
# 通过Qt的uic.exe生成UI文件对应的ui_XXXX.h文件
# 将生成的ui_XXXX.h文件放在变量Project_UIS_H里,
# QT4_WRAP_UI就是干这个事情。
QT4_WRAP_UI( Project_UIS_H ${Project_UIS} )
 
#-----------------------------------------------
# 通过Qt的moc.exe生成包含Q_OBJECT的头文件对应的
# moc_XXXX.cxx文件,将生成的moc_XXXX.cxx文件放在
# 变量Project_MOC_SRCS里。QT4_WRAP_CPP就是干这个事情。
QT4_WRAP_CPP( Project_MOC_SRCS ${Project_MOC_HDRS} )
 
#-----------------------------------------------
# Qt的MOC和UIC程序生成的moc_XXXX.cxx和ui_XXXX.h
# 等文件是存放在CMake的“Where to build the binaries"
# 里指定的目录里,所以必须都这些路径包含进来。
INCLUDE_DIRECTORIES( ${Project_SOURCE_DIR}
                     ${CMAKE_CURRENT_BINARY_DIR}
                   )
 
#-----------------------------------------------                          
# Qt程序如果有资源文件(*.qrc),要包含资源文件,
# 然后用Qt的rcc.exe生成相应的qrc_XXXX.cpp文件。
# QT4_ADD_RESOURCES就是干这个事情。
SET( Project_RCCS YourProject.qrc)
QT4_ADD_RESOURCES( Project_RCC_SRCS ${Project_RCCS})
 
#-----------------------------------------------
# 根据程序的cpp文件、头文件以及中间生成的ui_XXXX.h、
# moc_XXXX.cxx、qrc_XXXX.cxx等生成可执行文件,并链接
# Qt的动态库(Qt的动态库都定义在QT_LIBRARIES变量里了)
ADD_EXECUTABLE( YourProjectName
                ${Project_SRCS}
                ${Project_UIS_H}
                ${Project_MOC_SRCS}
                ${Project_RCC_SRCS}                           
              )
TARGET_LINK_LIBRARIES ( YourProjectName ${QT_LIBRARIES} )

1.2 用CMake来管理Qt与VTK工程

我们在上面的基础上添加VTK相关的CMake脚本文件,如下:

#----------------------------------------------------------------------------------
cmake_minimum_required( VERSION 2.8 )
project( CombineQtAndVTK )
 
#----------------------------------------------------------------------------------
find_package( VTK REQUIRED )
find_package( Qt4 REQUIRED )
 
include( ${VTK_USE_FILE} )
include( ${QT_USE_FILE} )
 
#----------------------------------------------------------------------------------
SET( PROJECT_SRCS
    main.cpp
    ProjectMainWindow.cpp
    )
 
SET( PROJECT_UIS
    ProjectMainWindow.ui
)
 
SET( PROJECT_MOC_HDRS
  ProjectMainWindow.h
)
 
#----------------------------------------------------------------------------------
QT4_WRAP_UI( PROJECT_UIS_H
             ${PROJECT_UIS}
           )
 
QT4_WRAP_CPP( PROJECT_MOC_SRCS
              ${PROJECT_MOC_HDRS}
            )
 
#----------------------------------------------------------------------------------
INCLUDE_DIRECTORIES( ${PROJECT_SOURCE_DIR}
                     ${CMAKE_CURRENT_BINARY_DIR}
                     ${VTK_DIR}
                   )
 
ADD_EXECUTABLE( CombineQtAndVTK
                ${PROJECT_SRCS}
                ${PROJECT_UIS_H}
                ${PROJECT_MOC_SRCS}
              )
 
TARGET_LINK_LIBRARIES ( CombineQtAndVTK
  ${VTK_LIBRARIES}
  QVTK
  )

以上的脚本除了红色字体标注的跟1.1注释的不太像之外,其他的都一样,不再解释。

1.3 CMake脚本里增加工程环境变量的加载

很多非计算机专业的用户在使用VTK进行编程时,经常会碰到类似下图所示的一些错误。

 

碰到这样的错误以后,可能很多用户就不知道怎么处理了,其实上面的提示信息已经写得非常清楚了,就是缺少“vtkCommon.dll”文件。但是又有人会说:我的电脑里明明有这个文件存在啊,为什么会找不到呢?

一般的解决方法可能是:

方法一:将缺少的dll文件全部拷贝的工程的Debug或者Release目录下(拷贝的时候要注意你编译的VTK是Debug版本的还是Release版本的,如果拷错的话,又会出现其他不可预知的错误了)。但是这个方法是你每建一个工程,运行工程之前得把缺少的动态库文件又要拷贝过去,如果你不嫌麻烦的话,可以采用。

方法二:将缺少的dll文件全部拷贝到Windows系统的目录下,即C:\\Windows\\system32或者C:\\Windows\\system目录下,这个方法是你拷贝一次,以后再基于你拷贝的VTK动态库的工程运行的时候问题都解决了。但它同样有一个问题,假如你电脑里的VTK升级成别的版本,重新编译了一份动态库,或者是同时在你电脑里编译了好几个版本的VTK,这个时候就有点凌乱了。

为什么这两种方法都可以解决问题?原来动态编译的程序在启动的时候,会搜索程序所在的目录以及系统环境变量PATH所列的目录,如果这些目录有该程序需要的动态库时,就加载它们,如果没有,就提示无法加载相应动态库的错误。

可以在工程的CMakeLists.txt文件里添加一些脚本,把系统的PATH环境变量作一些更改,在工程启动之前加载这些环境变量。也就是(在工程的CMakeLists.txt最后添加):

#-----------------------------------------------------------------------------------
# Construct a list of paths containing runtime directories for project applications on Windows
set(PROJECT_RUNTIME_PATH  "${VTK_LIBRARY_DIRS}/@VS_BUILD_TYPE@;
${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/@VS_BUILD_TYPE@"
    )
if(QT4_FOUND)
  set(PROJECT_RUNTIME_PATH "${PROJECT_RUNTIME_PATH};${QT_LIBRARY_DIR}/../bin")
endif()
 
include(CreateWindowsBatchScript.cmake)
 
# If we are under Windows, create two batch files which correctly
# set up the environment for the application and for Visual Studio
if(WIN32)
  set(VS_SOLUTION_FILE "${PROJECT_BINARY_DIR}/${PROJECT_NAME}.sln")
  foreach(VS_BUILD_TYPE debug release)
    CreateWindowsBatchScript("${CMAKE_SOURCE_DIR}/StartVS.bat.in"
      ${PROJECT_BINARY_DIR}/StartVS_${VS_BUILD_TYPE}.bat
      ${VS_BUILD_TYPE})
  endforeach()
endif(WIN32)

以上的脚本也不是特别复杂,但提到了两个文件:CreateWindowsBatchScript.cmake以及StartVS.bat.in。这两个文件的内容分别是:

CreateWindowsBatchScript.cmake:

 

function(CreateWindowsBatchScript in out build_type)
  if(VTK_DIR)
    set(VTK_BIN_DIR "${VTK_DIR}/bin/${build_type}")
  else()
    set(VTK_BIN_DIR)
  endif()
 
  set(VS_BUILD_TYPE ${build_type})
  configure_file(${in} ${out} @ONLY)
  # substitute again
  configure_file(${out} ${out} @ONLY)
endfunction()

StartVS.bat.in

 

@set CL=/D_CRT_SECURE_NO_DEPRECATE /D_CRT_NONSTDC_NO_DEPRECATE
@set LINK=/LARGEADDRESSAWARE
 
PATH=@PROJECT_RUNTIME_PATH@;%PATH%
"@VS_SOLUTION_FILE@"

将工程通过CMake的configure->generate以后,即可生成StartVS_debug.bat和StartVS_release.bat两个脚本文件。如果你要编译、运行Debug版本的工程,即双击StartVS_debug.bat文件打开对应的工程,同理,Release版本的也一样。一旦按这种方式打开相应的工程,就不用再担心类似“无法加载***.dll文件”的错误了。如果你的程序还增加了ITK等函数库,也可以照着上面的脚本作相应的修改。

注意:使用时将CreateWindowsBatchScript.cmakeStartVS.bat.in两个文件与工程的CMakeLists.txt放在同一级目录里。即类似下图的目录结构:

 

2.   用QVTKWidget整合Qt&VTK

Qt与VTK的整合可以使用VTK提供的类QVTKWidget,看这个类名就知道这个类其实就是一个Qt里的Widget (QVTKWidget派生自QWidget),所以可以把它当作普通的Qt里的Widget来使用,甚至可以在Qt Designer里像Qt的其他标准控件一样拖来拖去。

2.1 在Qt Designer里集成

要实现QVTKWidget在Qt Designer里像Qt的其他标准控件一样拖来拖去,需要把编译生成的QVTKWidgetPlugin.dll/QVTKWidgetPlugin.lib(Release版本)复制到Qt的安装目录里的plugins\\designer目录下。完了以后,你会在Qt Designer里面看到如下的控件:

 

2.2 读入一幅图像,并在Qt界面上显示

接下来,我们来完成一个小功能,就是读入一幅JPG图像,然后在Qt界面上,用VTK来显示。功能非常简单,程序也非常简单。上代码:

ProjectMainWindow.h:

#ifndef Project_MainWindow_H
#define Project_MainWindow_H
 
#include <QMainWindow>
#include "ui_ProjectMainWindow.h"
 
#include <vtkSmartPointer.h>
 
class vtkImageViewer2;
class vtkRenderer;
 
 
class ProjectMainWindow : public QMainWindow, public Ui::ProjectMainWindow
{
       Q_OBJECT
 
public:
       ProjectMainWindow();
       ~ProjectMainWindow();
 
private slots:
       //响应打开图像文件的槽函数
       void onOpenSlot();
 
private:
       vtkSmartPointer< vtkImageViewer2 > m_pImageViewer;
       vtkSmartPointer< vtkRenderer > m_pRenderder;
};
 
#endif

ProjectMainWindow.cpp:

#include "ProjectMainWindow.h"
 
#include <QFileDialog>
#include <QDir>
 
#include <vtkRenderWindow.h>
#include <vtkRenderer.h>
#include <vtkImageViewer2.h>
#include <QVTKWidget.h>
#include <vtkJPEGReader.h>
#include <vtkImageActor.h>
 
ProjectMainWindow::ProjectMainWindow()
{
       setupUi(this);
 
       m_pImageViewer  = vtkSmartPointer< vtkImageViewer2 >::New();
       m_pRenderder      = vtkSmartPointer< vtkRenderer >::New();
 
       // 设置m_QVTKWidget的渲染器
       m_QVTKWidget->GetRenderWindow()->AddRenderer(m_pRenderder);
 
       //连接打开的信号与相应的槽
       connect( m_OpenAction, SIGNAL( triggered() ), this, SLOT( onOpenSlot() ) );
}
 
ProjectMainWindow::~ProjectMainWindow()
{
}
 
void ProjectMainWindow::onOpenSlot()
{
       QString filter;
       filter = "JPEG image file (*.jpg *.jpeg)";
 
       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<vtkJPEGReader> reader = vtkSmartPointer<vtkJPEGReader>::New();
       reader->SetFileName(fileName_str);
 
       // 将reader的输出作为m_pImageViewer的输入,并设置m_pImageViewer与渲染器m_pRenderer的关联
       m_pImageViewer->SetInput(reader->GetOutput());
       m_pImageViewer->UpdateDisplayExtent();
       m_pImageViewer->SetRenderWindow(m_QVTKWidget->GetRenderWindow());
       m_pImageViewer->SetRenderer(m_pRenderder);
       m_pImageViewer->SetupInteractor(m_QVTKWidget->GetRenderWindow()->GetInteractor());
       m_pImageViewer->SetSliceOrientationToXY(); //默认就是这个方向的
       m_pImageViewer->GetImageActor()->InterpolateOff();
       m_pRenderder->ResetCamera();
       m_pRenderder->DrawOn();
       m_QVTKWidget->GetRenderWindow()->Render();
}

程序运行结果:

 

2.3 用vtkEventQtSlotConnect实现VTK事件与Qt槽的连接

类vtkEventQtSlotConnect可以实现VTK的事件与Qt的槽函数的连接,VTK的事件主要在vtkCommand.h文件里定义,包括鼠标单击、鼠标双击、鼠标移动等等,如:

vtkCommand::ProgressEvent

vtkCommand::ErrorEvent

vtkCommand::WarningEvent

vtkCommand::PickEvent

vtkCommand::StartPickEvent

vtkCommand::EndPickEvent

vtkCommand::CharEvent

vtkCommand::KeyPressEvent

vtkCommand::KeyReleaseEvent

vtkCommand::LeftButtonPressEvent

vtkCommand::LeftButtonReleaseEvent

vtkCommand::MouseMoveEvent

……

具体的代码实现:

private slots:
       //响应鼠标移动的消息,实时输出鼠标的当前位置
       void updateCoords(vtkObject* obj);
 
private:
       vtkEventQtSlotConnect* m_Connections;

源文件:

//构造函数里:
       m_Connections = vtkEventQtSlotConnect::New();
       m_Connections->Connect(m_QVTKWidget->GetRenderWindow()->GetInteractor(),
              vtkCommand::MouseMoveEvent,
              this,
              SLOT(updateCoords(vtkObject*)));
 
//槽函数的实现
void ProjectMainWindow::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]);
       m_StatusBar->showMessage(str);
}

程序运行结果:

 

示例代码及该博文文档下载地址:http://download.csdn.net/detail/www_doling_net/5137375


==========欢迎转载,转载时请保留该声明信息==========

版权归@东灵工作室所有,更多信息请访问东灵工作室


教程系列导航:http://blog.csdn.net/www_doling_net/article/details/8763686

================================================

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片