这里实现两个基本的 GIS 软件需求:矢量图层的属性表显示,以及根据属性筛选要素。
具体需求如下:
- 加载一个矢量图层并打开其属性表;
- 输入筛选条件确认无误后,画布上和属性表中均只显示筛选后的要素。
QGIS 提供了若干类用于实现图层属性表。属性表基于 Qt 的 QTableView 实现,利用了 QT中的Model/View 机制。QGIS 用于实现属性表所涉及到类比较多,彼此关系也不太直观, 这里仅做简单的总结。
本篇文章主要用到的与实现图层属性表相关的类有四个,分别为
QgsVectorLayerCache
该类继承自QObject,用于缓存给定QgsVectorLayer的特征,缓存的特征可以通过QgsAbstractCacheIndex进行索引,对给定用例进行适当的索引可能会大大提高性能。构造QgsAttributeTableModel时,只能使用图层的数据缓存,而不能直接使用原图层,应该是为了避免数据冲突之类的问题。
QgsAttributeTableModel
一个由QgsVectorLayerCache支持的模型,继承自QAbstractTableModel,它能够为QAbstractItemView提供特征/属性信息,作为QgsAttributeTableView的后端数据源。也能够为它的QModelIndexes生成编辑器小部件,在本QGIS源代码中主要被称为“主模型”。
QgsAttributeTableView
该类继承自QgsTableView,进而继承自QTableView。提供QgsVectorLayer特性的表视图,是最终显示图层属性表的视图组件。
QgsAttributeTableFilterModel
该类的继承关系如下图所示,为QSortFilterProxyModel和QgsFeatureModel的子类。其基类QSortFilterProxyModel作为数据源模型和视图的中间处理器,提供对源模型数据筛选和排序的能力。基类使用时必须通过setSourceModel()方法指定其源模型,然后调用QTreeView 的setModel()方法传入QSortFilterProxyModel这个中间模型(而非传入源模型),这样视图显示的数据就可以通过本中间模型进行排序和筛选而不影响到源模型中的数据。
运行截图
关键代码
#include "DefMainWindow.h"
#include <qgsattributetableview.h> // 显示图层属性表的视图组件
#include <qmessagebox.h> // 弹出提示框
DefMainWindow::DefMainWindow(QWidget *parent) :
QMainWindow(parent),
mCanvas(this)
{
ui.setupUi(this);
ui.verticalLayout->addWidget(&mCanvas);
mpVectorLayer = new QgsVectorLayer("E:\\TestImage\\中华人民共和国\\中华人民共和国.shp", "中华人民共和国");
// 因为图层编码为utf8,这里也需要设置编码为utf8
mpVectorLayer->setProviderEncoding("utf-8");
// 参数一为要缓存特征的图层,参数二为缓存的大小,这里我们直接将缓存大小设定为要素数量,即全部缓存
mpVectorLayerCache = new QgsVectorLayerCache(mpVectorLayer, mpVectorLayer->featureCount());
// 创建源模型
mpAttrTableModel = new QgsAttributeTableModel(mpVectorLayerCache);
// 在使用此模型作为任何其他中间模型的源模型之前,将该图层加载到模型中
mpAttrTableModel->loadLayer();
// 创建中间模型,用于制作属性表筛选器
// 参数一为Canvas画布,参数二为源模型
// 筛选执行的过程中,画布上同样要更新渲染(只显示筛选后的要素)
mpAttrTableFilterModel = new QgsAttributeTableFilterModel(&mCanvas, mpAttrTableModel);
// 为中间模型设置源模型,由于上面创建中间模型的构造函数中已经设置了源模型,所以此句可以忽略
mpAttrTableFilterModel->setSourceModel(mpAttrTableModel);
// 将图层加载到Canvas画布
mCanvas.setLayers(QList<QgsMapLayer*>() << mpVectorLayer);
// 将画布缩放到完整范围,用于显示完整图层
mCanvas.zoomToFullExtent();
QObject::connect(ui.btnOpenAttributeTable, &QPushButton::clicked, this, &DefMainWindow::onOpenAttributeTableButtonClicked);
QObject::connect(ui.btnApplyWhereClause, &QPushButton::clicked, this, &DefMainWindow::onApplyWhereClauseButtonClicked);
}
// 点击“打开属性表”按钮,弹出属性表窗口
void DefMainWindow::onOpenAttributeTableButtonClicked()
{
// 创建视图
QgsAttributeTableView* pView = new QgsAttributeTableView(this);
// 该方法来自QWidget,设置窗口为半模态类型
pView->setWindowModality(Qt::WindowModality::WindowModal);
// 设置窗口属性
// Qt::Window 表示无论是否有父窗口部件,新窗口部件都是一个窗口,通常有一个窗口边框和一个标题栏
pView->setWindowFlag(Qt::Window);
// 设置窗口标题
pView->setWindowTitle(u8"图层属性表");
// 设置模型 void QgsAttributeTableView::setModel(QgsAttributeTableFilterModel * filterModel)
pView->setModel(mpAttrTableFilterModel);
// 设置窗口大小
pView->resize(700, 450);
// 展示窗口
pView->show();
}
// 输入查询语句,筛查属性值
void DefMainWindow::onApplyWhereClauseButtonClicked()
{
// 获取输入的查询语句
QString whereClause = ui.leWhereClause->text();
// 设置的同时会进行查询,如果查询出现错误则返回false
if (!mpVectorLayer->setSubsetString(whereClause))
{
// 如果有查询出现错误
if (mpVectorLayer->dataProvider()->hasErrors())
{
// 显示记录的错误信息
QMessageBox::warning(this, u8"错误", QString(u8"查询执行错误。返回信息:\n\n%1").arg(mpVectorLayer->dataProvider()->errors().join("\n")));
// 清除记录的错误信息
mpVectorLayer->dataProvider()->clearErrors();
}
// 如果查询字符串设置失败,而且没有错误,说明设置过滤表达式失败
else
{
QMessageBox::warning(this, u8"错误", u8"设置过滤表达式失败。");
}
}
}
查询功能详解
// 输入查询语句,筛查属性值
void DefMainWindow::onApplyWhereClauseButtonClicked()
{
// 获取输入的查询语句
QString whereClause = ui.leWhereClause->text();
// 设置的同时会进行查询,如果查询出现错误则返回false
if (!mpVectorLayer->setSubsetString(whereClause))
{
// 如果有查询出现错误
if (mpVectorLayer->dataProvider()->hasErrors())
{
// 显示记录的错误信息
QMessageBox::warning(this, u8"错误", QString(u8"查询执行错误。返回信息:\n\n%1").arg(mpVectorLayer->dataProvider()->errors().join("\n")));
// 清除记录的错误信息
mpVectorLayer->dataProvider()->clearErrors();
}
// 如果查询字符串设置失败,而且没有错误,说明设置过滤表达式失败
else
{
QMessageBox::warning(this, u8"错误", u8"设置过滤表达式失败。");
}
}
}
(1)bool QgsVectorLayer::setSubsetString(const QString & subset)
subset可以是sql语句的where子句,也可以是特定于底层数据提供程序和数据存储的其他定义字符串。如果设置subset字符串成功则返回true,否则返回false。QgsVectorLayer::setSubsetString()方法会将用户输入的subset应用到图层上,筛选子句被设置成功后,会自动映射到以其为源的数据缓冲,即之前建立的 QgsVectorLayerCache 对象上。相应的数据源模型也将得到更改,进而中间模型也会改,最终反映到 QgsAttributeTableView 的显示上。
(2)const QgsVectorDataProvider * QgsVectorLayer::dataProvider()const
以正确的方式返回该层的数据提供程序,它可能是nullptr。在获取和处理图层错误信息时,需要通过QgsVectorLayer::dataProvider()方法获取QgsVectorDataProvider 类。这个类是对一切矢量数据源类型的抽象,类似于数据处理的中间件。
(3)bool QgsVectorDataProvider::hasErrors()const
查询出现错误会返回true
(4)QStringList QgsVectorDataProvider::errors()const
获取记录的错误信息
(5)void QgsVectorDataProvider::clearErrors()
清除记录的错误信息
注意:查询时输入的待查询的字符串要用单引号!
参考文章 文章页 | mriiiron's blog