PDF拆分、合并

This commit is contained in:
2025-06-08 23:55:24 +08:00
parent 0323e92151
commit 7d72f1f0e9
10 changed files with 250 additions and 56 deletions

View File

@@ -1,7 +1,9 @@
import sys import sys
from PyQt5.QtSvg import QSvgRenderer
from PyQt5.QtWidgets import QApplication, QMainWindow, QStackedWidget, QHBoxLayout, QWidget, QMessageBox, QPushButton, \ from PyQt5.QtWidgets import QApplication, QMainWindow, QStackedWidget, QHBoxLayout, QWidget, QMessageBox, QPushButton, \
QLabel, QVBoxLayout, QFrame, QSizePolicy QLabel, QVBoxLayout, QFrame, QSizePolicy
from PyQt5.QtCore import Qt, QPoint from PyQt5.QtCore import Qt, QPoint, QSize, QByteArray
from views.login import LoginPage from views.login import LoginPage
from views.nav import NavBar from views.nav import NavBar
from views.home import Home from views.home import Home
@@ -9,13 +11,13 @@ from views.word_to_pdf import WordToPDFPage
from views.pdf_tools_page import PDFToolsPage from views.pdf_tools_page import PDFToolsPage
from views.app_center import AppCenter from views.app_center import AppCenter
from views.history import HistoryPage from views.history import HistoryPage
from PyQt5.QtGui import QPalette, QColor, QFont from PyQt5.QtGui import QPalette, QColor, QFont, QPixmap, QIcon, QPainter
from styles.styles import STYLES_TXT from styles.styles import STYLES_TXT
class CustomTitleBar(QWidget): class CustomTitleBar(QWidget):
"""自定义标题栏控件""" """现代化标题栏控件直接设置SVG图标"""
def __init__(self, parent=None): def __init__(self, parent=None):
super().__init__(parent) super().__init__(parent)
@@ -24,55 +26,212 @@ class CustomTitleBar(QWidget):
self.drag_position = QPoint() self.drag_position = QPoint()
self.setObjectName("customTitleBar") self.setObjectName("customTitleBar")
self.init_ui() self.init_ui()
self.set_style()
self.setMinimumHeight(48)
def init_ui(self): def init_ui(self):
"""初始化标题栏UI""" """初始化标题栏UI"""
layout = QHBoxLayout(self) layout = QHBoxLayout(self)
layout.setContentsMargins(15, 8, 15, 8) layout.setContentsMargins(15, 8, 15, 8)
layout.setSpacing(12) layout.setSpacing(8)
# 添加应用图标和标题 # 应用图标和标题
title_container = QWidget()
title_layout = QHBoxLayout(title_container)
title_layout.setContentsMargins(0, 0, 0, 0)
title_layout.setSpacing(10)
# 应用图标使用纯CSS实现
icon_label = QLabel()
icon_label.setObjectName("appIcon")
icon_label.setFixedSize(24, 24)
# 应用标题
self.title_label = QLabel("奶酪云工具箱") self.title_label = QLabel("奶酪云工具箱")
self.title_label.setObjectName("titleLabel") self.title_label.setObjectName("titleLabel")
self.title_label.setFont(QFont("Microsoft YaHei UI", 12, QFont.Bold)) self.title_label.setFont(QFont("Microsoft YaHei UI", 10, QFont.DemiBold))
self.title_label.setAlignment(Qt.AlignCenter)
# 添加拖拽区域(占位区域) # 版本号
version_label = QLabel("v2.1.0")
version_label.setObjectName("versionLabel")
version_label.setFont(QFont("Microsoft YaHei UI", 8))
title_layout.addWidget(icon_label)
title_layout.addWidget(self.title_label)
title_layout.addWidget(version_label)
# 拖拽区域
self.drag_area = QWidget() self.drag_area = QWidget()
self.drag_area.setObjectName("dragArea")
self.drag_area.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred) self.drag_area.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
self.drag_area.setObjectName("dragArea")
# 添加窗口控制按钮 # 窗口控制按钮
self.minimize_btn = QPushButton("") buttons_container = QWidget()
buttons_layout = QHBoxLayout(buttons_container)
buttons_layout.setContentsMargins(0, 0, 0, 0)
buttons_layout.setSpacing(4)
# 最小化按钮直接设置SVG
self.minimize_btn = QPushButton()
self.minimize_btn.setObjectName("minimizeBtn") self.minimize_btn.setObjectName("minimizeBtn")
self.minimize_btn.setFixedSize(30, 30) self.minimize_btn.setFixedSize(34, 28)
self.minimize_btn.setToolTip("最小化")
self.minimize_btn.clicked.connect(lambda: self.parent.showMinimized()) self.minimize_btn.clicked.connect(lambda: self.parent.showMinimized())
self.minimize_btn.setIcon(self.create_svg_icon("""
<svg width="12" height="2" viewBox="0 0 12 2" xmlns="http://www.w3.org/2000/svg">
<rect width="12" height="2"/>
</svg>
""", "#8A92A6"))
self.maximize_btn = QPushButton("") # 最大化按钮直接设置SVG
self.maximize_btn = QPushButton()
self.maximize_btn.setObjectName("maximizeBtn") self.maximize_btn.setObjectName("maximizeBtn")
self.maximize_btn.setFixedSize(30, 30) self.maximize_btn.setFixedSize(34, 28)
self.maximize_btn.setToolTip("最大化")
self.maximize_btn.clicked.connect(self.toggle_maximize) self.maximize_btn.clicked.connect(self.toggle_maximize)
self.maximize_btn.setIcon(self.create_svg_icon("""
<svg width="10" height="10" viewBox="0 0 10 10" xmlns="http://www.w3.org/2000/svg">
<rect x="0.5" y="0.5" width="9" height="9" stroke-linejoin="round"/>
</svg>
""", "#8A92A6"))
self.close_btn = QPushButton("×") # 关闭按钮直接设置SVG
self.close_btn = QPushButton()
self.close_btn.setObjectName("closeBtn") self.close_btn.setObjectName("closeBtn")
self.close_btn.setFixedSize(30, 30) self.close_btn.setFixedSize(34, 28)
self.close_btn.setToolTip("关闭")
self.close_btn.clicked.connect(self.parent.close) self.close_btn.clicked.connect(self.parent.close)
self.close_btn.setIcon(self.create_svg_icon("""
<svg width="10" height="10" viewBox="0 0 10 10" xmlns="http://www.w3.org/2000/svg">
<path d="M1 1L9 9M9 1L1 9" stroke-linecap="round"/>
</svg>
""", "#8A92A6"))
# 将控件添加到布局中 buttons_layout.addWidget(self.minimize_btn)
layout.addWidget(self.title_label) buttons_layout.addWidget(self.maximize_btn)
buttons_layout.addWidget(self.close_btn)
# 添加所有组件
layout.addWidget(title_container)
layout.addWidget(self.drag_area) layout.addWidget(self.drag_area)
layout.addWidget(self.minimize_btn) layout.addWidget(buttons_container)
layout.addWidget(self.maximize_btn)
layout.addWidget(self.close_btn) def create_svg_icon(self, svg_data, color):
"""从SVG数据创建QIcon"""
# 创建SVG字节数组
svg_data = svg_data.replace('<svg', f'<svg stroke="{color}"')
byte_array = QByteArray()
byte_array.append(svg_data.encode('utf-8'))
# 创建SVG渲染器
svg_renderer = QSvgRenderer(byte_array)
# 创建图标
icon = QIcon()
# 正常状态
normal_pixmap = QPixmap(16, 16)
normal_pixmap.fill(Qt.transparent)
painter = QPainter(normal_pixmap)
svg_renderer.render(painter)
painter.end()
icon.addPixmap(normal_pixmap, QIcon.Normal, QIcon.Off)
# 悬停状态
hover_pixmap = QPixmap(16, 16)
hover_pixmap.fill(Qt.transparent)
painter = QPainter(hover_pixmap)
svg_renderer.render(painter)
painter.end()
icon.addPixmap(hover_pixmap, QIcon.Active, QIcon.Off)
return icon
def set_style(self):
"""设置标题栏样式"""
self.setStyleSheet("""
/* 标题栏整体样式 */
QWidget#customTitleBar {
background-color: #252836;
border-top-left-radius: 12px;
border-top-right-radius: 12px;
}
/* 应用图标 */
QLabel#appIcon {
min-width: 24px;
min-height: 24px;
background-color: #4f46e5;
border-radius: 6px;
}
/* 应用标题样式 */
QLabel#titleLabel {
color: #FFFFFF;
font-weight: 600;
letter-spacing: 0.5px;
}
/* 版本标签样式 */
QLabel#versionLabel {
color: #8A92A6;
font-size: 8pt;
padding-top: 2px;
}
/* 按钮基础样式 */
QPushButton {
background-color: transparent;
border: none;
border-radius: 6px;
font-size: 14pt;
}
/* 最小化按钮 */
QPushButton#minimizeBtn:hover {
background-color: rgba(255, 255, 255, 0.1);
}
QPushButton#minimizeBtn:pressed {
background-color: rgba(255, 255, 255, 0.15);
}
/* 最大化按钮 */
QPushButton#maximizeBtn:hover {
background-color: rgba(255, 255, 255, 0.1);
}
QPushButton#maximizeBtn:pressed {
background-color: rgba(255, 255, 255, 0.15);
}
/* 关闭按钮 */
QPushButton#closeBtn:hover {
background-color: #E85050;
}
QPushButton#closeBtn:pressed {
background-color: #D84242;
}
""")
def toggle_maximize(self): def toggle_maximize(self):
"""切换窗口最大化/正常状态""" """切换窗口最大化/正常状态"""
if self.parent.isMaximized(): if self.parent.isMaximized():
self.parent.showNormal() self.parent.showNormal()
self.maximize_btn.setText("") # 更新最大化按钮图标
self.maximize_btn.setIcon(self.create_svg_icon("""
<svg width="10" height="10" viewBox="0 0 10 10" xmlns="http://www.w3.org/2000/svg">
<rect x="0.5" y="0.5" width="9" height="9" stroke-linejoin="round"/>
</svg>
""", "#8A92A6"))
else: else:
self.parent.showMaximized() self.parent.showMaximized()
self.maximize_btn.setText("") # 更新最大化按钮图标为还原状态
self.maximize_btn.setIcon(self.create_svg_icon("""
<svg width="10" height="10" viewBox="0 0 10 10" xmlns="http://www.w3.org/2000/svg">
<path d="M3.5 0.5V3.5H0.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M6.5 9.5V6.5H9.5" stroke-linecap="round" stroke-linejoin="round"/>
<rect x="3.5" y="3.5" width="6" height="6" stroke-linejoin="round"/>
</svg>
""", "#8A92A6"))
def mousePressEvent(self, event): def mousePressEvent(self, event):
"""鼠标按下事件处理""" """鼠标按下事件处理"""
@@ -92,7 +251,6 @@ class CustomTitleBar(QWidget):
self.toggle_maximize() self.toggle_maximize()
event.accept() event.accept()
class MainWindow(QMainWindow): class MainWindow(QMainWindow):
def __init__(self): def __init__(self):
super().__init__() super().__init__()

