初始化

This commit is contained in:
2025-12-11 09:47:00 +08:00
commit 99933d62f8
65 changed files with 121176 additions and 0 deletions

495
ui/workspace.py Normal file
View File

@@ -0,0 +1,495 @@
"""
右侧工作区基类
- 装饰性背景光晕
- 毛玻璃效果头部
- 面包屑导航 + 版本标签
- 上传区域(虚线边框+图标)
"""
from PySide6.QtWidgets import (
QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushButton,
QFrame, QScrollArea, QFileDialog, QGraphicsDropShadowEffect,
QGraphicsBlurEffect
)
from PySide6.QtCore import Signal, Qt, QSize
from PySide6.QtGui import (
QFont, QDragEnterEvent, QDropEvent, QPainter,
QColor, QRadialGradient, QLinearGradient, QPen, QBrush
)
class BackgroundDecoration(QWidget):
"""装饰性背景 - 渐变光晕"""
def __init__(self, parent=None):
super().__init__(parent)
self.setAttribute(Qt.WidgetAttribute.WA_TransparentForMouseEvents)
def paintEvent(self, event):
painter = QPainter(self)
painter.setRenderHint(QPainter.RenderHint.Antialiasing)
# 右上角奶酪色光晕
gradient1 = QRadialGradient(
self.width() + 50, -50, # 圆心
500 # 半径
)
gradient1.setColorAt(0, QColor(251, 191, 36, 25)) # cheese-500/10
gradient1.setColorAt(1, QColor(251, 191, 36, 0))
painter.setBrush(gradient1)
painter.setPen(Qt.PenStyle.NoPen)
painter.drawEllipse(
int(self.width() * 0.5), int(-self.height() * 0.2),
int(self.width() * 0.7), int(self.height() * 0.7)
)
# 左侧紫色光晕
gradient2 = QRadialGradient(
self.width() * 0.1, self.height() * 0.3,
350
)
gradient2.setColorAt(0, QColor(139, 92, 246, 12)) # purple-500/5
gradient2.setColorAt(1, QColor(139, 92, 246, 0))
painter.setBrush(gradient2)
painter.drawEllipse(
int(-self.width() * 0.1), int(self.height() * 0.1),
int(self.width() * 0.5), int(self.height() * 0.5)
)
class UploadArea(QFrame):
"""上传区域 - 还原HTML设计样式"""
files_dropped = Signal(list)
def __init__(self, accept_types: str = "所有文件", parent=None):
super().__init__(parent)
self.accept_types = accept_types
self.setAcceptDrops(True)
self.setMinimumHeight(208) # h-52 = 13rem = 208px
self.setCursor(Qt.CursorShape.PointingHandCursor)
self._hovered = False
self.setup_ui()
def setup_ui(self):
layout = QVBoxLayout(self)
layout.setAlignment(Qt.AlignmentFlag.AlignCenter)
layout.setSpacing(12)
# 图标容器 (圆形背景)
self.icon_container = QFrame()
self.icon_container.setFixedSize(56, 56)
self.icon_container.setStyleSheet("""
background: #334155;
border-radius: 28px;
""")
icon_layout = QVBoxLayout(self.icon_container)
icon_layout.setContentsMargins(0, 0, 0, 0)
icon_layout.setAlignment(Qt.AlignmentFlag.AlignCenter)
# 云上传图标
icon_label = QLabel("☁️")
icon_label.setFont(QFont("Segoe UI Emoji", 24))
icon_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
icon_label.setStyleSheet("background: transparent;")
icon_layout.addWidget(icon_label)
layout.addWidget(self.icon_container, 0, Qt.AlignmentFlag.AlignCenter)
# 主文字
self.title_label = QLabel("点击或拖拽文件到此处")
self.title_label.setStyleSheet("color: white; font-size: 16px; font-weight: 500; background: transparent;")
self.title_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
layout.addWidget(self.title_label)
# 副文字
self.desc_label = QLabel(f"支持批量上传 ({self.accept_types})")
self.desc_label.setStyleSheet("color: #64748b; font-size: 13px; background: transparent;")
self.desc_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
layout.addWidget(self.desc_label)
self.update_style()
def update_style(self):
if self._hovered:
self.setStyleSheet("""
UploadArea {
background: rgba(30, 41, 59, 0.5);
border: 2px dashed rgba(251, 191, 36, 0.5);
border-radius: 16px;
}
""")
else:
self.setStyleSheet("""
UploadArea {
background: rgba(30, 41, 59, 0.3);
border: 2px dashed #334155;
border-radius: 16px;
}
""")
def enterEvent(self, event):
self._hovered = True
self.update_style()
def leaveEvent(self, event):
self._hovered = False
self.update_style()
def mousePressEvent(self, event):
if event.button() == Qt.MouseButton.LeftButton:
self.open_file_dialog()
def open_file_dialog(self):
files, _ = QFileDialog.getOpenFileNames(
self, "选择文件", "", f"{self.accept_types}"
)
if files:
self.files_dropped.emit(files)
def dragEnterEvent(self, event: QDragEnterEvent):
if event.mimeData().hasUrls():
event.acceptProposedAction()
self._hovered = True
self.update_style()
def dragLeaveEvent(self, event):
self._hovered = False
self.update_style()
def dropEvent(self, event: QDropEvent):
self._hovered = False
self.update_style()
files = []
for url in event.mimeData().urls():
if url.isLocalFile():
files.append(url.toLocalFile())
if files:
self.files_dropped.emit(files)
class GlassHeader(QFrame):
"""毛玻璃效果头部"""
def __init__(self, parent=None):
super().__init__(parent)
self.setFixedHeight(80)
self.setStyleSheet("""
GlassHeader {
background: rgba(30, 41, 59, 0.7);
border-bottom: 1px solid rgba(51, 65, 85, 0.3);
}
""")
class BaseWorkspace(QWidget):
"""工作区基类"""
def __init__(self, parent=None):
super().__init__(parent)
self.setObjectName("workspace")
self.setup_base_ui()
def setup_base_ui(self):
# 主布局
self.main_layout = QVBoxLayout(self)
self.main_layout.setContentsMargins(0, 0, 0, 0)
self.main_layout.setSpacing(0)
# 背景装饰层
self.bg_decoration = BackgroundDecoration(self)
# 头部区域
self.header = GlassHeader()
header_layout = QVBoxLayout(self.header)
header_layout.setContentsMargins(32, 16, 32, 16)
header_layout.setSpacing(4)
# 面包屑
breadcrumb_layout = QHBoxLayout()
breadcrumb_layout.setSpacing(8)
self.breadcrumb_category = QLabel("首页")
self.breadcrumb_category.setStyleSheet("color: #64748b; font-size: 12px;")
breadcrumb_layout.addWidget(self.breadcrumb_category)
arrow = QLabel("")
arrow.setStyleSheet("color: #64748b; font-size: 12px;")
breadcrumb_layout.addWidget(arrow)
self.breadcrumb_tool = QLabel("控制台")
self.breadcrumb_tool.setStyleSheet("color: #fbbf24; font-size: 12px;")
breadcrumb_layout.addWidget(self.breadcrumb_tool)
breadcrumb_layout.addStretch()
header_layout.addLayout(breadcrumb_layout)
# 标题行
title_layout = QHBoxLayout()
# 标题 + 版本标签
title_container = QHBoxLayout()
title_container.setSpacing(8)
self.title_label = QLabel("欢迎回来")
self.title_label.setStyleSheet("color: white; font-size: 22px; font-weight: 700;")
title_container.addWidget(self.title_label)
self.version_label = QLabel("v2.1")
self.version_label.setStyleSheet("""
color: #64748b;
font-size: 12px;
background: #1e293b;
border: 1px solid #334155;
border-radius: 4px;
padding: 2px 8px;
""")
self.version_label.setVisible(False)
title_container.addWidget(self.version_label)
title_container.addStretch()
title_layout.addLayout(title_container, 1)
# 按钮区域
btn_layout = QHBoxLayout()
btn_layout.setSpacing(12)
self.history_btn = QPushButton("🕐 历史")
self.history_btn.setStyleSheet("""
QPushButton {
background: transparent;
border: 1px solid #334155;
border-radius: 8px;
color: #cbd5e1;
padding: 8px 16px;
font-size: 13px;
}
QPushButton:hover {
background: #334155;
}
""")
btn_layout.addWidget(self.history_btn)
self.export_btn = QPushButton("📤 导出")
self.export_btn.setStyleSheet("""
QPushButton {
background: #f59e0b;
border: none;
border-radius: 8px;
color: white;
padding: 8px 16px;
font-size: 13px;
font-weight: 500;
}
QPushButton:hover {
background: #fbbf24;
}
""")
btn_layout.addWidget(self.export_btn)
title_layout.addLayout(btn_layout)
header_layout.addLayout(title_layout)
self.main_layout.addWidget(self.header)
# 内容区域
scroll_area = QScrollArea()
scroll_area.setWidgetResizable(True)
scroll_area.setFrameShape(QFrame.Shape.NoFrame)
scroll_area.setStyleSheet("background: transparent;")
self.content_widget = QWidget()
self.content_widget.setStyleSheet("background: transparent;")
self.content_layout = QVBoxLayout(self.content_widget)
self.content_layout.setContentsMargins(32, 24, 32, 24)
self.content_layout.setSpacing(24)
scroll_area.setWidget(self.content_widget)
self.main_layout.addWidget(scroll_area, 1)
def resizeEvent(self, event):
super().resizeEvent(event)
# 调整背景装饰大小
self.bg_decoration.setGeometry(0, 0, self.width(), self.height())
self.bg_decoration.lower()
def set_breadcrumb(self, category: str, tool: str):
self.breadcrumb_category.setText(category)
self.breadcrumb_tool.setText(tool)
def set_title(self, title: str):
self.title_label.setText(title)
self.version_label.setVisible(bool(title and title != "欢迎回来"))
def clear_content(self):
while self.content_layout.count():
item = self.content_layout.takeAt(0)
if item.widget():
item.widget().deleteLater()
class ShortcutCard(QFrame):
"""快捷入口卡片"""
clicked = Signal()
def __init__(self, data: dict, parent=None):
super().__init__(parent)
self.data = data
self.setFixedSize(200, 140)
self.setCursor(Qt.CursorShape.PointingHandCursor)
self._hovered = False
self.setup_ui()
def setup_ui(self):
layout = QVBoxLayout(self)
layout.setContentsMargins(16, 16, 16, 16)
layout.setSpacing(8)
# 图标
color = self.data.get("color", "#64748b")
self.icon_frame = QFrame()
self.icon_frame.setFixedSize(40, 40)
r, g, b = self.hex_to_rgb(color)
self.icon_frame.setStyleSheet(f"""
background: rgba({r}, {g}, {b}, 0.1);
border-radius: 8px;
""")
icon_layout = QVBoxLayout(self.icon_frame)
icon_layout.setContentsMargins(0, 0, 0, 0)
icon_layout.setAlignment(Qt.AlignmentFlag.AlignCenter)
icon = QLabel(self.data.get("icon", ""))
icon.setFont(QFont("Segoe UI Emoji", 16))
icon.setStyleSheet(f"color: {color}; background: transparent;")
icon_layout.addWidget(icon)
layout.addWidget(self.icon_frame)
# 名称
name = QLabel(self.data.get("name", ""))
name.setStyleSheet("color: white; font-size: 14px; font-weight: 500;")
layout.addWidget(name)
# 描述
desc = QLabel(self.data.get("desc", ""))
desc.setStyleSheet("color: #64748b; font-size: 11px;")
layout.addWidget(desc)
layout.addStretch()
self.update_style()
def update_style(self):
if self._hovered:
self.setStyleSheet("""
ShortcutCard {
background: rgba(30, 41, 59, 0.8);
border: 1px solid rgba(251, 191, 36, 0.5);
border-radius: 16px;
}
""")
else:
self.setStyleSheet("""
ShortcutCard {
background: rgba(30, 41, 59, 0.5);
border: 1px solid #334155;
border-radius: 16px;
}
""")
def enterEvent(self, event):
self._hovered = True
self.update_style()
def leaveEvent(self, event):
self._hovered = False
self.update_style()
def mousePressEvent(self, event):
if event.button() == Qt.MouseButton.LeftButton:
self.clicked.emit()
@staticmethod
def hex_to_rgb(hex_color: str) -> tuple:
hex_color = hex_color.lstrip('#')
return tuple(int(hex_color[i:i+2], 16) for i in (0, 2, 4))
class WelcomeWorkspace(BaseWorkspace):
"""欢迎页面工作区"""
tool_clicked = Signal(str, str)
def __init__(self, parent=None):
super().__init__(parent)
self.setup_welcome_ui()
def setup_welcome_ui(self):
self.set_breadcrumb("首页", "控制台")
self.set_title("欢迎回来")
self.version_label.setVisible(False)
self.history_btn.hide()
self.export_btn.hide()
# 中心内容
center_widget = QWidget()
center_layout = QVBoxLayout(center_widget)
center_layout.setAlignment(Qt.AlignmentFlag.AlignCenter)
center_layout.setSpacing(24)
# Logo区域
logo_container = QFrame()
logo_container.setFixedSize(128, 128)
logo_container.setStyleSheet("""
background: #1e293b;
border: 1px solid #334155;
border-radius: 64px;
""")
logo_layout = QVBoxLayout(logo_container)
logo_layout.setAlignment(Qt.AlignmentFlag.AlignCenter)
logo = QLabel("🧀")
logo.setFont(QFont("Segoe UI Emoji", 48))
logo.setStyleSheet("color: #fbbf24; background: transparent;")
logo_layout.addWidget(logo)
center_layout.addWidget(logo_container, 0, Qt.AlignmentFlag.AlignCenter)
# 标题
title = QLabel("开始你的创作")
title.setFont(QFont("Microsoft YaHei", 28, QFont.Weight.Bold))
title.setStyleSheet("color: white;")
title.setAlignment(Qt.AlignmentFlag.AlignCenter)
center_layout.addWidget(title)
# 描述
desc = QLabel("从左侧选择一个工具。无论是压缩图片、拆分PDF还是处理数据我们都能搞定。")
desc.setStyleSheet("color: #94a3b8; font-size: 14px;")
desc.setAlignment(Qt.AlignmentFlag.AlignCenter)
desc.setWordWrap(True)
desc.setMaximumWidth(500)
center_layout.addWidget(desc)
center_layout.addSpacing(20)
# 快捷入口
shortcuts_layout = QHBoxLayout()
shortcuts_layout.setSpacing(16)
shortcuts_layout.setAlignment(Qt.AlignmentFlag.AlignCenter)
shortcuts = [
{"id": "img-compress", "category": "image", "name": "图片压缩", "desc": "智能无损压缩", "icon": "🖼", "color": "#3b82f6"},
{"id": "pdf-split", "category": "pdf", "name": "PDF 拆分", "desc": "提取特定页面", "icon": "✂️", "color": "#ef4444"},
{"id": "img-convert", "category": "image", "name": "格式转换", "desc": "格式互转工具", "icon": "🔄", "color": "#22c55e"},
]
for item in shortcuts:
card = ShortcutCard(item)
card.clicked.connect(lambda i=item: self.tool_clicked.emit(i["id"], i["category"]))
shortcuts_layout.addWidget(card)
center_layout.addLayout(shortcuts_layout)
self.content_layout.addStretch()
self.content_layout.addWidget(center_widget)
self.content_layout.addStretch()