QGis二次开发基础 -- 构建图层管理器

2023-11-15

为了回应有些同学对上一篇博文的建议,这篇文章主要关注于QGis二次开发中的“图层管理器”的实现。

使用QGis构建独立应用系统,我相信大部分同学应该还是关注于GIS基本功能框架构建上,也就是一些基本的GIS功能,例如:

数据的显示、漫游浏览等
读入数据的管理,也就是“图层管理器”功能
数据基本信息的查询,例如空间坐标、投影查询,属性表查询等
数据编辑,包括对属性表的编辑和矢量文件形状的编辑等
虽然各个应用系统的功能不尽相同,各种处理算法层出不穷,但是我想以上几个功能需求应该是通用的。

通过上一篇文章(QGis 二次开发最基础的问题——“显示数据”)的讲解,解决了最基本的显示数据、漫游浏览的问题。这一篇文章集中在解决“图层管理器”的构建问题,并且是在上一篇文章的代码基础上进行的,所以没有看上一篇的同学,一定去把上一篇的代码copy下来。

目前来说QGis二次开发这块主要还是看源代码进行学习,有心的同学肯定会发现,我的文章中的代码很多就是QGis的源码,只不过做了一些精简,去掉了目前无关的东西,所以我希望大家去看源码。希望近期能够有足够的时间将以上列出的基本功能都讲解一次,填补QGis二次开发教程目前的一个空白,希望能够帮到一些同学,与大家一道交流、学习。

工程概述
将项目做一个简要的说明,代码主要是QGis 二次开发最基础的问题——“显示数据”中的,所以请一定先去看看那篇文章。

工程中主要包含3个文件,分别是

qgis_dev.h
qgis_dev.cpp
main.cpp
其中“qgis_dev”这个类控制应用程序的初始化以及功能,我们需要扩展的也是这个类中的代码。这一节中,对main.cpp这个文件没有做任何更改。下图展示的是目前工程的运行效果。

添加图层管理器

QGis中,图层管理器的GUI是由类 QgsLayerTreeView 提供的,而与地图画布控件的数据交互,是由 QgsLayerTreeMapCanvasBridge 类提供。
因此首先打开 qgis_dev.h 这个文件,添加如下代码

 //! 图层管理
 QgsLayerTreeView* m_layerTreeView;
 QgsLayerTreeMapCanvasBridge *m_layerTreeCanvasBridge;

 图层管理器的初始化单独放在一个方法中会使得代码结构清晰一些,因此添加一个私有方法负责初始化图层管理器

void initLayerTreeView();

现在开始在类构造函数中加入相关代码来实现这个图层管理的功能。打开 qgis_dev.cpp 这个文件,找到类的构造函数,添加初始化代码为:

//! 初始化map canvas
m_mapCanvas = new QgsMapCanvas();
m_mapCanvas->enableAntiAliasing( true );
m_mapCanvas->setCanvasColor( QColor( 255, 255, 255 ) );

//! 初始化图层管理器
m_layerTreeView = new QgsLayerTreeView( this );
initLayerTreeView();

//! 布局
QWidget* centralWidget = this->centralWidget();
QGridLayout* centralLayout = new QGridLayout( centralWidget );
centralLayout->addWidget( m_mapCanvas, 0, 0, 1, 1 );
centralLayout->addWidget( m_layerTreeView, 0, 1, 1, 1 ); 

// connections 这两句是上一篇文章里面就有的
connect( ui.actionAdd_Vector, SIGNAL( triggered() ), this, SLOT( addVectorLayers() ) );
connect( ui.actionAdd_Raster, SIGNAL( triggered() ), this, SLOT( addRasterLayers() ) );

添加好后运行程序,就可以看到图层管理器面板了。但现在的面板还只是一片空白而已,还需要添加进功能以及将它与地图画布控件绑定。

实现 initLayerTreeView 方法时,首先应该绑定一个 model 用来存储数据(这属于Qt的Model-View模型的知识),然后核心就是将 LayerTreeView 与地图画布控件关联上,因此主要代码为

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 );
m_layerTreeView->setModel( model );

// 连接地图画布和图层管理器
m_layerTreeCanvasBridge = new QgsLayerTreeMapCanvasBridge( QgsProject::instance()->layerTreeRoot(), m_mapCanvas, this );
connect( QgsProject::instance(), SIGNAL( writeProject( QDomDocument& ) ), m_layerTreeCanvasBridge, SLOT( writeProject( QDomDocument& ) ) );
connect( QgsProject::instance(), SIGNAL( readProject( QDomDocument ) ), m_layerTreeCanvasBridge, SLOT( readProject( QDomDocument ) ) );

恩,差不多了,就这几句而已。直接运行就能有下图的效果了。

 

是感觉太粗糙了一点,除了能显示数据图层外,还没有别的功能。下面再改进一下,为图层管理器添加一些功能。

添加图层管理器右键菜单
右键菜单我觉得算是一个比较基础的功能了,通过这里讲解如何添加右键菜单,做一个样例,让同学们大致了解一下机制,后面才能自己扩充更丰富的功能。