8
v1/resources.qrc Normal file
View File

@@ -0,0 +1,8 @@
<RCC>
<qresource prefix="/icons">
<file>minimize.svg</file>
<file>maximize.svg</file>
<file>restore.svg</file>
<file>close.svg</file>
</qresource>
</RCC>

3
v1/resources/close.svg Normal file
View File

@@ -0,0 +1,3 @@
<svg width="10" height="10" viewBox="0 0 10 10" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9 1L1 9M9 9L1 1" stroke="#8A92A6" stroke-width="1.5" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 190 B

BIN
v1/resources/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

View File

@@ -0,0 +1,3 @@
<svg width="10" height="10" viewBox="0 0 10 10" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="0.5" y="0.5" width="9" height="9" stroke="#8A92A6" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 188 B

View File

@@ -0,0 +1,3 @@
<svg width="12" height="2" viewBox="0 0 12 2" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="12" height="2" fill="#8A92A6"/>
</svg>

After

Width:  |  Height:  |  Size: 145 B

5
v1/resources/restore.svg Normal file
View File

@@ -0,0 +1,5 @@
<svg width="10" height="10" viewBox="0 0 10 10" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3.5 0.5V3.5H0.5" stroke="#8A92A6" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M6.5 9.5V6.5H9.5" stroke="#8A92A6" stroke-linecap="round" stroke-linejoin="round"/>
<rect x="3.5" y="3.5" width="6" height="6" stroke="#8A92A6" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 374 B

