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

为了回应有些同学对上一篇博文的建议,这篇文章主要关注于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();
  • 1

现在开始在类构造函数中加入相关代码来实现这个图层管理的功能。打开 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();

}
  •  

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

目前的功能展示如下所示

这里写图片描述

这里写图片描述
希望对同学们有帮助,感谢阅读!
如有遗漏或者错误的地方,请大家指正,谢谢!

QGIS二次开发中,图层管理器的实现和画布的链接是非常重要的。通过图层管理器,您可以管理和控制地图中的不同图层,包括添加、删除、隐藏和更改图层的属性等操作。而画布则是地图的可视化部分,用于显示地图数据和与用户交互。 要实现图层管理器的功能,您可以使用QGIS提供的PyQGIS Python API。以下是一个简单的示例代码,展示了如何创建一个图层管理器窗口,并添加一个图层: ```python from qgis.core import ( QgsProject, QgsVectorLayer, QgsMapLayerProxyModel ) from qgis.gui import QgsLayerTreeView # 创建一个图层管理器窗口 layer_tree_view = QgsLayerTreeView() # 设置图层管理器的数据模型 layer_proxy_model = QgsMapLayerProxyModel(QgsProject.instance()) layer_proxy_model.setFilterMode(QgsMapLayerProxyModel.FilterMode.ShowAll) layer_tree_view.setLayerModel(layer_proxy_model) # 添加一个图层 layer = QgsVectorLayer('/path/to/your/layer.shp', 'Layer Name', 'ogr') QgsProject.instance().addMapLayer(layer) # 将图层管理器窗口添加到主窗口中 main_window.addDockWidget(Qt.LeftDockWidgetArea, layer_tree_view) ``` 至于画布和图层管理器之间的链接,您可以使用QGIS提供的信号和槽机制来实现。通过连接画布的信号(如`currentLayerChanged`)到图层管理器的槽函数,您可以在用户选择不同图层时更新图层管理器的状态。 ```python canvas.currentLayerChanged.connect(layer_tree_view.setCurrentLayer) ``` 这里的`canvas`是您创建的画布对象。通过这个连接,当用户在画布上选择不同的图层时,图层管理器会自动更新当前选中的图层。 希望这些代码能帮助您开始实现图层管理器和画布的链接!请注意,这只是一个简单示例,您可以根据实际需求进行更多的定制和功能扩展。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值