Files
nl-utils/v1/main.py
2025-06-08 23:55:24 +08:00

490 lines
17 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import sys
from PyQt5.QtSvg import QSvgRenderer
from PyQt5.QtWidgets import QApplication, QMainWindow, QStackedWidget, QHBoxLayout, QWidget, QMessageBox, QPushButton, \
QLabel, QVBoxLayout, QFrame, QSizePolicy
from PyQt5.QtCore import Qt, QPoint, QSize, QByteArray
from views.login import LoginPage
from views.nav import NavBar
from views.home import Home
from views.word_to_pdf import WordToPDFPage
from views.pdf_tools_page import PDFToolsPage
from views.app_center import AppCenter
from views.history import HistoryPage
from PyQt5.QtGui import QPalette, QColor, QFont, QPixmap, QIcon, QPainter
from styles.styles import STYLES_TXT
class CustomTitleBar(QWidget):
"""现代化标题栏控件直接设置SVG图标"""
def __init__(self, parent=None):
super().__init__(parent)
self.parent = parent
self.setMouseTracking(True)
self.drag_position = QPoint()
self.setObjectName("customTitleBar")
self.init_ui()
self.set_style()
self.setMinimumHeight(48)
def init_ui(self):
"""初始化标题栏UI"""
layout = QHBoxLayout(self)
layout.setContentsMargins(15, 8, 15, 8)
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.setObjectName("titleLabel")
self.title_label.setFont(QFont("Microsoft YaHei UI", 10, QFont.DemiBold))
# 版本号
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.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
self.drag_area.setObjectName("dragArea")
# 窗口控制按钮
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.setFixedSize(34, 28)
self.minimize_btn.setToolTip("最小化")
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"))
# 最大化按钮直接设置SVG
self.maximize_btn = QPushButton()
self.maximize_btn.setObjectName("maximizeBtn")
self.maximize_btn.setFixedSize(34, 28)
self.maximize_btn.setToolTip("最大化")
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"))
# 关闭按钮直接设置SVG
self.close_btn = QPushButton()
self.close_btn.setObjectName("closeBtn")
self.close_btn.setFixedSize(34, 28)
self.close_btn.setToolTip("关闭")
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)
buttons_layout.addWidget(self.maximize_btn)
buttons_layout.addWidget(self.close_btn)
# 添加所有组件
layout.addWidget(title_container)
layout.addWidget(self.drag_area)
layout.addWidget(buttons_container)
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):
"""切换窗口最大化/正常状态"""
if self.parent.isMaximized():
self.parent.showNormal()
# 更新最大化按钮图标
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:
self.parent.showMaximized()
# 更新最大化按钮图标为还原状态
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):
"""鼠标按下事件处理"""
if event.button() == Qt.LeftButton:
self.drag_position = event.globalPos() - self.parent.frameGeometry().topLeft()
event.accept()
def mouseMoveEvent(self, event):
"""鼠标移动事件处理"""
if event.buttons() == Qt.LeftButton:
self.parent.move(event.globalPos() - self.drag_position)
event.accept()
def mouseDoubleClickEvent(self, event):
"""鼠标双击事件处理"""
if event.button() == Qt.LeftButton:
self.toggle_maximize()
event.accept()
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.username = None
self.setWindowFlag(Qt.FramelessWindowHint) # 设置为无边框窗口
self.init_ui()
def init_ui(self):
self.setWindowTitle("文档处理工具")
self.setGeometry(100, 100, 1200, 800)
# 添加自定义标题栏样式
titlebar_styles = """
/* 自定义标题栏样式 */
#customTitleBar {
background-color: #1a202c;
border-bottom: 1px solid #2d3748;
height: 50px;
}
#titleLabel {
color: #cbd5e0;
font-size: 16px;
font-weight: bold;
}
#dragArea {
background-color: transparent;
}
QPushButton#minimizeBtn,
QPushButton#maximizeBtn,
QPushButton#closeBtn {
background-color: transparent;
border: none;
border-radius: 4px;
font-size: 14px;
color: #a0aec0;
padding: 0;
}
QPushButton#minimizeBtn:hover,
QPushButton#maximizeBtn:hover {
background-color: rgba(74, 85, 104, 0.3);
color: #e2e8f0;
}
QPushButton#closeBtn:hover {
background-color: rgba(254, 178, 178, 0.3);
color: #f56565;
}
/* 窗口边框效果 */
#mainWidget {
border: 1px solid #2d3748;
}
"""
# 合并样式表
self.setStyleSheet(STYLES_TXT + titlebar_styles)
# 设置暗色主题调色板
palette = QPalette()
palette.setColor(QPalette.Window, QColor(26, 32, 44)) # #1a202c
palette.setColor(QPalette.WindowText, QColor(226, 232, 240)) # #e2e8f0
palette.setColor(QPalette.Base, QColor(45, 55, 72)) # #2d3748
palette.setColor(QPalette.AlternateBase, QColor(42, 50, 65)) # #2a3241
palette.setColor(QPalette.ToolTipBase, QColor(26, 32, 44)) # #1a202c
palette.setColor(QPalette.ToolTipText, QColor(226, 232, 240)) # #e2e8f0
palette.setColor(QPalette.Text, QColor(226, 232, 240)) # #e2e8f0
palette.setColor(QPalette.Button, QColor(79, 70, 229)) # #4f46e5
palette.setColor(QPalette.ButtonText, QColor(226, 232, 240)) # #e2e8f0
palette.setColor(QPalette.BrightText, Qt.white)
palette.setColor(QPalette.Link, QColor(129, 140, 248)) # #818cf8
palette.setColor(QPalette.Highlight, QColor(129, 140, 248)) # #818cf8
palette.setColor(QPalette.HighlightedText, Qt.white)
palette.setColor(QPalette.Disabled, QPalette.Text, QColor(160, 174, 192)) # #a0aec0
self.setPalette(palette)
# 主窗口部件
self.main_widget = QWidget()
self.main_widget.setObjectName("mainWidget")
self.main_layout = QVBoxLayout(self.main_widget)
self.main_layout.setContentsMargins(0, 0, 0, 0)
self.main_layout.setSpacing(0)
# 添加自定义标题栏
self.title_bar = CustomTitleBar(self)
self.main_layout.addWidget(self.title_bar)
# 添加分隔线
separator = QFrame()
separator.setFrameShape(QFrame.HLine)
separator.setFrameShadow(QFrame.Sunken)
separator.setMaximumHeight(1)
separator.setStyleSheet("background-color: #2d3748;")
self.main_layout.addWidget(separator)
# 主内容区域
content_widget = QWidget()
content_layout = QHBoxLayout(content_widget)
content_layout.setContentsMargins(0, 0, 0, 0)
content_layout.setSpacing(0)
# 导航栏 (初始隐藏)
self.nav_bar = NavBar("")
self.nav_bar.setObjectName("navBar")
self.nav_bar.hide()
self.nav_bar.nav_item_clicked.connect(self.handle_nav_click)
self.nav_bar.logout_clicked.connect(self.handle_logout)
# 页面堆栈
self.stacked_pages = QStackedWidget()
self.stacked_pages.setObjectName("stackedPages")
# 登录页
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)
# Word转PDF页
self.word_to_pdf = WordToPDFPage()
# Word转PDF页
self.pdf_tools_page = PDFToolsPage()
# 使用记录页
self.history_page = HistoryPage()
# 设置页(占位)
self.settings_page = QWidget()
self.settings_page.setObjectName("settingsPage")
# 添加页面
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.pdf_tools_page)
self.stacked_pages.addWidget(self.history_page)
self.stacked_pages.addWidget(self.settings_page)
# 主内容布局
content_layout.addWidget(self.nav_bar)
content_layout.addWidget(self.stacked_pages, 1)
# 添加到主布局
self.main_layout.addWidget(content_widget, 1)
self.setCentralWidget(self.main_widget)
# 鼠标悬停在边缘时的调整光标
self.setMouseTracking(True)
def handle_login_success(self, username):
self.username = username
# 更新标题栏显示
self.title_bar.title_label.setText(f"文档处理工具 - 欢迎 {username}")
# 更新导航栏用户信息
self.nav_bar.username = username
self.nav_bar.show()
# 设置默认激活页
self.nav_bar.set_active_nav("home")
self.stacked_pages.setCurrentWidget(self.home)
def handle_logout(self):
self.username = None
self.nav_bar.hide()
self.stacked_pages.setCurrentWidget(self.login_page)
# 重置标题栏
self.title_bar.title_label.setText("文档处理工具")
def handle_nav_click(self, nav_id):
# 确保导航栏状态更新
self.nav_bar.set_active_nav(nav_id)
# 根据导航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)
elif nav_id == "settings":
self.stacked_pages.setCurrentWidget(self.settings_page)
def handle_app_selected(self, app_id):
try:
if app_id == "word_to_pdf":
self.stacked_pages.setCurrentWidget(self.word_to_pdf)
elif app_id == "pdf_tools_page":
print('1')
self.stacked_pages.setCurrentWidget(self.pdf_tools_page)
elif app_id == "history":
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():
app = QApplication(sys.argv)
app.setStyle('Fusion')
# 设置高DPI支持
app.setAttribute(Qt.AA_EnableHighDpiScaling)
app.setAttribute(Qt.AA_UseHighDpiPixmaps)
window = MainWindow()
window.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()