给QGIS开发一个卷帘工具

2024-04-01 01:08
文章标签 工具 开发 qgis 卷帘

本文主要是介绍给QGIS开发一个卷帘工具,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

版权声明:未经作者允许不得转载,此插件不得用于商业用途。

目录

开发环境

插件开发

__init__.py

map_swipe_plugin.py

map_swipe_tool.py

active

deactivate

canvasPressEvent

canvasReleaseEvent

canvasMoveEvent

swipe_map.py

实现结果


开发环境

  • QGIS 3.2
  • python(QGIS自带)

插件开发

关于QGIS中使用python开发插件的方法,自行查看官方文档

__init__.py

from .map_swipe_plugin import MapSwipePlugindef classFactory(iface):return MapSwipePlugin(iface)

map_swipe_plugin.py

import osfrom PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QWidget, QHBoxLayout, QComboBox, QPushButton
from qgis.core import QgsProject
from qgis.gui import QgsMapToolPanfrom .map_swipe_tool import MapSwipeToolplugin_path = os.path.dirname(__file__)class MapSwipePlugin:def __init__(self, iface):self.iface = ifaceself.canvas = iface.mapCanvas()# 图层变化信号QgsProject.instance().layerTreeRoot().layerOrderChanged.connect(self.combobox_add_items)def initGui(self):self.menu = self.title = "卷帘工具"self._create_widget()self.tool = MapSwipeTool(plugin_path, self.combobox, self.iface)self.tool.deactivated.connect(self.tool_deactivated)self.widget_action = self.iface.addToolBarWidget(self.widget)def unload(self):self.canvas.setMapTool(QgsMapToolPan(self.iface.mapCanvas()))self.iface.removeToolBarIcon(self.widget_action)del self.widget_actiondef run(self):if self.canvas.mapTool() != self.tool:self.prevTool = self.canvas.mapTool()self.canvas.setMapTool(self.tool)else:self.canvas.setMapTool(self.prevTool)if self.pushbutton.isChecked() and self.combobox.isHidden():self.combobox.show()self.combobox_add_items()else:self.combobox.hide()def _create_widget(self):icon = QIcon(os.path.join(plugin_path, 'icon.png'))# 新建widgetself.widget = QWidget(self.iface.mainWindow())self.hlayout = QHBoxLayout(self.widget)self.hlayout.setContentsMargins(0, 0, 0, 0)self.pushbutton = QPushButton(icon, '', self.widget)self.pushbutton.setCheckable(True)self.pushbutton.setFlat(True)self.combobox = QComboBox(self.widget)self.hlayout.addWidget(self.pushbutton)self.hlayout.addWidget(self.combobox)self.combobox.hide()self.combobox_add_items()self.pushbutton.clicked.connect(self.run)def combobox_add_items(self):self.combobox.clear()layers = QgsProject.instance().layerTreeRoot().layerOrder()self.combobox.addItems([layer.name() for layer in layers])def tool_deactivated(self):'''tool非激活状态'''self.pushbutton.setChecked(False)self.combobox.hide()

map_swipe_tool.py

