QGIS符号(样式)配置研究与实现

最近接到任务是进行QGIS符号配置界面的开发,由于当时把QGIS集成到我们桌面三维GIS平台时用到的QGIS版本是2.18.5(比较坑的一个版本,集成之后发现不少bug,补bug补了好久,换版本再集成不是那么容易的,无奈沿用至今),所以就还选用这个版本,毕竟最后还是要集成到三维平台里。其实各个版本核心功能差别不大。
其实符号配置这个网上都有,直接拿来用完成简单任务不成问题,但是个人觉得对深入学习QGIS并无多大助益,要想学好必须要深入源码进行研究。这里还想再说一点,QGIS不仅是学习开源GIS的最佳平台,还是学习Qt极好的选择,只不过对于非GIS专业的同学来说有点吃力,因为在研究源码里Qt相关内容时,要有意识的去掉GIS的内容。

符号配置研究

我们一般配置样式的步骤就是双击图层,弹出属性对话框,调整到样式界面,开始配置符号,如下图
QGIS符号(样式)配置研究与实现_第1张图片
图层属性对话框对应的类是QgsVectorLayerProperties隶属于APP模块。其继承自QgsVectorLayerPropertiesBase,其实就是一个ui文件。如下图
QGIS符号(样式)配置研究与实现_第2张图片
可以看到右侧是空白,其实是一个QStackWidget,根据点击左边的list的item不同,显示出不同的Widget。
当我们点击style选项卡时,出现如下界面
QGIS符号(样式)配置研究与实现_第3张图片
右面这个的组成分为两部分,第一是不能上下滑动的部分,它的类名叫QgsRendererV2PropertiesDialog,还有一部分是滑动的部分,其本质是嵌套在一个QStackWidget上的,它和上面的Comobox关联,根据符号渲染类型的不同,显示对应的widget。这些widget都继承自QgsRendererV2Widget,根据不同的符号渲染类型又派生出QgsSingleSymbolRendererV2Widget、Qgs25DRendererWidget、QgsCategorizedSymbolRendererV2Widget等。由于本次重点研究的是单一符号配置,所以关注焦点放在QgsSingleSymbolRendererV2Widget,进一步查看源码发现,并结合ui文件发现,其中的一个成员QgsSymbolV2SelectorWidget正是整个符号配置对话框的核心。那它怎么用呢?打开qgisapp.cpp尝试搜索一下这个类,发现没找到。后来偶然发现,在QgsSymbolV2SelectorWidget文件里还有一个类QgsSymbolV2SelectorDialog,这个类完全就是把QgsSymbolV2SelectorWidget包了一下,那这个类有没有呢,还在qgisapp.cpp的文件里,果然搜到了。实在是不得不佩服QGIS的作者们,这个类用法极其简单,而且与其他类耦合程度很低,完全就是一个工具类的感觉,只要构造一下,传入需要的参数,接下来你就不用管了,里面都给你做好了,你直接显示就行。另外,QgsSymbolV2SelectorWidget里面包含了QgsSymbolV2,QgsSymbolV2也很独立。特别符合GIS的相关概念,QgsSymbolV2就是GIS中符号这一概念在QGIS中的体现。下面展示了QGIS符号配置界面相关结构图
QGIS符号(样式)配置研究与实现_第4张图片
对话框创建顺序图如下
QGIS符号(样式)配置研究与实现_第5张图片

代码实现

最后再贴出完整的代码,时间仓促,匆匆写了一个测试代码,目的是实现功能,后续肯定要优化。

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include 
#include 
#include 
#include 
#include 
class QgsLayerTreeMapCanvasBridge;
namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

    void showMap();
public slots:
    void extentChanged();
    void openStyleDlg(QModelIndex index);
private:
    Ui::MainWindow *ui;
    QgsMapCanvas m_Canvas;
    QgsLayerTreeView m_Tree;
    QgsLayerTreeMapCanvasBridge* m_bridge;
};

#endif // MAINWINDOW_H