QGis的图层管理器右键菜单有个专门的抽象类,叫 QgsLayerTreeViewMenuProvider。通过继承这个类,可以为图层管理器添加右键菜单功能,其中最主要的就是实现 “createContextMenu()”这个方法。

明白了操作方式,下面开始动手。先创建一个新类,取名叫“qgis_devLayerTreeViewMenuProvider“继承自 “QgsLayerTreeViewMenuProvider”类,当然,它还必须继承自 “QObject”。 这个类的原型如下

class qgis_devLayerTreeViewMenuProvider : public QObject, public QgsLayerTreeViewMenuProvider
{
    Q_OBJECT
public:
    qgis_devLayerTreeViewMenuProvider( QgsLayerTreeView *view, QgsMapCanvas* canvas ); // 构造函数这样定义,直接获得图层管理器和地图画布控件,以便对它们建立联系
    ~qgis_devLayerTreeViewMenuProvider();

    //! 重写这个方法来获取右键菜单
    QMenu* createContextMenu() override;

protected:
    QgsLayerTreeView* mView;
    QgsMapCanvas* mCanvas;

};

通过以上原型可以看到分别建立了一个图层管理器和地图画布控件的指针实例,自然是要在构造函数中初始化它们。

// 构造函数
qgis_devLayerTreeViewMenuProvider::qgis_devLayerTreeViewMenuProvider( QgsLayerTreeView *view, QgsMapCanvas* canvas )
    : mView( view )
    , mCanvas( canvas )
{
}

剩下的也就是最关键的”createContextMenu()”这个方法的实现了,直接给代码吧,我不啰嗦了,相信大家也都看得明白。

QMenu* qgis_devLayerTreeViewMenuProvider::createContextMenu()
{
    // 设置这个路径是为了获取图标文件
    QString iconDir = "../images/themes/default/";

    QMenu* menu = new QMenu;
    QgsLayerTreeViewDefaultActions* actions = mView->defaultActions();
    QModelIndex idx = mView->currentIndex();

    // global menu
    if ( !idx.isValid() )
    {
        menu->addAction( actions->actionAddGroup( menu ) );
        menu->addAction( QIcon( iconDir + "mActionExpandTree.png" ), tr( "&Expand All" ), mView, SLOT( expandAll() ) );
        menu->addAction( QIcon( iconDir + "mActionCollapseTree.png" ), tr( "&Collapse All" ), mView, SLOT( collapseAll() ) );
    }
    else if ( QgsLayerTreeNode* node = mView->layerTreeModel()->index2node( idx ) )
    {
        // layer or group selected
        if ( QgsLayerTree::isGroup( node ) )
        {
            menu->addAction( actions->actionZoomToGroup( mCanvas, menu ) );
            menu->addAction( actions->actionRenameGroupOrLayer( menu ) );
            if ( mView->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( mCanvas, menu ) );
            menu->addAction( actions->actionShowInOverview( menu ) );

            // 如果选择的是栅格图层
            if ( layer && layer->type() == QgsMapLayer::RasterLayer )
            {
                // TO DO:
            }
        }
    }

    return menu;
}

 

其中,“QgsLayerTreeViewDefaultActions”这个类提供了一些默认的功能。说明:在以上代码中,为了直观明白,我有意略掉了一些内容,文章的最后,我会贴上所有文件的完整源代码。

最后,到”qgis_dev.cpp”文件中,在初始化图层管理器的方法中,加入添加右键菜单的一些代码就行了。核心代码如下:

// 添加右键菜单
m_layerTreeView->setMenuProvider( new qgis_devLayerTreeViewMenuProvider( m_layerTreeView, m_mapCanvas ) );

connect( QgsProject::instance()->layerTreeRegistryBridge(), SIGNAL( addedLayersToLayerTree( QList<QgsMapLayer*> ) ), this, SLOT( autoSelectAddedLayer( QList<QgsMapLayer*> ) ) );

其中,”layerTreeRegistryBridge”是为了同步地图控件与图层管理器的LayerRigegistry。

如果我没有漏掉什么的话,到这里应该就可以显示右键菜单了。

完整源代码

OK,差不多了,我把这个例子的完整源代码放出来给大家参考吧。再重申一次,我这里的代码基本都是仿造QGis源代码写的,所以我推荐有时间、有精力的同学最好去看一看源代码,我的博文能够讲明白的毕竟太有限了,还是自己去看代码学习比较好。

qgis_dev.h

#ifndef QGIS_DEV_H
#define QGIS_DEV_H

#include <QtGui/QMainWindow>
#include "ui_qgis_dev.h"

#include <QList>
#include <QDockWidget>
#include <QProgressBar>
#include <QLabel>
#include <QDockWidget>

// QGis include
#include <qgsmapcanvas.h>
#include <qgsmaplayer.h>
#include <qgslayertreeview.h>
#include <qgslayertreemapcanvasbridge.h>
#include <qgsscalecombobox.h>

class qgis_dev : public QMainWindow
{
    Q_OBJECT

public:
    qgis_dev( QWidget *parent = 0, Qt::WFlags flags = 0 );
    ~qgis_dev();

    void addDockWidget( Qt::DockWidgetArea area, QDockWidget* dockwidget );

