引言
目前PIE SDK的资料与帮助大多使用的是C#语言,而C++的资料不多,同时为了督促自身学习,因此打算记录一下自己的学习过程,如果错误可以私信我,请各位大佬多多指教,本文为学习记录日志,承诺如有侵权立马删除。
这里我选择的是使用PIE SDK的组件式开发框架从零开始编写自己的程序,插件式开发其实也可以参考一下,因为要从头开始搭建一个可以正常使用的程序框架,就是在用类似插件式开发的方式对这个框架进行功能完善,集合成我们想要得到的效果。
目前尚未完成编写,后续会根据编写进度时时更新。
便于书写后文将PIE SDK简称为SDK
基础框架搭建
这里PIE SDK文件加中的Demo文件夹中已经给了我们基础的模板,但由于运行环境的不同,需要我们重新对其进行环境配置。
下面进入正式部分:
框架环境配置
首先我们要找到SDK的安装目录(这里展示的是我的部分目录)
\pie for c++\PIESDK\Demo
使用装配有C++运行环境的visual studio 2015打开Demo中的02.组件式二次开发的vs项目文件
没有选择04.PIEApplication案例是因为04中很多未处理好的bug功能,避免刚上手调整出错
PS:如果没什么意外应该都是会报错的,不管你打开了哪一个Demo
其实不算是什么大问题,只是需要我们根据博客去配置好基本的C++运行环境。
这样就可以解决报错的问题,但如果直接对Demo中的项目文件环境配置完成后仍然报错的话就只有重新创建一个PIE Application项目,环境配置完毕后在将代码复制粘贴到创建好的项目中即可。
直接配置环境的话依旧报错所以我选择了新建一个项目在把代码粘贴过去
注意:如果和我一样选择组件式开发的话就需要创建PIE Application项目而并非QT Class Library
环境配置的相关操作在博客中已经讲的很详细了,在这里就只展示一下PIE Application的Release版本配置过程。
选中项目文件,右键=>属性=>C/C++=>常规=>附加包含目录,进行如下设置:
选中项目文件,右键=>属性=>链接器=>输入=>附加依赖项=>编辑,加上下列中的文件:
SysUtility.lib
SysUI.lib
SysGeometry.lib
SysDisplay.lib
SysDataSource.lib
SysCarto.lib
SysAdapterUI.lib
SysFramework.lib
SysAlgo.lib
注意:如果是Debug版本则需要在每个“ .lib ”之前加一个“ D ”。
就结束了,其他都不用修改,还有要注意的就是如果你修改的这两处之前已经有路径或lib文件在的话,不要删除,而是在最下面添加新行。
参考博客:
尝试运行,可以得到一个如图所示的框体:
完善框架功能
打开项目文件中的PIE Application.cpp,你会发现很多功能都是空着的没有实现,你可以在各个Demo文件中找到你想要的功能(要注释中没有bug字样的)
这里后期需要对栅格数据进行波段运算,因此必须要配置的是加载栅格数据以及一些基础的栅格运算功能。
其实还想搞一个Ribbon界面的,无奈本人编程太菜,编不出来...
界面其实不重要,最后大概就是这样一个效果吧
这里要提一下,组件式开发有些类似基层开发,它大多数的代码都不是用了可以直接解决最终问题的SDK的库,而是采用了很多QT与SDK结合的方式,这个菜单栏就是用的QMenu来进行创建的。
更新——2023/12/12(接上文的基础框架结构)
框架内部逻辑
这里采用的仍旧是之前提到的QT自带的菜单栏框架,如果有大佬知道怎么做Ribbon界面请一定私信我
===========分割线===========
书归正传,界面功能的话可以找到项目中的PIEMain.cpp文件,打开后在其构造函数中可以找到注释着添加菜单的部分,如下图:
同样的工具栏也是找到注释部分:
这里就是菜单栏和工具栏的实现部分,这部分是靠QT来进行显示窗体的,所以需要一定的QT基础
我这里也简单的对两部分介绍一下,先看源码:
菜单栏
//============
//分页Page1
//============
QMenu* pMenu_File = this->menuBar()->addMenu(tr("文件(&F)"));
QAction* pAction_NewDocument = pMenu_File->addAction(QIcon("./Resources/DocumentNew16.png"), tr("新建..."));
pAction_NewDocument->setShortcut(QKeySequence("Ctrl+N"));
QAction* pAction_OpenDocument = pMenu_File->addAction(QIcon("./Resources/DocumentOpen16.png"), tr("打开..."));
pAction_OpenDocument->setShortcut(QKeySequence("Ctrl+O"));
pMenu_File->addSeparator();
QAction* pAction_SaveDocument = pMenu_File->addAction(QIcon("./Resources/DocumentSave16.png"), tr("保存"));
pAction_SaveDocument->setShortcut(QKeySequence("Ctrl+S"));
QAction* pAction_SaveAsDocument = pMenu_File->addAction(QIcon("./Resources/DocumentSaveAs16.png"), tr("另存"));
pMenu_File->addSeparator();
QAction* pAction_Exit = pMenu_File->addAction(QIcon("./Resources/Close16.png"), tr("退出"));
pAction_Exit->setShortcut(QKeySequence("Alt+F4"));
connect(pAction_NewDocument, &QAction::triggered, this, &PIEMainWindow::On_ActionNewDocument_Triggered);
connect(pAction_OpenDocument, &QAction::triggered, this, &PIEMainWindow::On_ActionOpenDocument_Triggered);
connect(pAction_SaveDocument, &QAction::triggered, this, &PIEMainWindow::On_ActionSaveDocument_Triggered);
connect(pAction_SaveAsDocument, &QAction::triggered, this, &PIEMainWindow::On_ActionSaveAsDocument_Triggered);
connect(pAction_Exit, &QAction::triggered, this, &PIEMainWindow::On_ActionExit_Triggered);
首先是声明了一个pMenu_File的QT菜单指针,然后将第一页菜单取名文件,最后定义自己需要的功能,这里是‘新建’等功能,再把它和后续的函数链接起来就完成了菜单栏部分的编写,具体的实现功能则需要到对应的链接函数中完成。
如果你想创建新的一页菜单栏,再次声明一个新的菜单指针即可,要保证与第一页的声明格式相同。
注:setShortcut是设置快捷键,可以斟酌添加,如果你暂时没有声明QAction的功能函数,也也可以不进行链接(connect)操作,如此则仅仅出现一个没有功能的选项,后续编写完成后再链接即可。
工具栏
//1.添加数据工具
ui.mainToolBar->hide();
QToolBar* pToolBar_AddData = this->addToolBar(tr("Tools"));
QAction* pAction_AddDataTool = pToolBar_AddData->addAction(QIcon("./Resources/DataAdd16.png"), tr("加载数据"));
QAction* pAction_AddFeatureTool = pToolBar_AddData->addAction(QIcon("./Resources/DataAdd16.png"), tr("加载矢量数据"));
QAction* pAction_AddRasterTool = pToolBar_AddData->addAction(QIcon("./Resources/DataAdd16.png"), tr("加载栅格数据"));
pToolBar_AddData->setObjectName("ToolBar_Data");
connect(pAction_AddDataTool, &QAction::triggered, this, &PIEMainWindow::On_ActionAddData_Triggered);
connect(pAction_AddFeatureTool, &QAction::triggered, this, &PIEMainWindow::On_ActionAddFeature_Triggered);
connect(pAction_AddRasterTool, &QAction::triggered, this, &PIEMainWindow::On_ActionAddRaster_Triggered);
这里是先隐藏了默认的工具栏,后面如果想自己添加工具的话不用重新隐藏,只需要新声明工具栏指针即可。
类似的,工具栏也是先声明工具栏指针再利用QAction链接功能函数,和菜单栏的逻辑相同,我在这部分增加了加载矢量和栅格数据的功能以便后续操作。
自定义基础功能
如前文所述,只需要在菜单栏或工具栏添加自己想要的功能,在功能函数中编写对应的功能,只要想得到就能做出来,自由度很高,如果不太会的也可以看看PIE自带的demo里面有没有对应的功能。
但基础功能中需要提一提的就是添加数据的部分,这部分必须采取类插件开发的方式写一个功能函数,再将函数引用到主函数PIEMain.cpp中。
原因分析:
如果直接将添加数据功能代码写到主函数中会让 “m_ptrHookHelper” 报错,以RasterDataAdd举例:
选中之后按F12可以找到其在 SysFramework::BaseCommand 是有定义的,但是却显示没有定义。
仔细看这里有个protected将其保护起来了,这就是我们无法从外部访问它的原因。
因此我们需要实例化它的父级函数,也就是SysUI::ICommand 的智能指针。
而直接写入功能区是不能进行实例化的,类插件式开发的方法,也就是写一个独立类的引用可以很好的解决这个问题。
解决步骤:
在Header Files右键添加一个头文件,再在Source Files中右键添加一个源文件,要注意二者名称尽量一致以避免混淆(非必须),如果已经有编写好的想添加的功能函数(比如从插件式转成了组件式,就可以把之前插件式的功能直接添加进来,然后从主函数调用即可),那么就选择添加现有项目。
接下来的操作就是插件式开发的同样操作,以RasterDataAdd举例:
RasterDataAdd.h
#ifndef RASTERDATAADD_H
#define RASTERDATAADD_H
#include "SysFramework/BaseCommand.h"
#include <QString>
#include <QFileDialog>
/**@class AddDataCommand
* @brief 添加数据Command
*/
class RasterDataAdd :public SysFramework::BaseCommand
{
public:
/**
* @brief 构造函数
* @return
*/
RasterDataAdd();
/**
* @brief 析构函数
* @return
*/
virtual ~RasterDataAdd();
/**
* @brief 按钮点击事件
* @return
*/
virtual void OnClick() override;
private:
};
#endif // ADDDATACOMMAND_H
RasterDataAdd.cpp
#include "SysCarto/LayerFactory.h"
#include "SysCarto/RasterLayer.h"
#include "SysDataSource/RasterDataset.h"
#include "SysDataSource/DatasetFactory.h"
#include "RasterDataAdd.h"
/**
* @brief 构造函数
* @return
*/
RasterDataAdd::RasterDataAdd()
{
}
/**
* @brief 析构函数
* @return
*/
RasterDataAdd::~RasterDataAdd()
{
}
/**
* @brief 按钮点击事件
* @return
*/
void RasterDataAdd::OnClick()
{
//1.获取栅格数据路径
QString filter = "Raster Files (*.tif *.tiff *.img *.bmp *.jpg *.ldf *.dat *.1bd *.1b)";
QString FilePath = QFileDialog::getOpenFileName(nullptr, "添加栅格数据", "", filter);
if (FilePath.count() < 1) return;
//2.根据栅格数据路径 创建栅格数据集
SysDataSource::RasterDatasetPtr ptrRasterDataset = SysDataSource::DatasetFactory::Instance()->OpenRasterDataset(FilePath);
//3.由栅格数据集 创建栅格数据图层
SysCarto::RasterLayerPtr ptrRasterLayer = new SysCarto::RasterLayer();
ptrRasterLayer->SetDataset(ptrRasterDataset); //栅格数据图层 设置 栅格数据集
//4.获取当前焦点地图添加 栅格数据图层
m_ptrHookHelper->GetFocusMap()->AddLayer(ptrRasterLayer);
//5.刷新
m_ptrHookHelper->GetActiveView()->Refresh();
}
编写完成后需要在主函数中激活引用:
/**
* @brief 添加栅格数据
* @param[in] bool checked 是否选中
* @return
*/
void PIEMainWindow::On_ActionAddRaster_Triggered(bool checked)
{
SysUI::ICommandPtr ptrCmd = new RasterDataAdd();
if (ptrCmd == nullptr) return;
ptrCmd->OnCreate(m_pCurrentControl); //创建插件对象
ptrCmd->OnClick(); //按钮点击事件
delete ptrCmd;
}
简单介绍一下独立类:
PS:本人水平有限,如果有介绍不清或错误的请各位大佬随时指出(叠甲)
有关独立类可以参考这篇文章:
C++如何创建一个类并引用_cpp如何把类引过来-CSDN博客
总结一下,独立类耦合度低,可以免得报错时产生连锁效应,而且通过层层检查也可以较为迅速的找到问题所在的地方,这就是它的优势之处。
使用时要注意头文件的引用,同时如果要在独立类的.cpp中添加新功能就需要在对应的.h中添加声明,主函数调用时只需引入这个类的头文件即可。
上文中也可以看到RasterDataAdd.cpp中的函数都是在RasterDataAdd.h中声明的。
还有就是如果该类头文件中引用了某个库,其他引用这个头文件的函数也可以使用这个库里面的功能。
有关类成员的调用的话可以参考这篇文章:
C++学习—类的成员函数和变量的访问、静态与非静态成员函数_c++中静态成员函数和非静态成员函数的调用方式-CSDN博客
总结来说就是,非静态成员调用就需要实例化,静态成员调用则不需要。
===========分割线===========
这部分就是框架的搭建部分了,后续将对波段运算部分进行学习和研究,避免笔记混淆会重新写一篇新的文章来记录学习情况,如果有问题欢迎大家私信我或在评论区积极讨论。