【开源解析】基于PyQt5+Folium的谷歌地图应用开发:从入门到实战

🌐【开源解析】基于PyQt5+Folium的谷歌地图应用开发:从入门到实战请添加图片描述

🌈 个人主页:创客白泽 - CSDN博客
🔥 系列专栏:🐍《Python开源项目实战》
💡 热爱不止于代码,热情源自每一个灵感闪现的夜晚。愿以开源之火,点亮前行之路。
👍 如果觉得这篇文章有帮助,欢迎您一键三连,分享给更多人哦

请添加图片描述

在这里插入图片描述

📌 概述

在当今数据可视化与地理信息系统的交叉领域,交互式地图应用已成为不可或缺的工具。本文将深入剖析一个基于Python技术栈(PyQt5+Folium+Geopy)开发的**"谷歌地图"桌面应用**,它集成了地址解析、地图标注、距离测量等实用功能,并提供了三种不同的地图样式选择。

相较于传统Web地图应用,本项目的创新点在于:

  • 桌面端集成:通过PyQt5实现原生应用体验
  • 混合渲染技术:结合Folium的HTML生成与QWebEngineView的嵌入式渲染
  • 跨框架通信:实现Python与JavaScript的双向交互
  • 轻量级架构:无需复杂GIS系统即可实现核心功能

🛠️ 功能特性

核心功能矩阵

功能模块实现技术特色说明
地理编码Geopy/Nominatim支持全球地址解析
地图渲染Folium+Leaflet三种专业地图样式
距离测量Geodesic算法高精度大圆距离计算
交互界面PyQt5响应式桌面UI
地图导出HTML5可独立运行的网页地图

特色功能详解

  1. 智能地址解析:基于OpenStreetMap的Nominatim服务,支持模糊地址匹配
  2. 实时距离测量:选择两个标记点即可显示精确的球面距离
  3. 动态标记高亮:可视化连线辅助空间关系分析
  4. 多地图样式:街道图、卫星图、地形图一键切换
  5. 跨平台运行:生成的HTML地图可在任何浏览器查看

🎨 效果展示

街道地图

在这里插入图片描述

卫星地图

在这里插入图片描述
在这里插入图片描述

地形图

在这里插入图片描述
在这里插入图片描述

距离测量演示

在这里插入图片描述

🧩 实现步骤详解

1. 环境搭建

pip install PyQt5 folium geopy PyQtWebEngine

2. 核心架构设计

在这里插入图片描述

3. 关键技术实现

3.1 混合地图渲染
def initialize_map(self):# 加载Leaflet库html = """<!DOCTYPE html><html><head><link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"/><script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script></head><body><div id="map"></div><script>// JavaScript地图控制逻辑var map = L.map('map').setView([39.9042, 116.4074], 4);</script></body></html>"""self.map_view.setHtml(html)
3.2 跨语言通信
# Python调用JavaScript
self.map_view.page().runJavaScript("addMarker(39.9, 116.4, '北京', '中国首都');")# JavaScript回调Python
self.map_view.page().runJavaScript("""map.on('click', function(e) {console.log(e.latlng);});
""")
3.3 距离测量算法
from geopy.distance import geodesicdef calculate_distance(loc1, loc2):"""使用Vincenty公式计算球面距离"""return geodesic((loc1['latitude'], loc1['longitude']),(loc2['latitude'], loc2['longitude'])).kilometers

🔍 代码深度解析

1. 地理编码服务封装

def geocode_location(self, address):try:location = self.geolocator.geocode(address)if location:return (location.latitude, location.longitude)return Noneexcept (GeocoderTimedOut, GeocoderServiceError) as e:# 实现自动重试机制time.sleep(0.5)return self.geocode_location(address)

优化点:增加了异常处理和自动重试机制,提高服务稳定性

2. 动态标记管理

def update_embedded_map(self):# 使用JS批量操作DOM元素js_clear = "clearMarkers();"js_add_markers = []for loc in self.locations:js_add_markers.append(f"addMarker({loc['latitude']}, {loc['longitude']}, "f"'{loc['name']}', '{loc['address']}');")self.map_view.page().runJavaScript(js_clear + "".join(js_add_markers))

性能优化:减少Python-JS通信次数,使用批量操作提升渲染效率

3. 地图样式热切换

