AttributeError: ‘str‘ object has no attribute ‘page_content‘ 解决方案

  大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的知识进行总结与归纳,不仅形成深入且独到的理解,而且能够帮助新手快速入门。

  本文主要介绍了AttributeError: ‘str’ object has no attribute 'page_content’解决方案,希望能对使用langchain的同学们有所帮助。

1. 问题描述

  今天在使用langchain的text_splitter加载文档时,却出现了AttributeError: ‘str’ object has no attribute 'page_content’的错误提示,具体报错信息如下图所示:
在这里插入图片描述

  在经过了亲身的实践后,终于找到了解决问题的方案,最终将逐步的操作过程总结如下。希望能对遇到同样bug的同学们有所帮助。

<think>我们之前提供的代码中,由于篇幅限制,没有完整地包含`save_map_state`等方法。现在需要为`MapMarkerApp`类补充这些缺失的方法。具体需要补充的方法包括:`save_map_state`,`save_map_state_callback`,`load_map_state`,`load_existing_markers`等。下面我们给出完整的代码,包括之前缺失的方法:</think> 这个错误是因为 `MapMarkerApp` 类中没有定义 `save_map_state` 方法。您需要为类添加这个方法,以及它依赖的其他方法。以下是为您提供的完整解决方案: ```python import os import json import shutil import sys import time from PyQt5.QtWidgets import ( QApplication, QMainWindow, QDialog, QLabel, QLineEdit, QTextEdit, QPushButton, QFileDialog, QVBoxLayout, QHBoxLayout, QToolBar, QAction, QWidget, QScrollArea, QMessageBox ) from PyQt5.QtCore import Qt, QObject, pyqtSlot from PyQt5.QtGui import QPixmap from PyQt5.QtWebEngineWidgets import QWebEngineView from PyQt5.QtWebChannel import QWebChannel class BridgeObject(QObject): """Python和JavaScript之间的通信桥梁""" def __init__(self, parent=None): super().__init__(parent) self.parent = parent @pyqtSlot(str) def handle_message(self, message): """处理来自JavaScript的消息""" self.parent.handle_js_message(message) class MapMarkerApp(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle("地图标记工具") self.setGeometry(100, 100, 1200, 800) # 创建中央部件 central_widget = QWidget() self.setCentralWidget(central_widget) layout = QVBoxLayout(central_widget) # 创建地图视图 self.map_view = QWebEngineView() layout.addWidget(self.map_view) # 创建工具栏 self.toolbar = QToolBar("主工具栏") self.addToolBar(self.toolbar) # 添加标记模式按钮 self.add_marker_action = QAction("添加标记模式", self) self.add_marker_action.setCheckable(True) self.add_marker_action.toggled.connect(self.toggle_add_marker_mode) self.toolbar.addAction(self.add_marker_action) # 保存状态按钮 save_action = QAction("保存地图状态", self) save_action.triggered.connect(self.save_map_state) self.toolbar.addAction(save_action) # 状态变量 self.add_mode = False self.markers = {} # 存储标记数据 # 创建通信桥梁 self.bridge = BridgeObject(self) # 设置WebChannel通信 self.channel = QWebChannel() self.channel.registerObject("bridge", self.bridge) self.map_view.page().setWebChannel(self.channel) # 加载地图 self.load_map() self.map_view.loadFinished.connect(self.on_map_loaded) # 创建必要目录 os.makedirs("markers_data", exist_ok=True) def load_map(self): """加载百度地图""" # 百度地图API密钥(确保您有有效的API密钥) baidu_key = "68kdoA24XGfaUwDZD7TviZO9QY0RCb0I" # 确保qwebchannel.js文件存在 script_dir = os.path.dirname(os.path.abspath(__file__)) qwebchannel_path = os.path.join(script_dir, 'qwebchannel.js') if not os.path.exists(qwebchannel_path): try: from PyQt5.QtWebChannel import QWebChannel src_dir = os.path.dirname(QWebChannel.__file__) qwebchannel_src = os.path.join(src_dir, 'qwebchannel.js') if os.path.exists(qwebchannel_src): shutil.copyfile(qwebchannel_src, qwebchannel_path) except Exception as e: print(f"无法复制qwebchannel.js: {e}") # 将路径中的反斜杠替换为斜杠 if os.path.exists(qwebchannel_path): qwebchannel_path_forward = qwebchannel_path.replace('\\', '/') # 使用拼接方式构建HTML内容 html_content = '''<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>地图标记</title> <style> html, body, #map-container { width:100%; height:100%; margin:0; padding:0; overflow: hidden; } </style> <script type="text/javascript" src="https://api.map.baidu.com/api?v=3.0&ak=''' + baidu_key + '''"></script> <script type="text/javascript" src="file:///''' + qwebchannel_path_forward + '''"></script> </head> <body> <div id="map-container"></div> <script> // 解决跨域问题 document.domain = document.domain; // 初始化地图 var map = new BMap.Map("map-container"); var point = new BMap.Point(116.404,39.915); // 默认坐标:北京市中心 map.centerAndZoom(point,12); map.enableScrollWheelZoom(true); map.addControl(new BMap.NavigationControl()); // 添加缩放控件 // 存储标记对象 var markers = {}; // 初始化通信通道 new QWebChannel(qt.webChannelTransport, function(channel) { window.bridge = channel.objects.bridge; }); // 地图点击事件 map.addEventListener("click", function(e) { if (window.bridge && window.addMode) { window.bridge.handle_message(JSON.stringify({ type: 'map_click', lat: e.point.lat, lng: e.point.lng })); } }); // 添加标记的方法 function addMarker(lat, lng, id, title, content) { var point = new BMap.Point(lng, lat); var marker = new BMap.Marker(point); map.addOverlay(marker); // 添加点击事件 marker.addEventListener("click", function() { if (!window.addMode) { window.bridge.handle_message(JSON.stringify({ type: 'marker_click', marker_id: id })); } }); // 添加到全局标记对象 markers[id] = { marker: marker, title: title, content: content }; } // 移除标记的方法 function removeMarker(id) { if (markers[id]) { map.removeOverlay(markers[id].marker); delete markers[id]; } } // 获取地图状态 function getMapState() { var center = map.getCenter(); return { center: { lat: center.lat, lng: center.lng }, zoom: map.getZoom() }; } // 设置地图视图 function setMapView(state) { if (state && state.center) { var point = new BMap.Point(state.center.lng, state.center.lat); map.centerAndZoom(point, state.zoom ||15); } } // 暴露方法给Python window.mapFunctions = { addMarker: addMarker, removeMarker: removeMarker, getMapState: getMapState, setMapView: setMapView }; // 初始化模式 window.addMode = false; // 确保地图加载完成 map.addEventListener("tilesloaded", function() { console.log("地图加载完成"); if (window.bridge) { window.bridge.handle_message(JSON.stringify({ type: 'map_loaded' })); } }); </script> </body> </html>''' self.map_view.setHtml(html_content) else: QMessageBox.critical(self, "错误", "找不到qwebchannel.js文件,地图无法加载") def on_map_loaded(self, success): """地图加载完成后初始化""" if success: print("地图加载成功") # 等待JavaScript初始化完成 self.map_view.page().runJavaScript( "if (typeof window.mapFunctions !== 'undefined') true; else false;", self.init_after_js_ready ) else: print("地图加载失败") QMessageBox.critical(self, "错误", "地图加载失败,请检查网络连接和API密钥") def init_after_js_ready(self, result): """JavaScript环境准备就绪后回调""" if result: # 加载地图状态 self.load_map_state() # 加载已有标记 self.load_existing_markers() else: # 延迟重试 time.sleep(0.5) self.map_view.page().runJavaScript( "if (typeof window.mapFunctions !== 'undefined') true; else false;", self.init_after_js_ready ) # 以下是新增的方法 def toggle_add_marker_mode(self, checked): """切换添加标记模式""" self.add_mode = checked # 将布尔值转换为字符串发送到JavaScript js_code = f"window.addMode = {'true' if checked else 'false'};" self.map_view.page().runJavaScript(js_code) def handle_js_message(self, message): """处理来自JavaScript的消息""" try: data = json.loads(message) if data["type"] == "map_click" and self.add_mode: self.create_new_marker(data["lat"], data["lng"]) elif data["type"] == "marker_click": self.show_marker_info(data["marker_id"]) except Exception as e: print("处理JS消息错误:", e) print("收到的消息:", message) def create_new_marker(self, lat, lng): """创建新的标记""" # 生成唯一ID marker_id = f"marker_{int(time.time() * 1000)}" # 对话框获取标记信息 dialog = QDialog(self) dialog.setWindowTitle("添加标记") layout = QVBoxLayout() # 标题输入 title_label = QLabel("标题:") title_input = QLineEdit() layout.addWidget(title_label) layout.addWidget(title_input) # 内容输入 content_label = QLabel("内容:") content_input = QTextEdit() layout.addWidget(content_label) layout.addWidget(content_input) # 按钮 button_box = QHBoxLayout() ok_btn = QPushButton("确定") cancel_btn = QPushButton("取消") button_box.addWidget(ok_btn) button_box.addWidget(cancel_btn) layout.addLayout(button_box) # 连接按钮信号 def accept(): title = title_input.text().strip() content = content_input.toPlainText().strip() if not title: QMessageBox.warning(dialog, "输入错误", "标题不能为空") return dialog.accept() # 在JavaScript中添加标记 js_code = f"window.mapFunctions.addMarker({lat}, {lng}, '{marker_id}', '{title}', `{content}`);" self.map_view.page().runJavaScript(js_code) # 保存标记数据 self.markers[marker_id] = { "lat": lat, "lng": lng, "title": title, "content": content } # 保存标记数据到文件 self.save_marker_to_file(marker_id) ok_btn.clicked.connect(accept) cancel_btn.clicked.connect(dialog.reject) dialog.setLayout(layout) dialog.exec_() def show_marker_info(self, marker_id): """显示标记的信息""" if marker_id in self.markers: marker = self.markers[marker_id] info_dialog = QDialog(self) info_dialog.setWindowTitle(marker["title"]) layout = QVBoxLayout() content_label = QTextEdit(marker["content"]) content_label.setReadOnly(True) layout.addWidget(content_label) # 删除按钮 del_btn = QPushButton("删除标记") del_btn.clicked.connect(lambda: self.delete_marker(marker_id, info_dialog)) layout.addWidget(del_btn) info_dialog.setLayout(layout) info_dialog.exec_() def delete_marker(self, marker_id, dialog): """删除标记""" if marker_id in self.markers: # 从地图中移除 js_code = f"window.mapFunctions.removeMarker('{marker_id}');" self.map_view.page().runJavaScript(js_code) # 从内存中删除 del self.markers[marker_id] # 删除文件 marker_file = os.path.join("markers_data", f"{marker_id}.json") if os.path.exists(marker_file): os.remove(marker_file) dialog.accept() def save_map_state(self): """保存地图状态""" # 获取当前地图状态 js_code = "window.mapFunctions.getMapState();" self.map_view.page().runJavaScript(js_code, self.save_map_state_callback) def save_map_state_callback(self, state_json): """地图状态获取后的回调函数""" if not state_json: QMessageBox.warning(self, "错误", "获取地图状态失败") return try: state = json.loads(state_json) # 保存到文件 with open("map_state.json", "w") as f: json.dump(state, f) QMessageBox.information(self, "保存成功", "地图状态已保存") except Exception as e: QMessageBox.critical(self, "错误", f"保存地图状态时出错: {str(e)}") def load_map_state(self): """加载保存的地图状态""" map_state_file = "map_state.json" if os.path.exists(map_state_file): try: with open(map_state_file) as f: state = json.load(f) # 设置地图视图 js_code = f"window.mapFunctions.setMapView({json.dumps(state)});" self.map_view.page().runJavaScript(js_code) except Exception as e: print("加载地图状态失败:", e) def load_existing_markers(self): """加载已有的标记数据""" markers_dir = "markers_data" if os.path.exists(markers_dir): for file_name in os.listdir(markers_dir): if file_name.endswith(".json"): marker_id = file_name[:-5] # 移除 .json 后缀 marker_file = os.path.join(markers_dir, file_name) try: with open(marker_file) as f: marker_data = json.load(f) # 将标记添加到内存 self.markers[marker_id] = marker_data # 在JavaScript中添加标记 js_code = f"window.mapFunctions.addMarker({marker_data['lat']}, {marker_data['lng']}, '{marker_id}', '{marker_data['title']}', `{marker_data['content']}`);" self.map_view.page().runJavaScript(js_code) except Exception as e: print(f"加载标记{marker_id}失败:", e) def save_marker_to_file(self, marker_id): """保存标记数据到文件""" if marker_id in self.markers: marker_data = self.markers[marker_id] marker_file = os.path.join("markers_data", f"{marker_id}.json") try: with open(marker_file, "w") as f: json.dump(marker_data, f) except Exception as e: QMessageBox.critical(self, "保存失败", f"无法保存标记数据: {str(e)}") if __name__ == "__main__": # 解决高DPI显示问题 if hasattr(Qt, 'AA_EnableHighDpiScaling'): QApplication.setAttribute(Qt.AA_EnableHighDpiScaling, True) if hasattr(Qt, 'AA_UseHighDpiPixmaps'): QApplication.setAttribute(Qt.AA_UseHighDpiPixmaps, True) app = QApplication(sys.argv) # 确保当前目录正确 script_dir = os.path.dirname(os.path.abspath(__file__)) os.chdir(script_dir) main_window = MapMarkerApp() main_window.show() sys.exit(app.exec_()) ``` ### 新增方法说明 1. **`save_map_state` 和 `save_map_state_callback`** - `save_map_state` 从JavaScript获取当前地图状态 - `save_map_state_callback` 处理获取到的状态数据并保存到文件 2. **`load_map_state`** - 加载保存的地图状态文件并应用到地图 3. **`create_new_marker`** - 在用户点击地图时创建新标记 - 显示对话框让用户输入标记信息 4. **`show_marker_info` 和 `delete_marker`** - 显示标记信息对话框 - 提供删除标记的功能 5. **`save_marker_to_file` 和 `load_existing_markers`** - 保存标记数据到JSON文件 - 从磁盘加载已有的标记数据并添加到地图 ### 功能增强 1. **持久化存储**:标记数据和地图状态被保存到文件 2. **完整标记管理**:用户可以添加、查看和删除标记 3. **UI交互优化**: - 对话框表单收集标记信息 - 信息展示使用只读文本编辑框 - 错误处理提示 ### 修复方案 1. **添加缺失的方法**:实现了所有缺少的方法,包括地图状态保存和标记管理 2. **完善标记生命周期**:标记从创建、展示到删除的完整流程 3. **数据持久化**:使用JSON文件存储地图状态和标记数据 这个完整解决方案不仅修复了 `AttributeError: 'MapMarkerApp' object has no attribute 'save_map_state'` 错误,还提供了完整的地图标记应用功能。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱编程的喵喵

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值