View File

@@ -8,6 +8,18 @@ QWidget {
border: none; border: none;
} }
/* 最小化、最大化按钮悬停状态 */
QPushButton#minimizeBtn:hover,
QPushButton#maximizeBtn:hover {
background-color: rgba(255, 255, 255, 0.1);
}
/* 关闭按钮悬停状态 */
QPushButton#closeBtn:hover {
background-color: #E85050;
color: white; /* 使图标变为白色 */
}
/* 输入框 */ /* 输入框 */
QLineEdit { QLineEdit {
background-color: #2d3748; background-color: #2d3748;

View File

@@ -616,7 +616,8 @@ class AppCenter(QWidget):
self.apps_container.setVisible(True) self.apps_container.setVisible(True)
# 计算列数并显示应用卡片 # 计算列数并显示应用卡片
cards_per_row = max(1, self.width() // 350) if self.width() > 0 else 3 # cards_per_row = max(1, self.width() // 350) if self.width() > 0 else 3
cards_per_row = 3
self.display_apps(self.apps, cards_per_row) self.display_apps(self.apps, cards_per_row)
# 重置动画状态 # 重置动画状态
@@ -648,7 +649,8 @@ class AppCenter(QWidget):
filtered_apps.append(app) filtered_apps.append(app)
# 计算列数并显示过滤后的应用 # 计算列数并显示过滤后的应用
cards_per_row = max(1, self.width() // 350) if self.width() > 0 else 3 # cards_per_row = max(1, self.width() // 350) if self.width() > 0 else 3
cards_per_row = 3
self.display_apps(filtered_apps, cards_per_row) self.display_apps(filtered_apps, cards_per_row)
def filter_by_category(self, category): def filter_by_category(self, category):

View File

@@ -195,41 +195,41 @@ class Home(QWidget):
"color": ("#6366F1", "#4F46E5"), # 紫色 "color": ("#6366F1", "#4F46E5"), # 紫色
"icon": "word_to_pdf" "icon": "word_to_pdf"
}, },
{ # {
"id": "ocr", # "id": "ocr",
"title": "OCR文字识别", # "title": "OCR文字识别",
"desc": "从图片中提取文字内容", # "desc": "从图片中提取文字内容",
"color": ("#4ADE80", "#22C55E"), # 绿色 # "color": ("#4ADE80", "#22C55E"), # 绿色
"icon": "ocr" # "icon": "ocr"
}, # },
{ # {
"id": "image_convert", # "id": "image_convert",
"title": "图片格式转换", # "title": "图片格式转换",
"desc": "转换图像文件格式并优化", # "desc": "转换图像文件格式并优化",
"color": ("#F97316", "#EA580C"), # 橙色 # "color": ("#F97316", "#EA580C"), # 橙色
"icon": "image_convert" # "icon": "image_convert"
}, # },
{ {
"id": "pdf_merge", "id": "pdf_merge",
"title": "PDF合并工具", "title": "PDF工具",
"desc": "合并多个PDF文件为一个", "desc": "合并多个PDF文件为一个拆分PDF文件为多个文档",
"color": ("#8B5CF6", "#7C3AED"), # 紫色 "color": ("#8B5CF6", "#7C3AED"), # 紫色
"icon": "pdf_merge" "icon": "pdf_merge"
}, },
{ # {
"id": "pdf_split", # "id": "pdf_split",
"title": "PDF拆分工具", # "title": "PDF拆分工具",
"desc": "拆分PDF文件为多个文档", # "desc": "拆分PDF文件为多个文档",
"color": ("#EC4899", "#DB2777"), # 粉色 # "color": ("#EC4899", "#DB2777"), # 粉色
"icon": "pdf_split" # "icon": "pdf_split"
}, # },
{ # {
"id": "video_compress", # "id": "video_compress",
"title": "视频压缩", # "title": "视频压缩",
"desc": "减小视频文件大小", # "desc": "减小视频文件大小",
"color": ("#3B82F6", "#2563EB"), # 蓝色 # "color": ("#3B82F6", "#2563EB"), # 蓝色
"icon": "video_compress" # "icon": "video_compress"
} # }
] ]
# 每行显示3张卡片 # 每行显示3张卡片