self.map_styles = {"🌍 街道地图": {"url": "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png","attr": "OpenStreetMap"},"🛰️ 卫星地图": {"url": "https://mt1.google.com/vt/lyrs=s&x={x}&y={y}&z={z}","attr": "Google"}
}def update_map_style(self):style = next(s for s in self.map_styles if self.style_buttons[s].isChecked())js = f"""map.eachLayer(layer => {{if (layer instanceof L.TileLayer) {{map.removeLayer(layer);}}}});L.tileLayer('{self.map_styles[style]["url"]}', {{attribution: '{self.map_styles[style]["attr"]}'}}).addTo(map);"""self.map_view.page().runJavaScript(js)

📥 源码下载

import folium
from geopy.geocoders import Nominatim
from geopy.distance import geodesic  # 添加距离计算功能
from geopy.exc import GeocoderTimedOut, GeocoderServiceError
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit, QPushButton, QTreeWidget, QTreeWidgetItem,QRadioButton, QGroupBox, QFileDialog, QMessageBox, QScrollArea)
from PyQt5.QtCore import Qt, QUrl, QTimer
from PyQt5.QtWebEngineWidgets import QWebEngineView
from PyQt5.QtGui import QIcon
import sys
import webbrowser
import os
import timeclass SimpleMapViewerApp(QMainWindow):def __init__(self):super().__init__()self.setWindowTitle("谷歌桌面地图")self.setGeometry(100, 100, 1200, 800)self.geolocator = Nominatim(user_agent="simple_map_viewer")self.locations = []self.current_map_file = os.path.join(os.path.expanduser("~"), "map.html")self.map_view = Noneself.map_initialized = Falseself.selected_markers = []  # 存储选中的标记用于距离计算# 地图样式选项self.map_styles = {"🌍 街道地图": "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png","🛰️ 卫星地图": "https://mt1.google.com/vt/lyrs=s&x={x}&y={y}&z={z}","⛰️ 地形图": "https://mt1.google.com/vt/lyrs=p&x={x}&y={y}&z={z}"}# 创建UIself.create_widgets()# 延迟初始化地图,确保WebEngineView完全加载QTimer.singleShot(500, self.initialize_map)def create_widgets(self):# 主窗口布局main_widget = QWidget()self.setCentralWidget(main_widget)main_layout = QHBoxLayout(main_widget)# 左侧控制面板control_panel = QWidget()control_panel.setMinimumWidth(350)control_panel.setMaximumWidth(400)control_layout = QVBoxLayout(control_panel)# 地图样式选择style_group = QGroupBox("🗂️ 地图样式")style_layout = QVBoxLayout()self.style_buttons = []for style_name in self.map_styles:btn = QRadioButton(style_name)btn.toggled.connect(lambda checked, name=style_name: self.update_map_style() if checked else None)style_layout.addWidget(btn)self.style_buttons.append(btn)self.style_buttons[0].setChecked(True)style_group.setLayout(style_layout)control_layout.addWidget(style_group)# 搜索框search_group = QGroupBox("🔍 位置搜索")search_layout = QVBoxLayout()self.search_entry = QLineEdit()self.search_entry.setPlaceholderText("输入地址或地名...")search_layout.addWidget(self.search_entry)search_btn = QPushButton("搜索")search_btn.setIcon(QIcon.fromTheme("edit-find"))search_btn.clicked.connect(self.search_location)search_layout.addWidget(search_btn)search_group.setLayout(search_layout)control_layout.addWidget(search_group)# 位置列表list_group = QGroupBox("📍 位置列表")list_layout = QVBoxLayout()self.location_list = QTreeWidget()self.location_list.setHeaderLabels(["名称", "地址"])self.location_list.setColumnWidth(0, 150)self.location_list.setSelectionMode(QTreeWidget.ExtendedSelection)self.location_list.itemSelectionChanged.connect(self.on_location_selection_changed)  # 添加选择变化事件list_layout.addWidget(self.location_list)# 距离显示标签self.distance_label = QLabel("两地距离: 未选择")self.distance_label.setAlignment(Qt.AlignCenter)self.distance_label.setStyleSheet("font-weight: bold; color: #2c3e50;")list_layout.addWidget(self.distance_label)# 列表操作按钮list_btn_layout = QHBoxLayout()remove_btn = QPushButton("🗑️ 删除选中")remove_btn.clicked.connect(self.remove_location)list_btn_layout.addWidget(remove_btn)clear_btn = QPushButton("🧹 清空列表")clear_btn.clicked.connect(self.clear_locations)list_btn_layout.addWidget(clear_btn)list_layout.addLayout(list_btn_layout)list_group.setLayout(list_layout)control_layout.addWidget(list_group)# 添加位置表单form_group = QGroupBox("➕ 添加位置")form_layout = QVBoxLayout()name_layout = QHBoxLayout()name_layout.addWidget(QLabel("名称:"))self.name_entry = QLineEdit()name_layout.addWidget(self.name_entry)form_layout.addLayout(name_layout)addr_layout = QHBoxLayout()addr_layout.addWidget(QLabel("地址:"))self.address_entry = QLineEdit()addr_layout.addWidget(self.address_entry)form_layout.addLayout(addr_layout)add_btn = QPushButton("➕ 添加位置")add_btn.clicked.connect(self.add_location)form_layout.addWidget(add_btn)form_group.setLayout(form_layout)control_layout.addWidget(form_group)# 地图操作按钮map_btn_group = QGroupBox("🛠️ 地图操作")map_btn_layout = QHBoxLayout()create_btn = QPushButton("🖨️ 生成地图")create_btn.clicked.connect(self.create_map)map_btn_layout.addWidget(create_btn)show_btn = QPushButton("👀 查看地图")show_btn.clicked.connect(self.show_map)map_btn_layout.addWidget(show_btn)map_btn_group.setLayout(map_btn_layout)control_layout.addWidget(map_btn_group)control_layout.addStretch()# 右侧地图预览self.map_view = QWebEngineView()self.map_view.setHtml(self.get_empty_html())# 添加到主布局main_layout.addWidget(control_panel)main_layout.addWidget(self.map_view, stretch=1)def get_empty_html(self):"""返回初始空白HTML"""return """<!DOCTYPE html><html><head><title>地图预览</title><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0"></head><body><div id="map" style="height:100%;width:100%;"></div></body></html>"""def initialize_map(self):"""初始化地图,确保Leaflet库正确加载"""html = """<!DOCTYPE html><html><head><title>地图预览</title><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0"><link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY="crossorigin=""/><style>body { margin: 0; padding: 0; }#map { height: 100vh; width: 100%; }</style></head><body><div id="map"></div><script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo="crossorigin=""></script><script>var map = L.map('map').setView([39.9042, 116.4074], 4);var osmLayer = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'});osmLayer.addTo(map);// 存储标记的数组var markers = [];var selectedMarkers = [];var line = null;function clearMarkers() {for (var i = 0; i < markers.length; i++) {map.removeLayer(markers[i]);}markers = [];if (line) {map.removeLayer(line);line = null;}}function addMarker(lat, lng, name, address) {var marker = L.marker([lat, lng]).addTo(map).bindPopup('<b>' + name + '</b><br>' + address).bindTooltip(name);markers.push(marker);return marker;}function setView(lat, lng, zoom) {map.setView([lat, lng], zoom);}function highlightMarkers(markerIndices) {// 重置所有标记样式for (var i = 0; i < markers.length; i++) {markers[i].setIcon(L.icon({iconUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/images/marker-icon.png',iconSize: [25, 41],iconAnchor: [12, 41],popupAnchor: [1, -34],shadowUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/images/marker-shadow.png',shadowSize: [41, 41]}));}// 清除之前的线if (line) {map.removeLayer(line);line = null;}// 高亮选中的标记selectedMarkers = [];for (var i = 0; i < markerIndices.length; i++) {var idx = markerIndices[i];if (idx >= 0 && idx < markers.length) {markers[idx].setIcon(L.icon({iconUrl: 'https://raw.githubusercontent.com/pointhi/leaflet-color-markers/master/img/marker-icon-red.png',iconSize: [25, 41],iconAnchor: [12, 41],popupAnchor: [1, -34],shadowUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/images/marker-shadow.png',shadowSize: [41, 41]}));selectedMarkers.push(markers[idx]);}}// 如果选中了两个点,绘制连线if (selectedMarkers.length === 2) {var latlngs = [selectedMarkers[0].getLatLng(),selectedMarkers[1].getLatLng()];line = L.polyline(latlngs, {color: 'red',weight: 3,opacity: 0.7,dashArray: '10, 10'}).addTo(map);}}</script></body></html>"""self.map_view.setHtml(html)self.map_initialized = TrueQTimer.singleShot(500, self.update_map_style)def on_location_selection_changed(self):"""当位置列表选择变化时触发"""selected_items = self.location_list.selectedItems()selected_indices = [self.location_list.indexOfTopLevelItem(item) for item in selected_items]# 更新地图上的高亮标记if self.map_initialized:js = f"highlightMarkers({selected_indices});"self.map_view.page().runJavaScript(js)# 计算并显示距离if len(selected_indices) == 2:loc1 = self.locations[selected_indices[0]]loc2 = self.locations[selected_indices[1]]# 使用geodesic计算两点间距离point1 = (loc1['latitude'], loc1['longitude'])point2 = (loc2['latitude'], loc2['longitude'])distance = geodesic(point1, point2).kilometersself.distance_label.setText(f"两地距离: {distance:.2f} 公里")else:self.distance_label.setText("两地距离: 请选择两个地点")def update_map_style(self):"""更新地图样式"""if not self.map_initialized:returnfor btn in self.style_buttons:if btn.isChecked():style_name = btn.text()tiles = self.map_styles[style_name]breakjs = f"""var newLayer = L.tileLayer('{tiles}', {{attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'}});// 先清除所有瓦片图层map.eachLayer(function(layer) {{if (layer instanceof L.TileLayer) {{map.removeLayer(layer);}}}});// 添加新图层newLayer.addTo(map);"""self.map_view.page().runJavaScript(js)self.update_embedded_map()def geocode_location(self, address):"""将地址转换为经纬度"""try:location = self.geolocator.geocode(address)if location:return (location.latitude, location.longitude)return Noneexcept (GeocoderTimedOut, GeocoderServiceError) as e:QMessageBox.critical(self, "错误", f"地理编码错误: {e}")return Nonedef add_location(self):"""添加位置到列表"""name = self.name_entry.text().strip()address = self.address_entry.text().strip()if not name or not address:QMessageBox.warning(self, "警告", "请填写名称和地址")returncoords = self.geocode_location(address)if coords:self.locations.append({'name': name,'address': address,'latitude': coords[0],'longitude': coords[1]})item = QTreeWidgetItem([name, address])self.location_list.addTopLevelItem(item)self.name_entry.clear()self.address_entry.clear()self.update_embedded_map()else:QMessageBox.critical(self, "错误", f"无法找到地址: {address}")def remove_location(self):"""删除选中的位置"""selected = self.location_list.selectedItems()if not selected:QMessageBox.warning(self, "警告", "请先选择要删除的位置")returnfor item in selected:index = self.location_list.indexOfTopLevelItem(item)if 0 <= index < len(self.locations):del self.locations[index]self.location_list.takeTopLevelItem(index)self.update_embedded_map()self.distance_label.setText("两地距离: 未选择")  # 清除距离显示def clear_locations(self):"""清空所有位置"""if not self.locations:returnreply = QMessageBox.question(self, '确认', '确定要清空所有位置吗?', QMessageBox.Yes | QMessageBox.No, QMessageBox.No)if reply == QMessageBox.Yes:self.locations.clear()self.location_list.clear()self.update_embedded_map()self.distance_label.setText("两地距离: 未选择")  # 清除距离显示def search_location(self):"""搜索位置并定位"""query = self.search_entry.text().strip()if not query:QMessageBox.warning(self, "警告", "请输入搜索内容")returntry:location = self.geolocator.geocode(query)if location:js = f"""setView({location.latitude}, {location.longitude}, 15);addMarker({location.latitude}, {location.longitude}, '{query.replace("'", "\\'")}', '');"""self.map_view.page().runJavaScript(js)else:QMessageBox.information(self, "提示", "未找到匹配的位置")except Exception as e:QMessageBox.critical(self, "搜索错误", str(e))def create_map(self):"""创建地图并添加所有位置标记"""if not self.locations:QMessageBox.warning(self, "警告", "没有可显示的位置")return# 使用folium创建地图first_loc = self.locations[0]map_obj = folium.Map(location=[first_loc['latitude'], first_loc['longitude']],zoom_start=12)# 根据当前选择的样式设置地图瓦片for btn in self.style_buttons:if btn.isChecked():style_name = btn.text()tiles = self.map_styles[style_name]breakif style_name == "🌍 街道地图":tiles = "OpenStreetMap"attr = Noneelif style_name == "🛰️ 卫星地图":tiles = "https://mt1.google.com/vt/lyrs=s&x={x}&y={y}&z={z}"attr = "Google Satellite"elif style_name == "⛰️ 地形图":tiles = "https://mt1.google.com/vt/lyrs=p&x={x}&y={y}&z={z}"attr = "Google Terrain"if attr:folium.TileLayer(tiles=tiles, attr=attr, name=style_name).add_to(map_obj)else:folium.TileLayer(tiles=tiles, name=style_name).add_to(map_obj)# 添加所有位置标记for loc in self.locations:folium.Marker(location=[loc['latitude'], loc['longitude']],popup=f"<b>{loc['name']}</b><br>{loc['address']}",tooltip=loc['name']).add_to(map_obj)# 如果有两个点被选中,添加连线selected_items = self.location_list.selectedItems()if len(selected_items) == 2:loc1 = self.locations[self.location_list.indexOfTopLevelItem(selected_items[0])]loc2 = self.locations[self.location_list.indexOfTopLevelItem(selected_items[1])]# 添加两点间连线folium.PolyLine(locations=[[loc1['latitude'], loc1['longitude']],[loc2['latitude'], loc2['longitude']]],color='red',weight=3,opacity=0.7,dash_array='10, 10').add_to(map_obj)# 计算并显示距离distance = geodesic((loc1['latitude'], loc1['longitude']),(loc2['latitude'], loc2['longitude'])).kilometers# 在两点中间添加距离标签midpoint = [(loc1['latitude'] + loc2['latitude']) / 2,(loc1['longitude'] + loc2['longitude']) / 2]folium.Marker(location=midpoint,icon=folium.DivIcon(icon_size=(150, 36),icon_anchor=(75, 18),html=f'<div style="font-size: 12pt; color: red; background: white; padding: 2px; border-radius: 3px;">{distance:.2f} km</div>'),tooltip=f"直线距离: {distance:.2f} 公里").add_to(map_obj)# 保存地图file_path, _ = QFileDialog.getSaveFileName(self, "保存地图", self.current_map_file, "HTML Files (*.html)")if file_path:self.current_map_file = file_pathmap_obj.save(self.current_map_file)QMessageBox.information(self, "成功", f"地图已生成: {self.current_map_file}")def show_map(self):"""在浏览器中显示地图"""if not os.path.exists(self.current_map_file):QMessageBox.critical(self, "错误", "请先生成地图")returnwebbrowser.open('file://' + os.path.realpath(self.current_map_file))def update_embedded_map(self):"""更新内嵌地图视图"""if not self.map_initialized or not hasattr(self, 'map_view'):return# 清除所有标记self.map_view.page().runJavaScript("clearMarkers();")if not self.locations:# 如果没有位置,重置到默认视图self.map_view.page().runJavaScript("setView(39.9042, 116.4074, 4);")return# 设置地图中心和缩放级别first_loc = self.locations[0]self.map_view.page().runJavaScript(f"setView({first_loc['latitude']}, {first_loc['longitude']}, 12);")# 添加标记for loc in self.locations:js = f"""addMarker({loc['latitude']}, {loc['longitude']}, '{loc['name'].replace("'", "\\'")}', '{loc['address'].replace("'", "\\'")}');"""self.map_view.page().runJavaScript(js)def main():app = QApplication(sys.argv)app.setStyle('Fusion')  # 使用Fusion风格使UI更现代window = SimpleMapViewerApp()window.show()sys.exit(app.exec_())if __name__ == "__main__":main()