import os
from PyQt5.QtCore import Qt, QPoint
from PyQt5.QtGui import QCursor, QPixmap
from qgis.gui import QgsMapTool
from qgis.core import QgsProject, QgsMapSettings, QgsMapRendererParallelJob
from .swipe_map import SwipeMapclass MapSwipeTool(QgsMapTool):def __init__(self, plugin_path, combobox, iface):super(MapSwipeTool, self).__init__(iface.mapCanvas())self.combobox = comboboxself.map_canvas = iface.mapCanvas()self.view = iface.layerTreeView()self.swipe = SwipeMap(self.map_canvas)self.hasSwipe = Noneself.start_point = QPoint()self.cursorSV = QCursor(QPixmap(os.path.join(plugin_path, 'images/split_v.png')))self.cursorSH = QCursor(QPixmap(os.path.join(plugin_path, 'images/split_h.png')))self.cursorUP = QCursor(QPixmap(os.path.join(plugin_path, 'images/up.png')))self.cursorDOWN = QCursor(QPixmap(os.path.join(plugin_path, 'images/down.png')))self.cursorLEFT = QCursor(QPixmap(os.path.join(plugin_path, 'images/left.png')))self.cursorRIGHT = QCursor(QPixmap(os.path.join(plugin_path, 'images/right.png')))def activate(self):self.map_canvas.setCursor(QCursor(Qt.CrossCursor))self._connect()self.hasSwipe = Falseself.setLayersSwipe()def canvasPressEvent(self, e):self.hasSwipe = Truedirection = Nonew, h = self.map_canvas.width(), self.map_canvas.height()if 0.25 * w < e.x() < 0.75 * w and e.y() < 0.5 * h:direction = 0  # '⬇'self.swipe.isVertical = Falseif 0.25 * w < e.x() < 0.75 * w and e.y() > 0.5 * h:direction = 1  # '⬆'self.swipe.isVertical = Falseif e.x() < 0.25 * w:direction = 2  # '➡'self.swipe.isVertical = Trueif e.x() > 0.75 * w:direction = 3  # '⬅'self.swipe.isVertical = Trueself.swipe.set_direction(direction)self.map_canvas.setCursor(self.cursorSH if self.swipe.isVertical else self.cursorSV)self.swipe.set_img_extent(e.x(), e.y())def canvasReleaseEvent(self, e):self.hasSwipe = Falseself.canvasMoveEvent(e)# 鼠标释放后,移除绘制的线self.swipe.set_img_extent(-9999, -9999)def canvasMoveEvent(self, e):if self.hasSwipe:self.swipe.set_img_extent(e.x(), e.y())else:# 设置当前cursorw, h = self.map_canvas.width(), self.map_canvas.height()if e.x() < 0.25 * w:self.canvas().setCursor(self.cursorRIGHT)if e.x() > 0.75 * w:self.canvas().setCursor(self.cursorLEFT)if 0.25 * w < e.x() < 0.75 * w and e.y() < 0.5 * h:self.canvas().setCursor(self.cursorDOWN)if 0.25 * w < e.x() < 0.75 * w and e.y() > 0.5 * h:self.canvas().setCursor(self.cursorUP)def _connect(self, isConnect=True):signal_slot = ({'signal': self.map_canvas.mapCanvasRefreshed, 'slot': self.setMap},{'signal': self.combobox.currentIndexChanged, 'slot': self.setLayersSwipe},{'signal': QgsProject.instance().removeAll, 'slot': self.disable})if isConnect:for item in signal_slot:item['signal'].connect(item['slot'])else:for item in signal_slot:item['signal'].disconnect(item['slot'])def setLayersSwipe(self, ):current_layer = QgsProject.instance().mapLayersByName(self.combobox.currentText())if len(current_layer) == 0:returnlayers = QgsProject.instance().layerTreeRoot().layerOrder()layer_list = []for layer in layers:if layer.id() == current_layer[0].id():continuelayer_list.append(layer)self.swipe.clear()self.swipe.setLayersId(layer_list)self.setMap()def disable(self):self.swipe.clear()self.hasSwipe = Falsedef deactivate(self):self.deactivated.emit()self.swipe.clear()self._connect(False)def setMap(self):def finished():self.swipe.setContent(job.renderedImage(), self.map_canvas.extent())if len(self.swipe.layers) == 0:returnsettings = QgsMapSettings(self.map_canvas.mapSettings())settings.setLayers(self.swipe.layers)job = QgsMapRendererParallelJob(settings)job.start()job.finished.connect(finished)job.waitForFinished()

MapSwipeTool继承了QgsMapTool类,重新实现了activate、deactivate、canvasPressEvent、canvasReleaseEvent、canvasMoveEvent方法,还用到了QgsMapRendererParallelJob类,QgsMapRendererParallelJob是QGIS提供的并行绘图类,不做详解,下面对重新实现的方法详细说明:

active

工具激活后执行此函数

deactivate

工具非激活状态执行此函数

canvasPressEvent

    def canvasPressEvent(self, e):self.hasSwipe = Truedirection = Nonew, h = self.map_canvas.width(), self.map_canvas.height()if 0.25 * w < e.x() < 0.75 * w and e.y() < 0.5 * h:direction = 0  # '⬇'self.swipe.isVertical = Falseif 0.25 * w < e.x() < 0.75 * w and e.y() > 0.5 * h:direction = 1  # '⬆'self.swipe.isVertical = Falseif e.x() < 0.25 * w:direction = 2  # '➡'self.swipe.isVertical = Trueif e.x() > 0.75 * w:direction = 3  # '⬅'self.swipe.isVertical = Trueself.swipe.set_direction(direction)self.map_canvas.setCursor(self.cursorSH if self.swipe.isVertical else self.cursorSV)self.swipe.set_img_extent(e.x(), e.y())

