1. 程式人生 > >VTK與MFC單文件程式聯合程式設計

VTK與MFC單文件程式聯合程式設計

  興趣需要,想做下VTK與MFC想結合的程式,MFC快要在桌面程式上面失去市場份額了,現在大多使用QT來做,但是本科的時候學的就是MFC,也相對來說比較熟悉,所以就想使用MFC來寫一個簡單的單文件程式。首先我們需要在編譯的時候將USEGUISUPPORT->USEMFC勾選上,才能在MFC平臺上使用VTK。網路上現在大多流行兩種VTK和MFC的方法,其實兩者結合的關鍵就是將VTK的繪製視窗vtkrenderwindow與MFC中的view視窗相一致,讓VTK上的繪製圖形能夠在MFC上的VIEW類上顯示出來。所以網上的方法一:就是使用vtkMFCWindow類,方法二:使用renWin->SetParentId(myhwnd);在這兩個方法的選擇上,水靈大神的在CSDN的部落格上面就是使用方法二,但是在他的期刊論文上則是推薦使用方法一,所以我就方法一進行了測試,但是很不幸,我按網上的步驟一步一步的新增程式碼,編譯無錯,但是在執行時出現異常,查其原因就是出現了空指標,在C++中,出現空指標無疑是最致命的,所以在網友的勸說下,我就使用了第二種方法進行測試,雖然在途中同樣出現空指標錯誤,但是經過思考解決了這一問題,我想其實可能第一種方法的問題也是差不多的,所以將程式碼和結果寫上,以免自己時間久了忘記了。

1.首先將程式中的控制檯程式貼上來,其實就是讀取一個格式為XYZ的三維點座標,並將這些點按順序連線起來,形成一個線圈。

#include <iostream>
#include <vector>
#include "vtkActor.h"
#include "vtkRenderer.h"
#include "vtkRenderWindow.h"
#include "vtkRenderWindowInteractor.h"
#include "vtkProperty.h"
#include "vtkInteractorStyleTrackballCamera.h"
#include "vtkPoints.h"
#include "vtkPolyVertex.h"
#include "vtkUnstructuredGrid.h"
#include "vtkDataSetMapper.h"
#include "vtkPolyData.h"
#include "vtkCellArray.h"
#include "vtkInteractorStyleTrackball.h"
#include "vtkPolyDataMapper.h"
#include "vtkSmartPointer.h"
#include "vtkLine.h"
#include "vtkLineSource.h"
using namespace std;
 
void main(int argc, char* argv[])

    vtkPoints *m_Points = vtkPoints::New();
    vtkCellArray *vertices = vtkCellArray::New();    //_存放細胞頂點,用於渲染(顯示點雲所必須的)
    vtkPolyData *polyData = vtkPolyData::New();
    vtkPolyDataMapper *pointMapper = vtkPolyDataMapper::New();
    vtkActor *pointActor = vtkActor::New();
    vtkRenderer *ren1= vtkRenderer::New();
    vtkRenderWindow *renWin = vtkRenderWindow::New();
    vtkRenderWindowInteractor *iren = vtkRenderWindowInteractor::New();
    vtkInteractorStyleTrackball *istyle = vtkInteractorStyleTrackball::New();
 
    //_讀進點雲資料資訊
    FILE*fp = NULL;
    fp=fopen("point.txt","r");    //讀取TXT中的XYZ座標
    if(!fp)
    {
        
        printf("開啟檔案失敗!!\n");
        exit(0);
    }
    double x=0,y=0,z=0;
    int i = 0;
    while (!feof(fp))
    {
        fscanf(fp,"%lf    %lf    %lf",&x,&y,&z);    
        m_Points->InsertPoint(i,x,y,z);    //_加入點資訊
        //cout<<x<<" "<<y<<" "<<z<<endl;
        cout<<x<<endl;
        vertices->InsertNextCell(1);        //_加入細胞頂點資訊----用於渲染點集
        vertices->InsertCellPoint(i);
        i ++;
    }    
    fclose(fp);
    cout<<"i-1="<<i-1<<endl;
    
    vtkCellArray *lines=vtkCellArray::New();
    for (int m=0;m<=i-1;m++)
    {  
        if(m<i-1)
        {
            vtkSmartPointer<vtkLine> line =
            vtkSmartPointer<vtkLine>::New();
        line->GetPointIds()->SetId(0,m);
        line->GetPointIds()->SetId(1,m+1);
        lines->InsertNextCell(line);
        }
        else
        {
            vtkSmartPointer<vtkLine> line =
                vtkSmartPointer<vtkLine>::New();
            line->GetPointIds()->SetId(0,m);
            line->GetPointIds()->SetId(1,1);
            lines->InsertNextCell(line);
        }
    }
    
    
    
    //_建立待顯示資料來源
 
    polyData->SetPoints(m_Points);        //_設定點集
    polyData->SetVerts(vertices);//_設定渲染頂點
    polyData->SetLines(lines);
    
    pointMapper->SetInput(polyData);
 
    pointActor->SetMapper(pointMapper);
    pointActor->GetProperty()->SetColor(0.0,0.1,1.0);
    pointActor->GetProperty()->SetAmbient(0.5);
    pointActor->GetProperty()->SetPointSize(2);
    //pointActor->GetProperty()->SetRepresentationToWireframe();
    //pointActor->GetProperty()->SetRepresentationToSurface();
 
    ren1->AddActor( pointActor );
    ren1->SetBackground( 0, 0, 0);
 
    renWin->AddRenderer( ren1 );
    renWin->SetSize(800,800);
 
    iren->SetInteractorStyle(istyle);  
    iren->SetRenderWindow(renWin);  //互動
 
    renWin->Render();
    iren->Start();
 
    //刪除各指標
    m_Points->Delete();
    vertices->Delete();
    polyData->Delete();
    pointMapper->Delete();
    pointActor->Delete();
    ren1->Delete();
    renWin->Delete();
    iren->Delete();
    istyle->Delete();
}