    //! 保持单一实例
    static inline qgis_dev *instance() { return sm_instance; };
public slots:
    //! 添加矢量图层
    void addVectorLayers();
    //! 添加栅格图层
    void addRasterLayers();

    void autoSelectAddedLayer( QList<QgsMapLayer*> layers );
private:
    Ui::qgis_devClass ui;
    static qgis_dev* sm_instance;

    QgsMapCanvas* m_mapCanvas; // 地图画布

    //! 图层管理
    QgsLayerTreeView* m_layerTreeView;
    QgsLayerTreeMapCanvasBridge *m_layerTreeCanvasBridge;


    QDockWidget *m_layerTreeDock;
    QDockWidget *m_layerOrderDock;
    /*QDockWidget *m_OverviewDock;
    QDockWidget *m_pGpsDock;
    QDockWidget *m_logDock;*/

    QLabel* m_scaleLabel; // 在状态栏中显示“scale 1:”
    QgsScaleComboBox* m_scaleEdit; //! 在状态栏中显示比例尺值
    QProgressBar* m_progressBar;
    QLabel* m_coordsLabel; //! 在状态栏显示"Coordinate / Extent"
    QLineEdit* m_coordsEdit; //! 在状态栏显示地理坐标

    QList<QgsMapCanvasLayer> mapCanvasLayerSet; // 地图画布所用的图层集合



    void initLayerTreeView();
    void createStatusBar();

};

#endif // QGIS_DEV_H

 qgis_dev.cpp

#include "qgis_dev.h"

#include <QDialog>
#include <QFileDialog>
#include <QMessageBox>
#include <QLayout>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QWidget>
#include <QGridLayout>
#include <QFont>
#include <QLineEdit>
#include <QToolButton>
#include <QMargins>

// QGis include
#include <qgsvectorlayer.h>
#include <qgsmaplayerregistry.h>
#include <qgsrasterlayer.h>
#include <qgsproject.h>
#include <qgslayertreemodel.h>
#include <qgsapplication.h>
#include <qgslayertreelayer.h>
#include <qgslayertreegroup.h>
#include <qgslayertreeregistrybridge.h>
#include <qgslayertreeviewdefaultactions.h>
#include "qgis_devlayertreeviewmenuprovider.h"


qgis_dev* qgis_dev::sm_instance = 0;

qgis_dev::qgis_dev( QWidget *parent, Qt::WFlags flags )
    : QMainWindow( parent, flags )
{
    /*if ( sm_instance )
    {
        sm_instance->show();
    }
    sm_instance = this;*/

    ui.setupUi( this );

    //! 初始化map canvas, 并装入layout中
    m_mapCanvas = new QgsMapCanvas();
    m_mapCanvas->enableAntiAliasing( true );
    m_mapCanvas->setCanvasColor( QColor( 255, 255, 255 ) );

    //! 初始化图层管理器
    m_layerTreeView = new QgsLayerTreeView( this );
    initLayerTreeView();

    //! 布局
    QWidget* centralWidget = this->centralWidget();
    QGridLayout* centralLayout = new QGridLayout( centralWidget );
    centralLayout->addWidget( m_mapCanvas, 0, 0, 1, 1 );
    //centralLayout->addWidget( m_layerTreeView, 0, 1, 1, 1 );

    // connections
    connect( ui.actionAdd_Vector, SIGNAL( triggered() ), this, SLOT( addVectorLayers() ) );
    connect( ui.actionAdd_Raster, SIGNAL( triggered() ), this, SLOT( addRasterLayers() ) );
}

qgis_dev::~qgis_dev()
{

}

void qgis_dev::addVectorLayers()
{
    QString filename = QFileDialog::getOpenFileName( this, tr( "open vector" ), "", "*.shp" );
    QStringList temp = filename.split( QDir::separator() );
    QString basename = temp.at( temp.size() - 1 );
    QgsVectorLayer* vecLayer = new QgsVectorLayer( filename, basename, "ogr", false );
    if ( !vecLayer->isValid() )
    {
        QMessageBox::critical( this, "error", "layer is invalid" );
        return;
    }

    QgsMapLayerRegistry::instance()->addMapLayer( vecLayer );
    mapCanvasLayerSet.append( vecLayer );
    m_mapCanvas->setExtent( vecLayer->extent() );
    m_mapCanvas->setLayerSet( mapCanvasLayerSet );
    m_mapCanvas->setVisible( true );
    m_mapCanvas->freeze( false );
    m_mapCanvas->refresh();

}

void qgis_dev::addRasterLayers()
{
    QString filename = QFileDialog::getOpenFileName( this, tr( "open vector" ), "", "*.tif" );
    QStringList temp = filename.split( QDir::separator() );
    QString basename = temp.at( temp.size() - 1 );
    QgsRasterLayer* rasterLayer = new QgsRasterLayer( filename, basename, "gdal", false );
    if ( !rasterLayer->isValid() )
    {
        QMessageBox::critical( this, "error", "layer is invalid" );
        return;
    }

    QgsMapLayerRegistry::instance()->addMapLayer( rasterLayer );
    mapCanvasLayerSet.append( rasterLayer );
    m_mapCanvas->setExtent( rasterLayer->extent() );
    m_mapCanvas->setLayerSet( mapCanvasLayerSet );
    m_mapCanvas->setVisible( true );
    m_mapCanvas->freeze( false );
    m_mapCanvas->refresh();
}

