|
|
|
|
@ -4,21 +4,25 @@ import os
|
|
|
|
|
from PyQt5.QtWidgets import (QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
|
|
|
|
|
QTextEdit, QLabel, QFrame, QMenuBar,
|
|
|
|
|
QAction, QFileDialog, QMessageBox, QApplication,
|
|
|
|
|
QSplitter, QScrollArea, QStatusBar, QProgressBar)
|
|
|
|
|
QSplitter, QScrollArea, QStatusBar, QProgressBar, QTextBrowser, QSizePolicy,
|
|
|
|
|
QListWidget, QListWidgetItem, QDialog, QGraphicsScene, QGraphicsView)
|
|
|
|
|
from PyQt5.QtCore import Qt, QThread, pyqtSignal, QTimer, QRect
|
|
|
|
|
from PyQt5.QtGui import QFont, QPalette, QColor, QIcon, QPixmap, QTextCharFormat, QTextCursor
|
|
|
|
|
|
|
|
|
|
from src.ui.components import CustomTitleBar, TextDisplayWidget
|
|
|
|
|
from src.typing_logic import TypingLogic
|
|
|
|
|
from src.file_parser import FileParser
|
|
|
|
|
from src.ui.theme_manager import theme_manager
|
|
|
|
|
# 修复导入路径
|
|
|
|
|
from ui.components import CustomTitleBar, TextDisplayWidget
|
|
|
|
|
from typing_logic import TypingLogic
|
|
|
|
|
from file_parser import FileParser
|
|
|
|
|
from ui.theme_manager import theme_manager
|
|
|
|
|
import tempfile
|
|
|
|
|
import hashlib
|
|
|
|
|
|
|
|
|
|
class LearningModeWindow(QMainWindow):
|
|
|
|
|
# 定义内容变化信号
|
|
|
|
|
content_changed = pyqtSignal(str, int) # 参数:内容,位置
|
|
|
|
|
# 定义关闭信号
|
|
|
|
|
closed = pyqtSignal()
|
|
|
|
|
def __init__(self, parent=None, imported_content="", current_position=0):
|
|
|
|
|
def __init__(self, parent=None, imported_content="", current_position=0, image_data=None, image_positions=None):
|
|
|
|
|
"""
|
|
|
|
|
学习模式窗口
|
|
|
|
|
- 顶部显示UI.png图片
|
|
|
|
|
@ -29,13 +33,18 @@ class LearningModeWindow(QMainWindow):
|
|
|
|
|
parent: 父窗口
|
|
|
|
|
imported_content: 从主窗口传递的导入内容
|
|
|
|
|
current_position: 当前学习进度位置
|
|
|
|
|
image_data: 图片数据字典 {文件名: 二进制数据}
|
|
|
|
|
image_positions: 图片位置信息列表
|
|
|
|
|
"""
|
|
|
|
|
super().__init__(parent)
|
|
|
|
|
self.parent_window = parent
|
|
|
|
|
self.imported_content = imported_content
|
|
|
|
|
self.current_position = current_position
|
|
|
|
|
self.image_data = image_data or {}
|
|
|
|
|
self.image_positions = image_positions or []
|
|
|
|
|
self.typing_logic = None
|
|
|
|
|
self.is_loading_file = False
|
|
|
|
|
self.extracted_images = [] # 用于存储提取的图片数据
|
|
|
|
|
|
|
|
|
|
# 初始化UI
|
|
|
|
|
self.initUI()
|
|
|
|
|
@ -60,6 +69,12 @@ class LearningModeWindow(QMainWindow):
|
|
|
|
|
# 重置打字逻辑
|
|
|
|
|
if self.typing_logic:
|
|
|
|
|
self.typing_logic.reset(self.imported_content)
|
|
|
|
|
|
|
|
|
|
# 设置图片数据到打字逻辑
|
|
|
|
|
if self.image_data:
|
|
|
|
|
self.typing_logic.set_image_data(self.image_data)
|
|
|
|
|
if self.image_positions:
|
|
|
|
|
self.typing_logic.set_image_positions(self.image_positions)
|
|
|
|
|
|
|
|
|
|
# 显示已学习的内容
|
|
|
|
|
display_text = self.imported_content[:self.current_position]
|
|
|
|
|
@ -144,38 +159,48 @@ class LearningModeWindow(QMainWindow):
|
|
|
|
|
ui_image_path = os.path.join(os.path.dirname(__file__), 'ui', 'UI.png')
|
|
|
|
|
if os.path.exists(ui_image_path):
|
|
|
|
|
pixmap = QPixmap(ui_image_path)
|
|
|
|
|
|
|
|
|
|
# 保存原始图片尺寸
|
|
|
|
|
self.original_pixmap = pixmap
|
|
|
|
|
|
|
|
|
|
# 设置图片完全铺满标签
|
|
|
|
|
self.image_label.setPixmap(pixmap)
|
|
|
|
|
self.image_label.setScaledContents(True) # 关键:让图片缩放填充整个标签
|
|
|
|
|
|
|
|
|
|
# 设置图片标签的尺寸策略,使其可以扩展
|
|
|
|
|
from PyQt5.QtWidgets import QSizePolicy
|
|
|
|
|
self.image_label.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
|
|
|
|
|
|
|
|
|
|
# 设置图片区域的最小高度为图片高度的1/3,确保图片可见
|
|
|
|
|
min_height = max(200, pixmap.height() // 3)
|
|
|
|
|
self.image_label.setMinimumHeight(min_height)
|
|
|
|
|
|
|
|
|
|
# 重新设置窗口大小以适配图片
|
|
|
|
|
self.resize(pixmap.width(), self.height())
|
|
|
|
|
if not pixmap.isNull():
|
|
|
|
|
# 保存原始图片用于缩放计算
|
|
|
|
|
self.original_pixmap = pixmap
|
|
|
|
|
|
|
|
|
|
# 设置图片到标签
|
|
|
|
|
self.image_label.setPixmap(pixmap)
|
|
|
|
|
self.image_label.setScaledContents(True) # 关键:让图片缩放填充整个标签
|
|
|
|
|
|
|
|
|
|
# 设置大小策略
|
|
|
|
|
self.image_label.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
|
|
|
|
|
|
|
|
|
|
# 计算合适的最小高度(保持图片比例)
|
|
|
|
|
window_width = 900 # 默认窗口宽度
|
|
|
|
|
original_width = pixmap.width()
|
|
|
|
|
original_height = pixmap.height()
|
|
|
|
|
if original_width > 0:
|
|
|
|
|
min_height = int(window_width * original_height / original_width)
|
|
|
|
|
self.image_label.setMinimumHeight(min_height)
|
|
|
|
|
else:
|
|
|
|
|
self.image_label.setMinimumHeight(200)
|
|
|
|
|
else:
|
|
|
|
|
self.image_label.setText("UI图片加载失败")
|
|
|
|
|
self.image_label.setStyleSheet("""
|
|
|
|
|
QLabel {
|
|
|
|
|
background-color: #f8f9fa;
|
|
|
|
|
color: #666666;
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
qproperty-alignment: AlignCenter;
|
|
|
|
|
}
|
|
|
|
|
""")
|
|
|
|
|
else:
|
|
|
|
|
self.image_label.setText("UI图片未找到")
|
|
|
|
|
self.image_label.setStyleSheet("""
|
|
|
|
|
QLabel {
|
|
|
|
|
background-color: #f8f9fa;
|
|
|
|
|
border: none;
|
|
|
|
|
color: #666666;
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
padding: 20px;
|
|
|
|
|
qproperty-alignment: AlignCenter;
|
|
|
|
|
}
|
|
|
|
|
""")
|
|
|
|
|
self.image_label.setMinimumHeight(200)
|
|
|
|
|
|
|
|
|
|
# 直接添加图片标签到主布局,不使用滚动区域
|
|
|
|
|
# 添加到主布局
|
|
|
|
|
main_layout.addWidget(self.image_label)
|
|
|
|
|
|
|
|
|
|
def resizeEvent(self, event):
|
|
|
|
|
@ -210,6 +235,7 @@ class LearningModeWindow(QMainWindow):
|
|
|
|
|
创建输入区域
|
|
|
|
|
- 创建文本显示组件
|
|
|
|
|
- 设置与主系统相同的样式
|
|
|
|
|
- 创建图片列表区域
|
|
|
|
|
"""
|
|
|
|
|
# 创建文本显示组件(复用主系统的组件)
|
|
|
|
|
self.text_display_widget = TextDisplayWidget(self)
|
|
|
|
|
@ -226,7 +252,40 @@ class LearningModeWindow(QMainWindow):
|
|
|
|
|
# 连接文本变化信号
|
|
|
|
|
self.text_display_widget.text_display.textChanged.connect(self.on_text_changed)
|
|
|
|
|
|
|
|
|
|
main_layout.addWidget(self.text_display_widget, 1) # 占据剩余空间
|
|
|
|
|
# 创建图片显示区域
|
|
|
|
|
self.image_list_widget = QListWidget()
|
|
|
|
|
self.image_list_widget.setMaximumHeight(150)
|
|
|
|
|
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)
|
|
|
|
|
|
|
|
|
|
# 创建布局容器
|
|
|
|
|
input_container = QWidget()
|
|
|
|
|
input_layout = QVBoxLayout()
|
|
|
|
|
input_layout.setContentsMargins(0, 0, 0, 0)
|
|
|
|
|
input_layout.setSpacing(5)
|
|
|
|
|
|
|
|
|
|
input_layout.addWidget(self.text_display_widget, 1) # 文本显示区域占据剩余空间
|
|
|
|
|
input_layout.addWidget(self.image_list_widget) # 图片列表区域
|
|
|
|
|
|
|
|
|
|
input_container.setLayout(input_layout)
|
|
|
|
|
|
|
|
|
|
main_layout.addWidget(input_container, 1) # 占据剩余空间
|
|
|
|
|
|
|
|
|
|
def create_menu_bar(self):
|
|
|
|
|
"""
|
|
|
|
|
@ -301,38 +360,93 @@ class LearningModeWindow(QMainWindow):
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
if file_path:
|
|
|
|
|
self.is_loading_file = True
|
|
|
|
|
try:
|
|
|
|
|
self.is_loading_file = True
|
|
|
|
|
|
|
|
|
|
# 使用文件解析器
|
|
|
|
|
parser = FileParser()
|
|
|
|
|
content = parser.parse_file(file_path)
|
|
|
|
|
# 获取文件扩展名
|
|
|
|
|
_, ext = os.path.splitext(file_path)
|
|
|
|
|
ext = ext.lower()
|
|
|
|
|
|
|
|
|
|
if content:
|
|
|
|
|
# 存储导入的内容
|
|
|
|
|
self.imported_content = content
|
|
|
|
|
self.current_position = 0
|
|
|
|
|
|
|
|
|
|
# 重置打字逻辑
|
|
|
|
|
if self.typing_logic:
|
|
|
|
|
self.typing_logic.reset(content)
|
|
|
|
|
|
|
|
|
|
# 清空文本显示
|
|
|
|
|
self.text_display_widget.text_display.clear()
|
|
|
|
|
|
|
|
|
|
# 更新状态
|
|
|
|
|
self.status_label.setText(f"已导入: {os.path.basename(file_path)}")
|
|
|
|
|
self.progress_label.setText(f"进度: 0% (0/{len(content)} 字符)")
|
|
|
|
|
|
|
|
|
|
# 显示成功消息
|
|
|
|
|
QMessageBox.information(self, "导入成功",
|
|
|
|
|
f"文件导入成功!\n文件: {os.path.basename(file_path)}\n字符数: {len(content)}\n\n开始打字以显示学习内容。")
|
|
|
|
|
|
|
|
|
|
# 对于docx文件,直接解析而不转换为txt
|
|
|
|
|
if ext == '.docx':
|
|
|
|
|
# 直接解析docx文件内容
|
|
|
|
|
content = FileParser.parse_docx(file_path)
|
|
|
|
|
# 提取图片数据
|
|
|
|
|
images = FileParser.extract_images_from_docx(file_path)
|
|
|
|
|
else:
|
|
|
|
|
QMessageBox.warning(self, "导入失败", "无法解析文件内容,请检查文件格式。")
|
|
|
|
|
# 其他文件类型使用原来的转换方法
|
|
|
|
|
result = FileParser.parse_and_convert_to_txt(file_path)
|
|
|
|
|
content = result['content']
|
|
|
|
|
images = result.get('images', [])
|
|
|
|
|
|
|
|
|
|
if not content:
|
|
|
|
|
QMessageBox.warning(self, "导入失败", "文件内容为空或解析失败!")
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
# 保存导入的内容
|
|
|
|
|
self.imported_content = content
|
|
|
|
|
self.current_position = 0
|
|
|
|
|
|
|
|
|
|
# 保存提取的图片数据
|
|
|
|
|
self.extracted_images = images
|
|
|
|
|
|
|
|
|
|
# 设置打字逻辑
|
|
|
|
|
if self.typing_logic:
|
|
|
|
|
self.typing_logic.reset(content)
|
|
|
|
|
|
|
|
|
|
# 如果有图片,设置图片数据到打字逻辑
|
|
|
|
|
if images:
|
|
|
|
|
image_data_dict = {}
|
|
|
|
|
image_positions = []
|
|
|
|
|
|
|
|
|
|
# 为每张图片生成位置信息 - 改进位置计算逻辑
|
|
|
|
|
for i, (filename, image_data) in enumerate(images):
|
|
|
|
|
image_data_dict[filename] = image_data
|
|
|
|
|
|
|
|
|
|
# 改进图片位置计算,确保图片能在用户早期打字时显示
|
|
|
|
|
content_length = len(content)
|
|
|
|
|
if content_length == 0:
|
|
|
|
|
content_length = 1000 # 备用长度
|
|
|
|
|
|
|
|
|
|
if len(images) == 1:
|
|
|
|
|
# 只有一张图片,放在文档开始位置附近(前10%),确保用户能快速看到
|
|
|
|
|
image_pos = max(10, content_length // 10)
|
|
|
|
|
else:
|
|
|
|
|
# 多张图片:前几张放在较前位置,确保用户能看到
|
|
|
|
|
if i < 3:
|
|
|
|
|
# 前3张图片放在文档前30%
|
|
|
|
|
segment = content_length // 3
|
|
|
|
|
image_pos = max(10, segment * (i + 1) // 4)
|
|
|
|
|
else:
|
|
|
|
|
# 其余图片均匀分布
|
|
|
|
|
remaining_start = content_length // 2
|
|
|
|
|
remaining_index = i - 3
|
|
|
|
|
remaining_count = len(images) - 3
|
|
|
|
|
if remaining_count > 0:
|
|
|
|
|
segment = (content_length - remaining_start) // (remaining_count + 1)
|
|
|
|
|
image_pos = remaining_start + segment * (remaining_index + 1)
|
|
|
|
|
else:
|
|
|
|
|
image_pos = content_length // 2
|
|
|
|
|
|
|
|
|
|
image_positions.append({
|
|
|
|
|
'start_pos': image_pos,
|
|
|
|
|
'end_pos': image_pos + 50, # 图片占位符长度
|
|
|
|
|
'filename': filename
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
# 设置图片数据到打字逻辑
|
|
|
|
|
self.typing_logic.set_image_data(image_data_dict)
|
|
|
|
|
self.typing_logic.set_image_positions(image_positions)
|
|
|
|
|
|
|
|
|
|
# 显示图片列表
|
|
|
|
|
self.display_image_list(images)
|
|
|
|
|
|
|
|
|
|
# 显示初始内容(空)
|
|
|
|
|
self.text_display_widget.text_display.clear()
|
|
|
|
|
self.status_label.setText("已导入文件,请开始打字学习...")
|
|
|
|
|
self.progress_label.setText("进度: 0%")
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
QMessageBox.critical(self, "导入错误", f"导入文件时出错:\n{str(e)}")
|
|
|
|
|
QMessageBox.critical(self, "导入错误", f"导入文件时发生错误:\n{str(e)}")
|
|
|
|
|
|
|
|
|
|
finally:
|
|
|
|
|
self.is_loading_file = False
|
|
|
|
|
@ -343,6 +457,7 @@ class LearningModeWindow(QMainWindow):
|
|
|
|
|
- 根据导入的内容逐步显示
|
|
|
|
|
- 更新学习进度
|
|
|
|
|
- 同步内容到打字模式
|
|
|
|
|
- 处理图片插入
|
|
|
|
|
"""
|
|
|
|
|
# 如果正在加载文件,跳过处理
|
|
|
|
|
if self.is_loading_file:
|
|
|
|
|
@ -398,6 +513,8 @@ class LearningModeWindow(QMainWindow):
|
|
|
|
|
f"进度: {progress:.1f}% ({self.current_position}/{len(self.imported_content)} 字符)"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 只在用户新输入的字符上同步到打字模式
|
|
|
|
|
if self.parent_window and hasattr(self.parent_window, 'text_edit'):
|
|
|
|
|
# 获取用户这一轮新输入的字符(与上一轮相比的新内容)
|
|
|
|
|
@ -414,6 +531,8 @@ class LearningModeWindow(QMainWindow):
|
|
|
|
|
else:
|
|
|
|
|
self.status_label.setText("继续输入以显示更多内容...")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def show_about(self):
|
|
|
|
|
"""
|
|
|
|
|
显示关于对话框
|
|
|
|
|
@ -424,7 +543,8 @@ class LearningModeWindow(QMainWindow):
|
|
|
|
|
"• 顶部显示UI界面图片\n"
|
|
|
|
|
"• 下方为打字输入区域\n"
|
|
|
|
|
"• 导入文件后逐步显示内容\n"
|
|
|
|
|
"• 实时显示学习进度\n\n"
|
|
|
|
|
"• 实时显示学习进度\n"
|
|
|
|
|
"• 支持图片显示\n\n"
|
|
|
|
|
"使用方法:\n"
|
|
|
|
|
"1. 点击'文件'->'导入文件'选择学习材料\n"
|
|
|
|
|
"2. 在下方文本区域开始打字\n"
|
|
|
|
|
@ -452,4 +572,152 @@ class LearningModeWindow(QMainWindow):
|
|
|
|
|
if event.key() == Qt.Key_Escape:
|
|
|
|
|
self.close()
|
|
|
|
|
else:
|
|
|
|
|
super().keyPressEvent(event)
|
|
|
|
|
super().keyPressEvent(event)
|
|
|
|
|
|
|
|
|
|
def display_image_list(self, images):
|
|
|
|
|
"""
|
|
|
|
|
显示图片列表
|
|
|
|
|
"""
|
|
|
|
|
try:
|
|
|
|
|
# 清空之前的图片列表
|
|
|
|
|
self.image_list_widget.clear()
|
|
|
|
|
|
|
|
|
|
# 如果没有图片,隐藏图片列表区域
|
|
|
|
|
if not images:
|
|
|
|
|
self.image_list_widget.setVisible(False)
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
# 显示图片列表区域
|
|
|
|
|
self.image_list_widget.setVisible(True)
|
|
|
|
|
|
|
|
|
|
# 添加图片项到列表
|
|
|
|
|
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.setIcon(QIcon(thumbnail))
|
|
|
|
|
item.setText(f"{filename} ({pixmap.width()}x{pixmap.height()})")
|
|
|
|
|
item.setData(Qt.UserRole, index) # 保存图片索引
|
|
|
|
|
self.image_list_widget.addItem(item)
|
|
|
|
|
else:
|
|
|
|
|
# 如果无法加载图片,显示默认文本
|
|
|
|
|
item = QListWidgetItem(f"{filename} (无法预览)")
|
|
|
|
|
item.setData(Qt.UserRole, index)
|
|
|
|
|
self.image_list_widget.addItem(item)
|
|
|
|
|
|
|
|
|
|
# 更新状态栏
|
|
|
|
|
self.status_label.setText(f"已提取 {len(images)} 张图片,双击查看大图")
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
self.status_label.setText(f"显示图片列表失败: {str(e)}")
|
|
|
|
|
|
|
|
|
|
def on_image_item_double_clicked(self, item):
|
|
|
|
|
"""
|
|
|
|
|
双击图片项时显示大图
|
|
|
|
|
"""
|
|
|
|
|
try:
|
|
|
|
|
# 获取图片索引
|
|
|
|
|
index = item.data(Qt.UserRole)
|
|
|
|
|
if 0 <= index < len(self.extracted_images):
|
|
|
|
|
image_filename, image_data = self.extracted_images[index]
|
|
|
|
|
self.show_image_viewer(image_filename, image_data)
|
|
|
|
|
except Exception as e:
|
|
|
|
|
self.status_label.setText(f"显示图片失败: {str(e)}")
|
|
|
|
|
|
|
|
|
|
def show_image_viewer(self, filename, image_data):
|
|
|
|
|
"""
|
|
|
|
|
显示图片查看器 - 支持缩放功能
|
|
|
|
|
"""
|
|
|
|
|
try:
|
|
|
|
|
# 创建自定义图片查看窗口
|
|
|
|
|
viewer = QDialog(self)
|
|
|
|
|
viewer.setWindowTitle(f"图片查看 - {filename}")
|
|
|
|
|
viewer.setModal(False)
|
|
|
|
|
|
|
|
|
|
# 设置窗口标志,保留标题栏以便用户可以移动和调整大小
|
|
|
|
|
viewer.setWindowFlags(Qt.Tool | Qt.WindowCloseButtonHint | Qt.WindowMinMaxButtonsHint)
|
|
|
|
|
|
|
|
|
|
# 设置窗口背景为黑色
|
|
|
|
|
viewer.setStyleSheet("""
|
|
|
|
|
QDialog {
|
|
|
|
|
background-color: #000000;
|
|
|
|
|
}
|
|
|
|
|
""")
|
|
|
|
|
|
|
|
|
|
# 创建场景和视图
|
|
|
|
|
scene = QGraphicsScene(viewer)
|
|
|
|
|
view = QGraphicsView(scene)
|
|
|
|
|
view.setStyleSheet("border: none;") # 移除视图边框
|
|
|
|
|
|
|
|
|
|
# 设置视图为可交互的,并启用滚动条
|
|
|
|
|
view.setDragMode(QGraphicsView.ScrollHandDrag)
|
|
|
|
|
view.setViewportUpdateMode(QGraphicsView.FullViewportUpdate)
|
|
|
|
|
|
|
|
|
|
# 创建布局
|
|
|
|
|
layout = QVBoxLayout()
|
|
|
|
|
layout.setContentsMargins(0, 0, 0, 0)
|
|
|
|
|
layout.addWidget(view)
|
|
|
|
|
viewer.setLayout(layout)
|
|
|
|
|
|
|
|
|
|
# 加载图片
|
|
|
|
|
pixmap = QPixmap()
|
|
|
|
|
if not pixmap.loadFromData(image_data):
|
|
|
|
|
self.status_label.setText(f"加载图片失败: {filename}")
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
# 将图片添加到场景
|
|
|
|
|
scene.addPixmap(pixmap)
|
|
|
|
|
|
|
|
|
|
# 设置视图大小和位置
|
|
|
|
|
if self:
|
|
|
|
|
parent_geometry = self.geometry()
|
|
|
|
|
screen_geometry = QApplication.primaryScreen().geometry()
|
|
|
|
|
|
|
|
|
|
# 设置窗口宽度与主窗口相同,高度为屏幕高度的40%
|
|
|
|
|
window_width = parent_geometry.width()
|
|
|
|
|
window_height = int(screen_geometry.height() * 0.4)
|
|
|
|
|
|
|
|
|
|
# 计算位置:显示在主窗口正上方
|
|
|
|
|
x = parent_geometry.x()
|
|
|
|
|
y = parent_geometry.y() - window_height
|
|
|
|
|
|
|
|
|
|
# 确保不会超出屏幕边界
|
|
|
|
|
if y < screen_geometry.top():
|
|
|
|
|
y = parent_geometry.y() + 50 # 如果上方空间不足,显示在下方
|
|
|
|
|
|
|
|
|
|
# 调整宽度确保不超出屏幕
|
|
|
|
|
if x + window_width > screen_geometry.right():
|
|
|
|
|
window_width = screen_geometry.right() - x
|
|
|
|
|
|
|
|
|
|
viewer.setGeometry(x, y, window_width, window_height)
|
|
|
|
|
|
|
|
|
|
viewer.show()
|
|
|
|
|
|
|
|
|
|
# 设置视图适应图片大小
|
|
|
|
|
view.fitInView(scene.sceneRect(), Qt.KeepAspectRatio)
|
|
|
|
|
|
|
|
|
|
# 重写视图的滚轮事件以支持缩放
|
|
|
|
|
def wheelEvent(event):
|
|
|
|
|
factor = 1.2
|
|
|
|
|
if event.angleDelta().y() > 0:
|
|
|
|
|
view.scale(factor, factor)
|
|
|
|
|
else:
|
|
|
|
|
view.scale(1.0/factor, 1.0/factor)
|
|
|
|
|
|
|
|
|
|
view.wheelEvent = wheelEvent
|
|
|
|
|
|
|
|
|
|
# 添加双击重置视图功能
|
|
|
|
|
def mouseDoubleClickEvent(event):
|
|
|
|
|
view.fitInView(scene.sceneRect(), Qt.KeepAspectRatio)
|
|
|
|
|
|
|
|
|
|
view.mouseDoubleClickEvent = mouseDoubleClickEvent
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
self.status_label.setText(f"创建图片查看器失败: {str(e)}")
|
|
|
|
|
import traceback
|
|
|
|
|
traceback.print_exc()
|