增加切换 #51

Merged
p9o3yklam merged 8 commits from maziang into main 4 months ago

@ -1,5 +1,7 @@
import os
from typing import Union
import zipfile
import tempfile
from typing import Union, List, Tuple
class FileParser:
@staticmethod
@ -86,6 +88,41 @@ class FileParser:
# 默认返回UTF-8
return 'utf-8'
@staticmethod
def extract_images_from_docx(file_path: str) -> List[Tuple[str, bytes]]:
"""从Word文档中提取图片
Args:
file_path: Word文档路径
Returns:
图片列表每个元素为(图片文件名, 图片二进制数据)的元组
"""
if not FileParser.validate_file_path(file_path):
raise ValueError(f"Invalid file path: {file_path}")
images = []
try:
# Word文档实际上是ZIP文件可以直接解压
with zipfile.ZipFile(file_path, 'r') as zip_file:
# 遍历ZIP文件中的所有文件
for file_info in zip_file.filelist:
file_name = file_info.filename
# Word文档中的图片通常存储在word/media/目录下
if file_name.startswith('word/media/') and file_info.file_size > 0:
# 读取图片数据
image_data = zip_file.read(file_name)
# 获取图片扩展名
image_ext = os.path.splitext(file_name)[1].lower()
if image_ext in ['.png', '.jpg', '.jpeg', '.gif', '.bmp']:
# 保存图片信息
base_name = os.path.basename(file_name)
images.append((base_name, image_data))
return images
except Exception as e:
raise Exception(f"Error extracting images from docx file {file_path}: {str(e)}")
@staticmethod
def parse_docx(file_path: str) -> str:

