VTK與Qt整合的示例

 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


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章