在实际开发中我们通常会遇到同时显示多个图层,并且还要实时显示和隐藏各图层的需求,如同 ArcGIS 的图层列表那样,界面左侧显示图层列表,列出当前已加载的所有图层,同时每个图层前面有复选框可以控制图层的显示/隐藏;界面右侧为画布,按图层列表的适当顺序显示所有未隐藏的图层。具体该怎么实现呢?
代码
#include <qgsapplication.h> // 管理图形界面
#include <qgsproviderregistry.h> // 设置并检查数据插件目录
#include <qgsmapcanvas.h> // 创建画布
#include <qgsvectorlayer.h> // 用于创建矢量图层
#include <qgslayertree.h> // 提供命名空间,包含用于层树操作的辅助函数
#include <qgslayertreemodel.h> // 创建模型
#include <qgslayertreeview.h> // 创建view
#include <qboxlayout.h> // 创建布局
#include <qgsproject.h> // 管理qgis工程的头文件// 自定义控件LayerTreeDemo,继承自QWidget
class LayerTreeDemo :public QWidget
{
public:LayerTreeDemo(QWidget * parent = 0);private:// 画布QgsMapCanvas mMapCanvas;// 图层树 View// QgsLayerTreeView 是 QTreeView 的子类,进而是QWidget的子类QgsLayerTreeView mLayerTreeView;// 更新画布图层的“事件回调”void updateCanvasLayerSet();
};// 构造函数
LayerTreeDemo::LayerTreeDemo(QWidget * parent) :QWidget(parent),mMapCanvas(this),mLayerTreeView(this) {// layerTreeRoot() 返回指向项目层树的根(不可见)节点的指针QgsLayerTree* pLayerTreeRoot = QgsProject::instance()->layerTreeRoot();// visibilityChanged当树中节点的检查状态被更改时触发QObject::connect(pLayerTreeRoot, &QgsLayerTreeNode::visibilityChanged, this, &LayerTreeDemo::updateCanvasLayerSet);// 模型监听层树中的更改,并适当地发出更改信号,以便使用该模型的任何视图都相应地更新,参数填根节点QgsLayerTreeModel* pLayerTreeModel = new QgsLayerTreeModel(pLayerTreeRoot);// 设置模型标志 QgsLayerTreeModel::Flag,Flag为枚举类型// AllowNodeChangeVisibility允许用户使用复选框设置节点可见性pLayerTreeModel->setFlag(QgsLayerTreeModel::AllowNodeChangeVisibility);// model只接收QgsLayerTreeModel,视图进行模型的绑定mLayerTreeView.setModel(pLayerTreeModel);// 设置一下最大宽度以便给画布留出更多显示空间,是QWidget的函数mLayerTreeView.setMaximumWidth(200); // 创建一个水平布局QHBoxLayout* pLayout = new QHBoxLayout();// addWidget()传入一个指针// mLayerTreeView控件放在左边pLayout->addWidget(&mLayerTreeView);// mMapCanvas控件放在右边pLayout->addWidget(&mMapCanvas);// 设置布局到此窗体,setLayout是QWidget的函数this->setLayout(pLayout);this->resize(1000, 600); // 设置窗体尺寸为 1000 * 600this->setWindowTitle(u8"QGIS 二次开发:图层树"); // 设置窗体标题// 从磁盘 .shp 文件创建矢量图层QgsVectorLayer* pVectorLayer_1 = new QgsVectorLayer("E:\\TestImage\\深圳各区矢量图\\宝安区\\宝安区_中华人民共和国.shp", "中华人民共和国");QgsVectorLayer* pVectorLayer_2 = new QgsVectorLayer("E:\\TestImage\\深圳各区矢量图\\宝安区\\宝安区_省界.shp", "省界");QgsVectorLayer* pVectorLayer_3 = new QgsVectorLayer("E:\\TestImage\\深圳各区矢量图\\宝安区\\宝安区_市界.shp", "市界");QgsVectorLayer* pVectorLayer_4 = new QgsVectorLayer("E:\\TestImage\\深圳各区矢量图\\宝安区\\宝安区_县界.shp", "县界");QgsVectorLayer* pVectorLayer_5 = new QgsVectorLayer("E:\\TestImage\\深圳各区矢量图\\宝安区\\宝安区_乡镇边界.shp", "乡镇边界");// 确认图层是否创建成功qDebug() << "Is layer valid:" << pVectorLayer_1->isValid();qDebug() << "Is layer valid:" << pVectorLayer_2->isValid();qDebug() << "Is layer valid:" << pVectorLayer_3->isValid();qDebug() << "Is layer valid:" << pVectorLayer_4->isValid();qDebug() << "Is layer valid:" << pVectorLayer_5->isValid();// 把图层添加到工程,越先添加的图层,越靠近底部QgsProject::instance()->addMapLayer(pVectorLayer_1);QgsProject::instance()->addMapLayer(pVectorLayer_2);QgsProject::instance()->addMapLayer(pVectorLayer_3);QgsProject::instance()->addMapLayer(pVectorLayer_4);QgsProject::instance()->addMapLayer(pVectorLayer_5);// 执行回调函数,将图层添加到画布updateCanvasLayerSet();// 缩放到全图mMapCanvas.zoomToFullExtent();
}void LayerTreeDemo::updateCanvasLayerSet()
{// 设置应该在画布上显示的层列表// layerTreeRoot()返回一个QgsLayerTreeNode// checkedLayers()返回属于此节点或其子节点的所有已选中层的列表mMapCanvas.setLayers(QgsProject::instance()->layerTreeRoot()->checkedLayers());// 重新绘制画布地图mMapCanvas.refresh();
}int main(int argc, char **argv)
{// 创建 QgsApplication 实例QgsApplication app(argc, argv, true);// 设置并检查数据插件目录QgsProviderRegistry::instance("D:\\OSGeo4W\\apps\\qgis-ltr\\plugins");// 控制台打印已载入的插件目录qDebug() << "QGIS data providers loaded:" << QgsProviderRegistry::instance()->providerList();// 设置 GDAL 数据目录环境变量qputenv("GDAL_DATA", "D:\\OSGeo4W\\apps\\gdal\\share\\gdal");// 创建主窗体LayerTreeDemo w;w.show();// 启动 QgsApplication 实例return app.exec();
}
讲解
一、QgsLayerTreeView
头文件<qgslayertreeview.h>
继承自QTreeView,进而继承自QWidget
QGIS API Documentation: QgsLayerTreeView Class Reference
二、QgsProject
需要头文件<qgsproject.h>
QGIS API Documentation: QgsProject Class Reference
封装一个QGIS项目,包括一组地图层及其样式、布局、注释、画布等。
QgsProject既可以作为单个对象(QgsProject::instance())使用,也可以作为独立对象使用。QGIS项目单例总是允许访问主QGIS应用程序中打开的规范项目引用。
layerTreeRoot() 返回指向项目层树的根(不可见)节点的指针。
三、QgsLayerTree
头文件<qgslayertree.h>
QGIS API Documentation: QgsLayerTree Class Reference
四、QgsLayerTreeModel
头文件<qgslayertreemodel.h>
QGIS API Documentation: QgsLayerTreeModel Class Reference
QgsLayerTreeModel类是Qt项目视图框架的模型实现。该模型可以在任何QTreeView中使用,但是建议与QgsLayerTreeView一起使用,它为层树处理带来了额外的功能。模型监听层树中的更改,并适当地发出更改信号,以便使用该模型的任何视图都相应地更新。模型的行为可以用标志来定制。例如,是否显示图例或是否允许更改层树。
五、void QgsLayerTreeView::setModel(QAbstractItemModel * model)
QGIS API Documentation: QgsLayerTreeView Class Reference
model只接收QgsLayerTreeModel
六、addMapLayer()
QGIS API Documentation: QgsProject Class Reference
把图层添加到工程和把图层添加到画布两者并没有必然关系。加入工程的图层并不一定在画布上显示(如隐藏的图层),加入画布的图层也并不一定属于工程(如额外创建一个 QgsMapCanvas
显示与当前工程无关的图层,一个例子是 QGIS 软件选择坐标系的时候,界面上会有一个小画布显示当前所选坐标系在地球上的适用范围)。
七、setLayers()
QGIS API Documentation: QgsMapCanvas Class Reference
设置应该在画布上显示的层列表
八、checkedLayers()
QGIS API Documentation: QgsLayerTreeNode Class Reference
属于QgsLayerTreeNode的函数,返回属于此节点或其子节点的所有已选中层的列表。
九、refresh()
QGIS API Documentation: QgsMapCanvas Class Reference
对画布的地图进行重新绘画 。
运行效果
参考文章 文章页 | mriiiron's blog