🚀 扩展方向

  1. 数据持久化:集成SQLite存储位置数据
  2. 轨迹绘制:支持路径规划和导航功能
  3. POI搜索:接入更多地理编码服务提供商
  4. 3D视图:集成Cesium实现三维可视化
  5. 插件系统:支持功能模块动态加载

💡 总结

本文详细剖析了基于PyQt5和Folium的地图应用开发全流程,关键技术点包括:

  1. 混合渲染架构:巧妙结合桌面应用的性能优势和Web地图的灵活性
  2. 精确距离测量:使用geodesic算法实现专业级距离计算
  3. 响应式UI设计:通过PyQt5构建美观易用的交互界面
  4. 跨框架通信:实现Python逻辑与JavaScript渲染的无缝衔接

该解决方案特别适合以下场景:

  • 企业内网GIS系统
  • 科研数据可视化
  • 物流路径规划
  • 教学演示工具

未来展望:随着WebAssembly技术的发展,这类混合架构应用将获得更接近原生应用的性能表现,在地理信息领域具有广阔的应用前景。


版权声明:本文采用CC BY-NC-SA 4.0协议,转载请注明出处。商业转载请联系作者授权。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

篇章五 数据结构——链表(一)

目录 1.ArrayList的缺陷 2. 链表 2.1 链表的概念及结构 2.2 链表结构 1. 单向或者双向 2.带头或者不带头 3.循环或者非循环 2.3 链表的实现 1.完整代码 2.图解 3.显示方法 4.链表大小 5. 链表是否存在 key 值 6.头插法 7.尾插法 8.中间插入 9.删除key值节点 10.…

