This commit is contained in:
2025-11-02 22:06:42 +08:00
parent e71b69db5f
commit c2aa65193d
19 changed files with 427 additions and 274 deletions

View File

@@ -268,6 +268,12 @@ class PreviewTextBox(MessageBoxBase):
self._id = _id
self.isChanged = False
# 初始化关键变量
self.isContentSaved = False # 明确初始化保存状态
self.httpd = None # 服务器实例引用
self.server_thread = None # 服务器线程引用
self.tempFilePath = None # 临时文件路径
# 设置编辑器HTML文件路径 - 修正为项目根目录的_internal文件夹
project_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
self.editor_index = os.path.join(project_root, "_internal/editor_main/index.html")
@@ -348,7 +354,10 @@ class PreviewTextBox(MessageBoxBase):
)
# 隐藏进度条
self.saveProgressBar.hide()
QTimer.singleShot(700, self.accept)
# 直接调用accept()关闭窗口,不再使用定时器延迟
# 确保窗口立即关闭并返回成功结果
self.accept()
def _errorSave(self, msg):
logger.error(f"文本文件保存失败文件ID: {self._id}, 错误: {msg}")
@@ -432,14 +441,9 @@ class PreviewTextBox(MessageBoxBase):
def setTextContent(self, content):
"""设置文本内容并在外部浏览器中打开"""
# 检查内容是否已保存,如果已保存则不允许再次编辑
if hasattr(self, 'isContentSaved') and self.isContentSaved:
logger.warning("内容已保存,不允许再次编辑")
self.placeholderLabel.setText('''<div style='text-align:center; padding:20px;'>
<h3>已经保存</h3>
<p>该内容已经保存,不再允许编辑</p>
</div>''')
return
# 每次打开文件时都重置isContentSaved状态为False允许重新编辑
self.isContentSaved = False
logger.info(f"重置isContentSaved为False文件ID: {self._id}")
logger.info(f"文本文件加载成功,原始内容长度: {len(content)}字符")
@@ -563,10 +567,10 @@ class PreviewTextBox(MessageBoxBase):
attempts = 0
while attempts < max_attempts:
try:
# 设置处理器的实例引用
EditorHTTPRequestHandler.preview_box_instance = self
# 使用自定义处理器创建服务器
self.httpd = ReuseTCPServer(("127.0.0.1", port), EditorHTTPRequestHandler)
# 保存当前实例的引用,以便处理器可以访问
self.httpd.RequestHandlerClass.preview_box_instance = self
logger.info(f"Web服务器成功在端口 {port} 启动")
break
except OSError as e:
@@ -616,87 +620,44 @@ class PreviewTextBox(MessageBoxBase):
self.pollingTimer.start()
def _checkBrowserContent(self):
"""检查浏览器是否已保存内容"""
# 首先尝试从localStorage读取
try:
import json
import os
import base64
# 尝试获取localStorage数据
# 在Windows上localStorage通常存储在用户的AppData目录中
# 由于直接访问localStorage有困难我们使用一个更可靠的方法
# 1. 首先检查临时文件
if self.tempFilePath and os.path.exists(self.tempFilePath):
try:
with open(self.tempFilePath, 'r', encoding='utf-8') as f:
data = json.load(f)
if data.get('saved', False):
logger.info("检测到浏览器已保存内容(通过临时文件)")
self._processSavedContent(data)
return
except Exception as e:
logger.error(f"读取临时文件失败: {e}")
# 2. 作为备用方案,检查是否有特定的保存文件
# 这个文件可以由浏览器通过特定的方法创建
import tempfile
app_data_dir = os.path.join(tempfile.gettempdir(), 'LeonPan')
os.makedirs(app_data_dir, exist_ok=True)
save_file_path = os.path.join(app_data_dir, 'editor_content.json')
if os.path.exists(save_file_path):
try:
with open(save_file_path, 'r', encoding='utf-8') as f:
data = json.load(f)
if data.get('saved', False):
logger.info("检测到浏览器已保存内容(通过备用文件)")
self._processSavedContent(data)
# 删除备用文件
os.unlink(save_file_path)
except Exception as e:
logger.error(f"读取备用保存文件失败: {e}")
except Exception as e:
logger.error(f"检查浏览器内容时出错: {e}")
"""检查浏览器是否已保存内容 - 现在仅作为备用方案"""
# 由于我们使用POST请求直接传递内容这里不再需要轮询检查文件
# 轮询可以保留作为备用方案但主要保存机制是通过POST请求
pass
def _processSavedContent(self, data):
"""处理已保存的内容"""
# 停止轮询
self.pollingTimer.stop()
logger.info(f"开始处理保存的内容文件ID: {self._id}")
# 更新内容
import base64
encoded_content = data.get('content', '')
try:
self.editorContent = base64.b64decode(encoded_content).decode('utf-8')
logger.info(f"内容解码成功,长度: {len(self.editorContent)}字符")
except Exception as e:
logger.error(f"解码内容失败: {e}")
return
# 标记内容已保存
self.isContentSaved = True
# 不再标记为已保存让_saveContent方法处理保存逻辑
# 直接调用_saveContent方法保存内容到服务器不再重置状态
logger.info(f"调用_saveContent方法保存内容到服务器文件ID: {self._id}")
self._saveContent(self.editorContent)
# 更新占位符文本,显示已保存信息
# 这里不再显示成功信息因为_saveContent会通过_successSave方法显示
# 也不再设置定时器重置状态,因为成功保存后窗口会直接关闭
def _resetSaveState(self):
"""重置保存状态,允许再次编辑和保存"""
self.isContentSaved = False
logger.info(f"已重置保存状态允许再次编辑和保存文件ID: {self._id}")
# 恢复默认的占位符文本
self.placeholderLabel.setText('''<div style='text-align:center; padding:20px;'>
<h3>已经保存</h3>
<p>内容已从浏览器同步到应用并已保存</p>
<p>该内容将不再允许编辑</p>
<h3>文本编辑已在外部浏览器中打开</h3>
<p><strong>重要提示:</strong>完成编辑后,请点击浏览器中的"保存并返回"按钮</p>
<p>应用正在自动检测...</p>
</div>''')
# 禁用保存按钮
self.saveButton.setEnabled(False)
# 清理临时文件
if self.tempFilePath and os.path.exists(self.tempFilePath):
try:
os.unlink(self.tempFilePath)
self.tempFilePath = None
except Exception as e:
logger.error(f"删除临时文件失败: {e}")
def handleError(self, error_msg):
@@ -741,15 +702,10 @@ class PreviewTextBox(MessageBoxBase):
<script>
let editor;
require.config({{ paths: {{ 'vs': './vs' }} }});
require.config({ paths: { 'vs': './vs' } });
require(['vs/editor/editor.main'], function() {{
editor = monaco.editor.create(document.getElementById('container'), {{
value: `{content.replace('`', '\\`')}`,
}});
// 重新获取编辑器实例以确保正确初始化
setTimeout(() => {{
editor = monaco.editor.getModels()[0] ? monaco.editor.getModels()[0].getContainerInfo().domNode.monacoEditor : null;
}}, 100);'\\`'}}`,
language: '{language}',
theme: 'vs-dark',
automaticLayout: true,
@@ -760,7 +716,11 @@ class PreviewTextBox(MessageBoxBase):
lineNumbers: 'on',
readOnly: false,
fontFamily: 'Consolas, "Microsoft YaHei", monospace'
}});
}});
// 重新获取编辑器实例以确保正确初始化
setTimeout(() => {{
editor = monaco.editor.getModels()[0] ? monaco.editor.getModels()[0].getContainerInfo().domNode.monacoEditor : null;
}}, 100);
// 添加内容变化监听器
editor.onDidChangeModelContent(function() {{
@@ -833,9 +793,40 @@ class PreviewTextBox(MessageBoxBase):
# JavaScript执行完成的处理已移除
def __del__(self):
"""析构函数,确保清理资源"""
# 停止轮询定时器
self.pollingTimer.stop()
# 停止HTTP服务器
if hasattr(self, 'httpd'):
try:
self.httpd.shutdown()
self.httpd.server_close()
logger.info("Web服务器已停止")
except Exception as e:
logger.warning(f"停止Web服务器时出错: {e}")
# 删除临时文件
if hasattr(self, 'tempFilePath') and self.tempFilePath and os.path.exists(self.tempFilePath):
try:
os.unlink(self.tempFilePath)
logger.info(f"临时文件已删除: {self.tempFilePath}")
except Exception as e:
logger.warning(f"删除临时文件失败: {e}")
def _saveContent(self, content):
"""保存编辑器内容并提交修改"""
logger.info(f"保存文本文件修改文件ID: {self._id}")
# 确保断开之前可能存在的连接,避免多次连接
if hasattr(self, 'saveTextThread') and self.saveTextThread:
try:
self.saveTextThread.successUpdated.disconnect()
self.saveTextThread.errorUpdated.disconnect()
except:
pass
# 创建新的保存线程
self.saveTextThread = UpdateFileContentThread(
self._id,
content,