void qgis_dev::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 );

    m_layerTreeView->setModel( model );

    // 添加右键菜单
    m_layerTreeView->setMenuProvider( new qgis_devLayerTreeViewMenuProvider( m_layerTreeView, m_mapCanvas ) );

    connect( QgsProject::instance()->layerTreeRegistryBridge(), SIGNAL( addedLayersToLayerTree( QList<QgsMapLayer*> ) ),
             this, SLOT( autoSelectAddedLayer( QList<QgsMapLayer*> ) ) );

    // 设置这个路径是为了获取图标文件
    QString iconDir = "../images/themes/default/";

    // add group tool button
    QToolButton * btnAddGroup = new QToolButton();
    btnAddGroup->setAutoRaise( true );
    btnAddGroup->setIcon( QIcon( iconDir + "mActionAdd.png" ) );

    btnAddGroup->setToolTip( tr( "Add Group" ) );
    connect( btnAddGroup, SIGNAL( clicked() ), m_layerTreeView->defaultActions(), SLOT( addGroup() ) );

    // expand / collapse tool buttons
    QToolButton* btnExpandAll = new QToolButton();
    btnExpandAll->setAutoRaise( true );
    btnExpandAll->setIcon( QIcon( iconDir + "mActionExpandTree.png" ) );
    btnExpandAll->setToolTip( tr( "Expand All" ) );
    connect( btnExpandAll, SIGNAL( clicked() ), m_layerTreeView, SLOT( expandAll() ) );

    QToolButton* btnCollapseAll = new QToolButton();
    btnCollapseAll->setAutoRaise( true );
    btnCollapseAll->setIcon( QIcon( iconDir + "mActionCollapseTree.png" ) );
    btnCollapseAll->setToolTip( tr( "Collapse All" ) );
    connect( btnCollapseAll, SIGNAL( clicked() ), m_layerTreeView, SLOT( collapseAll() ) );

    // remove item button
    QToolButton* btnRemoveItem = new QToolButton();
    // btnRemoveItem->setDefaultAction( this->m_actionRemoveLayer );
    btnRemoveItem->setAutoRaise( true );


    // 按钮布局
    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( m_layerTreeView );

    // 装进dock widget中
    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 );

    // 连接地图画布和图层管理器
    m_layerTreeCanvasBridge = new QgsLayerTreeMapCanvasBridge( QgsProject::instance()->layerTreeRoot(), m_mapCanvas, this );
    connect( QgsProject::instance(), SIGNAL( writeProject( QDomDocument& ) ),
             m_layerTreeCanvasBridge, SLOT( writeProject( QDomDocument& ) ) );
    connect( QgsProject::instance(), SIGNAL( readProject( QDomDocument ) ),
             m_layerTreeCanvasBridge, SLOT( readProject( QDomDocument ) ) );



}

void qgis_dev::createStatusBar()
{
    statusBar()->setStyleSheet( "QStatusBar::item {border: none;}" );

    //! 添加进度条
    m_progressBar = new QProgressBar( statusBar() );
    m_progressBar->setObjectName( "m_progressBar" );
    m_progressBar->setMaximum( 100 );
    m_progressBar->hide();
    statusBar()->addPermanentWidget( m_progressBar, 1 );
    connect( m_mapCanvas, SIGNAL( renderStarting() ), this, SLOT( canvasRefreshStarted() ) );
    connect( m_mapCanvas, SIGNAL( mapCanvasRefreshed() ), this, SLOT( canvasRefreshFinished() ) );

    QFont myFont( "Arial", 9 );
    statusBar()->setFont( myFont );

    //! 添加坐标显示标签
    m_coordsLabel = new QLabel( QString(), statusBar() );
    m_coordsLabel->setObjectName( "m_coordsLabel" );
    m_coordsLabel->setFont( myFont );
    m_coordsLabel->setMinimumWidth( 10 );
    m_coordsLabel->setMargin( 3 );
    m_coordsLabel->setAlignment( Qt::AlignCenter );
    m_coordsLabel->setFrameStyle( QFrame::NoFrame );
    m_coordsLabel->setText( tr( "Coordinate:" ) );
    m_coordsLabel->setToolTip( tr( "Current map coordinate" ) );
    statusBar()->addPermanentWidget( m_coordsLabel, 0 );

    //! 坐标编辑标签
    m_coordsEdit = new QLineEdit( QString(), statusBar() );
    m_coordsEdit->setObjectName( "m_coordsEdit" );
    m_coordsEdit->setFont( myFont );
    m_coordsEdit->setMinimumWidth( 10 );
    m_coordsEdit->setMaximumWidth( 300 );
    m_coordsEdit->setContentsMargins( 0, 0, 0, 0 );
    m_coordsEdit->setAlignment( Qt::AlignCenter );
    statusBar()->addPermanentWidget( m_coordsEdit, 0 );
    connect( m_coordsEdit, SIGNAL( returnPressed() ), this, SLOT( userCenter() ) );

    //! 比例尺标签
    m_scaleLabel = new QLabel( QString(), statusBar() );
    m_scaleLabel->setObjectName( "m_scaleLabel" );
    m_scaleLabel->setFont( myFont );
    m_scaleLabel->setMinimumWidth( 10 );
    m_scaleLabel->setMargin( 3 );
    m_scaleLabel->setAlignment( Qt::AlignCenter );
    m_scaleLabel->setFrameStyle( QFrame::NoFrame );
    m_scaleLabel->setText( tr( "Scale" ) );
    m_scaleLabel->setToolTip( tr( "Current map scale" ) );
    statusBar()->addPermanentWidget( m_scaleLabel, 0 );

    m_scaleEdit = new QgsScaleComboBox( statusBar() );
    m_scaleEdit->setObjectName( "m_scaleEdit" );
    m_scaleEdit->setFont( myFont );
    m_scaleEdit->setMinimumWidth( 10 );
    m_scaleEdit->setMaximumWidth( 100 );
    m_scaleEdit->setContentsMargins( 0, 0, 0, 0 );
    m_scaleEdit->setToolTip( tr( "Current map scale (formatted as x:y)" ) );
    statusBar()->addPermanentWidget( m_scaleEdit, 0 );
    connect( m_scaleEdit,  SIGNAL( scaleChanged() ), this, SLOT( userScale() ) );

}