数据库相关面试

数据库相关面试 Mysql MySQL中的事务隔离级别及其特点 参考&#xff1a;事务的隔离级别&#xff1a;可重复读 未提交读(Read Uncommitted)&#xff1a;允许脏读&#xff0c;也就是可能读取到其他会话中未提交事务修改的数据 已提交读(Read Committed)&#xff1a;只能读取到…

基于Scrapy的天猫商品数据爬取与分析实战(含API签名破解与可视化)

基于Scrapy的天猫商品数据爬取与分析实战&#xff08;含API签名破解与可视化&#xff09; 本文以华为Mate 60 Pro为例&#xff0c;详细介绍如何使用Scrapy框架爬取天猫商品数据&#xff0c;涵盖API签名破解、反爬应对、数据存储及可视化全流程&#xff0c;适合爬虫进阶学习者实…

【C++进阶篇】哈希表的模拟实现(赋源码)

这里写目录标题 前言一. 开放地址法实现哈希表1.1 闭散列结构定义1.2 构造函数1.3 插入&#xff08;线性探测&#xff09;1.3.1 传统写法1.3.2 现代写法 1.4 查找1.5 删除 二. 链地址法实现哈希表&#xff08;哈希桶&#xff09;2.1 开散列结构定义2.2 构造函数2.3 插入2.4 查找…