(2)其後就是在MFC中建立一個單文件程式,配置好VTK的環境,這個時候既可以採用水靈大神在部落格裡面寫的CmakeList.txt的方法,也可以一個一個在MFC程式中將附加庫路徑,包含庫路徑等設定好,也是同樣的效果。
在設定好環境之後,我們在MFC中建立一個新類CVTK,為了將我們上述的控制檯程式作為一個類的形式寫進去,方便後面的呼叫。

首先在CVTK.h中新增標頭檔案並宣告各類的指標變數

// Vtk.h: interface for the CVtk class.
//
//////////////////////////////////////////////////////////////////////
 
#if !defined(AFX_VTK_H__B872AED3_7A78_4473_BB74_44D3E9117A8F__INCLUDED_)
#define AFX_VTK_H__B872AED3_7A78_4473_BB74_44D3E9117A8F__INCLUDED_
 
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#include <iostream>
#include <vector>
#include "vtkActor.h"
#include "vtkRenderer.h"
#include "vtkRenderWindow.h"
#include "vtkRenderWindowInteractor.h"
#include "vtkProperty.h"
#include "vtkInteractorStyleTrackballCamera.h"
#include "vtkPoints.h"
#include "vtkPolyVertex.h"
#include "vtkUnstructuredGrid.h"
#include "vtkDataSetMapper.h"
#include "vtkPolyData.h"
#include "vtkCellArray.h"
#include "vtkInteractorStyleTrackball.h"
#include "vtkPolyDataMapper.h"
#include "vtkSmartPointer.h"
#include "vtkLine.h"
#include "vtkLineSource.h"
class CVtk  
{
public:
    
    vtkPoints *m_Points;
    vtkCellArray *lines;
    vtkCellArray *vertices;
    vtkPolyData *polyData;
    vtkLine *line;
    vtkPolyDataMapper *pointMapper;
    vtkActor *pointActor;
    vtkRenderer *ren1;
    vtkRenderWindow *renWin;
    vtkRenderWindowInteractor *iren;
    int i;
    //vtkInteractorStyleTrackball *istyle;
    void BeginRenderOn(HWND myhwnd);
    CVtk();
    virtual ~CVtk();
    void TextRenderer(void);
};
 
#endif // !defined(AFX_VTK_H__B872AED3_7A78_4473_BB74_44D3E9117A8F__INCLUDED_)

