上文讲解了窗口布局和图层管理器的实现,说明了图层管理器右键菜单实现方法,这节讲解具体实现;同时图层管理器和上节QGIS中的3(Layers)仍有不同,为实现基本一致,这节将提出几个新概念-QDockWidget、QToolButton、QHBoxLayout。
提示:以下是本篇文章正文内容,下面案例可供参考
继承自qgslayertreeviewmenuprovider这个类的MyQGIS01Layertreeviewmenuprovider类(MyQGIS01layertreeviewmenuprovider.h、MyQGIS01layertreeviewmenuprovider.cpp)
代码如下(示例):
#include
#include "ui_MyQGIS01.h"
//自己引入的库
#include //QT菜单类
#include //QT行为类
#include "qgsrasterlayer.h" //QGIS栅格图层
#include "qgsvectorlayer.h" //QGIS矢量图层
#include "qgsmaplayer.h" //QGIS图层
#include "qgsmapcanvas.h" //QGIS画布
#include "qgslayertreeview.h" //QGIS图层管理器
#include "qgslayertreemapcanvasbridge.h" //连接画布和图层管理器
class MyQGIS01 : public QMainWindow
{
Q_OBJECT
public:
MyQGIS01(QWidget *parent = Q_NULLPTR); //构造函数
Ui::MyQGIS01Class ui; //定义ui界面
//! 保持单一实例
static MyQGIS01* instance() { return sm_instance; }
private:
//单一实例
static MyQGIS01* sm_instance;
//菜单栏
QMenu* fileMenu; //定义文件菜单(一级菜单)
QAction* openRasterFileAction; //定义打开栅格文件菜单行为(二级菜单)
QAction* openVectorFileAction; //定义打开矢量文件菜单行为(二级菜单)
QAction* removeFileAction; //定义移除文件菜单行为(二级菜单)
//地图画布
QgsMapCanvas* mapCanvas;
QList<QgsMapLayer*> layers; //存储加载的图层
//图层管理器
QgsLayerTreeView* layerTreeView;
QgsLayerTreeMapCanvasBridge* layerTreeCanvasBridge;
public slots: //槽函数
void on_openRasterFileAction_triggered();
void on_openVectorFileAction_triggered();
void on_removeFileAction_triggered();
void slot_autoSelectAddedLayer(QList<QgsMapLayer*> layers);
public:
void initLayerTreeView(); //初始化图层管理器函数
void addDockWidget(Qt::DockWidgetArea area, QDockWidget* dockwidget); //添加可悬浮窗口初始位置
};
代码如下(示例):
#include "MyQGIS01.h"
//自己引入的库
#include //QT菜单栏库,用来存放菜单
#include //QT文件目录库,用来打开文件选择窗口
#include "qmessagebox.h" //QT信息盒子,用来显示操作提示
#include "qgslayertreemodel.h" //使用给定层树构建新的树模型,一般与QgsLayerTreeView 一起使用
#include "qgridlayout.h" //栅格布局管理器
#include "qtoolbutton.h" //工具按钮,和普通工具相比可以带图标
#include "qdockwidget.h" //可悬浮窗口
#include "MyQGIS01Layertreeviewmenuprovider.h" //图层管理器右键菜单类
#include "qgslayertreeregistrybridge.h" //创建与层树根同步给定项目的实例,收听地图层注册表中的更新,并在层树中进行更改。
MyQGIS01* MyQGIS01::sm_instance = nullptr; //初始化实例
//构造函数
MyQGIS01::MyQGIS01(QWidget *parent)
: QMainWindow(parent)
{
ui.setupUi(this); //不定义ui界面,栅格布局后会出现问题
this->resize(1200, 800); //设置MyQGIS01类窗口大小
// 菜单栏初始化
fileMenu =this->menuBar()->addMenu("File"); //菜单栏添加文件菜单
openRasterFileAction = new QAction("Open raster", this); //设置文件菜单栏下二级菜单Open菜单
this->connect(openRasterFileAction, SIGNAL(triggered(bool)), this, SLOT(on_openRasterFileAction_triggered())); //关联Open和槽函数(点击产生结果)
fileMenu->addAction(openRasterFileAction); //一级菜单文件添加二级菜单Open
openVectorFileAction = new QAction("Open vector", this);
this->connect(openVectorFileAction, SIGNAL(triggered(bool)), this, SLOT(on_openVectorFileAction_triggered()));
fileMenu->addAction(openVectorFileAction);
removeFileAction = new QAction("Remove file", this);
this->connect(removeFileAction, SIGNAL(triggered(bool)), this, SLOT(on_removeFileAction_triggered()));
fileMenu->addAction(removeFileAction);
//----------------------
//初始化地图画布
mapCanvas = new QgsMapCanvas();
//this->setCentralWidget(mapCanvas); //将地图画布放置我们创建的主窗口中心
mapCanvas->setCanvasColor(QColor(255, 255, 255)); //设置地图画布为白色
mapCanvas->setVisible(true);
mapCanvas->enableAntiAliasing(true);
//----------------------
//初始化图层管理器
layerTreeView = new QgsLayerTreeView(this);
initLayerTreeView();
//----------------------
//栅格布局
QWidget* centralWidget = this->centralWidget();
QGridLayout* centralLayout = new QGridLayout(centralWidget);
//centralLayout->addWidget(layerTreeView, 0, 0, 2, 1); //图层管理器位置,添加可悬浮窗口后需要注释此句
centralLayout->addWidget(mapCanvas, 0, 1, 2, 1); //画布位置 后面四个参数:初始行列 占位行类
}
//-------------------------------------------------------------------------------------------------------------------------------
//槽函数:打开并显示栅格数据
void MyQGIS01::on_openRasterFileAction_triggered() {
//步骤1:打开文件选择对话框
QString fileName = QFileDialog::getOpenFileName(this, tr("Open raster file"), "", "remote sensing image(*.tif *.tiff);;image(*.jpg *.jpeg *.png *.bmp)");
if (fileName.isNull()) //如果文件未选择则返回
{
return;
}
QStringList temp = fileName.split('/');
QString basename = temp.at(temp.size() - 1);//获取栅格数据名称
//步骤2:创建QgsRasterLayer类
QgsRasterLayer* rasterLayer = new QgsRasterLayer(fileName, basename, "gdal");
//如果不是geotiff文件,则提示错误
if (!rasterLayer->isValid())
{
QMessageBox::critical(this, "error", QString("layer is invalid: \n") + fileName);
return;
}
//步骤3:添加栅格数据
QgsProject::instance()->addMapLayer(rasterLayer); //注册
mapCanvas->setExtent(rasterLayer->extent()); //将画布范围设置为栅格图层范围
layers.append(rasterLayer); //将栅格图层追加到链表中
mapCanvas->setLayers(layers); //将图层画到画布上
mapCanvas->setVisible(true);
mapCanvas->freeze(false);
mapCanvas->refresh(); //更新画布
}
//-------------------------------------------------------------------------------------------------------------------------------
//槽函数:打开并显示矢量数据
void MyQGIS01::on_openVectorFileAction_triggered()
{
QString fileName = QFileDialog::getOpenFileName(this, tr("Open shape file"), "", "*.shp");
if (fileName.isNull()) //如果文件未选择则返回
{
return;
}
QStringList temp = fileName.split('/');
QString basename = temp.at(temp.size() - 1);
QgsVectorLayer* vecLayer = new QgsVectorLayer(fileName, basename, "ogr");
if (!vecLayer->isValid())
{
QMessageBox::critical(this, "error", QString("layer is invalid: \n") + fileName);
return;
}
//QGIS 3注册方式
QgsProject::instance()->addMapLayer(vecLayer);
mapCanvas->setExtent(vecLayer->extent());
layers.append(vecLayer);
mapCanvas->setLayers(layers);
mapCanvas->setVisible(true);
mapCanvas->freeze(false);
mapCanvas->refresh();
}
//-------------------------------------------------------------------------------------------------------------------------------
//槽函数:移除图层
void MyQGIS01::on_removeFileAction_triggered() {
layers.clear(); //从链表中清除所有图层
mapCanvas->setLayers(layers); //将图层画到画布上
mapCanvas->setVisible(true);
mapCanvas->freeze(false);
mapCanvas->refresh(); //更新画布
layerTreeView->close(); //这里直接关闭图层管理器 ,需优化
}
//-------------------------------------------------------------------------------------------------------------------------------
//初始化图层管理器
void MyQGIS01::initLayerTreeView()
{
QgsLayerTreeModel* model = new QgsLayerTreeModel(QgsProject::instance()->layerTreeRoot(), this);
model->setFlag(QgsLayerTreeModel::AllowNodeRename);
model->setFlag(QgsLayerTreeModel::AllowNodeReorder);
model->setFlag(QgsLayerTreeModel::AllowNodeChangeVisibility);
model->setFlag(QgsLayerTreeModel::ShowLegendAsTree);
model->setAutoCollapseLegendNodes(10);
layerTreeView->setModel(model);
layerTreeView->setFixedWidth(200);
//------------------------------------------------
// 新增
//右键菜单
layerTreeView->setMenuProvider(new MyQGIS01LayerTreeViewMenuProvider(layerTreeView, mapCanvas));
//connect(QgsProject::instance()->layerTreeRegistryBridge(), SIGNAL(addedLayersToLayerTree(const QList)), this,
// SLOT(slot_autoSelectAddedLayer(const QList)));
connect(QgsProject::instance()->layerTreeRegistryBridge(), &QgsLayerTreeRegistryBridge::addedLayersToLayerTree, this,
&MyQGIS01::slot_autoSelectAddedLayer);
// 设置这个路径是为了获取图标文件
QString iconDir = "E:\\GIS development\\QGIS\\gisTest2\\image\\"; //注意包含中文路径图标会不显示,这里的图标可以去原QGIS软件中去找
// add group tool button
QToolButton* btnAddGroup = new QToolButton();
btnAddGroup->setAutoRaise(true);
btnAddGroup->setIcon(QIcon(iconDir + "layer-group-add.png"));
btnAddGroup->setFixedSize(24, 24);
btnAddGroup->setToolTip(tr("Add Group"));
connect(btnAddGroup, SIGNAL(clicked()), layerTreeView->defaultActions(), SLOT(addGroup()));
// expand / collapse tool buttons
QToolButton* btnExpandAll = new QToolButton();
btnExpandAll->setAutoRaise(true);
btnExpandAll->setIcon(QIcon(iconDir + "mActionExpandTree.png"));
btnAddGroup->setFixedSize(24, 24);
btnExpandAll->setToolTip(tr("Expand All"));
connect(btnExpandAll, SIGNAL(clicked()), layerTreeView, SLOT(expandAll()));
QToolButton* btnCollapseAll = new QToolButton();
btnCollapseAll->setAutoRaise(true);
btnCollapseAll->setIcon(QIcon(iconDir + "mActionCollapseTree.png"));
btnAddGroup->setFixedSize(24, 24);
btnCollapseAll->setToolTip(tr("Collapse All"));
connect(btnCollapseAll, SIGNAL(clicked()), layerTreeView, SLOT(collapseAll()));
// remove item button
QToolButton* btnRemoveItem = new QToolButton();
// btnRemoveItem->setDefaultAction( this->m_actionRemoveLayer );
btnRemoveItem->setAutoRaise(true);
btnRemoveItem->setIcon(QIcon(iconDir + "layer-remove.png"));
btnAddGroup->setFixedSize(24, 24);
btnRemoveItem->setToolTip(tr("Remove"));
connect(btnRemoveItem, SIGNAL(clicked()), layerTreeView, SLOT(remove()));
// 按钮布局
QHBoxLayout* toolbarLayout = new QHBoxLayout();
toolbarLayout->setContentsMargins(QMargins(5, 0, 5, 0));
toolbarLayout->addWidget(btnAddGroup);
toolbarLayout->addWidget(btnCollapseAll);
toolbarLayout->addWidget(btnExpandAll);
toolbarLayout->addWidget(btnRemoveItem);
toolbarLayout->addStretch();
QVBoxLayout* vboxLayout = new QVBoxLayout();
vboxLayout->setMargin(0);
vboxLayout->addLayout(toolbarLayout);
vboxLayout->addWidget(layerTreeView);
// 装进dock widget中
QDockWidget* m_layerTreeDock = new QDockWidget(tr("Layers"), this);
m_layerTreeDock->setObjectName("Layers");
m_layerTreeDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
QWidget* w = new QWidget();
w->setLayout(vboxLayout);
m_layerTreeDock->setWidget(w);
addDockWidget(Qt::LeftDockWidgetArea, m_layerTreeDock); //初始位置
// 新增
//------------------------------------------------
// 连接地图画布和图层管理器
layerTreeCanvasBridge = new QgsLayerTreeMapCanvasBridge(QgsProject::instance()->layerTreeRoot(), mapCanvas, this);
connect(QgsProject::instance(), SIGNAL(writeProject(QDomDocument&)),
layerTreeCanvasBridge, SLOT(writeProject(QDomDocument&)));
connect(QgsProject::instance(), SIGNAL(readProject(QDomDocument)),
layerTreeCanvasBridge, SLOT(readProject(QDomDocument)));
}
//*新增
void MyQGIS01::slot_autoSelectAddedLayer(const QList<QgsMapLayer*> layers)
{
if (!layers.isEmpty())
{
QgsLayerTreeLayer* nodeLayer = QgsProject::instance()->layerTreeRoot()->findLayer(layers[0]->id());
if (!nodeLayer)
return;
QModelIndex index = layerTreeView->layerTreeModel()->node2index(nodeLayer);
layerTreeView->setCurrentIndex(index);
}
}
void MyQGIS01::addDockWidget(Qt::DockWidgetArea area, QDockWidget* dockwidget)
{
QMainWindow::addDockWidget(area, dockwidget);
setCorner(Qt::TopLeftCorner, Qt::LeftDockWidgetArea);
setCorner(Qt::BottomLeftCorner, Qt::LeftDockWidgetArea);
setCorner(Qt::TopRightCorner, Qt::RightDockWidgetArea);
setCorner(Qt::BottomRightCorner, Qt::RightDockWidgetArea);
dockwidget->show();
mapCanvas->refresh();
}
//*新增
//-------------------------------------------------------------------------------------------------------------------------------
#ifndef MYQGIS01LAYERTREEVIEWMENUPROVIDER_H
#define MYQGIS01LAYERTREEVIEWMENUPROVIDER_H
#include //QT对象
#include
#include
#include
#include
#include
#include
#include
class QAction;
class QgsMapCanvas;
struct LegendLayerAction
{
LegendLayerAction(QAction* a, QString m, QString i, bool all)
: action(a), menu(m), id(i), allLayers(all) {}
QAction* action = nullptr;
QString menu;
QString id;
bool allLayers;
QList<QgsMapLayer*> layers;
};
//图层管理器右键菜单类
class MyQGIS01LayerTreeViewMenuProvider : public QObject, public QgsLayerTreeViewMenuProvider
{
Q_OBJECT
public:
MyQGIS01LayerTreeViewMenuProvider(QgsLayerTreeView* view, QgsMapCanvas* canvas); // 构造函数这样定义,直接获得图层管理器和地图画布控件,以便对它们建立联系
~MyQGIS01LayerTreeViewMenuProvider();
//! 重写这个方法来获取右键菜单
QMenu* createContextMenu() override;
//
void addLegendLayerAction(QAction* action, const QString& menu, QString id,
QgsMapLayerType type, bool allLayers);
bool removeLegendLayerAction(QAction* action);
void addLegendLayerActionForLayer(QAction* action, QgsMapLayer* layer);
void removeLegendLayerActionsForLayer(QgsMapLayer* layer);
QList< LegendLayerAction > legendLayerActions(QgsMapLayerType type) const;
protected:
void addCustomLayerActions(QMenu* menu, QgsMapLayer* layer);
QgsLayerTreeView* m_layerTreeView = nullptr;;
QgsMapCanvas* m_mapCanvas = nullptr;
QMap<QgsMapLayerType, QList< LegendLayerAction>> mLegendLayerActionMap;
};
#endif // MYQGIS01LAYERTREEVIEWMENUPROVIDER_H
#include "MyQGIS01.h"
#include "MyQGIS01Layertreeviewmenuprovider.h"
#include
#include
#include
// QGis include
#include
#include
#include
#include
#include
#include
#include "qmainwindow.h"
// 构造函数
MyQGIS01LayerTreeViewMenuProvider::MyQGIS01LayerTreeViewMenuProvider(QgsLayerTreeView* view, QgsMapCanvas* canvas)
: m_layerTreeView(view)
, m_mapCanvas(canvas)
{
}
//析构
MyQGIS01LayerTreeViewMenuProvider::~MyQGIS01LayerTreeViewMenuProvider()
{
}
//重写方法获取右键菜单
QMenu* MyQGIS01LayerTreeViewMenuProvider::createContextMenu()
{
// 设置这个路径是为了获取图标文件
QString iconDir = "E:\\GIS development\\QGIS\\gisTest2\\image\\";
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(QIcon(iconDir + "mActionExpandTree.png"), tr("&Expand All"), m_layerTreeView, SLOT(expandAll()));
menu->addAction(QIcon(iconDir + "mActionCollapseTree.png"), tr("&Collapse All"), m_layerTreeView, SLOT(collapseAll()));
menu->addSeparator();
}
else if (QgsLayerTreeNode* node = m_layerTreeView->layerTreeModel()->index2node(idx))
{
// layer or group selected
if (QgsLayerTree::isGroup(node))
{
menu->addAction(actions->actionZoomToGroup(m_mapCanvas, menu));
menu->addAction(QIcon(iconDir + "layer-remove.png"), tr("&Remove"), MyQGIS01::instance(), SLOT(removeLayer()));
menu->addAction(actions->actionRenameGroupOrLayer(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->actionShowInOverview(menu));
menu->addAction(actions->actionRemoveGroupOrLayer(menu));
menu->addAction(tr("&Label"), MyQGIS01::instance(), SLOT(slot_labelShowAction()));
// 如果选择的是矢量图层
if (layer->type() == QgsMapLayerType::VectorLayer)
{
//点矢量图层增加点样式设置菜单
QgsVectorLayer* veclayer = qobject_cast<QgsVectorLayer*>(layer);
if (veclayer->geometryType() == QgsWkbTypes::PointGeometry)
{
menu->addAction(tr("PointStyle"), MyQGIS01::instance(), SLOT(slot_pointstyle()));
}
else if (veclayer->geometryType() == QgsWkbTypes::PolygonGeometry)
{
menu->addAction(tr("PolygonStyle"), MyQGIS01::instance(), SLOT(slot_polygonstyle()));
}
}
// 如果选择的是栅格图层
if (layer && layer->type() == QgsMapLayerType::RasterLayer)
{
// TO DO:
}
}
}
return menu;
}
void MyQGIS01LayerTreeViewMenuProvider::addLegendLayerAction(QAction* action, const QString& menu, QString id, QgsMapLayerType type, bool allLayers)
{
}
void MyQGIS01LayerTreeViewMenuProvider::addLegendLayerActionForLayer(QAction* action, QgsMapLayer* layer)
{
}
void MyQGIS01LayerTreeViewMenuProvider::removeLegendLayerActionsForLayer(QgsMapLayer* layer)
{
}
QList< LegendLayerAction > MyQGIS01LayerTreeViewMenuProvider::legendLayerActions(QgsMapLayerType type) const
{
return mLegendLayerActionMap.contains(type) ? mLegendLayerActionMap.value(type) : QList<LegendLayerAction>();
}
bool MyQGIS01LayerTreeViewMenuProvider::removeLegendLayerAction(QAction* action)
{
QMap< QgsMapLayerType, QList< LegendLayerAction > >::iterator it;
for (it = mLegendLayerActionMap.begin();
it != mLegendLayerActionMap.end(); ++it)
{
for (int i = 0; i < it->count(); i++)
{
if ((*it)[i].action == action)
{
(*it).removeAt(i);
return true;
}
}
}
return false;
}
void MyQGIS01LayerTreeViewMenuProvider::addCustomLayerActions(QMenu* menu, QgsMapLayer* layer)
{
}
#include "MyQGIS01.h"
#include
int main(int argc, char *argv[])
{
QgsApplication a(argc, argv, true); //创建一个QGIS应用
//设置qgis应用的路径
QgsApplication::setPrefixPath("D:\QGIS-3.18.3\apps\qgis", true);
QgsApplication::initQgis(); //初始化QGIS应用
MyQGIS01 w;
w.show();
return a.exec();
}
以上就是今天要讲的内容,结果如下图,本文介绍了图层管理器右键菜单的创建,但仍存在小问题:当对图层管理器里具体图层或者group右键会出现帧不在模块中的提示。如果有同学发现解决此问题方法望不吝赐教。下节讲解ui文件和工具栏的创建。