#include "MainWindow.h"
#include "ui_MainWindow.h"
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
    ,m_bridge(NULL)
{
    ui->setupUi(this);
    m_Canvas.setParent(this->centralWidget());//此句一定要在ui->setupUi(this);之后
    QSplitter* sp = new QSplitter(Qt::Horizontal,this->centralWidget());
    m_Tree.setParent(sp);
    m_Canvas.setParent(sp);
    sp->setStretchFactor(1,10);
    QHBoxLayout* hclay = new QHBoxLayout(this->centralWidget());
    hclay->addWidget(sp);

    connect(&m_Tree,SIGNAL( doubleClicked( QModelIndex )),this, SLOT(openStyleDlg(QModelIndex)));
    qDebug()<<885544;
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::showMap()
{
    //1.初始化
    //设置插件及资源目录前缀
    QDir dir(qApp->applicationDirPath());//获取应用程序所在路径

    QString strAppPath(dir.absolutePath());
    QgsProviderRegistry::instance( strAppPath + "/QGIS_Plugins");//注册插件
    qDebug()<<"插件目录为" + strAppPath + "/QGIS_Plugins";

    //设置资源目录
    QgsApplication::setPkgDataPath(strAppPath);

    //设置GDAL_DATA
    QString strENVValue = strAppPath + "/resources/gdal_data";
    if (!qputenv( "GDAL_DATA",  strENVValue.toStdString().c_str() ))
    {
        std::cout << "GDAL_DATA设置失败" << std::endl;
    }


    //2读取工程
    QFileInfo pro("F:/VBF/Data/QGIS/shp/bac/VBF_CommonMapbac.qgs");

    QgsProject* pPro = QgsProject::instance();
    if((!pPro) || (!pPro->read(pro)))//读取工程失败
    {
        std::cout << "读取工程失败" << std::endl;
        return ;
    }
    std::cout<<"GDAL_DATA设置失败\n";

    QgsLayerTreeModel* model = new QgsLayerTreeModel( QgsProject::instance()->layerTreeRoot(), this );
    model->setFlag( QgsLayerTreeModel::AllowNodeReorder );
    model->setFlag( QgsLayerTreeModel::AllowNodeRename );
    model->setFlag( QgsLayerTreeModel::AllowNodeChangeVisibility );
    model->setFlag( QgsLayerTreeModel::ShowLegendAsTree );
    model->setFlag( QgsLayerTreeModel::UseEmbeddedWidgets );
    m_Tree.setModel( model );
    m_Tree.collapseAll();


    connect(&m_Canvas,SIGNAL(extentsChanged()),this, SLOT(extentChanged()));

    //设置图层
    QList <QgsMapCanvasLayer> listCanvasLayer;
    QList<QgsLayerTreeLayer*> listLayer = QgsProject::instance()->layerTreeRoot()->findLayers();
    foreach (QgsLayerTreeLayer* pLayer, listLayer)
    {
        QgsMapCanvasLayer CanvasLayer(pLayer->layer());
        listCanvasLayer.append(CanvasLayer);
    }

    QgsMapToolPan* pTool = new QgsMapToolPan(&m_Canvas);
    m_Canvas.setMapTool(pTool);
    m_Canvas.setLayerSet(listCanvasLayer);
    m_Canvas.zoomToFullExtent();
    m_bridge = new QgsLayerTreeMapCanvasBridge(QgsProject::instance()->layerTreeRoot(), &m_Canvas, this);
    m_Canvas.setParallelRenderingEnabled(true);


}

void MainWindow::extentChanged()
{
    m_Tree.layerTreeModel()->setLegendMapViewData( m_Canvas.mapUnitsPerPixel(), m_Canvas.mapSettings().outputDpi(), m_Canvas.scale());
}

void MainWindow::openStyleDlg(QModelIndex index)
{


    if (QgsSymbolV2LegendNode* node = dynamic_cast<QgsSymbolV2LegendNode*>( m_Tree.currentLegendNode() ))//图例节点
    {
        //获取符号
        const QgsSymbolV2* originalSymbol = node->symbol();
        if ( !originalSymbol ) return;
        QScopedPointer< QgsSymbolV2 > symbol( originalSymbol->clone() );
        QgsVectorLayer* vlayer = qobject_cast<QgsVectorLayer*>( node->layerNode()->layer() );
        QgsSymbolV2SelectorDialog dlg( symbol.data(), QgsStyleV2::defaultStyle(), vlayer, this );
        dlg.setMapCanvas( &m_Canvas );
        if ( dlg.exec() )
        {
            node->setSymbol( symbol.take() );
        }
        return;
    }

    if(QgsVectorLayer* vlayer = dynamic_cast<QgsVectorLayer*>( m_Tree.currentLayer() ))//图层节点
    {


        if (!vlayer->rendererV2()) return;
//        try{
//            QgsSingleSymbolRendererV2* pRender = QgsSingleSymbolRendererV2::convertFromRenderer(vlayer->rendererV2());
//        }
//        catch(...){
//            qDebug()<<555;
//        }

        //return;
//        QgsSingleSymbolRendererV2* pRender = QgsSingleSymbolRendererV2::convertFromRenderer(vlayer->rendererV2());
        QgsSingleSymbolRendererV2* pRender = NULL;
        try {
            pRender = dynamic_cast<QgsSingleSymbolRendererV2*>(vlayer->rendererV2());
        }
        catch(...){
            qDebug()<<45789;
        }

        if (!pRender) return;

        QScopedPointer< QgsSymbolV2 > symbol(  pRender->symbol()->clone() );

        QgsSymbolV2SelectorDialog dlg(symbol.data(), QgsStyleV2::defaultStyle(), vlayer, m_Tree.window() );
        dlg.setMapCanvas( &m_Canvas );
        if ( dlg.exec() )
        {
            pRender->setSymbol( symbol.take() );
            vlayer->triggerRepaint();
            m_Tree.refreshLayerSymbology(vlayer->id());

        }

        if (QgsProject::instance()->write()) qDebug() <<"保存失败";

    }


}
#include "MainWindow.h"
#include 
#include 
#include 
int main(int argc, char *argv[])
{

    QApplication a(argc, argv);

    设置文件编码
//    QApplication::addLibraryPath("./plugins");
    //return 0;
    QTextCodec* code= QTextCodec::codecForName ( "GBK" );
    QTextCodec::setCodecForCStrings(code);
    QTextCodec::setCodecForTr(code);
    QTextCodec::setCodecForLocale(code);
    MainWindow w;
    w.showMap();
    w.showMaximized();
    return a.exec();
}
#-------------------------------------------------
#
# Project created by QtCreator 2020-09-07T08:41:12
#
#-------------------------------------------------

QT       += core gui xml

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
CONFIG +=console
CONFIG +=rtti
TARGET = SymbolConfigTool
TEMPLATE = app

# The following define makes your compiler emit warnings if you use
# any feature of Qt which as been marked as deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS

DEFINES += CORE_EXPORT=Q_DECL_IMPORT
DEFINES += GUI_EXPORT=Q_DECL_IMPORT
DEFINES += noexcept=
DEFINES += override=
DEFINES += nullptr=0
DEFINES += _USE_MATH_DEFINES
DEFINES -= HAVE_TOUCH
#3rdparty
INCLUDEPATH += E:\VBF_GlobalGIS\3rdParty_vs2010_x64\include
INCLUDEPATH += E:\VBF_GlobalGIS\3rdParty_vs2010_x64\include\sqlite3
#QGIS
INCLUDEPATH += ../../3rdParty/include\
               ../../3rdParty/include/core\
               ../../3rdParty/include/core/geos\
               ../../3rdParty/include/core/geometry\
               ../../3rdParty/include/core/layertree\
               ../../3rdParty/include/core/raster\
               ../../3rdParty/include/core/symbology-ng\
               ../../3rdParty/include/gui\
               ../../3rdParty/include/gui/layertree\
               ../../3rdParty/include/gui/symbology-ng\
               ../../3rdParty/include/gui/editorwidgets\
               ../../3rdParty/include/gui/effects\

CONFIG(debug, debug|release){
    DESTDIR = $$PWD/../build/bin/Debug
    TARGET = SymbolConfigToold
    LIBS += -L$$PWD/../../3rdParty/lib -lqgis_cored -lqgis_guid #-lVBF_SliceByProd

}else{
    DESTDIR = $$PWD/../build/bin/Release
    TARGET = SymbolConfigTool
    LIBS += -L$$PWD/../../3rdParty/lib -lqgis_core -lqgis_gui #-lVBF_SliceByPro

}


SOURCES += main.cpp\
    MainWindow.cpp
HEADERS  += \
    MainWindow.h

FORMS    += \
gssymbolv2selectordialogbase.ui

最后的效果

其它阅读
HCZJEarth介绍

你可能感兴趣的:(QGIS,qgis,qt,c++)