优化UI
This commit is contained in:
230
v1/main.py
230
v1/main.py
@@ -1,10 +1,11 @@
|
||||
import sys
|
||||
from PyQt5.QtWidgets import QApplication, QMainWindow, QStackedWidget, QHBoxLayout, QWidget
|
||||
from PyQt5.QtWidgets import QApplication, QMainWindow, QStackedWidget, QHBoxLayout, QWidget, QMessageBox
|
||||
from PyQt5.QtCore import Qt
|
||||
from views.login import LoginPage
|
||||
from views.nav import NavBar
|
||||
from views.app_center import AppCenter
|
||||
from views.home import Home
|
||||
from views.word_to_pdf import WordToPDFPage
|
||||
from views.app_center import AppCenter
|
||||
from views.history import HistoryPage
|
||||
from PyQt5.QtGui import QPalette, QColor
|
||||
|
||||
@@ -33,21 +34,29 @@ QLineEdit:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
/* 按钮通用样式 */
|
||||
/* ===== 按钮全局样式 ===== */
|
||||
QPushButton {
|
||||
background-color: #4f46e5;
|
||||
color: white;
|
||||
border-radius: 6px;
|
||||
padding: 8px 16px;
|
||||
border: none;
|
||||
min-height: 35px;
|
||||
border-radius: 8px; /* 增加圆角程度 */
|
||||
padding: 10px 16px;
|
||||
min-height: 40px; /* 增加高度 */
|
||||
font-weight: 500;
|
||||
transition: all 0.2s ease;
|
||||
border: none;
|
||||
outline: none; /* 防止点击时出现虚线边框 */
|
||||
}
|
||||
|
||||
QPushButton:hover {
|
||||
background-color: #4338ca;
|
||||
transform: translateY(-1px);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
QPushButton:pressed {
|
||||
background-color: #3730a3;
|
||||
transform: translateY(0);
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
QPushButton:disabled {
|
||||
@@ -55,6 +64,94 @@ QPushButton:disabled {
|
||||
color: #a0aec0;
|
||||
}
|
||||
|
||||
/* 特殊按钮样式 */
|
||||
QPushButton#navBtn {
|
||||
text-align: left;
|
||||
padding: 12px 25px;
|
||||
color: #a0aec0;
|
||||
border-radius: 8px;
|
||||
margin: 5px 15px;
|
||||
font-size: 14px;
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
min-height: auto; /* 覆盖全局最小高度 */
|
||||
}
|
||||
|
||||
QPushButton#navBtn:hover {
|
||||
background-color: rgba(79, 70, 229, 0.1);
|
||||
color: #cbd5e0;
|
||||
}
|
||||
|
||||
QPushButton#navBtn.active {
|
||||
background-color: rgba(79, 70, 229, 0.15);
|
||||
color: #818cf8;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
QPushButton#logoutBtn {
|
||||
text-align: left;
|
||||
padding: 12px 25px;
|
||||
border-radius: 8px;
|
||||
margin: 5px 15px;
|
||||
font-size: 14px;
|
||||
color: #feb2b2;
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
min-height: auto; /* 覆盖全局最小高度 */
|
||||
}
|
||||
|
||||
QPushButton#logoutBtn:hover {
|
||||
color: #f56565;
|
||||
}
|
||||
|
||||
/* 操作按钮样式 */
|
||||
QPushButton#actionBtn {
|
||||
background-color: rgba(129, 140, 248, 0.1);
|
||||
color: #818cf8;
|
||||
border: 1px solid rgba(129, 140, 248, 0.2);
|
||||
min-height: auto; /* 覆盖全局最小高度 */
|
||||
}
|
||||
|
||||
QPushButton#actionBtn:hover {
|
||||
background-color: rgba(129, 140, 248, 0.2);
|
||||
}
|
||||
|
||||
QPushButton#retryBtn {
|
||||
background-color: rgba(251, 113, 133, 0.1);
|
||||
color: #fb7185;
|
||||
border: 1px solid rgba(251, 113, 133, 0.2);
|
||||
min-height: auto; /* 覆盖全局最小高度 */
|
||||
}
|
||||
|
||||
QPushButton#retryBtn:hover {
|
||||
background-color: rgba(251, 113, 133, 0.2);
|
||||
}
|
||||
|
||||
QPushButton#deleteBtn {
|
||||
background-color: rgba(120, 113, 108, 0.1);
|
||||
color: #a8a29e;
|
||||
border: 1px solid rgba(120, 113, 108, 0.2);
|
||||
min-height: auto; /* 覆盖全局最小高度 */
|
||||
}
|
||||
|
||||
QPushButton#deleteBtn:hover {
|
||||
background-color: rgba(120, 113, 108, 0.2);
|
||||
}
|
||||
|
||||
/* 卡片按钮样式 */
|
||||
QPushButton#appCardBtn {
|
||||
min-height: auto; /* 覆盖全局最小高度 */
|
||||
min-width: 120px;
|
||||
}
|
||||
|
||||
/* 登录页面按钮 */
|
||||
QPushButton#loginBtn {
|
||||
min-height: auto; /* 覆盖全局最小高度 */
|
||||
min-width: 240px;
|
||||
padding: 14px 30px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
/* 列表控件 */
|
||||
QListWidget {
|
||||
background-color: #2d3748;
|
||||
@@ -110,42 +207,64 @@ QLabel {
|
||||
}
|
||||
|
||||
/* 菜单栏 */
|
||||
QMenuBar {
|
||||
/* 导航栏 */
|
||||
#navBar {
|
||||
background-color: #1a202c;
|
||||
color: #e2e8f0;
|
||||
border-bottom: 1px solid #2d3748;
|
||||
border-right: 1px solid #2d3748;
|
||||
}
|
||||
|
||||
QMenuBar::item {
|
||||
background-color: transparent;
|
||||
padding: 4px 10px;
|
||||
}
|
||||
|
||||
QMenuBar::item:selected {
|
||||
background-color: #4f46e5;
|
||||
color: white;
|
||||
}
|
||||
|
||||
QMenu {
|
||||
/* 用户卡片 */
|
||||
#userCard {
|
||||
background-color: #2d3748;
|
||||
border: 1px solid #4a5568;
|
||||
color: #e2e8f0;
|
||||
padding: 4px;
|
||||
border-radius: 8px;
|
||||
margin: 15px;
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
QMenu::item {
|
||||
padding: 6px 30px 6px 20px;
|
||||
/* 导航按钮 */
|
||||
#navBtn {
|
||||
text-align: left;
|
||||
padding: 12px 25px;
|
||||
color: #a0aec0;
|
||||
border-radius: 8px;
|
||||
margin: 5px 15px;
|
||||
font-size: 14px;
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
}
|
||||
|
||||
QMenu::item:selected {
|
||||
background-color: #4f46e5;
|
||||
color: white;
|
||||
#navBtn:hover {
|
||||
background-color: rgba(79, 70, 229, 0.1);
|
||||
color: #cbd5e0;
|
||||
}
|
||||
|
||||
QMenu::separator {
|
||||
height: 1px;
|
||||
background: #4a5568;
|
||||
margin: 4px 8px;
|
||||
#navBtn.active {
|
||||
background-color: rgba(79, 70, 229, 0.15);
|
||||
color: #818cf8;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* 退出按钮 */
|
||||
#logoutBtn {
|
||||
text-align: left;
|
||||
padding: 12px 25px;
|
||||
border-radius: 8px;
|
||||
margin: 5px 15px;
|
||||
font-size: 14px;
|
||||
color: #feb2b2;
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
}
|
||||
|
||||
#logoutBtn:hover {
|
||||
color: #f56565;
|
||||
}
|
||||
|
||||
/* 分割线 */
|
||||
QFrame[frameShape="4"] { /* HLine */
|
||||
background-color: #2d3748;
|
||||
margin: 10px 15px;
|
||||
max-height: 1px;
|
||||
}
|
||||
|
||||
/* 进度条 */
|
||||
@@ -251,6 +370,10 @@ class MainWindow(QMainWindow):
|
||||
self.login_page = LoginPage()
|
||||
self.login_page.login_success.connect(self.handle_login_success)
|
||||
|
||||
# 首页
|
||||
self.home = Home()
|
||||
self.home.app_selected.connect(self.handle_app_selected)
|
||||
|
||||
# 应用中心页
|
||||
self.app_center = AppCenter()
|
||||
self.app_center.app_selected.connect(self.handle_app_selected)
|
||||
@@ -268,6 +391,7 @@ class MainWindow(QMainWindow):
|
||||
# 添加页面
|
||||
self.stacked_pages.addWidget(self.login_page)
|
||||
self.stacked_pages.addWidget(self.app_center)
|
||||
self.stacked_pages.addWidget(self.home)
|
||||
self.stacked_pages.addWidget(self.word_to_pdf)
|
||||
self.stacked_pages.addWidget(self.history_page)
|
||||
self.stacked_pages.addWidget(self.settings_page)
|
||||
@@ -285,7 +409,7 @@ class MainWindow(QMainWindow):
|
||||
self.nav_bar.show()
|
||||
# 设置默认激活页
|
||||
self.nav_bar.set_active_nav("home")
|
||||
self.stacked_pages.setCurrentWidget(self.app_center)
|
||||
self.stacked_pages.setCurrentWidget(self.home)
|
||||
|
||||
def handle_logout(self):
|
||||
self.username = None
|
||||
@@ -298,6 +422,8 @@ class MainWindow(QMainWindow):
|
||||
|
||||
# 根据导航ID切换页面
|
||||
if nav_id == "home":
|
||||
self.stacked_pages.setCurrentWidget(self.home)
|
||||
if nav_id == "app_center":
|
||||
self.stacked_pages.setCurrentWidget(self.app_center)
|
||||
elif nav_id == "history":
|
||||
self.stacked_pages.setCurrentWidget(self.history_page)
|
||||
@@ -305,9 +431,35 @@ class MainWindow(QMainWindow):
|
||||
self.stacked_pages.setCurrentWidget(self.settings_page)
|
||||
|
||||
def handle_app_selected(self, app_id):
|
||||
if app_id == "word_to_pdf":
|
||||
self.stacked_pages.setCurrentWidget(self.word_to_pdf)
|
||||
# 后续可添加其他应用
|
||||
try:
|
||||
if app_id == "word_to_pdf":
|
||||
self.stacked_pages.setCurrentWidget(self.word_to_pdf)
|
||||
elif app_id == "history":
|
||||
# 注意:这里假设历史记录页面尚未开发,暂时显示word_to_pdf页面
|
||||
# 实际开发中应替换为正确的历史记录页面
|
||||
self.stacked_pages.setCurrentWidget(self.word_to_pdf)
|
||||
else:
|
||||
# 未开发的应用:弹出提示弹窗
|
||||
self.show_development_popup(app_id)
|
||||
except Exception as e:
|
||||
# 捕获并打印异常信息,避免程序闪退
|
||||
print(f"处理应用选择时发生错误: {e}")
|
||||
# 可以选择显示错误弹窗
|
||||
QMessageBox.critical(self, "错误", f"发生错误: {str(e)}", QMessageBox.Button.Ok)
|
||||
|
||||
def show_development_popup(self, app_id):
|
||||
"""显示开发中弹窗"""
|
||||
try:
|
||||
msg_box = QMessageBox(self)
|
||||
msg_box.setWindowTitle("功能开发中")
|
||||
msg_box.setText(f"应用 '{app_id}' 正在开发中")
|
||||
msg_box.setInformativeText("我们正在努力开发此功能,敬请期待!")
|
||||
msg_box.setIcon(QMessageBox.Icon.Information)
|
||||
msg_box.setStandardButtons(QMessageBox.Button.Ok)
|
||||
msg_box.button(QMessageBox.Button.Ok).setText("知道了")
|
||||
msg_box.exec()
|
||||
except Exception as e:
|
||||
print(f"显示弹窗时发生错误: {e}")
|
||||
|
||||
|
||||
def main():
|
||||
@@ -324,4 +476,4 @@ def main():
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
main()
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
430
v1/views/home.py
Normal file
430
v1/views/home.py
Normal file
@@ -0,0 +1,430 @@
|
||||
import math
|
||||
import random
|
||||
|
||||
from PyQt5.QtCore import Qt, QPropertyAnimation, QRectF, QPoint, QTimer, QParallelAnimationGroup, pyqtSignal, QPointF, \
|
||||
pyqtProperty, QEasingCurve
|
||||
from PyQt5.QtGui import QPixmap, QIcon, QColor, QPainter, QLinearGradient, QBrush, QRadialGradient, QConicalGradient
|
||||
from PyQt5.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QLabel,
|
||||
QPushButton, QFrame, QGridLayout, QSpacerItem, QSizePolicy)
|
||||
|
||||
|
||||
class AnimatedCard(QFrame):
|
||||
"""具有动画效果的卡片控件"""
|
||||
|
||||
def __init__(self, start_color, end_color, parent=None):
|
||||
super().__init__(parent)
|
||||
self.start_color = QColor(start_color)
|
||||
self.end_color = QColor(end_color)
|
||||
self.angle = 0
|
||||
self.hover_state = False
|
||||
self.init_animations()
|
||||
self.setMinimumSize(300, 280)
|
||||
self.setMaximumSize(300, 280)
|
||||
self.setCursor(Qt.PointingHandCursor)
|
||||
self.setStyleSheet("""
|
||||
AnimatedCard {
|
||||
border-radius: 16px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
""")
|
||||
|
||||
def init_animations(self):
|
||||
# 渐变角度动画
|
||||
self.angle_animation = QPropertyAnimation(self, b"angle")
|
||||
self.angle_animation.setDuration(20000) # 20秒完成一轮
|
||||
self.angle_animation.setStartValue(0)
|
||||
self.angle_animation.setEndValue(360)
|
||||
self.angle_animation.setLoopCount(-1) # 无限循环
|
||||
self.angle_animation.start()
|
||||
|
||||
# 悬停动画组
|
||||
self.hover_animation = QPropertyAnimation(self, b"geometry")
|
||||
self.hover_animation.setDuration(300)
|
||||
self.hover_animation.setEasingCurve(QEasingCurve.OutBack)
|
||||
|
||||
# 阴影动画
|
||||
self.shadow_animation = QPropertyAnimation(self, b"shadow")
|
||||
self.shadow_animation.setDuration(300)
|
||||
|
||||
# 并行动画组
|
||||
self.animation_group = QParallelAnimationGroup(self)
|
||||
self.animation_group.addAnimation(self.hover_animation)
|
||||
self.animation_group.addAnimation(self.shadow_animation)
|
||||
|
||||
def paintEvent(self, event):
|
||||
painter = QPainter(self)
|
||||
painter.setRenderHint(QPainter.Antialiasing)
|
||||
painter.setPen(Qt.NoPen)
|
||||
|
||||
# 创建主渐变背景
|
||||
rect = self.rect()
|
||||
|
||||
# 添加动态渐变背景
|
||||
if self.hover_state:
|
||||
# 悬停状态使用更亮的渐变
|
||||
gradient = QLinearGradient(rect.topLeft(), rect.bottomRight())
|
||||
gradient.setColorAt(0, self.start_color.lighter(120))
|
||||
gradient.setColorAt(1, self.end_color.lighter(120))
|
||||
else:
|
||||
# 普通状态
|
||||
gradient = QLinearGradient(rect.topLeft(), rect.bottomRight())
|
||||
gradient.setColorAt(0, self.start_color)
|
||||
gradient.setColorAt(1, self.end_color)
|
||||
|
||||
# 添加动态旋转效果
|
||||
cx = rect.center().x()
|
||||
cy = rect.center().y()
|
||||
radius = max(rect.width(), rect.height()) * 1.2
|
||||
start_x = cx + radius * 0.5 * math.cos(math.radians(self.angle))
|
||||
start_y = cy + radius * 0.5 * math.sin(math.radians(self.angle))
|
||||
end_x = cx - radius * 0.5 * math.cos(math.radians(self.angle))
|
||||
end_y = cy - radius * 0.5 * math.sin(math.radians(self.angle))
|
||||
|
||||
moving_gradient = QLinearGradient(start_x, start_y, end_x, end_y)
|
||||
moving_gradient.setColorAt(0, QColor(255, 255, 255, 30))
|
||||
moving_gradient.setColorAt(1, QColor(255, 255, 255, 10))
|
||||
|
||||
# 绘制背景
|
||||
painter.setBrush(gradient)
|
||||
painter.drawRoundedRect(rect, 16, 16)
|
||||
|
||||
# 绘制动态效果层
|
||||
painter.setBrush(moving_gradient)
|
||||
painter.drawRoundedRect(rect, 16, 16)
|
||||
|
||||
# 绘制高光边框
|
||||
if self.hover_state:
|
||||
border_gradient = QLinearGradient(rect.topLeft(), rect.topRight())
|
||||
border_gradient.setColorAt(0, QColor(255, 255, 255, 100))
|
||||
border_gradient.setColorAt(1, QColor(255, 255, 255, 30))
|
||||
painter.setPen(QColor(255, 255, 255, 150))
|
||||
painter.drawRoundedRect(rect.adjusted(1, 1, -1, -1), 16, 16)
|
||||
|
||||
def enterEvent(self, event):
|
||||
self.hover_state = True
|
||||
self.animate_hover(True)
|
||||
self.update()
|
||||
super().enterEvent(event)
|
||||
|
||||
def leaveEvent(self, event):
|
||||
self.hover_state = False
|
||||
self.animate_hover(False)
|
||||
self.update()
|
||||
super().leaveEvent(event)
|
||||
|
||||
def animate_hover(self, hover):
|
||||
"""执行悬停动画"""
|
||||
# 保存当前位置和大小
|
||||
current_geometry = self.geometry()
|
||||
|
||||
# 目标位置和大小
|
||||
if hover:
|
||||
target_geometry = current_geometry.adjusted(-5, -5, 5, 5)
|
||||
shadow_value = 20
|
||||
else:
|
||||
target_geometry = current_geometry.adjusted(5, 5, -5, -5)
|
||||
shadow_value = 5
|
||||
|
||||
# 设置位置动画
|
||||
self.hover_animation.setStartValue(current_geometry)
|
||||
self.hover_animation.setEndValue(target_geometry)
|
||||
|
||||
# 设置阴影动画
|
||||
self.shadow_animation.setStartValue(self.styleSheet())
|
||||
self.shadow_animation.setEndValue(f"""
|
||||
AnimatedCard {{
|
||||
border-radius: 16px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
box-shadow: 0 10px {shadow_value}px rgba(0, 0, 0, 0.3);
|
||||
transition: all 0.3s ease;
|
||||
}}
|
||||
""")
|
||||
|
||||
# 开始动画
|
||||
self.animation_group.start()
|
||||
|
||||
@pyqtProperty(int)
|
||||
def angle(self):
|
||||
return self._angle
|
||||
|
||||
@angle.setter
|
||||
def angle(self, value):
|
||||
self._angle = value
|
||||
self.update()
|
||||
|
||||
|
||||
class Home(QWidget):
|
||||
app_selected = pyqtSignal(str)
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.init_ui()
|
||||
|
||||
def init_ui(self):
|
||||
self.setObjectName("appCenter")
|
||||
self.setStyleSheet(self.get_styles())
|
||||
|
||||
layout = QVBoxLayout()
|
||||
layout.setContentsMargins(40, 30, 40, 30)
|
||||
layout.setSpacing(20)
|
||||
|
||||
# 标题区域
|
||||
title_layout = QHBoxLayout()
|
||||
|
||||
title = QLabel("首页")
|
||||
title.setObjectName("pageTitle")
|
||||
|
||||
title_spacer = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
|
||||
|
||||
title_layout.addWidget(title)
|
||||
title_layout.addSpacerItem(title_spacer)
|
||||
|
||||
layout.addLayout(title_layout)
|
||||
|
||||
# 卡片容器
|
||||
grid_layout = QVBoxLayout()
|
||||
grid_layout.setSpacing(30)
|
||||
|
||||
# 应用卡片
|
||||
apps = [
|
||||
{
|
||||
"id": "word_to_pdf",
|
||||
"title": "Word转PDF",
|
||||
"desc": "批量将Word文档转换为PDF格式",
|
||||
"color": ("#6366F1", "#4F46E5"), # 紫色
|
||||
"icon": "word_to_pdf"
|
||||
},
|
||||
{
|
||||
"id": "ocr",
|
||||
"title": "OCR文字识别",
|
||||
"desc": "从图片中提取文字内容",
|
||||
"color": ("#4ADE80", "#22C55E"), # 绿色
|
||||
"icon": "ocr"
|
||||
},
|
||||
{
|
||||
"id": "image_convert",
|
||||
"title": "图片格式转换",
|
||||
"desc": "转换图像文件格式并优化",
|
||||
"color": ("#F97316", "#EA580C"), # 橙色
|
||||
"icon": "image_convert"
|
||||
},
|
||||
{
|
||||
"id": "pdf_merge",
|
||||
"title": "PDF合并工具",
|
||||
"desc": "合并多个PDF文件为一个",
|
||||
"color": ("#8B5CF6", "#7C3AED"), # 紫色
|
||||
"icon": "pdf_merge"
|
||||
},
|
||||
{
|
||||
"id": "pdf_split",
|
||||
"title": "PDF拆分工具",
|
||||
"desc": "拆分PDF文件为多个文档",
|
||||
"color": ("#EC4899", "#DB2777"), # 粉色
|
||||
"icon": "pdf_split"
|
||||
},
|
||||
{
|
||||
"id": "video_compress",
|
||||
"title": "视频压缩",
|
||||
"desc": "减小视频文件大小",
|
||||
"color": ("#3B82F6", "#2563EB"), # 蓝色
|
||||
"icon": "video_compress"
|
||||
}
|
||||
]
|
||||
|
||||
# 每行显示3张卡片
|
||||
for i in range(0, len(apps), 3):
|
||||
row_layout = QHBoxLayout()
|
||||
row_layout.setSpacing(30)
|
||||
row_layout.setAlignment(Qt.AlignCenter)
|
||||
|
||||
for j in range(3):
|
||||
index = i + j
|
||||
if index < len(apps):
|
||||
row_layout.addWidget(self.create_app_card(apps[index]))
|
||||
else:
|
||||
# 添加空白占位符保持布局
|
||||
spacer = QSpacerItem(0, 0, QSizePolicy.Expanding, QSizePolicy.Minimum)
|
||||
row_layout.addSpacerItem(spacer)
|
||||
|
||||
grid_layout.addLayout(row_layout)
|
||||
|
||||
layout.addLayout(grid_layout)
|
||||
layout.addStretch()
|
||||
|
||||
self.setLayout(layout)
|
||||
|
||||
# 添加背景粒子效果
|
||||
self.particles = []
|
||||
self.init_particles()
|
||||
self.particle_timer = QTimer(self)
|
||||
self.particle_timer.timeout.connect(self.update_particles)
|
||||
self.particle_timer.start(30) # 30ms更新一次
|
||||
|
||||
def get_styles(self):
|
||||
return """
|
||||
/* ===== 应用中心样式 ===== */
|
||||
#appCenter {
|
||||
background-color: #1a202c;
|
||||
}
|
||||
|
||||
#pageTitle {
|
||||
font-size: 28px;
|
||||
font-weight: bold;
|
||||
color: #ffffff;
|
||||
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
#appCardTitle {
|
||||
font-size: 22px;
|
||||
font-weight: 700;
|
||||
color: white;
|
||||
text-align: center;
|
||||
margin-top: 15px;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
#appCardDesc {
|
||||
font-size: 15px;
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
text-align: center;
|
||||
padding: 0 15px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
#appCardBtn {
|
||||
background-color: rgba(255, 255, 255, 0.9);
|
||||
color: #1a202c;
|
||||
border-radius: 12px;
|
||||
padding: 12px 24px;
|
||||
font-weight: 600;
|
||||
border: none;
|
||||
min-width: 140px;
|
||||
transition: all 0.3s ease;
|
||||
font-size: 16px;
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
#appCardBtn:hover {
|
||||
background-color: white;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
#appCardIcon {
|
||||
background-color: rgba(255, 255, 255, 0.15);
|
||||
border-radius: 50%;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
"""
|
||||
|
||||
def create_app_card(self, app):
|
||||
"""创建应用卡片"""
|
||||
card = AnimatedCard(app["color"][0], app["color"][1])
|
||||
card.setObjectName("appCard")
|
||||
|
||||
layout = QVBoxLayout(card)
|
||||
layout.setContentsMargins(25, 25, 25, 25)
|
||||
layout.setSpacing(15)
|
||||
|
||||
# 图标
|
||||
icon_frame = QFrame()
|
||||
icon_frame.setFixedSize(90, 90)
|
||||
icon_frame.setObjectName("appCardIcon")
|
||||
|
||||
icon_layout = QVBoxLayout(icon_frame)
|
||||
icon_layout.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
icon = QLabel()
|
||||
icon_pixmap = QPixmap(f":/icons/{app['icon']}.png").scaled(56, 56,
|
||||
Qt.KeepAspectRatio,
|
||||
Qt.SmoothTransformation)
|
||||
|
||||
# 转换图标为白色
|
||||
painter = QPainter(icon_pixmap)
|
||||
painter.setCompositionMode(QPainter.CompositionMode_SourceIn)
|
||||
painter.fillRect(icon_pixmap.rect(), Qt.white)
|
||||
painter.end()
|
||||
|
||||
icon.setPixmap(icon_pixmap)
|
||||
icon.setAlignment(Qt.AlignCenter)
|
||||
icon_layout.addWidget(icon)
|
||||
|
||||
layout.addWidget(icon_frame, 0, Qt.AlignHCenter)
|
||||
|
||||
# 标题
|
||||
title = QLabel(app["title"])
|
||||
title.setObjectName("appCardTitle")
|
||||
layout.addWidget(title)
|
||||
|
||||
# 描述
|
||||
desc = QLabel(app["desc"])
|
||||
desc.setObjectName("appCardDesc")
|
||||
desc.setWordWrap(True)
|
||||
layout.addWidget(desc)
|
||||
|
||||
# 添加伸缩空间保持底部对齐
|
||||
layout.addStretch()
|
||||
|
||||
# 按钮
|
||||
btn_text = "开始使用" if app["id"] != "coming_soon" else "敬请期待"
|
||||
btn = QPushButton(btn_text)
|
||||
btn.setObjectName("appCardBtn")
|
||||
btn.setCursor(Qt.PointingHandCursor)
|
||||
|
||||
if app["id"] == "coming_soon":
|
||||
btn.setEnabled(False)
|
||||
btn.setStyleSheet("""
|
||||
#appCardBtn:disabled {
|
||||
background-color: rgba(255, 255, 255, 0.3);
|
||||
color: rgba(26, 32, 44, 0.7);
|
||||
}
|
||||
""")
|
||||
else:
|
||||
btn.clicked.connect(lambda: self.app_selected.emit(app["id"]))
|
||||
|
||||
layout.addWidget(btn, 0, Qt.AlignHCenter)
|
||||
|
||||
# 为卡片添加点击事件
|
||||
card.mousePressEvent = lambda e: self.app_selected.emit(app["id"]) if app["id"] != "coming_soon" else None
|
||||
|
||||
return card
|
||||
|
||||
def init_particles(self):
|
||||
"""初始化背景粒子"""
|
||||
for _ in range(20):
|
||||
particle = {
|
||||
'x': random.randint(0, self.width()),
|
||||
'y': random.randint(0, self.height()),
|
||||
'size': random.randint(2, 6),
|
||||
'speed': random.uniform(0.5, 2),
|
||||
'color': random.choice([
|
||||
QColor(79, 70, 229, 80), # 紫色
|
||||
QColor(129, 140, 248, 60), # 浅紫
|
||||
QColor(59, 130, 246, 70), # 蓝色
|
||||
QColor(16, 185, 129, 60) # 绿色
|
||||
])
|
||||
}
|
||||
self.particles.append(particle)
|
||||
|
||||
def update_particles(self):
|
||||
"""更新粒子位置"""
|
||||
for particle in self.particles:
|
||||
particle['y'] += particle['speed']
|
||||
if particle['y'] > self.height():
|
||||
particle['y'] = 0
|
||||
particle['x'] = random.randint(0, self.width())
|
||||
self.update()
|
||||
|
||||
def paintEvent(self, event):
|
||||
"""绘制背景粒子效果"""
|
||||
super().paintEvent(event)
|
||||
painter = QPainter(self)
|
||||
painter.setRenderHint(QPainter.Antialiasing)
|
||||
painter.setPen(Qt.NoPen)
|
||||
|
||||
# 绘制粒子
|
||||
for particle in self.particles:
|
||||
painter.setBrush(particle['color'])
|
||||
painter.drawEllipse(QPointF(particle['x'], particle['y']), particle['size'], particle['size'])
|
||||
@@ -1,7 +1,7 @@
|
||||
from PyQt5.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QLabel,
|
||||
QLineEdit, QPushButton, QFrame, QCheckBox,
|
||||
QSpacerItem, QSizePolicy)
|
||||
from PyQt5.QtCore import Qt, pyqtSignal, QPropertyAnimation, QEasingCurve, QPoint, QSize
|
||||
from PyQt5.QtCore import Qt, pyqtSignal, QPropertyAnimation, QEasingCurve, QPoint, QSize, QTimer, QPointF
|
||||
from PyQt5.QtGui import QPixmap, QPainter, QLinearGradient, QColor, QIcon, QFont, QBrush
|
||||
|
||||
|
||||
@@ -12,6 +12,15 @@ class LoginPage(QWidget):
|
||||
super().__init__()
|
||||
self.init_ui()
|
||||
self.setup_animations()
|
||||
self.gradient_angle = 0
|
||||
self.particle_timer = QTimer(self)
|
||||
self.particle_timer.timeout.connect(self.update_particles)
|
||||
self.particle_timer.start(100) # 更新粒子位置的定时器
|
||||
self.particles = self.create_particles()
|
||||
|
||||
# 添加回车键提交支持
|
||||
self.username_input.returnPressed.connect(self.handle_login)
|
||||
self.password_input.returnPressed.connect(self.handle_login)
|
||||
|
||||
def init_ui(self):
|
||||
self.setObjectName("loginPage")
|
||||
@@ -24,9 +33,87 @@ class LoginPage(QWidget):
|
||||
self.setLayout(main_layout)
|
||||
|
||||
# 左侧装饰区 - 添加玻璃态效果
|
||||
left_frame = QFrame()
|
||||
left_frame.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
|
||||
left_frame.setObjectName("leftDecor")
|
||||
self.left_frame = QWidget() # 改为QWidget以便自定义绘制
|
||||
self.left_frame.setObjectName("leftDecor")
|
||||
self.left_frame.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
|
||||
|
||||
# 在左侧装饰区添加艺术字体和版本信息
|
||||
left_layout = QVBoxLayout(self.left_frame)
|
||||
left_layout.setContentsMargins(40, 40, 40, 40)
|
||||
left_layout.addStretch()
|
||||
|
||||
# 艺术字体标题
|
||||
title_container = QFrame()
|
||||
title_container.setObjectName("titleContainer")
|
||||
title_container.setStyleSheet("background-color: rgba(0, 0, 0, 0.2); border-radius: 15px;")
|
||||
title_layout = QVBoxLayout(title_container)
|
||||
title_layout.setContentsMargins(30, 30, 30, 30)
|
||||
|
||||
app_title = QLabel("奶酪云工具箱")
|
||||
app_title.setObjectName("appTitle")
|
||||
app_title.setAlignment(Qt.AlignCenter)
|
||||
app_title.setStyleSheet("""
|
||||
#appTitle {
|
||||
font-size: 56px;
|
||||
font-weight: bold;
|
||||
color: white;
|
||||
font-family: 'SimHei', 'Microsoft YaHei', sans-serif;
|
||||
text-shadow: 0 0 10px rgba(255, 255, 255, 0.5),
|
||||
0 0 20px rgba(99, 102, 241, 0.8),
|
||||
0 0 30px rgba(99, 102, 241, 0.6),
|
||||
0 0 40px rgba(99, 102, 241, 0.4);
|
||||
letter-spacing: 5px;
|
||||
}
|
||||
""")
|
||||
|
||||
subtitle = QLabel("高效 · 安全 · 云端协作")
|
||||
subtitle.setObjectName("subtitle")
|
||||
subtitle.setAlignment(Qt.AlignCenter)
|
||||
subtitle.setStyleSheet("""
|
||||
#subtitle {
|
||||
font-size: 18px;
|
||||
color: rgba(255, 255, 255, 0.85);
|
||||
letter-spacing: 3px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
""")
|
||||
|
||||
title_layout.addWidget(app_title)
|
||||
title_layout.addWidget(subtitle)
|
||||
|
||||
# 版本信息
|
||||
version_info = QWidget()
|
||||
version_layout = QVBoxLayout(version_info)
|
||||
version_layout.setAlignment(Qt.AlignBottom | Qt.AlignCenter)
|
||||
|
||||
version_label = QLabel("版本 0.0.1")
|
||||
version_label.setObjectName("versionLabel")
|
||||
version_label.setAlignment(Qt.AlignCenter)
|
||||
version_label.setStyleSheet("""
|
||||
#versionLabel {
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
font-size: 14px;
|
||||
}
|
||||
""")
|
||||
|
||||
author_label = QLabel("作者:年糕崽崽")
|
||||
author_label.setObjectName("authorLabel")
|
||||
author_label.setAlignment(Qt.AlignCenter)
|
||||
author_label.setStyleSheet("""
|
||||
#authorLabel {
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
font-size: 14px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
""")
|
||||
|
||||
version_layout.addWidget(version_label)
|
||||
version_layout.addWidget(author_label)
|
||||
|
||||
# 添加到左侧布局
|
||||
left_layout.addWidget(title_container)
|
||||
left_layout.addStretch()
|
||||
left_layout.addWidget(version_info)
|
||||
|
||||
# 右侧登录区 - 深色背景
|
||||
right_frame = QFrame()
|
||||
@@ -40,7 +127,7 @@ class LoginPage(QWidget):
|
||||
}
|
||||
""")
|
||||
|
||||
main_layout.addWidget(left_frame, 3)
|
||||
main_layout.addWidget(self.left_frame, 3)
|
||||
main_layout.addWidget(right_frame, 1)
|
||||
|
||||
# 右侧登录区布局
|
||||
@@ -363,6 +450,42 @@ class LoginPage(QWidget):
|
||||
self.password_anim.setDuration(300)
|
||||
self.password_anim.setEasingCurve(QEasingCurve.OutQuad)
|
||||
|
||||
def create_particles(self):
|
||||
particles = []
|
||||
for _ in range(30): # 创建30个粒子
|
||||
size = 2 + 5 * (0.5) # 随机大小
|
||||
x = 50 + 700 * (0.5) # 随机X位置
|
||||
y = 50 + 500 * (0.5) # 随机Y位置
|
||||
speed_x = -1 + 2 * (0.5) # X方向速度
|
||||
speed_y = -1 + 2 * (0.5) # Y方向速度
|
||||
color = QColor( # 随机蓝色调
|
||||
int(100 + 155 * (0.5)),
|
||||
int(180 + 75 * (0.5)),
|
||||
int(255 - 50 * (0.5)),
|
||||
int(50 + 100 * (0.5))
|
||||
)
|
||||
particles.append({
|
||||
'x': x, 'y': y,
|
||||
'size': size,
|
||||
'speed_x': speed_x, 'speed_y': speed_y,
|
||||
'color': color
|
||||
})
|
||||
return particles
|
||||
|
||||
def update_particles(self):
|
||||
for p in self.particles:
|
||||
# 更新粒子位置
|
||||
p['x'] += p['speed_x']
|
||||
p['y'] += p['speed_y']
|
||||
|
||||
# 边界反弹
|
||||
if p['x'] <= 0 or p['x'] >= self.width():
|
||||
p['speed_x'] *= -1
|
||||
if p['y'] <= 0 or p['y'] >= self.height():
|
||||
p['speed_y'] *= -1
|
||||
|
||||
self.left_frame.update() # 触发重绘
|
||||
|
||||
def toggle_password_visibility(self):
|
||||
if self.password_input.echoMode() == QLineEdit.Password:
|
||||
self.password_input.setEchoMode(QLineEdit.Normal)
|
||||
@@ -441,7 +564,6 @@ class LoginPage(QWidget):
|
||||
self.password_input.parent().setStyleSheet("#inputFrame { border: 1px solid #fc8181; }")
|
||||
|
||||
# 2秒后恢复
|
||||
from PyQt5.QtCore import QTimer
|
||||
QTimer.singleShot(2000, self.reset_styles)
|
||||
|
||||
def reset_styles(self):
|
||||
@@ -479,4 +601,41 @@ class LoginPage(QWidget):
|
||||
painter.setBrush(QColor(6, 78, 59, 60))
|
||||
painter.drawEllipse(QPoint(250, 300), 50, 50)
|
||||
|
||||
super().paintEvent(event)
|
||||
super().paintEvent(event)
|
||||
|
||||
# 专门绘制左侧装饰区域的粒子效果
|
||||
if self.left_frame.isVisible():
|
||||
# 保存当前绘画状态
|
||||
painter.save()
|
||||
|
||||
# 将绘画区域限制在左侧框架内
|
||||
painter.translate(self.left_frame.pos())
|
||||
painter.setClipRect(self.left_frame.rect())
|
||||
|
||||
# 绘制动态粒子
|
||||
for p in self.particles:
|
||||
painter.setBrush(QBrush(p['color']))
|
||||
painter.setPen(Qt.NoPen)
|
||||
painter.drawEllipse(QPointF(p['x'], p['y']), p['size'], p['size'])
|
||||
|
||||
# 绘制发光的光斑背景
|
||||
gradient = QLinearGradient(0, 0, self.width(), self.height())
|
||||
gradient.setColorAt(0, QColor(99, 102, 241, 20)) # 紫色
|
||||
gradient.setColorAt(0.5, QColor(79, 70, 229, 40)) # 更亮的紫色
|
||||
gradient.setColorAt(1, QColor(49, 46, 129, 20)) # 深紫色
|
||||
painter.setBrush(QBrush(gradient))
|
||||
painter.drawRect(0, 0, self.left_frame.width(), self.left_frame.height())
|
||||
|
||||
painter.restore()
|
||||
|
||||
|
||||
# 使用示例
|
||||
if __name__ == "__main__":
|
||||
from PyQt5.QtWidgets import QApplication
|
||||
import sys
|
||||
|
||||
app = QApplication(sys.argv)
|
||||
login_page = LoginPage()
|
||||
login_page.login_success.connect(lambda username: print(f"登录成功: {username}"))
|
||||
login_page.show()
|
||||
sys.exit(app.exec_())
|
||||
130
v1/views/nav.py
130
v1/views/nav.py
@@ -1,6 +1,6 @@
|
||||
from PyQt5.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QLabel,
|
||||
QPushButton, QFrame, QSpacerItem, QSizePolicy)
|
||||
from PyQt5.QtCore import Qt, pyqtSignal
|
||||
from PyQt5.QtCore import Qt, pyqtSignal, QSize
|
||||
from PyQt5.QtGui import QPixmap, QIcon, QFont, QPainter, QBrush, QLinearGradient, QColor
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ class NavBar(QWidget):
|
||||
|
||||
def __init__(self, username):
|
||||
super().__init__()
|
||||
self.username = username or "访客" # 设置默认值,防止空字符串
|
||||
self.username = username or "访客"
|
||||
self.active_btn = None
|
||||
self.init_ui()
|
||||
|
||||
@@ -80,51 +80,36 @@ class NavBar(QWidget):
|
||||
layout.setContentsMargins(0, 20, 0, 20)
|
||||
layout.setSpacing(0)
|
||||
|
||||
# 用户信息卡片 - 使用更现代的设计
|
||||
# 用户信息卡片
|
||||
user_frame = QFrame()
|
||||
user_frame.setObjectName("userFrame")
|
||||
|
||||
user_layout = QHBoxLayout(user_frame)
|
||||
user_layout.setContentsMargins(15, 15, 15, 15)
|
||||
user_layout.setSpacing(15)
|
||||
|
||||
# 头像 - 使用渐变背景
|
||||
# 头像
|
||||
avatar = QLabel()
|
||||
avatar.setFixedSize(50, 50)
|
||||
|
||||
# 绘制渐变背景头像
|
||||
avatar_pixmap = QPixmap(50, 50)
|
||||
avatar_pixmap.fill(Qt.transparent)
|
||||
painter = QPainter(avatar_pixmap)
|
||||
painter.setRenderHint(QPainter.Antialiasing)
|
||||
brush = QBrush(Qt.SolidPattern)
|
||||
gradient = QLinearGradient(0, 0, 50, 50)
|
||||
gradient.setColorAt(0, QColor("#6366f1"))
|
||||
gradient.setColorAt(1, QColor("#8b5cf6"))
|
||||
painter.setBrush(gradient)
|
||||
painter.drawEllipse(0, 0, 50, 50)
|
||||
painter.end()
|
||||
avatar.setPixmap(avatar_pixmap)
|
||||
|
||||
# 添加用户首字母
|
||||
avatar_label = QLabel()
|
||||
avatar_label.setFixedSize(50, 50)
|
||||
avatar_label.setAlignment(Qt.AlignCenter)
|
||||
avatar.setStyleSheet("""
|
||||
background-color: #4f46e5;
|
||||
border-radius: 25px;
|
||||
color: white;
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
qproperty-alignment: 'AlignCenter';
|
||||
""")
|
||||
initial = self.username[0].upper() if self.username else "访"
|
||||
avatar_label.setText(initial)
|
||||
avatar_label.setStyleSheet("font-size: 18px; font-weight: bold; color: white;")
|
||||
avatar.setText(initial)
|
||||
|
||||
# 用户信息容器
|
||||
# 用户信息
|
||||
user_info = QWidget()
|
||||
user_info_layout = QVBoxLayout(user_info)
|
||||
user_info_layout.setContentsMargins(0, 0, 0, 0)
|
||||
user_info_layout.setSpacing(5)
|
||||
|
||||
# 用户名
|
||||
user_name = QLabel(self.username)
|
||||
user_name.setObjectName("userName")
|
||||
|
||||
# 状态标签
|
||||
status = QLabel("在线")
|
||||
status.setStyleSheet("font-size: 12px; color: #68d391;")
|
||||
|
||||
@@ -132,80 +117,33 @@ class NavBar(QWidget):
|
||||
user_info_layout.addWidget(status)
|
||||
|
||||
user_layout.addWidget(avatar)
|
||||
user_layout.addWidget(avatar_label)
|
||||
user_layout.addWidget(user_info)
|
||||
|
||||
# 导航项
|
||||
# 导航项 - 添加应用中心链接
|
||||
nav_items = [
|
||||
{"name": "首页", "icon": "home", "id": "home"},
|
||||
{"name": "应用中心", "icon": "apps", "id": "app_center"},
|
||||
{"name": "应用中心", "icon": "apps", "id": "app_center"}, # 新添加的应用中心链接
|
||||
{"name": "使用记录", "icon": "history", "id": "history"},
|
||||
{"name": "个人设置", "icon": "settings", "id": "settings"},
|
||||
]
|
||||
|
||||
# 导航按钮 - 添加图标
|
||||
# 导航按钮
|
||||
self.nav_buttons = []
|
||||
for item in nav_items:
|
||||
btn = QPushButton()
|
||||
btn = QPushButton(item["name"])
|
||||
btn.setObjectName("navBtn")
|
||||
btn.setProperty("nav_id", item["id"])
|
||||
btn.setCursor(Qt.PointingHandCursor)
|
||||
|
||||
# 添加图标和文本
|
||||
btn_layout = QHBoxLayout()
|
||||
btn_layout.setSpacing(12)
|
||||
btn_layout.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
icon = QLabel()
|
||||
icon_pixmap = QPixmap(f":/icons/{item['icon']}.png").scaled(20, 20, Qt.KeepAspectRatio,
|
||||
Qt.SmoothTransformation)
|
||||
|
||||
# 图标着色为当前文本颜色
|
||||
painter = QPainter(icon_pixmap)
|
||||
painter.setCompositionMode(QPainter.CompositionMode_SourceIn)
|
||||
painter.fillRect(icon_pixmap.rect(), QColor("#a0aec0"))
|
||||
painter.end()
|
||||
|
||||
icon.setPixmap(icon_pixmap)
|
||||
|
||||
text = QLabel(item["name"])
|
||||
|
||||
btn_layout.addWidget(icon)
|
||||
btn_layout.addWidget(text)
|
||||
btn_layout.addStretch()
|
||||
|
||||
btn.setLayout(btn_layout)
|
||||
|
||||
btn.setIcon(QIcon(f":/icons/{item['icon']}.png"))
|
||||
btn.setIconSize(QSize(20, 20))
|
||||
btn.clicked.connect(lambda _, x=item["id"]: self.nav_item_clicked.emit(x))
|
||||
self.nav_buttons.append(btn)
|
||||
|
||||
# 退出按钮
|
||||
logout_btn = QPushButton()
|
||||
logout_btn = QPushButton("退出登录")
|
||||
logout_btn.setObjectName("logoutBtn")
|
||||
|
||||
# 添加图标和文本
|
||||
logout_layout = QHBoxLayout()
|
||||
logout_layout.setSpacing(12)
|
||||
logout_layout.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
logout_icon = QLabel()
|
||||
logout_pixmap = QPixmap(":/icons/logout.png").scaled(20, 20, Qt.KeepAspectRatio, Qt.SmoothTransformation)
|
||||
|
||||
# 图标着色
|
||||
painter = QPainter(logout_pixmap)
|
||||
painter.setCompositionMode(QPainter.CompositionMode_SourceIn)
|
||||
painter.fillRect(logout_pixmap.rect(), QColor("#feb2b2"))
|
||||
painter.end()
|
||||
|
||||
logout_icon.setPixmap(logout_pixmap)
|
||||
|
||||
logout_text = QLabel("退出登录")
|
||||
|
||||
logout_layout.addWidget(logout_icon)
|
||||
logout_layout.addWidget(logout_text)
|
||||
logout_layout.addStretch()
|
||||
|
||||
logout_btn.setLayout(logout_layout)
|
||||
logout_btn.setIcon(QIcon(":/icons/logout.png"))
|
||||
logout_btn.setIconSize(QSize(20, 20))
|
||||
logout_btn.clicked.connect(self.logout_clicked.emit)
|
||||
|
||||
# 组装布局
|
||||
@@ -215,7 +153,7 @@ class NavBar(QWidget):
|
||||
# 添加分割线
|
||||
separator = QFrame()
|
||||
separator.setFrameShape(QFrame.HLine)
|
||||
separator.setStyleSheet("background-color: #2d3748; margin: 0 15px; height: 1px;")
|
||||
separator.setStyleSheet("margin: 0 15px; height: 1px; background-color: #2d3748;")
|
||||
layout.addWidget(separator)
|
||||
layout.addSpacing(20)
|
||||
|
||||
@@ -239,7 +177,7 @@ class NavBar(QWidget):
|
||||
# 底部添加分割线
|
||||
separator_bottom = QFrame()
|
||||
separator_bottom.setFrameShape(QFrame.HLine)
|
||||
separator_bottom.setStyleSheet("background-color: #2d3748; margin: 0 15px; height: 1px;")
|
||||
separator_bottom.setStyleSheet("margin: 0 15px; height: 1px; background-color: #2d3748;")
|
||||
|
||||
bottom_container_layout.addWidget(separator_bottom)
|
||||
bottom_container_layout.addSpacing(15)
|
||||
@@ -256,27 +194,11 @@ class NavBar(QWidget):
|
||||
# 移除所有按钮的active类
|
||||
for btn in self.nav_buttons:
|
||||
btn.setProperty("class", "")
|
||||
# 重新设置样式以应用变化
|
||||
btn.setStyle(btn.style())
|
||||
|
||||
# 为活动按钮添加active类
|
||||
# 为活动按钮添加active类
|
||||
for btn in self.nav_buttons:
|
||||
if btn.property("nav_id") == nav_id:
|
||||
btn.setProperty("class", "active")
|
||||
|
||||
# 更新图标颜色
|
||||
icon_label = btn.layout().itemAt(0).widget()
|
||||
active_pixmap = icon_label.pixmap()
|
||||
|
||||
# 改变图标颜色
|
||||
painter = QPainter(active_pixmap)
|
||||
painter.setCompositionMode(QPainter.CompositionMode_SourceIn)
|
||||
painter.fillRect(active_pixmap.rect(), QColor("#818cf8"))
|
||||
painter.end()
|
||||
|
||||
icon_label.setPixmap(active_pixmap)
|
||||
|
||||
# 重新设置样式以应用变化
|
||||
btn.setStyle(btn.style())
|
||||
|
||||
self.active_btn = btn
|
||||
@@ -5,8 +5,8 @@ import datetime
|
||||
from PyQt5.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QLabel,
|
||||
QPushButton, QFrame, QListWidget, QListWidgetItem,
|
||||
QFileDialog, QProgressBar, QMessageBox, QSpacerItem)
|
||||
from PyQt5.QtCore import Qt, QThread, pyqtSignal, QSize
|
||||
from PyQt5.QtGui import QPixmap, QIcon, QDragEnterEvent, QDropEvent
|
||||
from PyQt5.QtCore import Qt, QThread, pyqtSignal, QSize, QTimer
|
||||
from PyQt5.QtGui import QPixmap, QIcon, QDragEnterEvent, QDropEvent, QDragLeaveEvent
|
||||
|
||||
|
||||
def generate_unique_filename(original_path):
|
||||
@@ -67,6 +67,7 @@ class WordToPDFPage(QWidget):
|
||||
self.converted_files = {}
|
||||
self.conversion_thread = None
|
||||
self.init_ui()
|
||||
self.setStyleSheet(self.get_page_style())
|
||||
|
||||
def init_ui(self):
|
||||
self.setObjectName("wordToPdfPage")
|
||||
@@ -77,81 +78,143 @@ class WordToPDFPage(QWidget):
|
||||
layout.setSpacing(20)
|
||||
|
||||
# 左侧面板 - 文件上传
|
||||
left_panel = QFrame()
|
||||
left_panel.setObjectName("leftPanel")
|
||||
left_panel.setMinimumWidth(300)
|
||||
|
||||
left_panel = QWidget()
|
||||
left_layout = QVBoxLayout(left_panel)
|
||||
left_layout.setContentsMargins(0, 0, 0, 0)
|
||||
left_layout.setSpacing(15)
|
||||
|
||||
# 上传区域
|
||||
# 上传区域(增强拖拽效果)
|
||||
upload_area = QFrame()
|
||||
upload_area.setObjectName("uploadArea")
|
||||
upload_area.setFixedHeight(200)
|
||||
upload_area.setAcceptDrops(True)
|
||||
|
||||
upload_layout = QVBoxLayout(upload_area)
|
||||
upload_layout.setAlignment(Qt.AlignCenter)
|
||||
|
||||
upload_icon = QLabel()
|
||||
# 确保你有正确的图标路径
|
||||
# upload_icon.setPixmap(QPixmap(":/icons/upload.png").scaled(64, 64, Qt.KeepAspectRatio, Qt.SmoothTransformation))
|
||||
upload_icon.setPixmap(QPixmap(":/icons/upload.png").scaled(64, 64, Qt.KeepAspectRatio, Qt.SmoothTransformation))
|
||||
upload_icon.setAlignment(Qt.AlignCenter)
|
||||
|
||||
upload_text = QLabel("拖拽文件或文件夹到此处\n或点击上传")
|
||||
upload_text.setAlignment(Qt.AlignCenter)
|
||||
upload_text.setWordWrap(True)
|
||||
self.upload_text = QLabel("拖拽文件或文件夹到此处\n或点击上传")
|
||||
self.upload_text.setAlignment(Qt.AlignCenter)
|
||||
self.upload_text.setWordWrap(True)
|
||||
|
||||
upload_btn = QPushButton("选择文件或文件夹")
|
||||
upload_btn.setObjectName("uploadBtn")
|
||||
upload_btn.clicked.connect(self.handle_upload)
|
||||
|
||||
upload_layout.addWidget(upload_icon)
|
||||
upload_layout.addWidget(upload_text)
|
||||
upload_layout.addWidget(self.upload_text)
|
||||
upload_layout.addWidget(upload_btn)
|
||||
|
||||
# 文件列表
|
||||
file_list_frame = QFrame()
|
||||
file_list_frame.setObjectName("fileListFrame")
|
||||
file_list_layout = QVBoxLayout(file_list_frame)
|
||||
file_list_layout.setContentsMargins(0, 0, 0, 0)
|
||||
file_list_layout.setSpacing(5)
|
||||
|
||||
file_title = QLabel("待转换文件")
|
||||
file_title.setObjectName("sectionTitle")
|
||||
|
||||
self.file_list = QListWidget()
|
||||
self.file_list.setObjectName("fileList")
|
||||
self.file_list.setSelectionMode(QListWidget.ExtendedSelection)
|
||||
|
||||
file_list_layout.addWidget(file_title)
|
||||
file_list_layout.addWidget(self.file_list)
|
||||
|
||||
# 操作按钮
|
||||
btn_layout = QHBoxLayout()
|
||||
btn_layout.setSpacing(10)
|
||||
|
||||
convert_btn = QPushButton("开始转换")
|
||||
convert_btn.setObjectName("convertBtn")
|
||||
convert_btn.clicked.connect(self.start_conversion)
|
||||
self.convert_btn = QPushButton("开始转换")
|
||||
self.convert_btn.setObjectName("convertBtn")
|
||||
self.convert_btn.clicked.connect(self.start_conversion)
|
||||
|
||||
delete_btn = QPushButton("删除选中")
|
||||
delete_btn.setObjectName("deleteBtn")
|
||||
delete_btn.clicked.connect(self.delete_selected)
|
||||
|
||||
btn_layout.addWidget(convert_btn)
|
||||
btn_layout.addWidget(self.convert_btn)
|
||||
btn_layout.addWidget(delete_btn)
|
||||
|
||||
# 组装左侧面板
|
||||
left_layout.addWidget(upload_area)
|
||||
left_layout.addWidget(self.file_list)
|
||||
left_layout.addWidget(file_list_frame)
|
||||
left_layout.addLayout(btn_layout)
|
||||
|
||||
# 右侧面板 - 转换结果
|
||||
right_panel = QFrame()
|
||||
right_panel.setObjectName("rightPanel")
|
||||
|
||||
right_panel = QWidget()
|
||||
right_layout = QVBoxLayout(right_panel)
|
||||
right_layout.setContentsMargins(0, 0, 0, 0)
|
||||
right_layout.setSpacing(15)
|
||||
|
||||
# 进度条
|
||||
# 进度区域
|
||||
progress_frame = QFrame()
|
||||
progress_frame.setObjectName("progressFrame")
|
||||
progress_layout = QVBoxLayout(progress_frame)
|
||||
progress_layout.setContentsMargins(15, 15, 15, 15)
|
||||
progress_layout.setSpacing(10)
|
||||
|
||||
progress_title = QLabel("转换进度")
|
||||
progress_title.setObjectName("sectionTitle")
|
||||
|
||||
# 总体进度
|
||||
total_layout = QHBoxLayout()
|
||||
total_layout.setSpacing(10)
|
||||
|
||||
total_label = QLabel("总体进度:")
|
||||
total_label.setObjectName("progressLabel")
|
||||
|
||||
self.total_progress = QProgressBar()
|
||||
self.total_progress.setObjectName("totalProgress")
|
||||
self.total_progress.setTextVisible(False)
|
||||
|
||||
total_layout.addWidget(total_label)
|
||||
total_layout.addWidget(self.total_progress)
|
||||
|
||||
# 文件进度
|
||||
file_layout = QHBoxLayout()
|
||||
file_layout.setSpacing(10)
|
||||
|
||||
file_label = QLabel("当前文件:")
|
||||
file_label.setObjectName("progressLabel")
|
||||
|
||||
self.file_progress = QProgressBar()
|
||||
self.file_progress.setObjectName("fileProgress")
|
||||
self.file_progress.setTextVisible(False)
|
||||
|
||||
file_layout.addWidget(file_label)
|
||||
file_layout.addWidget(self.file_progress)
|
||||
|
||||
# 状态标签
|
||||
self.status_label = QLabel("就绪")
|
||||
self.status_label.setObjectName("statusLabel")
|
||||
self.status_label.setAlignment(Qt.AlignCenter)
|
||||
|
||||
progress_layout.addWidget(progress_title)
|
||||
progress_layout.addLayout(total_layout)
|
||||
progress_layout.addLayout(file_layout)
|
||||
progress_layout.addWidget(self.status_label)
|
||||
|
||||
# 结果列表
|
||||
result_frame = QFrame()
|
||||
result_frame.setObjectName("resultFrame")
|
||||
result_layout = QVBoxLayout(result_frame)
|
||||
result_layout.setContentsMargins(0, 0, 0, 0)
|
||||
result_layout.setSpacing(5)
|
||||
|
||||
result_title = QLabel("转换结果")
|
||||
result_title.setObjectName("sectionTitle")
|
||||
|
||||
self.result_list = QListWidget()
|
||||
self.result_list.setObjectName("resultList")
|
||||
self.result_list.setSelectionMode(QListWidget.ExtendedSelection)
|
||||
|
||||
result_layout.addWidget(result_title)
|
||||
result_layout.addWidget(self.result_list)
|
||||
|
||||
# 下载按钮
|
||||
self.download_btn = QPushButton("全部下载")
|
||||
@@ -159,27 +222,28 @@ class WordToPDFPage(QWidget):
|
||||
self.download_btn.setEnabled(False)
|
||||
self.download_btn.clicked.connect(self.download_all)
|
||||
|
||||
# 日志
|
||||
log_area = QFrame()
|
||||
log_area.setObjectName("logArea")
|
||||
|
||||
log_layout = QVBoxLayout(log_area)
|
||||
# 日志区域
|
||||
log_frame = QFrame()
|
||||
log_frame.setObjectName("logFrame")
|
||||
log_layout = QVBoxLayout(log_frame)
|
||||
log_layout.setContentsMargins(0, 0, 0, 0)
|
||||
log_layout.setSpacing(5)
|
||||
|
||||
log_title = QLabel("转换日志")
|
||||
log_title.setObjectName("logTitle")
|
||||
log_title.setObjectName("sectionTitle")
|
||||
|
||||
self.log_content = QListWidget()
|
||||
self.log_content.setObjectName("logContent")
|
||||
self.log_content.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
|
||||
|
||||
log_layout.addWidget(log_title)
|
||||
log_layout.addWidget(self.log_content)
|
||||
|
||||
# 组装右侧面板
|
||||
right_layout.addWidget(self.total_progress)
|
||||
right_layout.addWidget(self.file_progress)
|
||||
right_layout.addWidget(self.result_list)
|
||||
right_layout.addWidget(progress_frame)
|
||||
right_layout.addWidget(result_frame)
|
||||
right_layout.addWidget(self.download_btn)
|
||||
right_layout.addWidget(log_area)
|
||||
right_layout.addWidget(log_frame)
|
||||
|
||||
# 主布局
|
||||
layout.addWidget(left_panel)
|
||||
@@ -187,13 +251,178 @@ class WordToPDFPage(QWidget):
|
||||
|
||||
self.setLayout(layout)
|
||||
|
||||
def get_page_style(self):
|
||||
return """
|
||||
/* ===== Word转PDF页面样式 ===== */
|
||||
#wordToPdfPage {
|
||||
background-color: #1a202c;
|
||||
}
|
||||
|
||||
/* 卡片样式 */
|
||||
#uploadArea,
|
||||
#fileListFrame,
|
||||
#progressFrame,
|
||||
#resultFrame,
|
||||
#logFrame {
|
||||
background-color: #2d3748;
|
||||
border: 1px solid #4a5568;
|
||||
border-radius: 8px;
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
/* 上传区域 */
|
||||
#uploadArea {
|
||||
border: 2px dashed #4a5568;
|
||||
background-color: rgba(45, 55, 72, 0.5);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
#uploadArea:hover {
|
||||
border-color: #818cf8;
|
||||
background-color: rgba(129, 140, 248, 0.1);
|
||||
}
|
||||
|
||||
#uploadArea[dragActive="true"] {
|
||||
border-color: #4ade80;
|
||||
background-color: rgba(74, 222, 128, 0.1);
|
||||
}
|
||||
|
||||
/* 标题样式 */
|
||||
#sectionTitle {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #e2e8f0;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
/* 文件列表 */
|
||||
#fileList, #resultList, #logContent {
|
||||
background-color: rgba(26, 32, 44, 0.3);
|
||||
border: 1px solid #4a5568;
|
||||
border-radius: 6px;
|
||||
min-height: 200px;
|
||||
max-height: 250px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
#fileList::item, #resultList::item, #logContent::item {
|
||||
padding: 8px 12px;
|
||||
border-bottom: 1px solid rgba(74, 85, 104, 0.5);
|
||||
}
|
||||
|
||||
#fileList::item:selected,
|
||||
#resultList::item:selected,
|
||||
#logContent::item:selected {
|
||||
background-color: rgba(129, 140, 248, 0.15);
|
||||
}
|
||||
|
||||
/* 进度条 */
|
||||
#totalProgress, #fileProgress {
|
||||
height: 8px;
|
||||
border-radius: 4px;
|
||||
background-color: #2d3748;
|
||||
}
|
||||
|
||||
#totalProgress::chunk {
|
||||
background-color: #818cf8;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
#fileProgress::chunk {
|
||||
background-color: #4ade80;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
#progressLabel {
|
||||
color: #a0aec0;
|
||||
font-size: 13px;
|
||||
min-width: 70px;
|
||||
}
|
||||
|
||||
#statusLabel {
|
||||
font-size: 14px;
|
||||
color: #e2e8f0;
|
||||
margin-top: 10px;
|
||||
padding: 8px;
|
||||
border-radius: 6px;
|
||||
background-color: rgba(26, 32, 44, 0.3);
|
||||
}
|
||||
|
||||
/* 按钮样式 */
|
||||
#uploadBtn, #convertBtn, #deleteBtn, #downloadBtn {
|
||||
min-height: 40px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
#uploadBtn {
|
||||
min-width: 180px;
|
||||
}
|
||||
|
||||
#convertBtn {
|
||||
background-color: #4f46e5;
|
||||
}
|
||||
|
||||
#convertBtn:disabled {
|
||||
background-color: #4a5568;
|
||||
}
|
||||
|
||||
#downloadBtn {
|
||||
background-color: #10b981;
|
||||
}
|
||||
|
||||
#downloadBtn:hover {
|
||||
background-color: #059669;
|
||||
}
|
||||
|
||||
#deleteBtn {
|
||||
background-color: #ef4444;
|
||||
}
|
||||
|
||||
#deleteBtn:hover {
|
||||
background-color: #dc2626;
|
||||
}
|
||||
|
||||
/* 日志样式 */
|
||||
#logContent::item[type="info"] {
|
||||
color: #a0aec0;
|
||||
}
|
||||
|
||||
#logContent::item[type="success"] {
|
||||
color: #34d399;
|
||||
}
|
||||
|
||||
#logContent::item[type="warning"] {
|
||||
color: #fbbf24;
|
||||
}
|
||||
|
||||
#logContent::item[type="error"] {
|
||||
color: #f87171;
|
||||
}
|
||||
"""
|
||||
|
||||
def dragEnterEvent(self, event: QDragEnterEvent):
|
||||
if event.mimeData().hasUrls():
|
||||
event.acceptProposedAction()
|
||||
# 高亮显示拖拽区域
|
||||
self.findChild(QFrame, "uploadArea").setProperty("dragActive", True)
|
||||
self.findChild(QFrame, "uploadArea").style().polish(self.findChild(QFrame, "uploadArea"))
|
||||
self.upload_text.setText("释放文件以添加")
|
||||
|
||||
def dragLeaveEvent(self, event: QDragLeaveEvent):
|
||||
# 恢复拖拽区域样式
|
||||
self.findChild(QFrame, "uploadArea").setProperty("dragActive", False)
|
||||
self.findChild(QFrame, "uploadArea").style().polish(self.findChild(QFrame, "uploadArea"))
|
||||
self.upload_text.setText("拖拽文件或文件夹到此处\n或点击上传")
|
||||
|
||||
def dropEvent(self, event: QDropEvent):
|
||||
# 恢复拖拽区域样式
|
||||
self.findChild(QFrame, "uploadArea").setProperty("dragActive", False)
|
||||
self.findChild(QFrame, "uploadArea").style().polish(self.findChild(QFrame, "uploadArea"))
|
||||
self.upload_text.setText("拖拽文件或文件夹到此处\n或点击上传")
|
||||
|
||||
urls = event.mimeData().urls()
|
||||
files = []
|
||||
added_count = 0
|
||||
|
||||
for url in urls:
|
||||
path = url.toLocalFile()
|
||||
@@ -207,46 +436,65 @@ class WordToPDFPage(QWidget):
|
||||
files.append(path)
|
||||
|
||||
if files:
|
||||
self.add_files(files)
|
||||
added_count = self.add_files(files)
|
||||
self.add_log(f"添加了 {added_count} 个文件", "success")
|
||||
else:
|
||||
self.add_log("未找到有效的Word文件", "warning")
|
||||
|
||||
def handle_upload(self):
|
||||
"""处理文件或文件夹上传"""
|
||||
# 使用getExistingDirectory获取文件夹
|
||||
folder = QFileDialog.getExistingDirectory(self, "选择文件夹")
|
||||
if folder:
|
||||
files = []
|
||||
for root, _, filenames in os.walk(folder):
|
||||
for f in filenames:
|
||||
if f.lower().endswith(('.doc', '.docx')):
|
||||
files.append(os.path.join(root, f))
|
||||
self.add_files(files)
|
||||
options = QFileDialog.Options()
|
||||
files, _ = QFileDialog.getOpenFileNames(
|
||||
self, "选择Word文件", "", "Word Files (*.doc *.docx);;All Files (*)", options=options
|
||||
)
|
||||
|
||||
if files:
|
||||
added_count = self.add_files(files)
|
||||
self.add_log(f"添加了 {added_count} 个文件", "success")
|
||||
else:
|
||||
# 如果用户没有选择文件夹,则选择文件
|
||||
files, _ = QFileDialog.getOpenFileNames(
|
||||
self, "选择Word文件", "", "Word Files (*.doc *.docx);;All Files (*)"
|
||||
)
|
||||
if files:
|
||||
self.add_files(files)
|
||||
folder = QFileDialog.getExistingDirectory(self, "选择包含Word文件的文件夹")
|
||||
if folder:
|
||||
files = []
|
||||
for root, _, filenames in os.walk(folder):
|
||||
for f in filenames:
|
||||
if f.lower().endswith(('.doc', '.docx')):
|
||||
files.append(os.path.join(root, f))
|
||||
|
||||
if files:
|
||||
added_count = self.add_files(files)
|
||||
self.add_log(f"从文件夹添加了 {added_count} 个文件", "success")
|
||||
else:
|
||||
self.add_log("文件夹中没有找到Word文件", "warning")
|
||||
|
||||
def add_files(self, files):
|
||||
"""添加文件到列表,避免重复"""
|
||||
added_count = 0
|
||||
for file in files:
|
||||
if file not in self.files and os.path.exists(file):
|
||||
self.files.append(file)
|
||||
item = QListWidgetItem(os.path.basename(file))
|
||||
item.setData(Qt.UserRole, file)
|
||||
self.file_list.addItem(item)
|
||||
added_count += 1
|
||||
return added_count
|
||||
|
||||
def delete_selected(self):
|
||||
"""删除选中的文件"""
|
||||
for item in self.file_list.selectedItems():
|
||||
selected_items = self.file_list.selectedItems()
|
||||
if not selected_items:
|
||||
self.add_log("请先选择要删除的文件", "warning")
|
||||
return
|
||||
|
||||
for item in selected_items:
|
||||
self.files.remove(item.data(Qt.UserRole))
|
||||
self.file_list.takeItem(self.file_list.row(item))
|
||||
|
||||
self.add_log(f"删除了 {len(selected_items)} 个文件", "info")
|
||||
|
||||
def start_conversion(self):
|
||||
"""开始转换文件"""
|
||||
if not self.files:
|
||||
QMessageBox.warning(self, "警告", "请先添加要转换的文件!")
|
||||
self.add_log("请先添加要转换的文件!", "warning")
|
||||
return
|
||||
|
||||
# 重置状态
|
||||
@@ -254,7 +502,18 @@ class WordToPDFPage(QWidget):
|
||||
self.result_list.clear()
|
||||
self.log_content.clear()
|
||||
self.download_btn.setEnabled(False)
|
||||
self.convert_btn.setEnabled(False)
|
||||
|
||||
# 重置进度条
|
||||
self.total_progress.setValue(0)
|
||||
self.file_progress.setValue(0)
|
||||
self.status_label.setText("正在准备转换...")
|
||||
|
||||
# 添加开始日志
|
||||
self.add_log("开始转换任务", "info")
|
||||
self.add_log(f"共 {len(self.files)} 个文件待处理", "info")
|
||||
|
||||
# 创建并启动转换线程
|
||||
self.conversion_thread = ConversionThread(self.files.copy())
|
||||
self.conversion_thread.progress_updated.connect(self.update_progress)
|
||||
self.conversion_thread.conversion_done.connect(self.handle_conversion_done)
|
||||
@@ -266,10 +525,28 @@ class WordToPDFPage(QWidget):
|
||||
self.total_progress.setMaximum(total)
|
||||
self.total_progress.setValue(current)
|
||||
|
||||
self.file_progress.setMaximum(100)
|
||||
# 重置文件进度为0
|
||||
self.file_progress.setValue(0)
|
||||
self.file_progress.setMaximum(100)
|
||||
|
||||
self.log_content.addItem(f"正在转换: {filename} ({current}/{total})")
|
||||
# 更新状态
|
||||
self.status_label.setText(f"正在转换: {os.path.basename(filename)}")
|
||||
self.add_log(f"开始转换: {os.path.basename(filename)} ({current}/{total})", "info")
|
||||
|
||||
# 模拟文件进度动画
|
||||
self.animate_file_progress()
|
||||
|
||||
def animate_file_progress(self):
|
||||
"""模拟文件转换进度动画(实际应用中应由实际进度驱动)"""
|
||||
self.file_progress_value = 0
|
||||
|
||||
def update_progress():
|
||||
if self.file_progress_value < 100:
|
||||
self.file_progress_value += 2
|
||||
self.file_progress.setValue(self.file_progress_value)
|
||||
QTimer.singleShot(50, update_progress)
|
||||
|
||||
update_progress()
|
||||
|
||||
def handle_conversion_done(self, file_path, success, message):
|
||||
"""处理单个文件转换完成"""
|
||||
@@ -291,28 +568,50 @@ class WordToPDFPage(QWidget):
|
||||
item.setData(Qt.UserRole, pdf_path)
|
||||
self.result_list.addItem(item)
|
||||
|
||||
self.log_content.addItem(f"✅ 转换成功: {basename} -> {os.path.basename(pdf_path)}")
|
||||
self.add_log(f"✅ 转换成功: {basename} -> {os.path.basename(pdf_path)}", "success")
|
||||
else:
|
||||
# 失败时显示错误信息
|
||||
self.log_content.addItem(f"❌ 转换失败: {basename} - {message}")
|
||||
self.add_log(f"❌ 转换失败: {basename} - {message}", "error")
|
||||
|
||||
def handle_finished_all(self):
|
||||
"""所有文件转换完成"""
|
||||
self.log_content.addItem(f"🎉 全部转换完成! 共转换 {len(self.converted_files)} 个文件")
|
||||
self.download_btn.setEnabled(True)
|
||||
success_count = len(self.converted_files)
|
||||
fail_count = len(self.files) - success_count
|
||||
|
||||
# 更新状态
|
||||
self.status_label.setText(f"转换完成! 成功: {success_count}, 失败: {fail_count}")
|
||||
self.total_progress.setValue(self.total_progress.maximum())
|
||||
self.file_progress.setValue(100)
|
||||
self.convert_btn.setEnabled(True)
|
||||
self.download_btn.setEnabled(bool(self.converted_files))
|
||||
|
||||
# 添加完成日志
|
||||
if success_count:
|
||||
self.add_log(f"🎉 全部转换完成! 成功转换 {success_count} 个文件", "success")
|
||||
if fail_count:
|
||||
self.add_log(f"⚠️ 有 {fail_count} 个文件转换失败", "warning")
|
||||
|
||||
if self.converted_files:
|
||||
self.add_log("点击'全部下载'按钮保存转换后的文件", "info")
|
||||
|
||||
def download_all(self):
|
||||
"""下载所有转换后的文件"""
|
||||
if not self.converted_files:
|
||||
self.add_log("没有可下载的文件", "warning")
|
||||
return
|
||||
|
||||
folder = QFileDialog.getExistingDirectory(self, "选择保存位置")
|
||||
if not folder:
|
||||
self.add_log("下载已取消", "info")
|
||||
return
|
||||
|
||||
success = 0
|
||||
errors = []
|
||||
|
||||
# 开始下载
|
||||
self.status_label.setText("正在下载文件...")
|
||||
self.add_log(f"开始下载 {len(self.converted_files)} 个文件到 {folder}", "info")
|
||||
|
||||
for pdf_path in self.converted_files.values():
|
||||
try:
|
||||
file_name = os.path.basename(pdf_path)
|
||||
@@ -323,16 +622,35 @@ class WordToPDFPage(QWidget):
|
||||
with open(destination, 'wb') as dest_file:
|
||||
dest_file.write(src_file.read())
|
||||
success += 1
|
||||
self.add_log(f"已下载: {file_name}", "success")
|
||||
except Exception as e:
|
||||
errors.append(f"{os.path.basename(pdf_path)}: {str(e)}")
|
||||
self.add_log(f"下载失败: {os.path.basename(pdf_path)} - {str(e)}", "error")
|
||||
|
||||
# 更新状态
|
||||
self.status_label.setText(f"下载完成! 成功: {success}, 失败: {len(errors)}")
|
||||
|
||||
# 显示结果
|
||||
if errors:
|
||||
error_msg = "\n".join(errors[:10]) # 最多显示10个错误
|
||||
if len(errors) > 10:
|
||||
error_msg += f"\n...等共 {len(errors)} 个错误"
|
||||
QMessageBox.warning(self, "部分文件下载失败", f"成功下载 {success} 个文件\n失败文件:\n{error_msg}")
|
||||
self.add_log(f"⚠️ 部分文件下载失败 ({len(errors)} 个)", "warning")
|
||||
else:
|
||||
QMessageBox.information(self, "下载完成", f"已成功下载 {success} 个文件到 {folder}")
|
||||
self.add_log(f"✅ 全部文件下载成功!", "success")
|
||||
|
||||
self.log_content.addItem(f"已下载 {success} 个文件到 {folder}")
|
||||
self.add_log(f"文件已保存到: {folder}", "info")
|
||||
|
||||
def add_log(self, message, log_type="info"):
|
||||
"""添加日志项"""
|
||||
item = QListWidgetItem(f"[{datetime.datetime.now().strftime('%H:%M:%S')}] {message}")
|
||||
item.setData(Qt.UserRole + 1, log_type) # 存储日志类型用于样式
|
||||
self.log_content.addItem(item)
|
||||
self.log_content.scrollToBottom()
|
||||
|
||||
def paintEvent(self, event):
|
||||
"""为拖拽区域添加悬停效果"""
|
||||
if self.underMouse():
|
||||
self.findChild(QFrame, "uploadArea").setProperty("hover", True)
|
||||
self.findChild(QFrame, "uploadArea").style().polish(self.findChild(QFrame, "uploadArea"))
|
||||
else:
|
||||
self.findChild(QFrame, "uploadArea").setProperty("hover", False)
|
||||
self.findChild(QFrame, "uploadArea").style().polish(self.findChild(QFrame, "uploadArea"))
|
||||
super().paintEvent(event)
|
||||
Reference in New Issue
Block a user