07-后端Web实战(部门管理)

5. 修改部门 对于任何业务的修改功能来说&#xff0c;一般都会分为两步进行&#xff1a;查询回显、修改数据。 5.1 查询回显 5.1.1 需求 当我们点击 "编辑" 的时候&#xff0c;需要根据ID查询部门数据&#xff0c;然后用于页面回显展示。 5.1.2 接口描述 参照参照…

深度解析项目集方向或目标根本性转变的应对策略 —— 项目集管理实战指南

在复杂多变的商业环境中&#xff0c;项目集管理面临着重重挑战&#xff0c;而项目集方向或目标的根本性转变无疑是其中最具冲击力的问题之一。本文将深度剖析这一难题&#xff0c;为项目集管理从业者提供实用、新颖且富有价值的应对策略&#xff0c;助力大家在项目集管理的复杂…

JAVA面试复习知识点

面试中遇到的题目&#xff0c;记录复习&#xff08;持续更新&#xff09; Java基础 1.String的最大长度 https://www.cnblogs.com/wupeixuan/p/12187756.html 2.集合 Collection接口的实现&#xff1a; List接口&#xff1a;ArraryList、LinkedList、Vector Set接口&#xff1a…

【烧脑算法】定长滑动窗口:算法题中的“窗口”智慧