画布的鼠标按压事件,将画布分为四个部分,获得卷帘的方向,然后设置鼠标光标,具体实现方式自行脑补,原理如下图:

canvasReleaseEvent

    def canvasReleaseEvent(self, e):self.hasSwipe = Falseself.canvasMoveEvent(e)# 鼠标释放后,移除绘制的线self.swipe.set_img_extent(-9999, -9999)

画布的鼠标释放事件,鼠标释放后要把绘制的线移出画布

self.swipe.set_img_extent(-9999, -9999)

canvasMoveEvent

    def canvasMoveEvent(self, e):if self.hasSwipe:self.swipe.set_img_extent(e.x(), e.y())else:# 设置当前cursorw, h = self.map_canvas.width(), self.map_canvas.height()if e.x() < 0.25 * w:self.canvas().setCursor(self.cursorRIGHT)if e.x() > 0.75 * w:self.canvas().setCursor(self.cursorLEFT)if 0.25 * w < e.x() < 0.75 * w and e.y() < 0.5 * h:self.canvas().setCursor(self.cursorDOWN)if 0.25 * w < e.x() < 0.75 * w and e.y() > 0.5 * h:self.canvas().setCursor(self.cursorUP)

画布的鼠标移动事件,当使用卷帘时,获得鼠标的位置(e.x(),e.y()),当不使用卷帘时,根据鼠标的位置设置鼠标的光标形状,原理与canvasPressEvent相同

swipe_map.py

from PyQt5.QtCore import QRectF, QPointF, Qt
from PyQt5.QtGui import QPen, QColor
from qgis.gui import QgsMapCanvasItemclass SwipeMap(QgsMapCanvasItem):def __init__(self, canvas):super(SwipeMap, self).__init__(canvas)self.length = 0self.isVertical = Trueself.layers = []self.is_paint = Falsedef setContent(self, image, rect):self.copyimage = imageself.setRect(rect)def clear(self):del self.layers[:]self.is_paint = Falsedef setLayersId(self, layers):del self.layers[:]for item in layers:self.layers.append(item)def set_direction(self, direction):# 0:'⬇', 1:'⬆', 2:'➡', 3:'⬅'if direction == 0:self.direction = 0elif direction == 1:self.direction = 1elif direction == 2:self.direction = 2else:self.direction = 3self.startx, self.starty, self.endx, self.endy = 0, 0, self.boundingRect().width(), self.boundingRect().height()def set_img_extent(self, x, y):self.x = xself.y = yif self.direction == 0:  # 0:'⬇'self.endy = yelif self.direction == 1:  # 1:'⬆'self.starty = yelif self.direction == 2:  # 2:'➡'self.endx = xelse:  # 3:'⬅'self.startx = xself.is_paint = Trueself.update()def paint(self, painter, *args):if len(self.layers) == 0 or self.is_paint == False:returnw = self.boundingRect().width()h = self.boundingRect().height()pen = QPen(Qt.DashDotDotLine)pen.setColor(QColor(18, 150, 219))pen.setWidth(4)if self.isVertical:painter.setPen(pen)painter.drawLine(QPointF(self.x, 0), QPointF(self.x, h))else:painter.setPen(pen)painter.drawLine(QPointF(0, self.y), QPointF(w, self.y))image = self.copyimage.copy(self.startx, self.starty, self.endx, self.endy)painter.drawImage(QRectF(self.startx, self.starty, self.endx, self.endy), image)

此模块为绘制地图模块,首先获得绘制地图的范围

self.startx, self.starty, self.endx, self.endy = 0, 0, self.boundingRect().width(), self.boundingRect().height()

然后重写绘制函数paint

    def paint(self, painter, *args):if len(self.layers) == 0 or self.is_paint == False:returnw = self.boundingRect().width()h = self.boundingRect().height()pen = QPen(Qt.DashDotDotLine)pen.setColor(QColor(18, 150, 219))pen.setWidth(4)if self.isVertical:painter.setPen(pen)painter.drawLine(QPointF(self.x, 0), QPointF(self.x, h))else:painter.setPen(pen)painter.drawLine(QPointF(0, self.y), QPointF(w, self.y))image = self.copyimage.copy(self.startx, self.starty, self.endx, self.endy)painter.drawImage(QRectF(self.startx, self.starty, self.endx, self.endy), image)