(3)我們在CVTK.cpp中新增一個成員函式TextRenderer(),其程式碼如下,關鍵是彈出一個檔案對話方塊,並可以選擇格式為XYZ的三維文字檔案,並將其插入到VtkPoints,並連線成一個封閉的線串。
void CVtk::TextRenderer(void)
{
    CString FilePathName;
    CString wenjianhouzhui;
    CFileDialog dlg(TRUE,NULL,NULL,OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT,(LPCTSTR)_TEXT("DCIOM Files (*.dcm)|*.dcm|TIFF Files (*.tif)|*.tif|TXT Files (*.txt)|*.txt|BMP Files (*.bmp)|*.bmp|JPG Files (*.jpg)|*.jpg|All Files (*.*)|*.*||"),NULL);
    if (dlg.DoModal()==IDOK)
    {  
        FilePathName=dlg.GetPathName();
        wenjianhouzhui=dlg.GetFileExt();
        //wenjianhouzhui=dlg.GetFileExt();
        //AfxMessageBox(wenjianhouzhui);
    }
    else
    {
        return ;
    }
    if(wenjianhouzhui=="txt")
    {
        FILE*fp = NULL;
        fp=fopen(FilePathName,"r");    //讀取TXT中的XYZ座標
        if(!fp)
        {
 
            AfxMessageBox("開啟檔案失敗!!\n");
            exit(0);
        }
        double x=0,y=0,z=0;
        int i = 0;
        m_Points=vtkPoints::New();
        vertices=vtkCellArray::New();
        while (!feof(fp))
        {
            fscanf(fp,"%lf    %lf    %lf",&x,&y,&z);    
            m_Points->InsertPoint(i,x,y,z);    //_加入點信
            //cout<<x<<" "<<y<<" "<<z<<endl;
            cout<<x<<endl;
            vertices->InsertNextCell(1);        //_加入細胞頂點資訊----用於渲染點集
            vertices->InsertCellPoint(i);
            i ++;
        }    
        fclose(fp);
 
    lines=vtkCellArray::New();
    for (int m=0;m<=i-1;m++)
    {  
        if(m<i-1)
        {
            line =vtkLine::New();
            line->GetPointIds()->SetId(0,m);
            line->GetPointIds()->SetId(1,m+1);
            lines->InsertNextCell(line);
        }
        else
        {
            line =vtkLine::New();
            line->GetPointIds()->SetId(0,m);
            line->GetPointIds()->SetId(1,1);
            lines->InsertNextCell(line);
        }
    }
    //_建立待顯示資料來源
    polyData=vtkPolyData::New();
    polyData->SetPoints(m_Points);        //_設定點集
    polyData->SetVerts(vertices);//_設定渲染頂點
    polyData->SetLines(lines);
    pointMapper=vtkPolyDataMapper::New();
    pointMapper->SetInput(polyData);
    pointActor=vtkActor::New();
    pointActor->SetMapper(pointMapper);
    pointActor->GetProperty()->SetColor(0.0,0.1,1.0);
    pointActor->GetProperty()->SetPointSize(2);
    ren1=vtkRenderer::New();
    ren1->AddActor( pointActor );
    ren1->SetBackground(1,1,1);
    //ren1->SetBackground( 0, 0, 0);
    renWin=vtkRenderWindow::New();
    renWin->AddRenderer( ren1 );
    iren=vtkRenderWindowInteractor::New();
    //istyle=vtkInteractorStyleTrackball::New();
    //iren->SetInteractorStyle(istyle);  
    iren->SetRenderWindow(renWin);
 
}
}

(4)我們在CVTK.cpp中新增一個成員函式BeginRenderer(),其程式碼如下,作用就是獲取當前的視窗指標並將其設為VTK繪製視窗的父視窗
void CVtk::BeginRenderOn(HWND myhwnd)
{
    renWin->SetParentId(myhwnd);
    //開始實現
    if(ren1)
    {
        renWin->Render();
    }
}

(5)其後我們在CVTK的解構函式!~CVTK()中刪除宣告的指標物件
CVtk::~CVtk()
{
    m_Points->Delete();
    vertices->Delete();
    line->Delete();
    lines->Delete();
    polyData->Delete();
    pointMapper->Delete();
    pointActor->Delete();
    ren1->Delete();
    renWin->Delete();
    iren->Delete();
 
}

(6)到現在為止,將VTK的控制檯程式轉為MFC中的程式碼就已經完成了,其後就是在MFC中實現圖形的繪製,我們現在主選單上新增一個開啟檔案的按鈕,並在Cview類下面新增他的事件處理程式。


其事件處理程式如下,及在此呼叫CVTK的TextRenderer()函式,下面的m_pVtk為CVTK類的物件,上述宣告是在View的標頭檔案中進行的宣告,