目录 引言 定长滑动窗口习题剖析 3439. 重新安排会议得到最多空余时间 I 2134. 最少交换次数来组合所有的 1 II 1297. 子串的最大出现次数 2653. 滑动子数组的美丽值 567. 字符串的排列 438. 找到字符串中所有字母异位词 30. 串联所有单词的子串 220. 存在重复元素 II…

JWT安全:接收无签名令牌.【签名算法设置为none绕过验证】

JWT安全&#xff1a;假密钥【签名随便写实现越权绕过.】 JSON Web 令牌 (JWT)是一种在系统之间发送加密签名 JSON 数据的标准化格式。理论上&#xff0c;它们可以包含任何类型的数据&#xff0c;但最常用于在身份验证、会话处理和访问控制机制中发送有关用户的信息(“声明”)。…

XGBoost与SHAP深度解析:从算法原理到实战价值

在机器学习领域&#xff0c;XGBoost以其卓越的性能长期占据Kaggle竞赛和工业界的主流地位&#xff0c;而SHAP&#xff08;SHapley Additive exPlanations&#xff09;则成为模型可解释性的标杆工具。本文将深度解析两者的技术内核&#xff0c;并通过实战案例揭示其结合应用的实…

Java SE Cloneable接口和深/浅拷贝

Java为我们提供了各种各样功能的接口&#xff0c;Clonable接口就是其中之一。 它通常配合Object类的 clone方法使用。这个方法可以为我们创建一个对象的拷贝&#xff0c;即复制一个对象。在进入本文的主要内容之前&#xff0c;先来对访问限定符 protected进行一个解剖。 1.再…