实现结果

项目地址:MapSwipeTool

这篇关于给QGIS开发一个卷帘工具的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


原文地址:
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.chinasem.cn/article/865850

相关文章

基于Python开发一个有趣的工作时长计算器

《基于Python开发一个有趣的工作时长计算器》随着远程办公和弹性工作制的兴起,个人及团队对于工作时长的准确统计需求日益增长,本文将使用Python和PyQt5打造一个工作时长计算器,感兴趣的小伙伴可... 目录概述功能介绍界面展示php软件使用步骤说明代码详解1.窗口初始化与布局2.工作时长计算核心逻辑3

Python使用FFmpeg实现高效音频格式转换工具

《Python使用FFmpeg实现高效音频格式转换工具》在数字音频处理领域,音频格式转换是一项基础但至关重要的功能,本文主要为大家介绍了Python如何使用FFmpeg实现强大功能的图形化音频转换工具... 目录概述功能详解软件效果展示主界面布局转换过程截图完成提示开发步骤详解1. 环境准备2. 项目功能结

Linux系统之stress-ng测压工具的使用

《Linux系统之stress-ng测压工具的使用》:本文主要介绍Linux系统之stress-ng测压工具的使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、理论1.stress工具简介与安装2.语法及参数3.具体安装二、实验1.运行8 cpu, 4 fo

Maven项目中集成数据库文档生成工具的操作步骤

《Maven项目中集成数据库文档生成工具的操作步骤》在Maven项目中,可以通过集成数据库文档生成工具来自动生成数据库文档,本文为大家整理了使用screw-maven-plugin(推荐)的完... 目录1. 添加插件配置到 pom.XML2. 配置数据库信息3. 执行生成命令4. 高级配置选项5. 注意事

python web 开发之Flask中间件与请求处理钩子的最佳实践

《pythonweb开发之Flask中间件与请求处理钩子的最佳实践》Flask作为轻量级Web框架,提供了灵活的请求处理机制,中间件和请求钩子允许开发者在请求处理的不同阶段插入自定义逻辑,实现诸如... 目录Flask中间件与请求处理钩子完全指南1. 引言2. 请求处理生命周期概述3. 请求钩子详解3.1

Python使用pynput模拟实现键盘自动输入工具

《Python使用pynput模拟实现键盘自动输入工具》在日常办公和软件开发中,我们经常需要处理大量重复的文本输入工作,所以本文就来和大家介绍一款使用Python的PyQt5库结合pynput键盘控制... 目录概述:当自动化遇上可视化功能全景图核心功能矩阵技术栈深度效果展示使用教程四步操作指南核心代码解析

如何基于Python开发一个微信自动化工具

《如何基于Python开发一个微信自动化工具》在当今数字化办公场景中,自动化工具已成为提升工作效率的利器,本文将深入剖析一个基于Python的微信自动化工具开发全过程,有需要的小伙伴可以了解下... 目录概述功能全景1. 核心功能模块2. 特色功能效果展示1. 主界面概览2. 定时任务配置3. 操作日志演示

使用Python和Tkinter实现html标签去除工具

《使用Python和Tkinter实现html标签去除工具》本文介绍用Python和Tkinter开发的HTML标签去除工具,支持去除HTML标签、转义实体并输出纯文本,提供图形界面操作及复制功能,需... 目录html 标签去除工具功能介绍创作过程1. 技术选型2. 核心实现逻辑3. 用户体验增强如何运行

JavaScript实战:智能密码生成器开发指南

本文通过JavaScript实战开发智能密码生成器,详解如何运用crypto.getRandomValues实现加密级随机密码生成,包含多字符组合、安全强度可视化、易混淆字符排除等企业级功能。学习密码强度检测算法与信息熵计算原理,获取可直接嵌入项目的完整代码,提升Web应用的安全开发能力 目录

Python使用Turtle实现精确计时工具

《Python使用Turtle实现精确计时工具》这篇文章主要为大家详细介绍了Python如何使用Turtle实现精确计时工具,文中的示例代码讲解详细,具有一定的借鉴价值,有需要的小伙伴可以参考一下... 目录功能特点使用方法程序架构设计代码详解窗口和画笔创建时间和状态显示更新计时器控制逻辑计时器重置功能事件