QGIS3.18.3+QT 5.11.2+VS2019二次开发(4)图层管理器右键和可移动部件

QGIS3.18.3+QT 5.11.2+VS2019二次开发(4)图层管理器右键和可移动部件



前言

上文讲解了窗口布局和图层管理器的实现,说明了图层管理器右键菜单实现方法,这节讲解具体实现;同时图层管理器和上节QGIS中的3(Layers)仍有不同,为实现基本一致,这节将提出几个新概念-QDockWidget、QToolButton、QHBoxLayout。


提示:以下是本篇文章正文内容,下面案例可供参考

一、和上节相比添加的新东西

  • 图层管理器右键所需

继承自qgslayertreeviewmenuprovider这个类的MyQGIS01Layertreeviewmenuprovider类(MyQGIS01layertreeviewmenuprovider.h、MyQGIS01layertreeviewmenuprovider.cpp)

二、项目代码

1.MyQGIS01.h

代码如下(示例):

#include <QtWidgets/QMainWindow>
#include "ui_MyQGIS01.h"

//自己引入的库
#include <qmenu.h>                  //QT菜单类
#include <qaction.h>                //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); //添加可悬浮窗口初始位置
};

2.MyQGIS01.cpp

代码如下(示例):

#include "MyQGIS01.h"

//自己引入的库
#include <qmenubar.h>                      //QT菜单栏库,用来存放菜单
#include <qfiledialog.h>                   //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<QgsMapLayer*>)), this, 
    //    SLOT(slot_autoSelectAddedLayer(const QList<QgsMapLayer*>)));
    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();
}
//*新增

//-------------------------------------------------------------------------------------------------------------------------------

3.MyQGIS01LayerTreeViewMenuProvider.h

#ifndef MYQGIS01LAYERTREEVIEWMENUPROVIDER_H
#define MYQGIS01LAYERTREEVIEWMENUPROVIDER_H

#include <QObject>                         //QT对象

#include <qgslayertreeview.h>
#include <qgsmaplayer.h>

#include <qgsmapcanvas.h>
#include <qdockwidget.h>                   
#include <qgslayertree.h>                  
#include <qgslayertreeviewdefaultactions.h> 
#include <qtoolbutton.h>                   

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

4.MyQGIS01LayerTreeViewMenuProvider.cpp

#include "MyQGIS01.h"
#include "MyQGIS01Layertreeviewmenuprovider.h"

#include <QMenu>
#include <QModelIndex>
#include <QIcon>

// QGis include
#include <qgslayertreeviewdefaultactions.h>
#include <qgslayertreenode.h>
#include <qgslayertreemodel.h>
#include <qgslayertree.h>
#include <qgsrasterlayer.h>
#include <qgsvectorlayer.h>
#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)
{

}

5.mian.cpp

#include "MyQGIS01.h"
#include <qgsapplication.h>  

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文件和工具栏的创建。
在这里插入图片描述

  • 4
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 10
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

学海一叶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值