void qgis_dev::autoSelectAddedLayer( QList<QgsMapLayer*> layers )
{
    if ( layers.count() )
    {
        QgsLayerTreeLayer* nodeLayer = QgsProject::instance()->layerTreeRoot()->findLayer( layers[0]->id() );

        if ( !nodeLayer )
        {
            return;
        }

        QModelIndex index = m_layerTreeView->layerTreeModel()->node2index( nodeLayer );
        m_layerTreeView->setCurrentIndex( index );
    }
}

void qgis_dev::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();
    m_mapCanvas->refresh();
}

 qgis_devlayertreeviewmenuprovider.h

#ifndef QGIS_DEVLAYERTREEVIEWMENUPROVIDER_H
#define QGIS_DEVLAYERTREEVIEWMENUPROVIDER_H

#include <QObject>

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

class QAction;

struct LegendLayerAction
{
    LegendLayerAction( QAction* a, QString m, QString i, bool all )
        : action( a ), menu( m ), id( i ), allLayers( all ) {}
    QAction* action;
    QString menu;
    QString id;
    bool allLayers;
    QList<QgsMapLayer*> layers;
};

class QgsMapCanvas;

class qgis_devLayerTreeViewMenuProvider : public QObject, public QgsLayerTreeViewMenuProvider
{
    Q_OBJECT

public:
    qgis_devLayerTreeViewMenuProvider( QgsLayerTreeView *view, QgsMapCanvas* canvas );
    ~qgis_devLayerTreeViewMenuProvider();

    //! 重写这个方法来获取右键菜单
    QMenu* createContextMenu() override;

    void addLegendLayerAction( QAction* action, QString menu, QString id,
                               QgsMapLayer::LayerType type, bool allLayers );
    bool removeLegendLayerAction( QAction* action );
    void addLegendLayerActionForLayer( QAction* action, QgsMapLayer* layer );
    void removeLegendLayerActionsForLayer( QgsMapLayer* layer );
    QList< LegendLayerAction > legendLayerActions( QgsMapLayer::LayerType type ) const;

protected:

    void addCustomLayerActions( QMenu* menu, QgsMapLayer* layer );

    QgsLayerTreeView* mView;
    QgsMapCanvas* mCanvas;

    QMap<QgsMapLayer::LayerType, QList< LegendLayerAction>> mLegendLayerActionMap;

};

#endif // QGIS_DEVLAYERTREEVIEWMENUPROVIDER_H

qgis_devlayertreeviewmenuprovider.cpp 

#include "qgis_devlayertreeviewmenuprovider.h"
#include "qgis_dev.h"

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

// QGis include
#include <qgslayertreeviewdefaultactions.h>
#include <qgslayertreenode.h>
#include <qgslayertreemodel.h>
#include <qgslayertree.h>
#include <qgsrasterlayer.h>

qgis_devLayerTreeViewMenuProvider::qgis_devLayerTreeViewMenuProvider( QgsLayerTreeView *view, QgsMapCanvas* canvas )
    : mView( view )
    , mCanvas( canvas )
{
}

qgis_devLayerTreeViewMenuProvider::~qgis_devLayerTreeViewMenuProvider()
{

}

