一、前言
本篇文章是QGIS3.10二次开发的第三个文档,可在本人首页查看所有QGIS3.10二次开发相关的文档,文档不定期更新。
https://blog.csdn.net/qfl_sdu?type=blog
本篇文章计划实现以下功能:
- 图层控制器右键菜单
- 通过右键菜单控制图层标签(label)的显示及显示的内容
- 通过右键菜单控制点图层的渲染效果
- 通过右键菜单控制线图层的渲染效果
- 通过右键菜单控制多边形图层的渲染效果
这一节的内容较多,因个人工作原因,内容会逐步补充完整,前面2个功能已经实现,后面3个功能会逐渐补充上来。
按照惯例,先上效果图:(效果图中添加了一个点图层,一个面图层,只截取了点图层的示例,面图层的操作相同就不再截图了)
二、图层控制器右键菜单
在上一篇文章中已经添加了图层控制器,此处说明在其基础上增加右键菜单。
(上篇链接:https://blog.csdn.net/qfl_sdu/article/details/113184043)
在QGis中,图层控制器右键菜单有个专门的抽象类QgsLayerTreeViewMenuProvider,通过继承这个类并实现 createContextMenu()这个方法,即可添加右键菜单。
在工程中添加一个类文件MyGistreeviewMenu.h和MyGistreeviewMenu.cpp,并继承QgsLayerTreeViewMenuProvider类,代码如下:
MyGistreeviewMenu.h代码:
#ifndef MYGISTREEVIEWMENU_H
#define MYGISTREEVIEWMENU_H
#include <QObject>
#include "qgslayertreeview.h"
#include "qgsmaplayer.h"
class QAction;
class QgsMapCanvas;
class MyGistreeviewMenu : public QObject, public QgsLayerTreeViewMenuProvider
{
Q_OBJECT
public:
MyGistreeviewMenu(QgsLayerTreeView* view,QgsMapCanvas* canvas);
~MyGistreeviewMenu() override;
QMenu * createContextMenu() override; // 创建菜单
private:
QgsLayerTreeView* m_layerTreeView;
QgsMapCanvas* m_mapCanvas;
};
#endif // MYGISTREEVIEWMENU_H
MyGistreeviewMenu.cpp代码:
#include "mygistreeviewmenu.h"
#include "mainwindow.h"
#include <QMenu>
#include <QModelIndex>
#include <QIcon>
#include <qgswkbtypes.h>
//QGIS include
#include "qgslayertreeviewdefaultactions.h"
#include "qgslayertreenode.h"
#include "qgslayertreemodel.h"
#include "qgslayertree.h"
#include "qgsrasterlayer.h"
#include "qgsvectorlayer.h"
MyGistreeviewMenu::MyGistreeviewMenu(QgsLayerTreeView* view,QgsMapCanvas* canvas)
{
m_layerTreeView = view;
m_mapCanvas = canvas;
}
MyGistreeviewMenu::~MyGistreeviewMenu()
{
}
//创建菜单
QMenu *MyGistreeviewMenu::createContextMenu()
{
QMenu* menu = new QMenu;
QgsLayerTreeViewDefaultActions* actions = m_layerTreeView->defaultActions();
QModelIndex idx = m_layerTreeView->currentIndex();
// global menu
if(!idx.isValid())
{
menu->addAction(actions->actionAddGroup(menu));
menu->addAction(tr("&Expand All"),m_layerTreeView,SLOT(expandAll()));
menu->addAction(tr("&Collapse All"),m_layerTreeView,SLOT(collapseAll()));
}else
{
QgsLayerTreeNode* node = m_layerTreeView->layerTreeModel()->index2node(idx);
//layer or group selected
if(QgsLayerTree::isGroup(node))
{
menu->addAction(actions->actionZoomToGroup(m_mapCanvas,menu));
menu->addAction(actions->actionRemoveGroupOrLayer(menu));
menu->addAction(actions->actionRenameGroupOrLayer());
if(m_layerTreeView->selectedNodes(true).count() >= 2)
menu->addAction(actions->actionGroupSelected(menu));
menu->addAction(actions->actionAddGroup(menu));
}else if(QgsLayerTree::isLayer(node))
{
QgsMapLayer* layer = QgsLayerTree::toLayer(node)->layer();
menu->addAction(actions->actionZoomToLayer(m_mapCanvas,menu));
menu->addAction(actions->actionRemoveGroupOrLayer(menu));
menu->addAction(tr("&Label"),MainWindow::Intance(),SLOT(slot_labelShowAction()));
if(layer->type() == QgsMapLayerType::VectorLayer)
{
//点矢量图层增加点样式设置菜单
QgsVectorLayer* veclayer = qobject_cast<QgsVectorLayer*>(layer);
if(veclayer->geometryType() == QgsWkbTypes::PointGeometry )
{
menu->addAction(tr("PointStyle"),MainWindow::Intance(),SLOT(slot_pointstyle()));
}else if(veclayer->geometryType() == QgsWkbTypes::PolygonGeometry)
{
menu->addAction(tr("PolygonStyle"),MainWindow::Intance(),SLOT(slot_polygonstyle()));
}
}
}
}
return menu;
}
在菜单中,添加了几个槽函数:
slot_labelShowAction()用于设置矢量图层的标签显示,会在第三节中完成;
slot_pointstyle()用于设置点图层的显示方式,会在第四节的第2小节中完成;
slot_polygonstyle()用于设置面图层的显示方式,会在第四节的第4小节中完成。
线图层的相关槽函数会在后面补充上来。
在mainwindow.cpp中添加右键菜单:
添加头文件:#include “mygistreeviewmenu.h”
添加右键菜单:
//右键菜单
m_layerTreeView->setMenuProvider(new MyGistreeviewMenu(m_layerTreeView,m_mapcanvas));
connect(QgsProject::instance()->layerTreeRegistryBridge(),SIGNAL(addedLayersToLayerTree(const QList<QgsMapLayer*>)),this,SLOT(slot_autoSelectAddedLayer( const QList<QgsMapLayer*>)));
槽函数slot_autoSelectAddedLayer用于处理图控制器中树结构的变化,其代码如下:
//图层控制器树结构变化
void MainWindow::slot_autoSelectAddedLayer(const QList<QgsMapLayer *> layers)
{
if ( !layers.isEmpty() )
{
QgsLayerTreeLayer *nodeLayer = QgsProject::instance()->layerTreeRoot()->findLayer( layers[0]->id() );
if ( !nodeLayer )
return;
QModelIndex index = m_layerTreeView->layerTreeModel()->node2index( nodeLayer );
m_layerTreeView->setCurrentIndex( index );
}
}
因为在MyGistreeviewMenu.cpp的方法中,用到了几个槽函数,用于响应右键菜单操作,所以在mainwindow中需要添加这几个槽函数。
mainwindow.h中添加如下:
public:
static MainWindow* Intance(); //返回主窗口实例指针
public slots:
void slot_labelShowAction(); //设置图层显示标签
void slot_pointstyle(); //点样式窗口显控
void slot_polygonstyle();//面样式窗口显控
private:
static MainWindow* m_appinstance;
mainwindow.cpp中添加实现:
MainWindow* MainWindow::m_appinstance = nullptr;
//返回实例指针
MainWindow *MainWindow::Intance()
{
return m_appinstance;
}
//设置图层显示标签
void MainWindow::slot_labelShowAction()
{//具体实现在下面的小节中说明
}
//点样式-右键菜单
void MainWindow::slot_pointstyle()
{//具体实现在下面的小节中说明
}
//面样式窗口显控-右键菜单
void MainWindow::slot_polygonstyle()
{//具体实现在下面的小节中说明
}
至此,编译运行就可以显示图层控制器的右键菜单了。
三、标签显示设置
通过一个窗口(LabelContral类)来显示图层有哪些标签字段,用户通过界面选择需要显示的标签字段。具体实现如下 :
LabelContral.h文件:
#ifndef LABELCONTRAL_H
#define LABELCONTRAL_H
#include <QWidget>
namespace Ui {
class LabelContral;
}
class LabelContral : public QWidget
{
Q_OBJECT
public:
explicit LabelContral(QWidget *parent = nullptr);
~LabelContral();
void SetItems(QStringList items); //设置combox列表内容
void SetInitInfo(bool bshow,QString name); //设置初始显示内容
QString GetSelectedItem(); //获取选中的项
bool isShowLabel(); //是否显示label
private slots:
void on_btnOk_clicked();
void on_btnCancle_clicked();
signals:
void sig_labelctrlBtnClicked(int,bool,QString);
private:
Ui::LabelContral *ui;
bool m_bshowLabels; //是否显示字段
QString m_strShowName; //显示的字段名称
};
#endif // LABELCONTRAL_H
LabelContral.cpp文件:
#include "labelcontral.h"
#include "ui_labelcontral.h"
#include <QPalette>
#include <QString>
LabelContral::LabelContral(QWidget *parent) :
QWidget(parent),
ui(new Ui::LabelContral)
{
ui->setupUi(this);
QStringList ll;
ll<<QString::fromLocal8Bit("显示") << QString::fromLocal8Bit("不显示");
ui->comboBox_show->addItems(ll);
ui->comboBox_show->setCurrentIndex(1);
m_bshowLabels = false;
m_strShowName = "";
setWindowTitle(QString::fromLocal8Bit("标签设置"));
QPalette pe = this->palette();
pe.setColor(QPalette::Background,Qt::white);
setPalette(pe);
}
LabelContral::~LabelContral()
{
delete ui;
}
//设置combox列表内容
void LabelContral::SetItems(QStringList items)
{
ui->comboBox_items->clear();
ui->comboBox_items->addItems(items);
}
//设置初始显示内容
void LabelContral::SetInitInfo(bool bshow, QString name)
{
if(bshow)
ui->comboBox_show->setCurrentIndex(0);
else
ui->comboBox_show->setCurrentIndex(1);
if(!name.isEmpty())
ui->comboBox_items->setCurrentText(name);
}
//获取选中的项
QString LabelContral::GetSelectedItem()
{
return ui->comboBox_items->currentText();
}
//是否显示label
bool LabelContral::isShowLabel()
{
return true;
}
//OK
void LabelContral::on_btnOk_clicked()
{
int ind = ui->comboBox_show->currentIndex();
bool sh = false;
if(ind == 0) sh = true;
QString name = ui->comboBox_items->currentText();
if(sh != m_bshowLabels || name != m_strShowName)
{
m_bshowLabels = sh;
m_strShowName = name;
emit sig_labelctrlBtnClicked(1,m_bshowLabels,m_strShowName);
}else
emit sig_labelctrlBtnClicked(0,m_bshowLabels,m_strShowName); //Do nothing}
//Cancle
void LabelContral::on_btnCancle_clicked()
{
emit sig_labelctrlBtnClicked(0,m_bshowLabels,m_strShowName);
}
在mainwindow中slot_labelShowAction()槽函数:
//设置图层显示标签
void MainWindow::slot_labelShowAction()
{
QgsMapLayer* layer =m_layerTreeView->currentLayer();
if(layer && layer->type() == QgsMapLayerType::VectorLayer)
{
QgsVectorLayer* veclayer = qobject_cast<QgsVectorLayer*>(layer);
//查找标签显示信息
QMap<QgsVectorLayer*,StLabelShowInfo>::iterator it = m_mapLabelshowInfo.find(veclayer); //标签显示信息
StLabelShowInfo st;
if(it != m_mapLabelshowInfo.end())
st = it.value();
else
{
st.bshow = false;
st.name = "";
}
QStringList items = veclayer->fields().names();
if(m_dlgLabelctrl == nullptr)
{
m_dlgLabelctrl = new LabelContral();
connect(m_dlgLabelctrl,SIGNAL(sig_labelctrlBtnClicked(int,bool,QString)),this,SLOT(slot_labelctrlChange(int,bool,QString)));
}
m_dlgLabelctrl->SetItems(items);
m_dlgLabelctrl->SetInitInfo(st.bshow,st.name);
m_dlgLabelctrl->show();
m_dlgLabelctrl->raise();
m_dlgLabelctrl->activateWindow();
}
}
其中,
struct StLabelShowInfo
{
bool bshow; //是否显示标签
QString name; //显示的标签字段
};
QMap<QgsVectorLayer*,StLabelShowInfo> m_mapLabelshowInfo; //记录每个图层上一次的标签显示信息
用户设置变更后,配置界面发送信号sig_labelctrlBtnClicked,主界面通过槽函数slot_labelctrlChange来刷新地图。
//标签显示变更
void MainWindow::slot_labelctrlChange(int id,bool bchange,QString name)
{
if(m_dlgLabelctrl)
{
m_dlgLabelctrl->hide();
delete m_dlgLabelctrl;
m_dlgLabelctrl = 0;
}
if(id == 1)
{
QgsMapLayer* ll = m_layerTreeView->currentLayer();
if(ll && ll->type() == QgsMapLayerType::VectorLayer )
{
QgsVectorLayer* vecLayer =qobject_cast<QgsVectorLayer*>(ll);
//标签显示信息查询
QMap<QgsVectorLayer*,StLabelShowInfo>::iterator it = m_mapLabelshowInfo.find(vecLayer); //标签显示信息
StLabelShowInfo st;
if(it != m_mapLabelshowInfo.end())
{
st = it.value();
m_mapLabelshowInfo.erase(it);
}
st.bshow = bchange;
st.name = name;
m_mapLabelshowInfo.insert(vecLayer,st);
if(bchange)
{
//显示标签
QgsPalLayerSettings layerSettings;
layerSettings.drawLabels = true;
layerSettings.fieldName = name;
layerSettings.isExpression = false;
layerSettings.placement = QgsPalLayerSettings::OverPoint;
layerSettings.yOffset = 2.50;
QgsTextBufferSettings buffersettings;
buffersettings.setEnabled(false);
buffersettings.setSize(1);
buffersettings.setColor(QColor());
QgsTextFormat format;
QFont font("SimSun",5,5,false);
font.setUnderline(false);
format.setFont(font);
format.setBuffer(buffersettings);
layerSettings.setFormat(format);
QgsVectorLayerSimpleLabeling * labeling = new QgsVectorLayerSimpleLabeling (layerSettings);
vecLayer->setLabeling(labeling);
vecLayer->setLabelsEnabled(true);
}
else
{
vecLayer->setLabelsEnabled(false);
}
m_mapcanvas->refresh();
} //vecterlayer
}// change
}
如果没有遗漏的话,至此,就可以手动配置矢量图层的标签显示了。
(因为代码是先写好的,隔了一段时间才写的文档,如有遗漏的内容请提示我一下,我会及时补充上来,谢谢。)
四、图层渲染
1、渲染类之间的关系
矢量图层(QgsVectorLayer)通过setRenderer()函数实现图层的渲染,该成员函数如下:
/**
* Sets renderer which will be invoked to represent this layer.
* Ownership is transferred.
*/
void setRenderer( QgsFeatureRenderer *r);
函数中需要传入QgsFeatureRenderer类型的实例,其有多个子类:
包括了2.5D渲染、热力图等等渲染方式,在本节中,我们只关注更改点的基本样式,因此只关注QgsSingleSymbolRenderer的实现,该类用于单一符号的渲染。需要注意的是,如果图层中指定了该渲染方式,则图层中的符号将都采用该渲染方式,无法指定该图层中符号的个性化渲染。
QgsSingleSymbolRenderer的构造函数如下:
QgsSingleSymbolRenderer (QgsSymbol *symbol)
需要传入QgsSymbol的一个实例,QgsSymbol包括了多个子类,如下图所示:
QgsFillSymbolLayer:填充符号图层
QgsGeometryGeneratorSymbolLayer:几何生成符号图层
QgsLineSymbolLayer:线符号图层
QgsMarkerSymbolLayer:标记符号图层(可狭义的理解为点的渲染)
2、点图层渲染
(1)点渲染方式
QgsEllipseSymbolLayer:椭圆形符号图层,实际上还能够支持圆,矩形,菱形,十字,箭头,半角,三角形,右三角形,左三角形,半圆形符号,可通过setShape函数设定。
QgsFontMarkerSymbolLayer:字体标识符号图层,在一些应用中,会把特殊符号做成字体,然后在程序中加载该字体,从而像使用英文字母一样使用这些符号。该类即为使用字体来渲染图层。
QgsSvgMarkerSymbolLayer:使用svg图渲染图层。(尝试用其它格式的图片看看效果)
(2)内置形状渲染
使用(1)中描述的形状进行渲染。
未完,待续…
(3)svg图渲染
未完,待续…
3、线图层渲染
未完,待续…
4、面图层渲染
未完,待续…