PyQt中Scattermapbox框选/套索选数据,解决selectedpoints未更新及获取选中数据
解决PyQt中Scattermapbox框选数据点后无法获取选中数据的问题
问题原因
你当前的代码里,fig对象是在Python端生成后转成静态HTML加载到QWebEngineView的,前端Plotly的框选/套索交互只会在浏览器端更新图表状态,不会自动同步回Python端的fig对象,因此selectedpoints始终是空数组。要获取选中数据,必须建立前端JS与Python代码的通信,把选中点的索引传递回Python。
修改后的完整代码
import sys from PyQt5.QtWebEngineWidgets import QWebEngineView from PyQt5.QtWidgets import QApplication, QPushButton, QVBoxLayout, QDialog import plotly.graph_objects as go import pandas as pd from PyQt5.QtCore import QObject, pyqtSlot class JavaScriptBridge(QObject): """JS与Python的通信桥接类""" def __init__(self, parent=None): super().__init__(parent) self.parent_widget = parent @pyqtSlot(list) def receive_selected_indices(self, indices): """接收JS传递的选中点索引""" self.parent_widget.handle_selected_indices(indices) class DashboardScreen(QDialog): def __init__(self): super(DashboardScreen, self).__init__() # 保存原始数据,方便后续筛选选中项 self.data = {'Fid': ['A', 'B', 'C'], 'Project': [1,2,3], 'Date': [23, 24, 25], 'Latitude': [10, 20, 30], 'Longitude': [70, 75, 80]} self.df = pd.DataFrame(self.data) # 创建Scattermapbox图表 fig = go.Figure(go.Scattermapbox( lat=self.df['Latitude'], lon=self.df['Longitude'], hovertext=self.df['Project'], mode='markers', marker={'size': 10, 'color': 'blue'}, selected=dict(marker={'color': 'red'}), selectedpoints=[], uid='scatter_trace' # 给trace设置唯一标识,方便JS定位 )) fig.update_layout( mapbox_style="white-bg" ) fig.update_layout(mapbox_layers=[ { "below": 'traces', "sourcetype": "raster", "sourceattribution": "United States Geological Survey", "source": [ "https://basemap.nationalmap.gov/arcgis/rest/services/USGSImageryOnly/MapServer/tile/{z}/{y}/{x}" ] } ], margin={"r":0,"t":0,"l":0,"b":0} ) self.setLayout(QVBoxLayout()) # 初始化WebView并加载带交互监听的图表 self.webView = QWebEngineView() # 注册JS桥接对象,实现双向通信 self.js_bridge = JavaScriptBridge(self) self.webView.page().setWebChannel(self.js_bridge) # 生成基础HTML并添加选中事件监听的JS代码 base_html = fig.to_html(include_plotlyjs='cdn') selected_event_js = """ <script> // 获取页面中的Plotly图表对象 const chart = document.querySelector('.plotly-graph-div'); // 监听Plotly的选中事件 chart.addEventListener('plotly_selected', (event) => { const selectedIndices = []; if (event.points) { // 提取每个选中点的索引 event.points.forEach(point => selectedIndices.push(point.pointIndex)); } // 将索引传递给Python端 window.receive_selected_indices(selectedIndices); }); </script> """ # 合并HTML与JS代码 full_html = base_html.replace('</body>', selected_event_js + '</body>') self.webView.setHtml(full_html) self.layout().addWidget(self.webView) # 添加查看选中数据的按钮 self.button = QPushButton("查看选中数据") self.button.clicked.connect(self.show_selected_data) self.layout().addWidget(self.button) # 存储当前选中的点索引 self.current_selected_indices = [] def handle_selected_indices(self, indices): """更新Python端的选中索引""" self.current_selected_indices = indices def show_selected_data(self): """展示选中的数据(支持转为DataFrame、列表、数组)""" if not self.current_selected_indices: print("未选中任何数据点") return # 转为DataFrame selected_df = self.df.iloc[self.current_selected_indices] print("选中数据(DataFrame):") print(selected_df) # 转为列表 selected_lat_list = selected_df['Latitude'].tolist() selected_lon_list = selected_df['Longitude'].tolist() print("\n选中纬度列表:", selected_lat_list) print("选中经度列表:", selected_lon_list) # 转为numpy数组 selected_lat_array = selected_df['Latitude'].to_numpy() selected_lon_array = selected_df['Longitude'].to_numpy() print("\n选中纬度数组:", selected_lat_array) print("选中经度数组:", selected_lon_array) if __name__ == '__main__': app = QApplication(sys.argv) welcome = DashboardScreen() welcome.show() try: sys.exit(app.exec_()) except Exception: print("Exiting!!")
关键修改说明
- JS-Python通信桥接:通过
JavaScriptBridge类和pyqtSlot装饰器,实现前端JS与Python代码的数据传递通道。 - Plotly事件监听:在HTML中插入JS代码,监听
plotly_selected事件,提取选中点的索引并传递给Python。 - 原始数据缓存:将原始DataFrame保存为类属性,避免从静态
fig对象中提取数据,确保数据获取的准确性和效率。 - 实时索引更新:维护
current_selected_indices变量实时同步前端选中状态,点击按钮即可快速筛选出对应数据。
内容的提问来源于stack exchange,提问作者PARVATHY RAMAKRISHNAN P
相关产品推荐
相关产品推荐