Python学习(3) ----- Python的函数定义及其使用

Python 中函数是组织好的、可重复使用的代码块&#xff0c;用于实现单一或相关联的功能。下面是函数定义和使用的完整说明&#xff1a; &#x1f4cc; 一、函数定义语法 def 函数名(参数1, 参数2默认值, *args, **kwargs):"""函数说明文档"""函…

vue2使用el-tree实现两棵树间节点的拖拽复制

原文链接&#xff1a;两棵el-tree的节点跨树拖拽实现 参照这篇文章&#xff0c;把它做成组件&#xff0c;新增左侧树&#xff08;可拖出&#xff09;被拖节点变灰提示&#xff1b; 拖拽中&#xff1a; 拖拽后&#xff1a; TreeDragComponent.vue <template><!-- …

智变与重构:AI 赋能基础教育教学的范式转型研究报告

一、研究背景与核心价值 &#xff08;一&#xff09;技术驱动下的教育转型浪潮 在全球数字化转型加速的背景下&#xff0c;人工智能作为核心技术力量&#xff0c;正重塑基础教育生态。据《人工智能赋能未来教育研究报告》指出&#xff0c;我国教育数字化战略行动已推动超 70…

Go语言中Print、Printf和Println的区别及使用场景详解

在Go语言的fmt包中&#xff0c;Print、Printf和Println是三个基础但功能各异的输出函数。本文将从多个维度进行详细对比分析&#xff0c;并给出具体的使用建议。 1. 核心区别深度解析 1.1. 函数签名与基本行为 func Print(a ...interface{}) (n int, err error) func Printf…

高端制造行业 VMware 替代案例合集:10+ 头部新能源、汽车、半导体制造商以国产虚拟化支持 MES、PLM 等核心应用系统

在“中国制造 2025”政策的推动下&#xff0c;国内的新能源、汽车制造、半导体、高端装备等高端制造产业迎来了蓬勃发展&#xff0c;成为全球制造业版图中举足轻重的力量。订单数量的激增与国产化转型的趋势&#xff0c;也为高端制造企业的 IT 基础设施带来了新的挑战&#xff…

Spring Ai | 从零带你一起走进AI项目(中英)

目录 Thinking Study question pox.xml Maven Gradle Configure API Key Use the AI Client Question Thinking 让数据变得更加贴近用户的想法 Study question null pox.xml 添加依赖 Maven <dependencies><dependency><groupId>org.springfram…

LiveGBS作为下级平台GB28181国标级联2016|2022对接海康大华宇视华为政务公安内网等GB28181国标平台查看级联状态及会话

LiveGBS作为下级平台GB28181国标级联2016|2022对接海康大华宇视华为政务公安内网等GB28181国标平台查看级联状态及会话 1、GB/T28181级联概述2、搭建GB28181国标流媒体平台3、获取上级平台接入信息3.1、向下级提供信息3.2、上级国标平台添加下级域3.3、接入LiveGBS示例 4、配置…

卸载 Office PLUS

Office PLUS作为微软官方推出的智能办公提效工具&#xff0c;自2015年问世以来&#xff0c;凭借其丰富的模板资源和便捷的智能功能&#xff0c;迅速赢得了广大职场人士和学生的青睐。本文将全面介绍Office PLUS的发展历程、核心功能、可能带来的使用问题&#xff0c;以及如何彻…

影响沉金价格的因素如何体现在多层电路板制造上?

随着科技的不断发展&#xff0c;电子产品越来越普及&#xff0c;对电路板的需求也越来越大。多层电路板作为电子产品的核心部件&#xff0c;其性能和质量直接影响到整个产品的稳定性和可靠性。在多层电路板的生产过程中&#xff0c;沉金工艺是一种常用的表面处理方法&#xff0…