PyQGIS开发 3 基础功能开发
1 添加图层树与地图视图
1.1 添加控件
1.2 Python代码
from PyQt5.QtCore import QMimeData
from qgis.PyQt.QtWidgets import QMainWindow
from qgis._core import QgsMapLayer, QgsRasterLayer, QgsVectorLayer
from qgis.core import QgsProject, QgsLayerTreeModel
from qgis.gui import QgsLayerTreeView, QgsMapCanvas, QgsLayerTreeMapCanvasBridge
import os.path as osp
from ui.mainWindow import Ui_MainWindow
from PyQt5.QtWidgets import QVBoxLayout, QHBoxLayout, QFileDialog, QMessageBox
PROJECT = QgsProject.instance()
class MainWindow(QMainWindow, Ui_MainWindow):
def __init__(self):
"""
MainWindow 类的初始化函数。
该函数初始化 MainWindow 类的实例,设置窗口标题、初始化图层树、地图画布、
图层树视图模型、建立图层树与地图画布的桥接,并注册界面事件功能。
"""
super(MainWindow, self).__init__()
self.setupUi(self)
# 1 修改标题
self.setWindowTitle("QGIS自定义界面")
# 2 初始化图层树
vl = QVBoxLayout(self.dockWidgetContents)
self.layerTreeView = QgsLayerTreeView(self)
vl.addWidget(self.layerTreeView)
# 3 初始化地图画布
self.mapCanvas = QgsMapCanvas(self)
hl = QHBoxLayout(self.frame)
hl.setContentsMargins(0, 0, 0, 0) # 设置周围间距
hl.addWidget(self.mapCanvas)
# 4 设置图层树风格
self.model = QgsLayerTreeModel(PROJECT.layerTreeRoot(), self)
self.model.setFlag(QgsLayerTreeModel.AllowNodeRename) # 允许图层节点重命名
self.model.setFlag(QgsLayerTreeModel.AllowNodeReorder) # 允许图层拖拽排序
self.model.setFlag(QgsLayerTreeModel.AllowNodeChangeVisibility) # 允许改变图层节点可视性
self.model.setFlag(QgsLayerTreeModel.ShowLegendAsTree) # 展示图例
self.model.setAutoCollapseLegendNodes(10) # 当节点数大于等于10时自动折叠
self.layerTreeView.setModel(self.model)
# 4 建立图层树与地图画布的桥接
self.layerTreeBridge = QgsLayerTreeMapCanvasBridge(PROJECT.layerTreeRoot(), self.mapCanvas, self)
# 注册界面事件功能
self.registerFunc()
# 5 初始加载影像
self.firstAdd = True
# 6 允许拖拽文件
self.setAcceptDrops(True)
def registerFunc(self):
"""
注册动作的触发函数
该函数将动作与对应的触发函数连接起来,使得当用户点击动作时,相应的函数会被触发
"""
self.actionOpenRaster.triggered.connect(self.actionOpenRasterTriggered)
self.actionOpenVector.triggered.connect(self.actionOpenVectorTriggered)
def actionOpenRasterTriggered(self):
"""
处理打开栅格文件的触发事件
此方法通过 QFileDialog 弹出文件选择对话框,获取用户选择的栅格文件路径。
如果用户选择了文件,将调用 addRasterLayer 方法添加该文件对应的栅格图层到窗口中。
参数:
None
返回值:
无
异常:
如果文件选择对话框取消或出错,不会添加任何图层。
"""
data_file, ext = QFileDialog.getOpenFileName(
self,
'打开',
'',
'GeoTiff(*.tif;*tiff;*TIF;*TIFF);;All Files(*);;JPEG(*.jpg;*.jpeg;*.JPG;*.JPEG);;*.png;;*.pdf')
if data_file:
self.addRasterLayer(data_file)
def actionOpenVectorTriggered(self):
"""
处理打开矢量文件的触发事件
此方法通过 QFileDialog 弹出文件选择对话框,获取用户选择的矢量文件路径。
如果用户选择了文件,将调用 addVectorLayer 方法添加该文件对应的矢量图层到窗口中。
参数:
None
返回值:
无
异常:
如果文件选择对话框取消或出错,不会添加任何图层。
"""
data_file, ext = QFileDialog.getOpenFileName(
self, '打开', '',
"ShapeFile(*.shp);;All Files(*);;Other(*.gpkg;*.geojson;*.kml)")
if data_file:
self.addVectorLayer(data_file)
def addRasterLayer(self, rasterFilePath):
"""
将栅格图层添加到地图画布中。
此方法用于将栅格文件加载为栅格图层,并将其添加到地图画布中显示。如果是第一次添加图层,它会作为基础图层添加,并设置为可编辑状态。否则,它会作为普通图层添加。
参数:
rasterFilePath (str): 要添加的栅格文件的路径。
返回:
无
注意:
如果文件路径无效或文件无法加载为栅格图层,则不会添加任何图层。
"""
# 读取栅格文件并创建栅格图层
raster_layer = self.readRasterFile(rasterFilePath)
# 如果是首次添加图层,则将其作为基础图层添加到地图画布,并设置为可编辑
if self.firstAdd:
self.addMapLayer(raster_layer, self.mapCanvas, True)
self.firstAdd = False
# 否则,将其作为普通图层添加到地图画布
else:
self.addMapLayer(raster_layer, self.mapCanvas)
def addVectorLayer(self, vectorFilePath):
"""
向当前QGIS项目中添加一个新的矢量图层,并显示在地图画布上。
参数:
vectorFilePath (str): 要添加的矢量文件的路径。
返回:
None
"""
vector_layer = self.readVectorFile(vectorFilePath)
if self.firstAdd:
self.addMapLayer(vector_layer, self.mapCanvas, True)
self.firstAdd = False
else:
self.addMapLayer(vector_layer, self.mapCanvas)
def addMapLayer(self, layer: QgsMapLayer, mapCanvas: QgsMapCanvas, firstAddLayer=False):
"""
将地图层添加到 QGIS 项目和地图画布中。
参数:
layer (QgsMapLayer):要添加的地图层。
mapCanvas (QgsMapCanvas):地图画布,用于显示地图层。
firstAddLayer (bool):是否是第一次添加图层。如果是,将设置画布的 CRS 和范围。
返回:
无
注意:
如果图层已经存在,它将重命名以避免冲突。如果图层无效,它将不会被添加。
"""
# 检查图层是否有效
if layer.isValid():
# 如果是第一次添加图层,设置画布的 CRS 和范围
if firstAddLayer:
mapCanvas.setDestinationCrs(layer.crs())
mapCanvas.setExtent(layer.extent())
# 确保图层名称唯一
while PROJECT.mapLayersByName(layer.name()):
layer.setName(layer.name() + "_1")
# 将图层添加到项目中
PROJECT.addMapLayer(layer)
# 更新画布的图层列表并刷新
layers = [layer] + [PROJECT.mapLayer(i) for i in PROJECT.mapLayers()]
mapCanvas.setLayers(layers)
mapCanvas.refresh()
def readRasterFile(self, rasterFilePath: str) -> QgsRasterLayer:
"""
从给定的文件路径中读取栅格文件,并返回相应的 QgsRasterLayer 对象。
参数:
rasterFilePath (str):要读取的栅格文件的完整路径。
返回:
QgsRasterLayer:如果成功读取文件,则返回相应的 QgsRasterLayer 对象;否则返回 None。
"""
raster_layer = QgsRasterLayer(rasterFilePath, osp.basename(rasterFilePath))
return raster_layer
def readVectorFile(self, vectorFilePath):
"""
从给定的文件路径中读取矢量文件,并返回相应的 QgsVectorLayer 对象。
参数:
vectorFilePath (str): 要读取的矢量文件的完整路径。
返回:
QgsVectorLayer: 如果成功读取文件,则返回相应的 QgsVectorLayer 对象;否则返回 None。
"""
vector_layer = QgsVectorLayer(vectorFilePath, osp.basename(vectorFilePath), "ogr")
return vector_layer
def dragEnterEvent(self, fileData):
"""
处理拖放文件进入窗口的事件。
如果拖放的文件是URL类型,则接受事件;否则,忽略事件。
参数:
fileData - 拖放事件的数据。
返回:
None
"""
if fileData.mimeData().hasUrls():
fileData.accept()
else:
fileData.ignore()
def dropEvent(self, fileData):
"""
处理拖放事件,根据文件类型添加对应的图层到QGIS中
参数:
fileData (QDragEnterEvent): 拖放事件对象
返回:
None
"""
mimeData: QMimeData = fileData.mimeData()
filePathList = [u.path()[1:] for u in mimeData.urls()]
for filePath in filePathList:
filePath: str = filePath.replace("/", "//")
if filePath.split(".")[-1] in ["tif", "TIF", "tiff", "TIFF", "GTIFF", "png", "jpg", "pdf"]:
self.addRasterLayer(filePath)
elif filePath.split(".")[-1] in ["shp", "SHP", "gpkg", "geojson", "kml"]:
self.addVectorLayer(filePath)
elif filePath == "":
pass
else:
QMessageBox.about(self, '警告', f'{filePath}为不支持的文件类型,目前支持栅格影像和shp矢量')
def getRasterLayerAttrs(self, rasterLayer: QgsRasterLayer):
print("name: ", rasterLayer.name()) # 图层名
print("type: ", rasterLayer.type()) # 栅格还是矢量图层
print("height - width: ", rasterLayer.height(), rasterLayer.width()) # 尺寸
print("bands: ", rasterLayer.bandCount()) # 波段数
print("extent", rasterLayer.extent()) # 外接矩形范围
print("source", rasterLayer.source()) # 图层的源文件地址
print("crs", rasterLayer.crs()) # 图层的坐标系统
def getVectorLayerAttrs(self, vectorLayer: QgsVectorLayer):
print("name: ", vectorLayer.name()) # 图层名
print("type: ", vectorLayer.type()) # 栅格还是矢量图层
print("extent", vectorLayer.extent()) # 外接矩形范围
print("source", vectorLayer.source()) # 图层的源文件地址
print("crs", vectorLayer.crs()) # 图层的坐标系统