QGIS 3.x 二次开发:Windows平台开发环境配置

1. 前言

研一下学期开始接触QGIS,断断续续地做了两年多的QGIS开发。现在硕士毕业,以后可能就不怎么再做QGIS二次开发工作了。打算在博客里记录一些开发心得,希望后面的人能少踩一些坑。本文以64位Windows平台为例,分别介绍QGIS3.x在Visual Studio和Qt Creator平台下的二次开发环境配置。

2. 准备工作

受中文社区很多博客的影响,许多人,包括我自己在内,QGIS二次开发的起手式就是——编译源码。由于依赖库繁多,编译耗时而又容易出错,因此这是一项十分考验人品和耐性的工作。在编译工作上浪费了漫长的人生后才知道,编译源码并不是一个必需的工作。OSGeo4W已经为我们提供了QGIS二次开发所需的release版开发包。

OSGeo4W是Windows平台下的多种开源GIS应用程序与开发库的安装工具,4W 即 for Windows。使用64位操作系统的用户可使用64位的OSGeo4W进行安装。安装时,选择”Advanced Install”,然后勾选 “qgis”或“qgis-ltr”即可下载安装对应版本的QGIS开发包。其中“qgis”指的是最新的发布版,“qgis-ltr”为长期发布(Long Term Release)版。目前(2019-06-12)OSGeo4W提供的qgis版本号为3.6,qgis-ltr版本号为3.4。OSGeo4W会自动安装GDAL等相关的依赖库。下图是我的QGIS开发库安装情况。

osgeo4w

Qt5同样可以在OSGeo4W下载安装。WebKit模块在Qt5.6之后的官方版本中已被WebEngine模块取代,但QGIS布局项中的HTML渲染功能依然由WebKit模块实现。OSGeo4W提供的Qt则自带WebKit模块。目前OSGEO4W提供的Qt5版本号为5.11.2。

3. 项目属性配置

根据项目要求,可以自行选择Visual Studio或Qt Creator作为IDE,个人更喜欢Qt Creator,主要是Creator的快捷键用得更顺手。

3.1 Visual Studio平台下配置

新建Qt GUI应用程序,在项目向导里添加XML模块。生成解决方案后,将编译环境设为release x64,然后右键项目,选择“属性”,开始进行项目的属性配置。

(1)添加包含目录。在“C++” >>“常规” >> “附加包含目录”中添加头文件路径:

1
2
C: \OSGeo4W64\include
C: \OSGeo4W64\apps\qgis\include

(2)添加预处理器命令。在“C++” >>“预处理器” >> “预处理器定义”中添加以下预处理命令:

1
2
GUI_EXPORT=__declspec(dllimport)
CORE_EXPORT=__declspec(dllimport)

(3)添加静态链接库目录。在“链接器” >>“常规” >> “附加库目录”中添加静态链接库路径:

1
2
C:\ OSGeo4W64\apps\qgis\lib
C:\ OSGeo4W64 \lib

(4)添加依赖项。在“链接器” >>“输入” >> “附加依赖项”中添加以下项:

1
2
qgis_core.lib
qgis_gui.lib

3.2 Qt Creator平台下配置

若使用Qt Creator作为开发所用IDE,可在pro文件中直接添加以下代码添加包含目录、库目录和预处理器命令:

1
2
3
4
5
6
7
8
INCLUDEPATH += "C:/OSGeo4W64/apps/qgis/include"
INCLUDEPATH += "C:/OSGeo4W64/include"

LIBS += -L"C:/OSGeo4W64/apps/qgis/lib" -lqgis_core -lqgis_gui
LIBS += -L"C:/OSGeo4W64/lib" -lgdal_i

DEFINES += CORE_EXPORT=__declspec(dllimport)
DEFINES += GUI_EXPORT=__declspec(dllimport)

4. 环境变量设置

在IDE中配置好头文件目录、静态链接库目录和预处理器命令后,还需要将动态链接库目录设置到Windows的环境变量。动态链接库所在目录如下:

1
2
C:\ OSGeo4W64\apps\qgis\bin
C:\ OSGeo4W64\bin

如果不设置为环境变量,也可以直接将两个目录中的全部文件直接拷贝到应用程序可执行文件(.exe)的同级目录中。