QMenu* qgis_devLayerTreeViewMenuProvider::createContextMenu()
{
    // 设置这个路径是为了获取图标文件
    QString iconDir = "../images/themes/default/";

    QMenu* menu = new QMenu;

    QgsLayerTreeViewDefaultActions* actions = mView->defaultActions();

    QModelIndex idx = mView->currentIndex();

    // global menu
    if ( !idx.isValid() )
    {
        menu->addAction( actions->actionAddGroup( menu ) );
        menu->addAction( QIcon( iconDir + "mActionExpandTree.png" ), tr( "&Expand All" ), mView, SLOT( expandAll() ) );
        menu->addAction( QIcon( iconDir + "mActionCollapseTree.png" ), tr( "&Collapse All" ), mView, SLOT( collapseAll() ) );
    }
    else if ( QgsLayerTreeNode* node = mView->layerTreeModel()->index2node( idx ) )
    {
        // layer or group selected
        if ( QgsLayerTree::isGroup( node ) )
        {
            menu->addAction( actions->actionZoomToGroup( mCanvas, menu ) );
            menu->addAction( QIcon( iconDir + "mActionRemoveLayer.svg" ), tr( "&Remove" ), qgis_dev::instance(), SLOT( removeLayer() ) );
            menu->addAction( actions->actionRenameGroupOrLayer( menu ) );
            if ( mView->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( mCanvas, menu ) );
            menu->addAction( actions->actionShowInOverview( menu ) );

            // 如果选择的是栅格图层
            if ( layer && layer->type() == QgsMapLayer::RasterLayer )
            {
                // TO DO
            }
        }
    }

    return menu;
}

void qgis_devLayerTreeViewMenuProvider::addLegendLayerAction( QAction * action, QString menu, QString id, QgsMapLayer::LayerType type, bool allLayers )
{

}

void qgis_devLayerTreeViewMenuProvider::addLegendLayerActionForLayer( QAction * action, QgsMapLayer * layer )
{

}

void qgis_devLayerTreeViewMenuProvider::removeLegendLayerActionsForLayer( QgsMapLayer * layer )
{

}

QList< LegendLayerAction > qgis_devLayerTreeViewMenuProvider::legendLayerActions( QgsMapLayer::LayerType type ) const
{
    return mLegendLayerActionMap.contains( type ) ? mLegendLayerActionMap.value( type ) : QList<LegendLayerAction>();
}

最后是main.cpp

#include<qgsapplication.h>

#include "qgis_dev.h"

int main( int argc, char *argv[] )
{
    QgsApplication myApp( argc, argv, true );
    QgsApplication::setPrefixPath( "C:/Program Files/qgis2.9.0", true );
    QgsApplication::initQgis();

    qgis_dev* myQgis = new qgis_dev();
    myQgis->show();
    return myApp.exec();

}

肯定有同学留意到我还有些功能代码框架写了,但实现没有填,留个坑,后面再补上。

目前的功能展示如下所示

 

 

 

原文链接:https://blog.csdn.net/deirjie/article/details/50428179

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