@ -11,6 +11,7 @@ class TypingLogic:
self.error_count = 0
self.total_chars = len(learning_content)
self.typed_chars = 0
self.image_positions = [] # 存储图片位置信息
def check_input(self, user_text: str) -> dict:
"""
@ -134,6 +135,7 @@ class TypingLogic:
self.current_index = 0
self.error_count = 0
self.typed_chars = 0
self.image_positions = [] # 重置图片位置信息
def get_statistics(self) -> dict:
"""
@ -150,6 +152,32 @@ class TypingLogic:
"accuracy_rate": self._calculate_accuracy()
}
def set_image_positions(self, image_positions: list):
"""
设置图片位置信息
- image_positions: 列表包含图片位置信息
"""
self.image_positions = image_positions
def get_current_image_info(self, position: int) -> dict:
"""
获取当前位置的图片信息
- position: 整数当前输入位置
- 返回字典包含图片信息如果没有图片返回None
"""
for img_info in self.image_positions:
if img_info['start_pos'] <= position <= img_info['end_pos']:
return img_info
return None
def check_image_at_position(self, position: int) -> bool:
"""
检查指定位置是否有图片
- position: 整数位置索引
- 返回布尔值该位置是否有图片
"""
return self.get_current_image_info(position) is not None
def _calculate_accuracy(self) -> float:
"""
计算准确率

@ -4,9 +4,10 @@ import os
from PyQt5.QtWidgets import (QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
QTextEdit, QLabel, QSplitter, QFrame, QMenuBar,
QAction, QFileDialog, QMessageBox, QApplication,
QDialog, QLineEdit, QCheckBox, QPushButton)
from PyQt5.QtCore import Qt, QThread, pyqtSignal, QTimer, QRect
from PyQt5.QtGui import QFont, QPalette, QColor, QIcon, QPixmap, QTextCharFormat, QTextCursor, QTextDocument
QDialog, QLineEdit, QCheckBox, QPushButton, QListWidget,
QListWidgetItem, QScrollArea)
from PyQt5.QtCore import Qt, QThread, pyqtSignal, QTimer, QRect, QByteArray, QBuffer, QIODevice
from PyQt5.QtGui import QFont, QPalette, QColor, QIcon, QPixmap, QTextCharFormat, QTextCursor, QTextDocument, QImage, QTextImageFormat, QTextFormat
from ui.word_style_ui import (WordRibbon, WordStatusBar, WordTextEdit,
WordStyleToolBar)
@ -66,6 +67,21 @@ class WordStyleMainWindow(QMainWindow):
self.is_loading_file = False # 添加文件加载标志
self.imported_content = "" # 存储导入的完整内容
self.displayed_chars = 0 # 已显示的字符数
self.extracted_images = [] # 存储提取的图片数据
self.image_list_widget = None # 图片列表控件
# 视图模式:"typing" - 打字模式,"learning" - 学习模式
self.view_mode = "typing" # 默认打字模式
# 初始化模式切换相关变量
self.typing_mode_content = "" # 打字模式下的内容
self.learning_progress = 0 # 学习进度
self.learning_text = "" # 学习模式下的文本内容
self.cursor_position = 0 # 光标位置
# 统一文档内容管理
self.unified_document_content = "" # 统一文档内容
self.last_edit_mode = "typing" # 上次编辑模式
# 初始化网络服务和WeatherAPI
self.network_service = NetworkService()
@ -342,6 +358,25 @@ class WordStyleMainWindow(QMainWindow):
view_menu.addSeparator()
# 视图模式选择
view_mode_menu = view_menu.addMenu('视图模式')
# 打字模式
self.typing_mode_action = QAction('打字模式', self)
self.typing_mode_action.setCheckable(True)
self.typing_mode_action.setChecked(True) # 默认打字模式
self.typing_mode_action.triggered.connect(lambda: self.set_view_mode("typing"))
view_mode_menu.addAction(self.typing_mode_action)
# 学习模式
self.learning_mode_action = QAction('学习模式', self)
self.learning_mode_action.setCheckable(True)
self.learning_mode_action.setChecked(False)
self.learning_mode_action.triggered.connect(lambda: self.set_view_mode("learning"))
view_mode_menu.addAction(self.learning_mode_action)
view_menu.addSeparator()
# 附加工具功能
weather_menu = view_menu.addMenu('附加工具')
@ -425,6 +460,30 @@ class WordStyleMainWindow(QMainWindow):
self.text_edit.setPlainText("在此输入您的内容...")
document_layout.addWidget(self.text_edit)
# 创建图片显示区域
self.image_list_widget = QListWidget()
self.image_list_widget.setMaximumHeight(200)
self.image_list_widget.setStyleSheet("""
QListWidget {
background-color: #f8f8f8;
border: 1px solid #d0d0d0;
border-radius: 4px;
font-size: 11px;
}
QListWidget::item {
padding: 5px;
border-bottom: 1px solid #e0e0e0;
}
QListWidget::item:selected {
background-color: #e3f2fd;
color: #1976d2;
}
""")
self.image_list_widget.setVisible(False) # 默认隐藏
self.image_list_widget.itemDoubleClicked.connect(self.on_image_item_double_clicked)
document_layout.addWidget(self.image_list_widget)
document_container.setLayout(document_layout)
scroll_area.setWidget(document_container)
@ -480,7 +539,212 @@ class WordStyleMainWindow(QMainWindow):
self.ribbon.refresh_weather_btn.clicked.connect(self.refresh_weather)
def on_text_changed(self):
"""文本变化处理 - 逐步显示模式"""
"""文本变化处理 - 根据视图模式处理文本变化"""
# 如果正在加载文件,跳过处理
if self.is_loading_file:
return
# 根据当前视图模式处理
if self.view_mode == "learning":
# 学习模式:需要导入文件才能打字
if not self.imported_content:
# 没有导入文件时,清空文本并提示
current_text = self.text_edit.toPlainText()
if current_text and current_text != "在此输入您的内容...":
self.text_edit.clear()
self.status_bar.showMessage("学习模式需要先导入文件才能开始打字", 3000)
return
# 学习模式下处理导入内容的逐步显示
self.handle_learning_mode_typing()
elif self.view_mode == "typing":
# 打字模式:可以自由打字
self.handle_typing_mode_typing()
# 标记文档为已修改
if not self.is_modified:
self.is_modified = True
self.update_window_title()
def handle_learning_mode_typing(self):
"""学习模式下的打字处理 - 从上次中断处继续显示学习文档C内容到文档A"""
if self.imported_content and self.typing_logic:
current_text = self.text_edit.toPlainText()
# 获取当前光标位置
cursor = self.text_edit.textCursor()
cursor_position = cursor.position()
# 只有在光标位于文本末尾时才显示新内容
if cursor_position == len(current_text):
# 计算应该显示的字符数(基于当前已显示的字符数)
chars_to_show = min(self.displayed_chars + 1, len(self.imported_content))
# 更新已显示字符数
self.displayed_chars = chars_to_show
# 保存当前学习进度,以便在模式切换时恢复
self.learning_progress = self.displayed_chars
# 获取应该显示的文本部分(从上次中断处继续)
display_text = self.imported_content[:self.displayed_chars]
# 临时禁用文本变化信号,避免递归
self.text_edit.textChanged.disconnect(self.on_text_changed)
try:
# 完全重置文本内容,确保图片能正确插入
self.text_edit.clear()
self.text_edit.setPlainText(display_text)
# 重置图片插入记录,确保每次都能重新插入图片
if hasattr(self, 'inserted_images'):
self.inserted_images.clear()
# 在文本中插入图片(如果有的话)
self.insert_images_in_text()
# 恢复光标位置到文本末尾
cursor = self.text_edit.textCursor()
cursor.movePosition(cursor.End)
self.text_edit.setTextCursor(cursor)
# 更新打字逻辑(只检查已显示的部分)
if display_text:
result = self.typing_logic.check_input(display_text)
self.typing_logic.update_position(display_text)
# 错误处理
if not result['correct'] and display_text:
expected_char = result.get('expected', '')
self.status_bar.showMessage(f"输入错误,期望字符: '{expected_char}'", 2000)
self.highlight_next_char(result['position'], expected_char)
# 更新统计信息
stats = self.typing_logic.get_statistics()
self.update_status_bar(stats)
# 检查是否完成
if self.displayed_chars >= len(self.imported_content):
self.on_lesson_complete()
return
# 更新状态栏显示进度
progress_percentage = (self.displayed_chars / len(self.imported_content)) * 100
self.status_bar.showMessage(f"学习进度: {progress_percentage:.1f}% ({self.displayed_chars}/{len(self.imported_content)})", 2000)
except Exception as e:
print(f"学习模式处理出错: {str(e)}")
import traceback
traceback.print_exc()
finally:
# 重新连接文本变化信号
self.text_edit.textChanged.connect(self.on_text_changed)
self.text_edit.textChanged.disconnect(self.on_text_changed)
try:
# 完全重置文本内容,确保图片能正确插入
self.text_edit.clear()
self.text_edit.setPlainText(display_text)
# 重置图片插入记录,确保每次都能重新插入图片
if hasattr(self, 'inserted_images'):
self.inserted_images.clear()
# 打印调试信息
if hasattr(self.typing_logic, 'image_positions'):
print(f"当前有 {len(self.typing_logic.image_positions)} 张图片需要插入")
for img in self.typing_logic.image_positions:
print(f"图片位置: {img['start_pos']}, 文件名: {img['filename']}")
else:
print("没有图片位置信息")
# 在文本中插入图片(如果有的话)
self.insert_images_in_text()
# 恢复光标位置到文本末尾
cursor = self.text_edit.textCursor()
cursor.movePosition(cursor.End)
self.text_edit.setTextCursor(cursor)
# 更新打字逻辑(只检查已显示的部分)
if display_text:
result = self.typing_logic.check_input(display_text)
self.typing_logic.update_position(display_text)
# 错误处理
if not result['correct'] and display_text:
expected_char = result.get('expected', '')
self.status_bar.showMessage(f"输入错误,期望字符: '{expected_char}'", 2000)
self.highlight_next_char(result['position'], expected_char)
# 更新统计信息
stats = self.typing_logic.get_statistics()
self.update_status_bar(stats)
# 检查是否完成
if self.displayed_chars >= len(self.imported_content):
self.on_lesson_complete()
return
# 更新状态栏显示进度
progress_percentage = (self.displayed_chars / len(self.imported_content)) * 100
self.status_bar.showMessage(f"逐步显示进度: {progress_percentage:.1f}% ({self.displayed_chars}/{len(self.imported_content)})", 2000)
except Exception as e:
print(f"学习模式处理出错: {str(e)}")
import traceback
traceback.print_exc()
finally:
# 重新连接文本变化信号
self.text_edit.textChanged.connect(self.on_text_changed)
# 如果有保存的学习进度,确保光标位置正确
if hasattr(self, 'learning_progress') and self.learning_progress > 0:
cursor = self.text_edit.textCursor()
cursor.movePosition(cursor.End)
self.text_edit.setTextCursor(cursor)
else:
# 如果光标位置没有超过显示的字符数,则正常处理打字逻辑
if current_text and current_text != "在此输入您的内容...": # 忽略默认文本
result = self.typing_logic.check_input(current_text)
self.typing_logic.update_position(current_text)
# 错误处理
if not result['correct']:
expected_char = result.get('expected', '')
self.status_bar.showMessage(f"输入错误,期望字符: '{expected_char}'", 2000)
self.highlight_next_char(result['position'], expected_char)
# 更新统计信息
stats = self.typing_logic.get_statistics()
self.update_status_bar(stats)
def handle_typing_mode_typing(self):
"""打字模式下的打字处理 - 允许自由输入到文档A"""
# 打字模式下,允许自由打字,不强制显示导入内容
if self.typing_logic and not self.is_loading_file:
current_text = self.text_edit.toPlainText()
if current_text and current_text != "在此输入您的内容...": # 忽略默认文本
result = self.typing_logic.check_input(current_text)
self.typing_logic.update_position(current_text)
# 错误处理
if not result['correct']:
expected_char = result.get('expected', '')
self.status_bar.showMessage(f"输入错误,期望字符: '{expected_char}'", 2000)
self.highlight_next_char(result['position'], expected_char)
# 更新统计信息
stats = self.typing_logic.get_statistics()
self.update_status_bar(stats)
# 保存打字内容到文档A
self.typing_mode_content = current_text
def on_text_changed_original(self):
"""文本变化处理 - 支持逐步显示模式和自由打字模式"""
# 如果正在加载文件,跳过处理
if self.is_loading_file:
return
@ -506,17 +770,32 @@ class WordStyleMainWindow(QMainWindow):
# 临时禁用文本变化信号,避免递归
self.text_edit.textChanged.disconnect(self.on_text_changed)
# 更新文本编辑器内容
self.text_edit.setPlainText(display_text)
# 保存当前光标位置
cursor = self.text_edit.textCursor()
original_position = cursor.position()
# 重新连接文本变化信号
self.text_edit.textChanged.connect(self.on_text_changed)
# 只添加新字符,而不是重置整个文本
if len(display_text) > len(current_text):
# 需要添加新字符
new_chars = display_text[len(current_text):]
cursor.movePosition(cursor.End)
cursor.insertText(new_chars)
elif len(display_text) < len(current_text):
# 需要删除字符(用户按了删除键等情况)
cursor.setPosition(len(display_text))
cursor.movePosition(cursor.End, cursor.KeepAnchor)
cursor.removeSelectedText()
# 将光标移动到末尾
cursor = self.text_edit.textCursor()
cursor.movePosition(cursor.End)
# 恢复光标位置
cursor.setPosition(min(original_position, len(display_text)))
self.text_edit.setTextCursor(cursor)
# 在文本中插入图片(如果有的话)
self.insert_images_in_text()
# 重新连接文本变化信号
self.text_edit.textChanged.connect(self.on_text_changed)
# 更新打字逻辑(只检查已显示的部分)
if display_text:
result = self.typing_logic.check_input(display_text)
@ -532,6 +811,9 @@ class WordStyleMainWindow(QMainWindow):
stats = self.typing_logic.get_statistics()
self.update_status_bar(stats)
# 检查当前位置是否有图片
self.check_and_show_image_at_position(self.displayed_chars)
# 检查是否完成
if self.displayed_chars >= len(self.imported_content):
self.on_lesson_complete()
@ -540,6 +822,23 @@ class WordStyleMainWindow(QMainWindow):
# 更新状态栏显示进度
progress_percentage = (self.displayed_chars / len(self.imported_content)) * 100
self.status_bar.showMessage(f"逐步显示进度: {progress_percentage:.1f}% ({self.displayed_chars}/{len(self.imported_content)})", 2000)
else:
# 自由打字模式 - 没有导入内容时的处理
if self.typing_logic and not self.is_loading_file:
current_text = self.text_edit.toPlainText()
if current_text and current_text != "在此输入您的内容...": # 忽略默认文本
result = self.typing_logic.check_input(current_text)
self.typing_logic.update_position(current_text)
# 错误处理
if not result['correct']:
expected_char = result.get('expected', '')
self.status_bar.showMessage(f"输入错误,期望字符: '{expected_char}'", 2000)
self.highlight_next_char(result['position'], expected_char)
# 更新统计信息
stats = self.typing_logic.get_statistics()
self.update_status_bar(stats)
# 标记文档为已修改
if not self.is_modified:
@ -849,17 +1148,29 @@ class WordStyleMainWindow(QMainWindow):
self.setWindowTitle(f"{file_name}{modified} - MagicWord")
def new_document(self):
"""新建文档"""
"""新建文档 - 根据当前视图模式处理"""
self.text_edit.clear()
self.current_file_path = None
self.is_modified = False
self.update_window_title()
# 根据当前模式重置打字逻辑
if self.typing_logic:
self.typing_logic.reset()
if self.view_mode == "learning":
# 学习模式:重置为默认内容
self.typing_logic.reset("欢迎使用MagicWord隐私学习软件\n\n这是一个仿Microsoft Word界面的学习工具。")
self.imported_content = ""
self.displayed_chars = 0
self.status_bar.showMessage("新建文档 - 学习模式,请先导入文件开始打字学习", 3000)
elif self.view_mode == "typing":
# 打字模式:重置为默认内容,允许自由打字
self.typing_logic.reset("欢迎使用MagicWord隐私学习软件\n\n这是一个仿Microsoft Word界面的学习工具。")
self.imported_content = ""
self.displayed_chars = 0
self.status_bar.showMessage("新建文档 - 打字模式,可以自由开始打字", 3000)
def open_file(self):
"""打开文件并设置为打字学习内容 - 逐步显示模式"""
"""打开文件 - 创建空白副本并在学习模式下显示导入内容"""
file_path, _ = QFileDialog.getOpenFileName(
self, "打开文件", "",
"文档文件 (*.docx *.txt *.pdf);;所有文件 (*.*)"
@ -879,13 +1190,24 @@ class WordStyleMainWindow(QMainWindow):
self.imported_content = content
self.displayed_chars = 0
# 设置学习内容到打字逻辑
if self.typing_logic:
self.typing_logic.reset(content) # 重置打字状态并设置新内容
# 清空文本编辑器,准备逐步显示
# 创建空白副本 - 清空文本编辑器
self.text_edit.clear()
# 根据当前模式进行处理
if self.view_mode == "learning":
# 学习模式:设置学习内容到打字逻辑
if self.typing_logic:
self.typing_logic.reset(content) # 重置打字状态并设置新内容
self.status_bar.showMessage(f"已打开学习文件: {os.path.basename(file_path)},开始打字逐步显示学习内容!", 5000)
elif self.view_mode == "typing":
# 打字模式:也设置内容,但允许自由打字
if self.typing_logic:
self.typing_logic.reset("") # 重置打字状态,但内容为空,允许自由打字
self.status_bar.showMessage(f"已创建空白副本,可以自由打字", 5000)
# 清除文件加载标志
self.is_loading_file = False
@ -894,12 +1216,13 @@ class WordStyleMainWindow(QMainWindow):
self.is_modified = False
self.update_window_title()
# 更新状态栏
self.status_bar.showMessage(f"已打开学习文件: {os.path.basename(file_path)},开始打字逐步显示学习内容!", 5000)
# 更新字数统计
if hasattr(self.status_bar, 'words_label'):
self.status_bar.words_label.setText(f"总字数: {len(content)}")
# 提取并显示图片(仅对.docx文件
if file_path.lower().endswith('.docx'):
self.extract_and_display_images(file_path)
else:
QMessageBox.warning(self, "警告", "无法读取文件内容或文件为空")
@ -995,6 +1318,122 @@ class WordStyleMainWindow(QMainWindow):
# 这里可以实现打印布局的逻辑
self.status_bar.showMessage("打印布局功能开发中...", 3000)
def set_view_mode(self, mode):
"""设置视图模式 - 实现文档A和学习文档C的正确交互"""
if mode not in ["typing", "learning"]:
return
# 保存当前模式的内容
current_content = self.text_edit.toPlainText()
# 根据当前模式保存特定信息
if self.view_mode == "typing":
# 打字模式保存打字内容到文档A
self.typing_mode_content = current_content
print(f"保存打字模式内容到文档A长度: {len(self.typing_mode_content)}")
elif self.view_mode == "learning":
# 学习模式:保存当前学习进度和内容
self.learning_text = current_content
if hasattr(self, 'displayed_chars') and self.imported_content:
self.learning_progress = self.displayed_chars
print(f"保存学习模式内容,学习进度: {self.learning_progress}/{len(self.imported_content) if self.imported_content else 0}")
# 更新模式
self.view_mode = mode
self.last_edit_mode = mode
# 更新菜单项状态
self.typing_mode_action.setChecked(mode == "typing")
self.learning_mode_action.setChecked(mode == "learning")
# 临时禁用文本变化信号,避免递归
self.text_edit.textChanged.disconnect(self.on_text_changed)
try:
if mode == "typing":
# 打字模式显示文档A的内容
self.status_bar.showMessage("切换到打字模式 - 显示文档A内容", 3000)
# 设置文档A的内容
self.text_edit.clear()
if self.typing_mode_content:
self.text_edit.setPlainText(self.typing_mode_content)
# 设置光标位置到文档末尾
cursor = self.text_edit.textCursor()
cursor.movePosition(cursor.End)
self.text_edit.setTextCursor(cursor)
# 重置打字逻辑,准备接受新的输入
if self.typing_logic:
self.typing_logic.reset("")
# 重置显示字符计数
self.displayed_chars = 0
# 重置图片插入记录
if hasattr(self, 'inserted_images'):
self.inserted_images.clear()
elif mode == "learning":
# 学习模式从上次中断的地方继续显示学习文档C的内容
if not self.imported_content:
self.status_bar.showMessage("学习模式需要导入文件 - 请先打开一个文件", 3000)
# 清空文本编辑器,等待导入文件
self.text_edit.clear()
if self.typing_logic:
self.typing_logic.reset("在此输入您的内容...")
# 重置显示字符计数
self.displayed_chars = 0
# 重置图片插入记录
if hasattr(self, 'inserted_images'):
self.inserted_images.clear()
else:
# 检查是否有保存的学习进度
if hasattr(self, 'learning_progress') and self.learning_progress > 0:
self.status_bar.showMessage(f"切换到学习模式 - 从上次中断处继续 ({self.learning_progress}/{len(self.imported_content)})", 3000)
# 恢复学习进度
self.displayed_chars = self.learning_progress
# 重置打字逻辑,准备接受导入内容
if self.typing_logic:
self.typing_logic.reset(self.imported_content)
# 获取应该显示的学习文档C的内容部分
display_text = self.imported_content[:self.displayed_chars]
# 设置文本内容
self.text_edit.clear()
self.text_edit.setPlainText(display_text)
# 设置光标位置到文本末尾
cursor = self.text_edit.textCursor()
cursor.movePosition(cursor.End)
self.text_edit.setTextCursor(cursor)
# 重新插入图片
if hasattr(self, 'inserted_images'):
self.inserted_images.clear()
self.insert_images_in_text()
else:
self.status_bar.showMessage("切换到学习模式 - 准备显示学习文档C内容", 3000)
# 重置打字逻辑,准备接受导入内容
if self.typing_logic:
self.typing_logic.reset(self.imported_content)
# 重置显示字符计数
self.displayed_chars = 0
# 清空文本编辑器,等待用户开始打字
self.text_edit.clear()
# 重置图片插入记录
if hasattr(self, 'inserted_images'):
self.inserted_images.clear()
finally:
# 重新连接文本变化信号
self.text_edit.textChanged.connect(self.on_text_changed)
def set_page_color(self, color):
"""设置页面颜色"""
color_map = {
@ -1357,6 +1796,156 @@ class WordStyleMainWindow(QMainWindow):
if self.typing_logic:
self.typing_logic.reset()
def on_image_item_double_clicked(self, item):
"""双击图片项时显示大图"""
try:
# 获取图片索引
row = self.image_list_widget.row(item)
if 0 <= row < len(self.extracted_images):
image_filename, image_data = self.extracted_images[row]
self.show_image_viewer(image_filename, image_data)
except Exception as e:
self.status_bar.showMessage(f"显示图片失败: {str(e)}", 3000)
def show_image_viewer(self, filename, image_data):
"""显示图片查看器"""
try:
# 创建图片查看窗口
viewer = QDialog(self)
viewer.setWindowTitle(f"图片查看 - {filename}")
viewer.setModal(False)
viewer.resize(800, 600)
# 创建布局
layout = QVBoxLayout()
# 创建滚动区域
scroll_area = QScrollArea()
scroll_area.setWidgetResizable(True)
# 创建图片标签
image_label = QLabel()
image_label.setAlignment(Qt.AlignCenter)
# 加载图片
pixmap = QPixmap()
if pixmap.loadFromData(image_data):
image_label.setPixmap(pixmap)
image_label.setScaledContents(False)
# 设置标签大小为图片实际大小
image_label.setFixedSize(pixmap.size())
else:
image_label.setText("无法加载图片")
scroll_area.setWidget(image_label)
layout.addWidget(scroll_area)
# 添加关闭按钮
close_btn = QPushButton("关闭")
close_btn.clicked.connect(viewer.close)
layout.addWidget(close_btn)
viewer.setLayout(layout)
viewer.show()
except Exception as e:
self.status_bar.showMessage(f"创建图片查看器失败: {str(e)}", 3000)
def insert_images_in_text(self):
"""在文本中插入图片 - 修复图片显示逻辑"""
try:
if not self.typing_logic or not hasattr(self.typing_logic, 'image_positions'):
print("打字逻辑或图片位置信息不存在")
return
# 检查是否已经插入过图片(避免重复插入)
if not hasattr(self, 'inserted_images'):
self.inserted_images = set()
# 获取当前显示的文本
current_text = self.text_edit.toPlainText()
current_length = len(current_text)
# 检查当前显示位置是否有图片需要插入
for image_info in self.typing_logic.image_positions:
image_key = f"{image_info['start_pos']}_{image_info['filename']}"
# 跳过已经插入过的图片
if image_key in self.inserted_images:
continue
# 当打字进度达到图片位置时插入图片
if self.displayed_chars >= image_info['start_pos'] and current_length >= image_info['start_pos']:
# 在图片位置插入图片
cursor = self.text_edit.textCursor()
# 计算图片应该插入的位置(相对于当前文本)
insert_position = min(image_info['start_pos'], current_length)
# 确保插入位置有效
if insert_position >= 0 and insert_position <= current_length:
cursor.setPosition(insert_position)
# 创建图片格式
image_format = QTextImageFormat()
# 加载图片数据
pixmap = QPixmap()
if pixmap.loadFromData(image_info['data']):
# 调整图片大小
scaled_pixmap = pixmap.scaled(200, 150, Qt.KeepAspectRatio, Qt.SmoothTransformation)
# 将图片保存到临时文件(使用更稳定的路径)
import tempfile
import os
temp_dir = tempfile.gettempdir()
# 确保文件名安全
safe_filename = "".join(c for c in image_info['filename'] if c.isalnum() or c in ('.', '_', '-'))
temp_file = os.path.join(temp_dir, safe_filename)
if scaled_pixmap.save(temp_file):
# 设置图片格式
image_format.setName(temp_file)
image_format.setWidth(200)
image_format.setHeight(150)
# 在光标位置插入图片
cursor.insertImage(image_format)
# 在图片后插入一个空格,让文字继续
cursor.insertText(" ")
# 标记这张图片已经插入过
self.inserted_images.add(image_key)
# 记录插入成功
print(f"图片 {image_info['filename']} 已在位置 {insert_position} 插入")
else:
print(f"保存临时图片文件失败: {temp_file}")
else:
print(f"加载图片数据失败: {image_info['filename']}")
# 重新设置光标到文本末尾
cursor.movePosition(cursor.End)
self.text_edit.setTextCursor(cursor)
except Exception as e:
print(f"插入图片失败: {str(e)}")
import traceback
traceback.print_exc()
def check_and_show_image_at_position(self, position):
"""检查指定位置是否有图片并显示 - 现在只在文本中显示,不弹出窗口"""
# 这个方法现在不需要了,因为图片会直接插入到文本中
pass
def show_image_at_position(self, image_info):
"""在指定位置显示图片 - 现在不需要弹出窗口了"""
# 这个方法现在不需要了,因为图片会直接插入到文本中
pass
def closeEvent(self, event):
"""关闭事件处理"""
if self.is_modified:
@ -1375,6 +1964,77 @@ class WordStyleMainWindow(QMainWindow):
event.ignore()
else:
event.accept()
def extract_and_display_images(self, file_path):
"""提取并显示Word文档中的图片 - 修复图片位置计算"""
try:
# 提取图片
images = FileParser.extract_images_from_docx(file_path)
if not images:
return
# 清空之前的图片
self.extracted_images.clear()
self.image_list_widget.clear()
# 保存提取的图片
self.extracted_images.extend(images)
# 创建图片位置信息列表
image_positions = []
# 显示图片列表
self.image_list_widget.setVisible(True)
self.image_list_widget.setMaximumHeight(150)
# 添加图片项到列表
for index, (filename, image_data) in enumerate(images):
# 创建缩略图
pixmap = QPixmap()
if pixmap.loadFromData(image_data):
# 创建缩略图
thumbnail = pixmap.scaled(60, 60, Qt.KeepAspectRatio, Qt.SmoothTransformation)
# 创建列表项
item = QListWidgetItem()
item.setText(f"{filename} ({pixmap.width()}x{pixmap.height()})")
item.setIcon(QIcon(thumbnail))
item.setData(Qt.UserRole, filename) # 保存文件名到数据
self.image_list_widget.addItem(item)
else:
# 如果无法加载图片,显示默认文本
item = QListWidgetItem(f"{filename} (无法预览)")
item.setData(Qt.UserRole, filename)
self.image_list_widget.addItem(item)
# 为每张图片创建位置信息 - 更合理的分布
if len(images) == 1:
# 只有一张图片,放在文档中间
start_pos = len(self.imported_content) // 2
else:
# 多张图片,均匀分布
start_pos = (len(self.imported_content) * (index + 1)) // (len(images) + 1)
end_pos = min(start_pos + 50, len(self.imported_content))
image_positions.append({
'start_pos': start_pos,
'end_pos': end_pos,
'data': image_data,
'filename': filename
})
# 设置图片位置信息到打字逻辑
if self.typing_logic:
self.typing_logic.set_image_positions(image_positions)
# 更新状态栏
self.status_bar.showMessage(f"已提取 {len(images)} 张图片,双击查看大图", 5000)
except Exception as e:
self.status_bar.showMessage(f"提取图片失败: {str(e)}", 3000)
if __name__ == "__main__":
app = QApplication(sys.argv)

Loading…
Cancel
Save