QGIS的投影支持依赖于GDAL,因此如果需要对不同坐标系的统一支持,需要将设置GDAL的环境变量,变量名需命名为“GDAL_DATA”。OSGeo4W会默认下载安装GDAL,路径如下:

1
C:\ OSGeo4W64\share\gdal

很多人使用QgsCoordinateReferenceSystem定义投影后发现无论是isValid()还是toProj4()测试,总是不能返回想要的值,两个不同坐标系的图层加载后完全不能兼容,就是因为没有添加GDAL的环境变量。

5. 测试代码

配置好项目属性后,如运行无错误,便可正式开始开发工作。我们的程序主窗体类名为MainWindow,头文件和源文件分别为mainwindow.h和mainwindow.cpp,下面以此为例,添加可以打开Shapefile矢量文件的QGIS代码。

(1)将主函数文件main.cpp修改为如下内容:

1
2
3
4
5
6
7
8
9
10
11
12
#include "mainwindow.h"
#include <qgsapplication.h>

int main(int argc, char *argv[])
{
QgsApplication a(argc, argv, true);
QgsApplication::setPrefixPath("C:/OSGeo4W64/apps/qgis", true);
QgsApplication::initQgis();
MainWindow w;
w.show();
return a.exec();
}

如果运行成功,弹出一个啥都没有的小窗体,那你可以大舒一口气了,胜利曙光就在眼前。但如果运行失败,尤其是Qt Creator,就告诉你一个crashed,啥报错没有,那就头大了。先重启一下IDE,因为可能是环境变量还没生效,还不行就重启一下电脑。再不行的话,重新下载OSGeo4W中的库,或者——跪下求求他吧。

祈求

我曾经遇见过最玄学的问题是,上一个demo成功运行,下一个demo完全一样的代码就crash了。最后把第二个demo的Qt从官网下载安装的5.12换成OSGeo4w下载的5.11,终得解决。

(2)在主窗体的UI文件mainwindow.ui添加一个新的action,命名为actionAddVectorLayer。

(3)修改主窗体头文件mainwindow.h,添加地图画布对象与加载矢量图层的槽函数的声明。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include "ui_mainwindow.h"

class QgsMapCanvas;
class MainWindow : public QMainWindow, private Ui::MainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);

public slots:
void addVectorLayer();

private:
QgsMapCanvas *canvas;
};

#endif // MAINWINDOW_H

其中,地图画布类QgsMapCanvas 用于所有支持的GIS数据与其他地图项目的显示,支持地图与平移、缩放等地图工具的交互。

(4)修改主窗体的源文件,设计地图画布对象,添加槽函数定义。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include "mainwindow.h"
#include <QFileDialog>
#include <QMessageBox>
#include <qgsmapcanvas.h>
#include <qgsvectorlayer.h>
#include <qgsproject.h>

MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
setupUi(this);
canvas = new QgsMapCanvas();
canvas->enableAntiAliasing(true);
this->setCentralWidget(canvas);
connect(actionAddVectorLayer, &QAction::triggered, this, &MainWindow::addVectorLayer);
}

void MainWindow:: addVectorLayer()
{
QString path = QFileDialog::getOpenFileName(this, tr("Open Vector File"), QString(), "ESRI Shapefile(*.shp)");
if (path.isEmpty())
return;
QString baseName = QFileInfo(path).completeBaseName();
QgsVectorLayer *layer = new QgsVectorLayer(path, baseName, "ogr");
if (!layer->isValid())
{
QMessageBox::critical(this, tr("Error"), tr("Open file failed!"));
return;
}
QList<QgsMapLayer *> myList;
myList << layer;
QgsProject::instance()->addMapLayers( myList );

canvas->zoomToFullExtent();
}

测试代码编写完毕,运行程序,如果可以正确加载Shapefile矢量数据,至此便完成了QGIS 3.x二次开发环境的配置。有时可能代码顺利运行,但地图画布上却只是白茫茫一片不显示任何数据,别担心,试用一下这个代码:

https://github.com/fangzhegeo/qgis_demo

这是一个更加完整的测试代码,我在里面添加了图层树控件,应该不会再有不显示数据的问题。