void CMyMFCPointView::OnTxTFileopen()
{
    // TODO: 在此新增命令處理程式程式碼
    
    m_pVtk.TextRenderer();
       Invalidate();
}
下面為view類標頭檔案的宣告
// MyMFCPointView.h : interface of the CMyMFCPointView class
//
/////////////////////////////////////////////////////////////////////////////
 
#if !defined(AFX_MYMFCPOINTVIEW_H__A74C4D7E_8977_4D06_9F00_610A95A7CFF9__INCLUDED_)
#define AFX_MYMFCPOINTVIEW_H__A74C4D7E_8977_4D06_9F00_610A95A7CFF9__INCLUDED_
 
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#include "Vtk.h"
 
class CMyMFCPointView : public CView
{
protected: // create from serialization only
    CMyMFCPointView();
    DECLARE_DYNCREATE(CMyMFCPointView)
 
// Attributes
public:
    CMyMFCPointDoc* GetDocument();
    /*CString FilePathName;
    CString wenjianhouzhui;*/
// Operations
public:
    CVtk m_pVtk;
    double sx;
    double sy;
    
 
// Overrides
    // ClassWizard generated virtual function overrides
    //{{AFX_VIRTUAL(CMyMFCPointView)
    public:
    virtual void OnDraw(CDC* pDC);  // overridden to draw this view
    virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
    protected:
    virtual BOOL OnPreparePrinting(CPrintInfo* pInfo);
    virtual void OnBeginPrinting(CDC* pDC, CPrintInfo* pInfo);
    virtual void OnEndPrinting(CDC* pDC, CPrintInfo* pInfo);
    //}}AFX_VIRTUAL
 
// Implementation
public:
    virtual ~CMyMFCPointView();
#ifdef _DEBUG
    virtual void AssertValid() const;
    virtual void Dump(CDumpContext& dc) const;
#endif
 
protected:
 
// Generated message map functions
protected:
    //{{AFX_MSG(CMyMFCPointView)
        // NOTE - the ClassWizard will add and remove member functions here.
        //    DO NOT EDIT what you see in these blocks of generated code !
    //}}AFX_MSG
    DECLARE_MESSAGE_MAP()
public:
    afx_msg void OnSize(UINT nType, int cx, int cy);
    
    afx_msg void OnTxTFileopen();
//    afx_msg void OnFileOpen();
};
 
#ifndef _DEBUG  // debug version in MyMFCPointView.cpp
inline CMyMFCPointDoc* CMyMFCPointView::GetDocument()
   { return (CMyMFCPointDoc*)m_pDocument; }
#endif
 
/////////////////////////////////////////////////////////////////////////////
 
//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
 
#endif // !defined(AFX_MYMFCPOINTVIEW_H__A74C4D7E_8977_4D06_9F00_610A95A7CFF9__INCLUDED_)
為了能將所畫的圖形隨著視窗大小變化而變化,起先開始我是在view的Onsize()函式中寫下如下程式碼,m_pVtk.renWin->SetSize(cx,cy);,但是這個時候會出現空指標錯誤,是因為,當視窗第一次建立的時候我們還沒有renWin這個指標物件,這個是我們在點選開啟檔案按鈕選擇好檔案之後才會建立的物件,所以此時會出現空指標錯誤,為了解決這個問題,我在view.h中定義了兩個變數sx,sy,然後在Onsize()函式中寫下如下的程式碼,目的是將當前視窗的大小的值賦給sx,sy。而我可以在其他的地方使用這個值。
void CMyMFCPointView::OnSize(UINT nType, int cx, int cy)
{
    CView::OnSize(nType, cx, cy);
    
    // TODO: 在此處新增訊息處理程式程式碼
    sx=cx;
    sy=cy;
}

最後,我在view類的OnDraw()函式中繪圖,並實現圖形隨視窗大小變動的功能
void CMyMFCPointView::OnDraw(CDC* pDC)
{
    CMyMFCPointDoc* pDoc = GetDocument();
    ASSERT_VALID(pDoc);
    // TODO: add draw code for native data here
    m_pVtk.BeginRenderOn(this->m_hWnd);
    m_pVtk.renWin->SetSize(sx,sy);
}

至此,我們就完成了VTK與MFC相結合的程式,當我們點選開啟檔案按鈕之後,就會在view窗口出現所畫的線圈,並且隨視窗大小改變

可以在這個程式進行更多三維重建的功能,還得繼續開發。至於VtkMFCWindow類的可用性還未證實,留待以後的實驗。

轉:https://blog.csdn.net/HW140701/article/details/51360656