QGis二次开发基础 -- 构建图层管理器 的相关文章

  • QGIS数据可视化学习笔记04(完结)——POI检索和数据上图

    一 POI数据检索概念 什么是POI检索 xff1f 为什么要使用POI检索 xff1f 有的时候 xff0c 我们可能不知道某个位置的地址 xff0c 比如说北京朝阳图书馆 xff0c 这个位置没有任何部门会去公布位置信息 xff0c 但
  • arcgis的lyr样式转qgis的sld样式

    需求 xff1a arcgis样式lyr要发布到geoserver 先说最终技术路线 xff1a qgis安装slyr插件 xff0c lyr转成xml xff0c 再通过xml配图后导出sld 避免的操作 xff1a lyr直接导出sld
  • Qgis python开发教程(一)Introduction

    最近工作中用到Qgis开发 找了网上一圈 关于python的开发很少 最后找到官方API 官方API很不错 但是英文难免麻烦一些 自己尝试翻译出来并实践一下 以备自己后续的学习 并非逐字逐句翻译 就是把自己认为重点的地方记录摘要 水平有限
  • VS2019 This application failed to start because no Qt platform plugin could be initialized

    解决C This application failed to start because no Qt platform plugin could be initialized Reinstalling the application may
  • 地信

    准备 用的是qgis desktop 3 12 0 官网上下载即可 打开后页面 语言要修改成中文 setting option第一栏 算土地利用类型 双击新建项目 在浏览界面寻找双击打开自己要处理的数据 显示在图层中 图层可以上下拖动调整显
  • QGIS批量将分幅遥感影像合并

    要在QGIS中编程实现批量将分幅遥感影像拼接成完整影像 可以按照以下步骤进行操作 1 首先 确保已经安装并正确配置了QGIS Python环境 并导入所需的库 以下是代码的导入部分 from qgis core import QgsProj
  • QGIS 栅格数据裁剪(扣除)

    利用QGIS 以矢量数据范围来扣除栅格数据中的区域 其实也就是赋0值 首先 安装Easy Raster Splitter 插件 准备好矢量数据和栅格数据 打开Easy Raster Splitter 插件 Method 选择 Extract
  • QGIS获取OSM地图矢量数据

    目录 一 QGIS加载OSM底图图层 二 quickosm插件导入openstreetmap数据 三 数据导出 四 Overpass query查询用法 五 写在最后 一 QGIS加载OSM底图图层 1 安装QuickMapServices
  • 创建一个QGIS plugin

    创建一个QGIS plugin 1 plugins gt Plugin Builder 2 4 6 选择自己创建的插件存储的地方 最好换一个地方 默认的插件存储地方比较深 我放的地方为D coding mygistest 点击generat
  • 使用QGIS软件对面数据顶点编辑实现数据的微改动

    在平时的项目中 会出现网上下载的数据与实际不符 有一些小小的偏差 这时候需要对一些区域数据进行微调 这就可以利用QGIS的顶点工具进行操作 目录 1 第一步 数据加载 2 第二步 图层编辑 3 第三步 顶点工具 4 第四步 具体操作 1 第
  • QGIS 使用矢量数据(shp)批量裁剪栅格数据(tif)

    前提描述 有一种情况是一个矢量数据有多个元素 比如A省有11个市 那么我用A省的矢量去裁剪栅格的时候 想直接输出11个市域独立的栅格结果 也就是输出11个栅格数据 这个在ArcGIS Pro和Arcmap目前还不能直接做到 用Python肯
  • #QGIS源码官方编译指南

    QGIS源码官方编译指南 将QGIS官方的编译指南windows部分翻译一下 供大家参考 这个版本是QGIS源代码工程中2017年4月30日最后修改的版本 对应QGIS 2 99 也就是即将发布为QGIS 3 0的版本 翻译 Jacory
  • QGIS编译

    一 准备工作 1 下载QGIS源码 最新版本的QGIS源码需要从git上下载 最新的发布版是2 0 下载地址见下 https github com qgis QGIS tree release 2 0 打开网页 在右侧有个Download
  • 从本地目录(即不是来自 git 存储库)在 R 传单中渲染自定义图块

    我使用 Qgis 中的 Qtiles 插件从一个非常大的栅格中创建了一些图块 我已将它们保存到计算机上的本地目录中 现在想使用 R 将它们渲染在传单地图中 The addTiles函数传递一个 URL 但似乎不适用于本地文件路径 在另一篇文
  • 构建 QGIS 时未解析的符号

    我已经能够从这一步取得进展 在 Windows 7 上构建 QGIS 源代码 不工作 我正在尝试使用 Visual Studio 10 Express 构建最新的 QGIS 2 10 1 但在构建 ALL BUILD 时获取这些未解析的符号
  • 无法在 R 中绘制 sf 线串:CPL_geos_is_empty(st_geometry(x)) 中的错误

    我有飓风轨迹点 我将其转换为 QGIS 中的线 https i stack imgur com Gtt61 png https i stack imgur com Gtt61 png https i stack imgur com 6z8M
  • Google 从 QGIS 元数据文件导入 XML

    我正在尝试使用 Google Sheets importxml 捕获 qmd 文件的元素 即 xml 标记 基于如何对 Google Drive 中的文件使用 importXML 函数 https stackoverflow com que
  • 谷歌地图破坏 KML

    我已经创建了一些世界地区的 KML 文件 以便使用 Google 地图 API 进行显示 这一直工作正常 但最近有一个区域开始显示数据好像已损坏 你可以在这里明白我的意思 https drive google com file d 0B9u
  • 如何反转 KML 以便突出显示多边形外部的区域

    我有一个 KML 文件 它是一个覆盖城市边界的多边形 目前我的多边形是灰色的 我想反转它 所以世界的其他部分都是灰色的 这是 kml 的链接 它应该带您到 Google 地图 埃德蒙顿市 https docs google com a ed
  • QGIS 和 PostGIS(地图点(美国地图上的纬度和经度以及半径)

    我安装了QGIS和PostGIS 我想在美国地图上以 100 英里为半径显示 200 个点 我已将纬度和经度导入 PostGIS 数据库中 所以我有三个字段 地址 纬度 经度 1 我需要将纬度和经度字段转换为点或几何字段吗 如果是这样怎么办

随机推荐

  • java math bigdecimal_java.math.BigDecimal类

    BigDecimal提供算数 标度操作 舍入 比较 哈希算法和格式转换 toString方法提供BigDecimal的规范表示形式 一 静态变量 ONE TEN ZERO 指定值 并返回 ROUND 值从0到7的范围 二 构造函数 new
  • 小米汽车,能否在新能源汽车江湖站稳脚跟?

    最近 圈内都在传小米汽车亦庄工厂已试生产近一个月 每周可产50辆样车 正在为首款新能源汽车量产做最后的准备 此前的业绩交流会上 小米集团总裁卢伟冰透露 小米汽车结束了夏测且进展非常顺利 2024年上半年量产的目标明确 同时 界面新闻的报道也
  • Java进阶篇之RabbitMQ的快速入门(一)

    文章目录 Java进阶篇之RabbitMQ快速入门篇 相关概念 什么是MQ 常见的MQ RabbitMQ RabbitMQ的四大核心概念 RabbitMQ中的各个名词介绍 RabbitMQ的安装 入门案列 Hello World Work
  • absolute导致的高度塌陷问题——解决方法

    前段时间写前端页面排版的时候又又又遇到了absolute导致的父元素高度塌陷的问题 以前代码写的少所以遇到问题就是查查查 问题解决后了一个漫长的学期后又遇到一样的问题又得重新查查查 所以这次索性总结一下 什么是高度塌陷 在文档流中 父元素的
  • 通过CSS3 Media Query实现响应式Web设计

    为什么80 的码农都做不了架构师 gt gt gt OK开始说正经的了 在之前一篇译文中 我们了解了响应式Web设计的概念 组成要素以及基本的实现思路 今天继续相关话题 我们将从前文介绍过的 弹性布局结构 这方面出发 通过一个具体的实例来深
  • Linux系统启动流程详解

    今天继续给大家介绍Linux相关内容 本文主要内容是Linux系统的启动流程 Linux系统启动流程如下图所示 从上图不难看出 Linux系统启动 一共分为步 1 加载BIOS BIOS是Basic Input Output System
  • selenium(3)----JAVA版本

    动态参数的使用 1 先对我们所修饰的方法加上 ParameterizedTest表示这个方法是多参数的一个类 2 MethodSource 数据来源的方法名称 MethodSource也可以不指定方法名称 会自动去找和测试方法同名的静态方法
  • Python 小技巧

    如果你有一个很长的数字变量 a 3000000000 怎么样让它在代码里看起来舒适 并且一下子就能知道它有多少位数呢 在 Python 中可以这么写 a 3 000 000 000 这样与直接 a 3000000000 是等价的 但是代码中
  • C++智能指针作为函数形参和函数返回值的应用场景

    当智能指针作为函数形参时 在调用此函数时 形参代表的智能指针的引用计数将 1 同时退出此函数时 该形参的引用计数将 1 当形参不是智能指针时 调用该函数和退出该函数时 该形参的引用计数不会变化 当函数的内部创建一个智能指针 并需要在函数外使
  • vulhub靶场搭建与使用

    vulhub靶场搭建与使用 1 前言 2 配置yum源 2 1备份原来的源文件 2 2 配置阿里源 2 3重置yum源 2 4更新yum源 3 安装docket 3 1安装docket 3 2启动docket 3 3设置国内镜像源 3 4重
  • 了解 什么是 22 端口

    一 22 是什么端口 22端口就是ssh应用端口 ssh用于远程连接Linux云服务器 一般Linux云服务器要放通22号端口 Windows云服务器需要放通3389号端口 云服务器用户需要在安全组中开放端口后 才可以使用端口对应的服务 在
  • HackInOS靶机渗透writeup

    HackInOS靶机渗透writeup 0x00准备测试环境 导入下载好的HackInOS ova文件后 将网络设置成桥接模式 并使用DHCP分配IP 成功后打开的靶机图如下 0x01渗透过程 使用nmap扫描确定目标机器IP nmap s
  • IT项目管理第6次作业

    IT项目管理第6次作业 一 教材练习6 a 画出该项目的双代号网络图 把节点的号码标在一个圆圈内 画出节点之间的箭线 在每一条箭线上标注代表活动的字母和活动工期 b 按照图6 8表示每条路径的方法 找出网络图中的所有路径并标出它们的长度 路
  • OA项目软件说明书

    最近写了一个OA项目 主要根据这个软件说明书来编写的 此处省略了封面的一些格式不友好的内容 目 录 1 引言 1 1 目的 1 2 定义 1 3 参考资料 2 软件总体概述 2 1 软件标识 2 1 1 项目名称 2 1 2 产品标识 2
  • 2023校园招聘求职报告

    导读 超四成学生选择就业 近三成学生选择继续深造 近两成的学生计划考公务员 还有6 1 的学生有自主创业的想法 从学生反馈来看大家在毕业后的计划多元化趋势明显 选择就业的人数偏少而深造和考公人数比例达到45 受疫情影响和不少行业的升级调整过
  • 【11】STM32·HAL库开发-STM32CubeMX简介、安装

    目录 1 STM32CubeMX简介 了解 2 STM32CubeMX安装 了解 2 1STM32CubeMX软件获取 2 1 1获取Java软件 2 1 2获取STM32CubeMX软件 2 2搭建Java运行环境 2 3安装STM32C
  • 积分变换->重要知识点总结

    知识点公式总结 Flourier变换总结 Flourier积分表达式 傅里叶变换和傅里叶逆变换 一些常用函数的Fourier变换 变换中可能用到的公式 诱导公式 指数三角转换 和差化积积化和差 函数的相关性质 傅里叶变换的相关性质 能量积分
  • java中的Iterator和Iterable 区别和具体使用{方法(Iterable values)}

    java lang Iterable java util Iterator 来自百度知道 Iterator是迭代器类 而Iterable是接口 好多类都实现了Iterable接口 这样对象就可以调用iterator 方法 一般都是结合着用
  • Java 对象

    Java对象是Java语言中最重要的概念之一 它允许开发人员将数据和方法组合在一起 创建具有自己行为和状态的实体 本文将详细介绍Java对象的概念 特性以及在实际工作中的应用 Java对象的概念和特性 在Java中 对象是类的实例 类是一个
  • QGis二次开发基础 -- 构建图层管理器

    为了回应有些同学对上一篇博文的建议 这篇文章主要关注于QGis二次开发中的 图层管理器 的实现 使用QGis构建独立应用系统 我相信大部分同学应该还是关注于GIS基本功能框架构建上 也就是一些基本的GIS功能 例如 数据的显示 漫游浏览等