Qt作為一個跨平台C++圖形用戶界面應用程序開發框架,相當於微軟的MFC(只能運行在Windows平台上),Qt命運多舛,幾經易主,現在屬於芬蘭IT服務公司Digia。
-
Qt環境安裝
Qt的最新版本是Qt5.0,該版本是在12月中旬發布的,在這里我用的是Qt4.8。1版本,也不建議大家着急着用最新版本,關於軟件的下載地址可以在下面找到。
http://download.qt.nokia.com/qt/source/
http://qt-project.org/downloads#qt-other
安裝過程很單,只需要點擊下一部即可。
因為我采用的是VS2010作為Qt的集成開發環境,整個開發環境需要下載兩個軟件
http://download.qt.nokia.com/qt/source/qt-win-opensource-4.8.1-vs2010.exe
http://releases.qt-project.org/vsaddin/qt-vs-addin-1.1.11-opensource.exe
說明:
如果有人不願意這么做,還可以使用QtCreator以及qt-win-opensource-4.8.1-mingw.exe的組合,因為新版本的QtCreator已經不包含mingw,所以要單獨下載。
-
Qt的第一個程序
安裝完后,需要配置幾個環境變量,QMAKESPE(根據自己的情況配置,因為我用的是VS2010,所有配置win32-msvc2010),如下圖:
QTDIR(Qt的安裝目錄),如下圖:
在Path中添加Qt的bin目錄如下圖:
打開VS2010,新建工程可以找到Qt4 的模板:
完成之后,在VS中運行,出現下面的界面,因為我們什么都沒做,在彈出的界面上什么都沒有,不過沒關系,只要能出現,就說明我們的Qt已經可以使用了,如下圖:
-
ArcGIS Engine的環境
安裝ArcGIS ArcObjects for Cross Platform C++ 的SDK,這個沒有什么好說的。
-
ArcGIS Engine+Qt(控制台開發)
安裝了SDK之后,我們就需要將ArcGIS Engine的類庫等引入到開發環境中,在Qt中引入ArcGIS Engine的類庫等信息.在新建立的Qt控制台程序工程右鍵,找到C/C++,然后找到常規,在右側的附加包含目錄中輸入下面三個目錄的地址(因為我的有x(86),所以出現了下面的特殊符號):
說明:在MFC中我們除了引入三個目錄地址,還配置了預處理器定義"ESRI_WINDOW",在這里我並沒有配置。
配置好這個之后,在主程序文件中輸入代碼(這個代碼我在這里就不做解釋,到時候可以看這個文檔的姊妹篇- 《VC2010+ArcGIS Engine10.1開發》
,最后效果如下:
#include <QtCore/QCoreApplication>
#include "ArcSDK.h"
#include "qtextstream.h"
int main(int argc, char *argv[])
{
::CoInitialize(NULL);
#pragma region 綁定許可
IArcGISVersionPtr ipVer(__uuidof(VersionManager));
VARIANT_BOOL succeeded;
if (FAILED(ipVer->LoadVersion(esriArcGISEngine , L"10.1",&succeeded)))
return 0;
#pragma endregion
//
#pragma region 初始化許可
IAoInitializePtr ipInit(CLSID_AoInitialize);
esriLicenseStatus status;
ipInit->Initialize(esriLicenseProductCodeEngine, &status);
if (status != esriLicenseCheckedOut)
{
AoExit(0);
return 0;
}
#pragma endregion
QCoreApplication a(argc, argv);
#pragma region 打開工作空間
IWorkspaceFactoryPtr ipWorkspaceFactory(CLSID_ShapefileWorkspaceFactory);
IWorkspacePtr pWs;
BSTR bstr_str;
QString q_str="D:\\guest\\chinasimplify";
bstr_str = SysAllocString(q_str.utf16());
HRESULT hr=ipWorkspaceFactory->OpenFromFile(bstr_str,0,&pWs);
SysFreeString(bstr_str);
QString q_str1="china_simply.shp";
BSTR bStringWS=SysAllocString(q_str1.utf16());
if (FAILED(hr))
{
return 0;
}
#pragma endregion
#pragma region QI 這里和NET下不一樣
IFeatureWorkspacePtr ipRastWork (pWs);
#pragma endregion
#pragma region 打開要素類並獲取個數
IFeatureClassPtr pFtClass;
hr=ipRastWork->OpenFeatureClass(bStringWS,&pFtClass);
SysFreeString(bStringWS);
if (FAILED(hr))
{
return 0;
}
long pCount=0;
pFtClass->FeatureCount(NULL,&pCount);
#pragma endregion
QString s = QString::number(pCount, 10);
QTextStream cout(stdout);
cout<<s<< endl;
QString str;
QTextStream in(stdin);
in >> str;
return a.exec();
}
運行后可以看到下面的效果
-
ArcGIS Engine+Qt(GUI開發)
在MFC中我們介紹了兩種開發GUI的方法,一種是通過生成相應的Activex控件MFC類,另一種是通過插入Activex控件的方法,在Qt里面做GUI的AE開發,也有兩種不同的做法,而這兩種做法需要引入的頭文件也有差異,除了頭文件的差異,我們需要配置額外的信息,在這里我們分別對兩種方法介紹。
-
ArcGIS Engine+Qt(GUI開發,使用Esri提供的控件類)
-
額外的配置
-
對於這種方法,需要配置很多信息,在工程項目的連接器的常規中找到SDK的lib目錄(在MFC的開發中,我們應該沒有這個步驟),如下圖:
在附加依賴項中輸入qt4ctl.lib和aoctl.lib文件如下圖:
此外還要在環境變量中配置Path(不一定要在Path中配置,只要在運行的時候能找到相應的文件即可),要不然在運行的時候會報下面的錯誤,如下圖:
這是因為Qt的AE在運行的時候用到了Engine安裝目錄下的bin下的三個文件:Qt4ctl.dll, aoctl.dll, ctlbase.dll,只要將Engine的bin目錄配置到Path中就可以了,如下圖:
-
Esri提供的控件類
當這些信息配置好了,我們就可以開發出GUI程序了,在Qt中我們認為一個可視化的組件是一個QWidget,這個QWidget類似MFC中的窗體,一旦有了這些QWidget,就可以用父組件的addWdiget方法加入,Esri提供了繼承這些控件QWidget的類,我們可以在qtaxtcl.h頭文件中找到,如下:
class ESRI_EXT_CLASS QAxCtl
: public QWidget
{
public:
QAxCtl(const char *progID = NULL, QWidget *parent = NULL, const char *name = NULL);
QAxCtl(const ControlDataPtr progID, QWidget *parent = NULL, const char *name = NULL);
~QAxCtl();
HRESULT getInterface(IUnknown **ppUnk);
HRESULT setCursor(HCURSOR cur);
protected:
bool eventFilter(QObject *qo, QEvent *qe);
#if defined(ESRI_UNIX)
bool x11Event(XEvent *event);
#endif /* ESRI_UNIX */
private:
char *m_sProgID;
AxContainerPtr m_pAxCont;
void initialize(const char *progID);
};
-
代碼編寫
我們將這種GUI開發的先決條件都准備好了,下來就開始一個簡單的GUI,這個例子我只將TOC,Map和Toolbar三個控件添加上去,並在Toolbar上添加了幾個命令和工具,完整的代碼如下(功能也比較簡單,界面也不好看,只是當做一個例子,大家就將就看):
#include <stdio.h>
#include <qapplication.h>
#include <qpushbutton.h>
#include <qboxlayout.h>
#include <qsplitter.h>
#include <ArcSDK.h>
#include <AxCtl/qt4axctl.h>
#include <Ao/AoControls.h>
void add_toolbar_items(IToolbarControl* pToolbar);
int main(int argc, char **argv)
{
::CoInitialize(NULL);
#pragma region 綁定許可
IArcGISVersionPtr ipVer(__uuidof(VersionManager));
VARIANT_BOOL succeeded;
if (FAILED(ipVer->LoadVersion(esriArcGISEngine , L"10.1",&succeeded)))
return 0;
#pragma endregion
IAoInitializePtr ipInit(CLSID_AoInitialize);
esriLicenseStatus status;
ipInit->Initialize(esriLicenseProductCodeEngine, &status);
if (status != esriLicenseCheckedOut)
{
printf("Invalid Licensing.\n");
AoExit(0);
}
QApplication qapp(argc, argv);
QWidget window;
window.resize(500,400);
QVBoxLayout vbox(NULL);
QAxCtl tlb(AoPROGID_ToolbarControl);
tlb.setMinimumHeight(30);
tlb.setMaximumHeight(30);
QSplitter split;
QAxCtl toc(AoPROGID_TOCControl);
QAxCtl map(AoPROGID_MapControl);
window.setLayout(&vbox);
vbox.addWidget(&tlb);
split.addWidget(&toc);
split.addWidget(&map);
vbox.addWidget(&split);
IToolbarControlPtr ipToolbar;
IMapControl3Ptr ipMap;
ITOCControlPtr ipToc;
HRESULT hr;
hr = tlb.getInterface((IUnknown **)&ipToolbar);
hr = toc.getInterface((IUnknown **)&ipToc);
hr = map.getInterface((IUnknown **)&ipMap);
if (ipToolbar != 0)
ipToolbar->SetBuddyControl(ipMap);
if (ipToc != 0)
ipToc->SetBuddyControl(ipMap);
add_toolbar_items(ipToolbar);
window.show();
qapp.exec();
ipInit->Shutdown();
::CoUninitialize();
AoExit(0);
return 0;
}
void add_toolbar_items(IToolbarControl* pToolbar)
{
CComVariant varTool;
long itemindex;
if (!pToolbar)
return;
varTool = L"esriControlCommands.ControlsOpenDocCommand";
pToolbar->AddItem(varTool, 0, -1, VARIANT_FALSE, 0,
esriCommandStyleIconOnly, &itemindex);
varTool = L"esriControlCommands.ControlsAddDataCommand";
pToolbar->AddItem(varTool, 0, -1, VARIANT_FALSE, 0,
esriCommandStyleIconOnly, &itemindex);
varTool = L"esriControlCommands.ControlsMapZoomInTool";
pToolbar->AddItem(varTool, 0, -1, VARIANT_TRUE, 0,
esriCommandStyleIconOnly, &itemindex);
varTool = L"esriControlCommands.ControlsMapZoomOutTool";
pToolbar->AddItem(varTool, 0, -1, VARIANT_FALSE, 0,
esriCommandStyleIconOnly, &itemindex);
varTool = L"esriControlCommands.ControlsMapZoomInFixedCommand";
pToolbar->AddItem(varTool, 0, -1, VARIANT_FALSE, 0,
esriCommandStyleIconOnly, &itemindex);
varTool = L"esriControlCommands.ControlsMapZoomOutFixedCommand";
pToolbar->AddItem(varTool, 0, -1, VARIANT_FALSE, 0,
esriCommandStyleIconOnly, &itemindex);
varTool = L"esriControlCommands.ControlsMapPanTool";
pToolbar->AddItem(varTool, 0, -1, VARIANT_FALSE, 0,
esriCommandStyleIconOnly, &itemindex);
varTool = L"esriControlCommands.ControlsMapFullExtentCommand";
pToolbar->AddItem(varTool, 0, -1, VARIANT_FALSE, 0,
esriCommandStyleIconOnly, &itemindex);
varTool = L"esriControlCommands.ControlsMapZoomToLastExtentBackCommand";
pToolbar->AddItem(varTool, 0, -1, VARIANT_FALSE, 0,
esriCommandStyleIconOnly, &itemindex);
varTool = L"esriControlCommands.ControlsMapZoomToLastExtentForwardCommand";
pToolbar->AddItem(varTool, 0, -1, VARIANT_FALSE, 0,
esriCommandStyleIconOnly, &itemindex);
varTool = L"esriControlCommands.ControlsSelectFeaturesTool";
pToolbar->AddItem(varTool, 0, -1, VARIANT_FALSE, 0,
esriCommandStyleIconOnly, &itemindex);
varTool = L"esriControlCommands.ControlsSelectTool";
pToolbar->AddItem(varTool, 0, -1, VARIANT_FALSE, 0,
esriCommandStyleIconOnly, &itemindex);
}
運行效果如下圖:
-
小結
這種方式跟在MFC中使用Axtivex的MFC類比較類似,都是自己實例化一個控件,然后將這個控件添加到父類控件中,但是Esri提供的這個方式,也有自己的弊端,不能調試,只能在Release版本下運行,估計是這中方式需要的幾個dll,沒有提供Debug版本的,如果調試,那么程序會在我們實例化控件的地方跳出去,這個問題搞了我好幾天,總找不到什么原因,借助偉大的Google,最后在Esri的英文論壇中也看到類似的,論壇中在求這個dll和相關lib的debug,看到這個,我也就明白了。