一、实验目的
QGIS作为一个开源、免费、轻量级的GIS软件,虽然在国内的普及程度不高,但其软件功能极其丰富,并且还有海量的插件库供不同方向的GIS人员选择,是一个充满活力的开源社区,同时QGIS支持Python语言进行二次开发,因此较为适合我们实验课进行学习。
该实验课从二次开发的环境配置、PyCharm配置、PyQGIS语法、Qt Designer使用教程、界面插件设计等方面出发,希望在实验课最后所有学生都可以初步掌握开发一个属于自己的GIS界面的基本方法。
本次第一节实验课为PyQGIS的二次开发的准备工作,在此次实验课结束后,学生应完成了QGIS的安装、PyCharm的环境配置、PyQt的工具配置、Qt Designer的工具配置,并能够运行出一个简单的GIS界面。
二、实验步骤
2.1 QGIS安装
QGIS作为一个开源软件,在其官方网站中即可以找到QGIS的下载地址,为方便后续实验的开展,此处特别推荐使用QGIS的稳定版本Version 3.34.4 LTR,点击MSI进行安装,可以打开QGIS的文件夹查看是否为这个文件结构:
记住bin文件夹的地址,在后续的PyCharm环境配置时我们需要使用到该文件夹,接下来检验我们的QGIS是否安装成功,打开QGIS软件,点击Ctrl + Alt + P,打开Python控制台并输入代码:from qgis.core import QgsRasterLayer,若无报错说明QGIS安装成功。
2.2 PyCharm安装与环境配置
在实验过程中,我们可以选择使用VS Code或者PyCharm作为我们的开发用IDE,此次实验中我选择使用的是较为熟悉且已安装完成的PyCharm。安装完成后,首先新建工程,因为PyCharm中许多的配置都是针对project来进行设置的,同时为新建的工程设置对应的环境与解释器,QGIS提供的解释器为:python-qgis-ltr.bat,在bin文件中可以找到:
特别注意:新建工程后选择翻译器类型时需要选择Custom environment,并选择已存在的QGIS提供的解释器,切记不要使用虚拟环境。选择好后,点击确认,进入PyCharm IDE的GUI中,等待一段时间的Indexing Python SDK ‘Python 3.9’ (右下角状态栏),我们就可以开始进行代码编写了。
2.3 PyCharm工具配置
除Python解释器外,PyCharm同样还需要配置对应的Designer、PyUic和PyRcc等工具。这里使用PyCharm的外部工具(external tools)设置来配置三个工具,我们打开File | Settings | Tools | External Tools,点击加号打开添加工具的参数框,如下图所示:
在参数框中填写对应的配置文件即可完成各个工具的配置工作,参数框中的Name与Description都仅表示工具的名称与描述,重点在于Program需要选择正确各个工具的应用程序路径,汇总如下表格所示:
工具 | Program | Arguments | Working directory |
Designer | qgis-ltr-designer.bat | $FileName$ | $FileDir$ |
PyUic | python-qgis-ltr.bat | -m PyQt5.uic.pyuic -o $FileNameWithoutExtension$.py | $FileDir$ |
PyRcc | python-qgis-ltr.bat | -m PyQt5.pyrcc_main$FileName$ -o $FileNameWithoutExtension$_rc.py | $FileDir$ |
需要重点理解的是各个工具的具体作用,Designer是PyQt程序UI界面的实现工具,使用Qt Designer可以通过拖拽、点击完成GUI界面设计;PyUic是将.ui后缀的文件转换为.py后缀的文件;PyRcc是将.qrc后缀的文件转换为.py的文件。
2.4 Qt设计主框体
相关工具配置完毕后,点击我们添加的Designer工具,会自动的打开一个Qt Designer来新建一个对应的主要界面,开发过程中我们首先新建一个主窗体,即可得到一个如下右图所示的界面:
如上右图所示的中心,就是我们设计的界面主体,我们二次开发的主体思路为:根据QGIS的主界面,进一步设计一个GIS的常见界面,主要包含图层管理、地图显示、菜单栏、工具栏和状态栏五个部分,在此次实验中我们首先参照QGIS的菜单,设计主窗体的菜单内容,点击“在这里输入”,即可输入文字,结果如下图所示:
菜单设置完毕,接下来布局地图的放置部分,在左边的Filter过滤器中搜索Frame(框架)并拖入至界面中,之后的地图画布都将会绑定在该Frame中,但此时由于Central Widget(中心部件)没有设定Lay out(布局),我们的Frame没有和Central Widget匹配大小,我们右键UI界面的白色部分,Lay out设为Lay Out Vertically,这样就可以将Frame和Central Widget的大小绑定了:
最后,我们在左边的Filter过滤器搜索dock,可以找到Dock Widget(可停靠面板),我们将其拖入到UI界面的左边,Dock Widget会自动贴合在Frame的左边,如图所示:
初步设计完毕后将主框体保存为后缀为.ui的文件在我们的PyCharm工程中,此时我们就需要利用到上述配置的PyUIC工具,将改.ui文件转换为.py文件,为后续的代码编写做准备:
2.5 继承窗体类
在软件开发过程中我们会经常修改UI界面,因此.ui文件也经常会修改,与之对应的.py文件也会变动,因此我们不能直接在转换得到的ui.py文件上进行代码编写,其在生成的.py文件中也有说明:
因此,为了将逻辑与界面分离,我们需要继承这个代码里的对象,在继承对象中奖进行我们的逻辑代码编写,继承窗体类的代码如下所示,在此代码中我们创建了一个Ui_MainWindow 的子类MainWindow,其主体函数是调用构造函数,同时调用ui.py中的setupUi方法来构造整个界面,该文件命名为Ui_frm_main.py。
需要特别注意的是,不少同学在运行此代码时会出现无法找到design文件的错误,这是因为此代码文件(Ui_frm_main.py)与design文件夹不在同一根目录下,只有在同一级目录下的文件才可互相引用。除此之外,在新建任何文件时都直接在PyCharm的工程里新建,避免出现意外情况。
2.6 调用主界面
在上一步我们继承好类后,需要将其调用并创建为对象,因此我们在我们在根目录的main.py文件中将代码改写为如下所示:
from qgis.core import QgsApplicationfrom PyQt5.QtCore import Qtfrom Ui_frm_main import MainWindowif __name__=='__main__': QgsApplication.setPrefixPath('C:\Program Files\QGIS 3.34.4\bin', True) QgsApplication.setAttribute(Qt.AA_EnableHighDpiScaling) app = QgsApplication([], True) app.initQgis() mainWindow = MainWindow() mainWindow.show() app.exec() app.exitQgis()
此时点击运行main文件,即可弹出我们的界面窗口,但虽然我们将界面运行出来了,该窗口只是一个空壳,软件使用者还不能在该界面中做任何操作。
2.7 加载地图
界面初步运行后,其后续的操作思路基本上是一致的,此次实验中我们演示一种最简单的添加“加载地图”操作的步骤。首先在Qt Designer的菜单中的Map中输入“Open Map”的字符,右侧的对象检查器会自动将其类分配为QAction(继承自QObject,表示抽象出一个用户的UI动作):
此时由于UI界面已经发生变化,我们自.ui文件转换而来的ui.py文件自然也需要更新,而继承窗体类的.py文件也需要更新,在其初始化函数最后,补充一个Action的槽函数绑定:
self.actionOpen_Map.triggered.connect(self.actionOpenMapTriggered)
并且,在继承窗体中,需要编写一个actionOpenMapTriggered的响应槽函数,该代码使用了一个Qt提供的文件对话框,来读取一个.qgz文件:
def actionOpenMapTriggered(self): map_file, ext = QFileDialog.getOpenFileName(self, '打开', '', "QGIS Map(*.qgz);;All Files(*);;Other(*.gpkg;*.geojson;*.kml)") PROJECT.read(map_file)
运行main文件,若可以打开文件对话框则表明此次实验成功。
三、实验结果
结合部分后续实验课的内容,最终我们可以利用打开的文件框读取一个.qgz文件,并在我们自行创建的界面中打开一幅地图,效果如下图所示:
四、实验总结与心得
1.Qt Designer使用心得:在此次实验中我们使用到了多个工具,具体包括QGIS、PyCharm、Qt Designer等等,其中的Qt Designer是我们第一次接触的工具,但在我们实验过程中承担了非常重要的界面设计工作,作为一个可视化图形界面编辑器,我们可以很方便地创建图像界面文件*.ui,然后将.ui文件应用的源代码中,做到我们的“所见”即“所得”,此次实验中我们初步接触了Qt Designer中的几个重要类:QMainWindow:应用程序中的主窗口,通常是应用程序启动后显示的第一个窗口;QWidget:所有可视化控件的基类,QDockWidget指停靠窗口,是放置在QMainWindow中央小部件周围的停靠小部件区域中的次要窗口;QFrame:继承于QWidget,是带有边框部件的基类;QMenuBar:所有窗口的菜单栏,在此基础上我们可以添加不同的QMenu(菜单栏里面菜单)和QAction;QAciton:Qt Designer将用户与界面进行交互的元素抽象为一种“动作”,使用QAction类表示,QAction即哪些真正负责执行操作的部件;
2.代码编写思路总结:在PyCharm中进行代码编写以实现具体功能时,许多同学对为什么要修改,修改什么.py文件都处于一种云里雾里的状态,在弄清这个问题之前,我们首先基于此次实验的内容画出如下所示的一个思维导图,该思维导图总结了我们PyCharm中主要的四个文件(.ui文件、ui.py文件、继承窗体文件、main文件)之间的关系:
通过该思路我们也可以看出,.ui与ui.py文件我们是不能直接修改的,它们是随着界面设计的变化而变化,而main文件是我们直接运行的文件,因此我们在实现功能时,是在继承主窗体类的新.py文件中增删修改代码;
3.软件二次开发思路总结:虽然目前我们还处于软件设计与开发的初期与准备工作阶段,但已经可以很明显感受到的是:拥有一个清晰明了的设计思路非常之重要,通过老师课堂的讲解与课上课下的学习实践,结合自身理解总结如下:
在得到一个初步的界面窗口后,Qt Designer是帮助我们进行界面可视化的工具,我们可以根据我们的需要在其中添加菜单、组件、控件等等,但在Qt Designer中的操作只是帮助我们搭建了一个可以操作的界面;
而界面中具体功能该如何实现,就需要回到PyCharm中进行代码的详细编写,这样的一个操作帮助我们将界面设计与功能实现这两部分从逻辑上分离开来,是我们软件二次开发的核心思想之一,在了解这一思想后,之后许多的任务其实也就是这两部分工作的重复进行。