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();

現在開始在類構造函數中加入相關代碼來實現這個圖層管理的功能。打開 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();

}

肯定有同學留意到我還有些功能代碼框架寫了,但實現沒有填,留個坑,後面再補上。

目前的功能展示如下所示

這裏寫圖片描述

這裏寫圖片描述
希望對同學們有幫助,感謝閱讀!
如有遺漏或者錯誤的地方,請大家指正,謝謝!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章