添加了开发票

This commit is contained in:
2025-11-29 12:04:26 +08:00
parent e2e275b6e4
commit 8f4636ab5a
53 changed files with 1235 additions and 4968 deletions

View File

@@ -2,9 +2,12 @@
批量导出选项对话框
"""
from PyQt5.QtWidgets import QDialog, QVBoxLayout, QHBoxLayout, QButtonGroup, QRadioButton
from PyQt5.QtWidgets import QDialog, QVBoxLayout, QHBoxLayout, QButtonGroup, QRadioButton, QFrame
from PyQt5.QtCore import Qt
from qfluentwidgets import BodyLabel, PushButton, PrimaryPushButton, SubtitleLabel
from PyQt5.QtGui import QFont, QPalette
from qfluentwidgets import (BodyLabel, PushButton, PrimaryPushButton, SubtitleLabel,
TitleLabel, HorizontalSeparator, CardWidget, FluentIcon, StrongBodyLabel,
theme, Theme)
class BatchExportDialog(QDialog):
@@ -16,60 +19,186 @@ class BatchExportDialog(QDialog):
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowTitle("导出选项")
self.setGeometry(200, 200, 400, 250)
self.setGeometry(200, 200, 680, 550)
self.setMinimumWidth(640)
self.setMinimumHeight(480)
# 设置背景色跟随主题
if theme() == Theme.DARK:
self.setStyleSheet("background-color: #232323;")
else:
self.setStyleSheet("background-color: #f7f9fc;")
self.export_type = self.EXPORT_NORMAL
self.init_ui()
def init_ui(self):
"""初始化UI"""
layout = QVBoxLayout(self)
layout.setContentsMargins(20, 20, 20, 20)
layout.setSpacing(20)
layout.setContentsMargins(24, 24, 24, 24)
layout.setSpacing(16)
# 标题
title_label = SubtitleLabel("选择导出方式")
layout.addWidget(title_label)
# 标题区域
title_layout = QVBoxLayout()
title_layout.setSpacing(8)
title_label = TitleLabel("选择导出方式")
title_layout.addWidget(title_label)
desc_label = BodyLabel("选择最适合您的导出格式")
title_layout.addWidget(desc_label)
layout.addLayout(title_layout)
layout.addWidget(HorizontalSeparator())
# 选项组
self.button_group = QButtonGroup()
# 普通导出选项
normal_radio = QRadioButton("普通导出")
# 普通导出选项
normal_card = self._create_option_card(
title="普通导出",
description="将每个交易的图片导出到单独的文件夹",
details="文件夹名称日期_金额\n每个交易的图片保存在独立文件夹中,便于查看和管理",
is_selected=True
)
normal_radio = normal_card.findChild(QRadioButton)
normal_radio.setChecked(True)
normal_radio.setToolTip("将每个交易的图片导出到单独的文件夹文件夹名日期_金额")
self.button_group.addButton(normal_radio, self.EXPORT_NORMAL)
layout.addWidget(normal_radio)
layout.addWidget(normal_card)
normal_desc = BodyLabel("每个交易的图片保存在独立文件夹中,便于查看和管理")
layout.addWidget(normal_desc)
layout.addSpacing(15)
# MRobot 格式导出选项
mrobot_radio = QRadioButton("MRobot 专用格式")
mrobot_radio.setToolTip("导出为 .mrobot 文件(专用格式,用于数据转交)")
# MRobot 格式导出选项卡
mrobot_card = self._create_option_card(
title="MRobot 专用格式",
description="导出为 .mrobot 文件ZIP 格式)",
details="包含完整的交易数据和图片\n用于转交给他人或备份",
is_selected=False
)
mrobot_radio = mrobot_card.findChild(QRadioButton)
self.button_group.addButton(mrobot_radio, self.EXPORT_MROBOT)
layout.addWidget(mrobot_radio)
mrobot_desc = BodyLabel("导出为 .mrobot 文件ZIP 格式),包含完整的交易数据和图片,用于转交给他人")
layout.addWidget(mrobot_desc)
layout.addWidget(mrobot_card)
layout.addStretch()
# 按钮
btn_layout = QHBoxLayout()
btn_layout.setSpacing(12)
btn_layout.addStretch()
cancel_btn = PushButton("取消")
cancel_btn.setMinimumWidth(110)
cancel_btn.clicked.connect(self.reject)
btn_layout.addWidget(cancel_btn)
ok_btn = PrimaryPushButton("确定")
ok_btn = PrimaryPushButton("确定导出")
ok_btn.setMinimumWidth(110)
ok_btn.clicked.connect(self.on_ok)
btn_layout.addWidget(ok_btn)
layout.addLayout(btn_layout)
def _create_option_card(self, title, description, details, is_selected=False):
"""创建导出选项卡片"""
card = CardWidget()
card_layout = QHBoxLayout()
card_layout.setContentsMargins(16, 16, 16, 16)
card_layout.setSpacing(16)
# 单选按钮
radio = QRadioButton()
radio.setMinimumWidth(40)
card_layout.addWidget(radio)
# 内容区域
content_layout = QVBoxLayout()
content_layout.setSpacing(8)
# 标题行
title_layout = QHBoxLayout()
title_layout.setSpacing(10)
# 图标
icon_label = BodyLabel()
icon_label.setText("📁" if title == "普通导出" else "📦")
icon_label.setStyleSheet("font-size: 20px;")
title_layout.addWidget(icon_label)
# 标题
title_label = StrongBodyLabel(title)
title_layout.addWidget(title_label)
title_layout.addStretch()
content_layout.addLayout(title_layout)
# 描述
desc_label = BodyLabel(description)
desc_label.setWordWrap(True)
# 使用 QPalette 来自适应主题
from PyQt5.QtGui import QPalette
content_layout.addWidget(desc_label)
# 详细信息
details_label = BodyLabel(details)
details_label.setWordWrap(True)
# 使用相对颜色而不是硬编码
content_layout.addWidget(details_label)
content_layout.addStretch()
card_layout.addLayout(content_layout, 1)
# 设置卡片样式 - 不使用硬编码颜色,让 CardWidget 自适应主题
# 只通过边框来显示选中状态
self._update_card_style(card, is_selected)
card.setLayout(card_layout)
card.setMinimumHeight(120)
# 点击卡片时选中单选按钮
def on_card_clicked():
radio.setChecked(True)
# 更新卡片样式
self._update_card_styles(radio)
radio.clicked.connect(on_card_clicked)
card.mousePressEvent = lambda e: on_card_clicked()
return card
def _update_card_style(self, card, is_selected):
"""更新单个卡片的样式"""
if is_selected:
card.setProperty("is_selected", True)
card.setStyleSheet("""
CardWidget[is_selected=true] {
border: 2px solid palette(highlight);
}
CardWidget[is_selected=false] {
border: 1px solid palette(mid);
}
CardWidget[is_selected=false]:hover {
border: 2px solid palette(highlight);
}
""")
else:
card.setProperty("is_selected", False)
card.setStyleSheet("""
CardWidget[is_selected=false] {
border: 1px solid palette(mid);
}
CardWidget[is_selected=false]:hover {
border: 2px solid palette(highlight);
}
""")
def _update_card_styles(self, selected_radio):
"""更新所有卡片的样式"""
for button in self.button_group.buttons():
card = button.parent()
while card and not isinstance(card, CardWidget):
card = card.parent()
if card:
is_checked = button.isChecked()
self._update_card_style(card, is_checked)
def on_ok(self):
"""确定按钮点击"""
checked_button = self.button_group.checkedButton()

View File

@@ -4,10 +4,12 @@
"""
from typing import Optional
from PyQt5.QtWidgets import (QDialog, QVBoxLayout, QHBoxLayout, QListWidget, QListWidgetItem)
from PyQt5.QtWidgets import (QDialog, QVBoxLayout, QHBoxLayout, QListWidget, QListWidgetItem, QMessageBox, QScrollArea, QWidget)
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QIcon, QColor, QFont
from qfluentwidgets import (BodyLabel, PushButton, PrimaryPushButton, LineEdit,
InfoBar, InfoBarPosition)
InfoBar, InfoBarPosition, SubtitleLabel, TitleLabel,
HorizontalSeparator, CardWidget, FluentIcon, StrongBodyLabel, theme, Theme)
from .tools.finance_manager import FinanceManager
@@ -19,49 +21,119 @@ class CategoryManagementDialog(QDialog):
self.finance_manager = finance_manager
self.account_id = account_id
self.setWindowTitle("分类管理")
self.setGeometry(100, 100, 500, 400)
self.setGeometry(100, 100, 650, 550)
self.setMinimumWidth(600)
self.setMinimumHeight(480)
# 设置背景色跟随主题
if theme() == Theme.DARK:
self.setStyleSheet("background-color: #232323;")
else:
self.setStyleSheet("background-color: #f7f9fc;")
self.init_ui()
def init_ui(self):
"""初始化UI"""
main_layout = QVBoxLayout()
main_layout.setContentsMargins(24, 24, 24, 24)
main_layout.setSpacing(16)
# 标
title_label = BodyLabel("选择分类进行管理:")
main_layout.addWidget(title_label)
# 标题区域
title_layout = QVBoxLayout()
title_layout.setSpacing(6)
title_label = TitleLabel("分类管理")
title_layout.addWidget(title_label)
desc_label = BodyLabel("新增、编辑或删除您的交易分类")
title_layout.addWidget(desc_label)
main_layout.addLayout(title_layout)
main_layout.addWidget(HorizontalSeparator())
# 内容区域(分类列表)
content_layout = QHBoxLayout()
content_layout.setSpacing(16)
# 左侧:分类列表卡片
list_card = CardWidget()
list_card_layout = QVBoxLayout()
list_card_layout.setContentsMargins(0, 0, 0, 0)
list_card_layout.setSpacing(0)
list_label = StrongBodyLabel("现有分类")
list_label.setStyleSheet("padding: 12px 16px; border-bottom: 1px solid var(--border-color);")
list_card_layout.addWidget(list_label)
# 分类列表
self.category_list = QListWidget()
self.category_list.itemSelectionChanged.connect(self.on_category_selected)
main_layout.addWidget(self.category_list)
self.category_list.setStyleSheet("""
QListWidget {
border: none;
background-color: transparent;
}
QListWidget::item {
padding: 10px 12px;
border-radius: 4px;
margin: 2px 4px;
}
QListWidget::item:hover {
background-color: rgba(0, 0, 0, 0.05);
}
QListWidget::item:selected {
background-color: var(--highlight-color);
color: var(--highlight-text-color);
font-weight: bold;
}
""")
list_card_layout.addWidget(self.category_list, 1)
list_card.setLayout(list_card_layout)
list_card.setMinimumHeight(280)
content_layout.addWidget(list_card, 1)
main_layout.addLayout(content_layout, 1)
# 加载分类
self.load_categories()
# 按钮区域
btn_layout = QHBoxLayout()
btn_layout.addStretch()
btn_layout.setSpacing(12)
# 新增按钮
add_btn = PrimaryPushButton("新增")
add_btn = PrimaryPushButton()
add_btn.setIcon(FluentIcon.ADD)
add_btn.setText("新增分类")
add_btn.clicked.connect(self.on_add_category)
add_btn.setMinimumWidth(120)
btn_layout.addWidget(add_btn)
# 重命名按钮
self.rename_btn = PushButton("重命名")
self.rename_btn = PushButton()
self.rename_btn.setIcon(FluentIcon.EDIT)
self.rename_btn.setText("重命名")
self.rename_btn.clicked.connect(self.on_rename_category)
self.rename_btn.setEnabled(False)
self.rename_btn.setMinimumWidth(110)
btn_layout.addWidget(self.rename_btn)
# 删除按钮
self.delete_btn = PushButton("删除")
self.delete_btn = PushButton()
self.delete_btn.setIcon(FluentIcon.DELETE)
self.delete_btn.setText("删除")
self.delete_btn.clicked.connect(self.on_delete_category)
self.delete_btn.setEnabled(False)
self.delete_btn.setMinimumWidth(110)
btn_layout.addWidget(self.delete_btn)
btn_layout.addStretch()
# 关闭按钮
close_btn = PushButton("关闭")
close_btn.clicked.connect(self.accept)
close_btn.setMinimumWidth(110)
btn_layout.addWidget(close_btn)
main_layout.addLayout(btn_layout)
@@ -88,17 +160,33 @@ class CategoryManagementDialog(QDialog):
"""新增分类"""
# 弹出输入对话框
from PyQt5.QtWidgets import QDialog as QStdDialog
from PyQt5.QtWidgets import QLabel
dialog = QStdDialog(self)
dialog.setWindowTitle("新增分类")
dialog.setGeometry(150, 150, 400, 150)
dialog.setGeometry(150, 150, 450, 220)
dialog.setStyleSheet("""
QDialog {
background-color: white;
}
""")
layout = QVBoxLayout(dialog)
layout.addWidget(BodyLabel("分类名称:"))
layout.setContentsMargins(24, 24, 24, 24)
layout.setSpacing(16)
# 标题
title = StrongBodyLabel("创建新分类")
layout.addWidget(title)
# 说明文字
desc = BodyLabel("请输入分类名称")
desc.setStyleSheet("color: #606366;")
layout.addWidget(desc)
# 输入框
input_edit = LineEdit()
input_edit.setPlaceholderText("例如:食品、交通、娱乐等")
input_edit.setPlaceholderText("例如:食品、交通、娱乐、购物")
input_edit.setMinimumHeight(40)
layout.addWidget(input_edit)
# 按钮
@@ -140,14 +228,21 @@ class CategoryManagementDialog(QDialog):
)
cancel_btn = PushButton("取消")
cancel_btn.setMinimumWidth(100)
cancel_btn.clicked.connect(dialog.reject)
btn_layout.addWidget(cancel_btn)
create_btn = PrimaryPushButton("创建")
create_btn.setMinimumWidth(100)
create_btn.clicked.connect(on_create)
btn_layout.addWidget(create_btn)
layout.addSpacing(12)
layout.addLayout(btn_layout)
# 回车快速创建
input_edit.returnPressed.connect(on_create)
dialog.exec()
def on_rename_category(self):
@@ -163,15 +258,41 @@ class CategoryManagementDialog(QDialog):
dialog = QStdDialog(self)
dialog.setWindowTitle("重命名分类")
dialog.setGeometry(150, 150, 400, 150)
dialog.setGeometry(150, 150, 450, 280)
dialog.setStyleSheet("""
QDialog {
background-color: white;
}
""")
layout = QVBoxLayout(dialog)
layout.addWidget(BodyLabel(f"原分类名: {old_name}"))
layout.addWidget(BodyLabel("新分类名:"))
layout.setContentsMargins(24, 24, 24, 24)
layout.setSpacing(16)
# 标题
title = StrongBodyLabel("重命名分类")
layout.addWidget(title)
# 原名称显示
old_info_layout = QHBoxLayout()
old_label = BodyLabel("原分类名:")
old_label.setMinimumWidth(80)
old_value = StrongBodyLabel(old_name)
old_value.setStyleSheet("color: #1976d2;")
old_info_layout.addWidget(old_label)
old_info_layout.addWidget(old_value)
old_info_layout.addStretch()
layout.addLayout(old_info_layout)
# 新名称输入
new_label = BodyLabel("新分类名:")
new_label.setMinimumWidth(80)
layout.addWidget(new_label)
input_edit = LineEdit()
input_edit.setText(old_name)
input_edit.selectAll()
input_edit.setMinimumHeight(40)
layout.addWidget(input_edit)
# 按钮
@@ -217,14 +338,21 @@ class CategoryManagementDialog(QDialog):
)
cancel_btn = PushButton("取消")
cancel_btn.setMinimumWidth(100)
cancel_btn.clicked.connect(dialog.reject)
btn_layout.addWidget(cancel_btn)
rename_btn = PrimaryPushButton("重命名")
rename_btn.setMinimumWidth(100)
rename_btn.clicked.connect(on_rename)
btn_layout.addWidget(rename_btn)
layout.addSpacing(12)
layout.addLayout(btn_layout)
# 回车快速重命名
input_edit.returnPressed.connect(on_rename)
dialog.exec()
def on_delete_category(self):
@@ -236,8 +364,6 @@ class CategoryManagementDialog(QDialog):
category_name = current_item.text()
# 确认删除
from PyQt5.QtWidgets import QMessageBox
reply = QMessageBox.question(
self,
"确认删除",

View File

@@ -36,7 +36,17 @@ class CreateTransactionDialog(QDialog):
self.finance_manager = finance_manager if finance_manager else FinanceManager()
self.setWindowTitle("新建交易记录" if not transaction else "编辑交易记录")
self.setGeometry(100, 100, 600, 500)
self.setGeometry(100, 100, 700, 650)
self.setMinimumWidth(650)
self.setMinimumHeight(580)
# 设置背景色跟随主题
from qfluentwidgets import theme, Theme
if theme() == Theme.DARK:
self.setStyleSheet("background-color: #232323;")
else:
self.setStyleSheet("background-color: #f7f9fc;")
self.init_ui()
if transaction:
@@ -48,23 +58,45 @@ class CreateTransactionDialog(QDialog):
def init_ui(self):
"""初始化UI"""
layout = QVBoxLayout(self)
layout.setContentsMargins(20, 20, 20, 20)
layout.setSpacing(15)
layout.setContentsMargins(24, 24, 24, 24)
layout.setSpacing(16)
# 标题区域
title_layout = QVBoxLayout()
title_layout.setSpacing(6)
title_label = TitleLabel("交易记录" if not self.transaction else "编辑交易")
title_layout.addWidget(title_label)
desc_label = BodyLabel("请填写交易的相关信息")
title_layout.addWidget(desc_label)
layout.addLayout(title_layout)
layout.addWidget(HorizontalSeparator())
# 使用ScrollArea实现可滚动的内容区
scroll_area = QScrollArea()
scroll_area.setWidgetResizable(True)
scroll_area.setFrameShape(QScrollArea.NoFrame)
scroll_widget = QWidget()
scroll_layout = QVBoxLayout(scroll_widget)
scroll_layout.setSpacing(12)
# 交易类型
type_layout = QHBoxLayout()
type_layout.addWidget(BodyLabel("交易类型:"))
type_label = BodyLabel("交易类型:")
type_label.setMinimumWidth(80)
type_layout.addWidget(type_label)
self.transaction_type_combo = ComboBox()
self.transaction_type_combo.addItem("入账 (正数)")
self.transaction_type_combo.addItem("支出 (负数)")
self.transaction_type_combo.setMaximumWidth(200)
self.transaction_type_combo.setMaximumWidth(250)
type_layout.addWidget(self.transaction_type_combo)
type_layout.addStretch()
layout.addLayout(type_layout)
scroll_layout.addLayout(type_layout)
# 分类
category_layout = QHBoxLayout()
category_layout.addWidget(BodyLabel("分类:"))
cat_label = BodyLabel("分类:")
cat_label.setMinimumWidth(80)
category_layout.addWidget(cat_label)
self.category_combo = ComboBox()
# 从财务管理器获取分类列表
categories = []
@@ -83,90 +115,116 @@ class CreateTransactionDialog(QDialog):
self.category_combo.addItem(cat)
self.category_combo.setEnabled(True)
self.category_combo.setMaximumWidth(200)
self.category_combo.setMaximumWidth(250)
category_layout.addWidget(self.category_combo)
category_layout.addStretch()
layout.addLayout(category_layout)
scroll_layout.addLayout(category_layout)
# 日期
date_layout = QHBoxLayout()
date_layout.addWidget(BodyLabel("日期:"))
date_label = BodyLabel("日期:")
date_label.setMinimumWidth(80)
date_layout.addWidget(date_label)
self.date_edit = DateEdit()
self.date_edit.setDate(QDate.currentDate())
self.date_edit.setMaximumWidth(250)
date_layout.addWidget(self.date_edit)
date_layout.addStretch()
layout.addLayout(date_layout)
scroll_layout.addLayout(date_layout)
# 金额
amount_layout = QHBoxLayout()
amount_layout.addWidget(BodyLabel("金额 (元):"))
amt_label = BodyLabel("金额 (元):")
amt_label.setMinimumWidth(80)
amount_layout.addWidget(amt_label)
self.amount_spin = DoubleSpinBox()
self.amount_spin.setRange(0, 999999999)
self.amount_spin.setDecimals(2)
self.amount_spin.setMaximumWidth(250)
amount_layout.addWidget(self.amount_spin)
amount_layout.addStretch()
layout.addLayout(amount_layout)
scroll_layout.addLayout(amount_layout)
# 交易人
trader_layout = QHBoxLayout()
trader_layout.addWidget(BodyLabel("交易人:"))
trader_label = BodyLabel("交易人:")
trader_label.setMinimumWidth(80)
trader_layout.addWidget(trader_label)
self.trader_edit = LineEdit()
self.trader_edit.setMaximumWidth(250)
trader_layout.addWidget(self.trader_edit)
layout.addLayout(trader_layout)
trader_layout.addStretch()
scroll_layout.addLayout(trader_layout)
# 备注
notes_layout = QHBoxLayout()
notes_layout.addWidget(BodyLabel("备注:"))
notes_layout = QVBoxLayout()
notes_label = BodyLabel("备注:")
notes_layout.addWidget(notes_label)
self.notes_edit = TextEdit()
self.notes_edit.setMaximumHeight(80)
self.notes_edit.setMaximumHeight(100)
self.notes_edit.setMinimumHeight(70)
notes_layout.addWidget(self.notes_edit)
layout.addLayout(notes_layout)
scroll_layout.addLayout(notes_layout)
# 图片部分
layout.addWidget(HorizontalSeparator())
layout.addWidget(SubtitleLabel("相关文件 (可选)"))
scroll_layout.addWidget(HorizontalSeparator())
scroll_layout.addWidget(SubtitleLabel("相关文件 (可选)"))
# 发票
invoice_layout = QHBoxLayout()
invoice_layout.addWidget(BodyLabel("发票图片:"))
invoice_label = BodyLabel("发票图片:")
invoice_label.setMinimumWidth(80)
invoice_layout.addWidget(invoice_label)
self.invoice_label = BodyLabel("未选择")
invoice_layout.addWidget(self.invoice_label)
invoice_layout.addWidget(self.invoice_label, 1)
invoice_btn = PushButton("选择")
invoice_btn.setMaximumWidth(100)
invoice_btn.clicked.connect(lambda: self.select_image("invoice"))
invoice_layout.addWidget(invoice_btn)
layout.addLayout(invoice_layout)
scroll_layout.addLayout(invoice_layout)
# 支付记录
payment_layout = QHBoxLayout()
payment_layout.addWidget(BodyLabel("支付记录:"))
payment_label = BodyLabel("支付记录:")
payment_label.setMinimumWidth(80)
payment_layout.addWidget(payment_label)
self.payment_label = BodyLabel("未选择")
payment_layout.addWidget(self.payment_label)
payment_layout.addWidget(self.payment_label, 1)
payment_btn = PushButton("选择")
payment_btn.setMaximumWidth(100)
payment_btn.clicked.connect(lambda: self.select_image("payment"))
payment_layout.addWidget(payment_btn)
layout.addLayout(payment_layout)
scroll_layout.addLayout(payment_layout)
# 购买记录
purchase_layout = QHBoxLayout()
purchase_layout.addWidget(BodyLabel("购买记录:"))
purchase_label = BodyLabel("购买记录:")
purchase_label.setMinimumWidth(80)
purchase_layout.addWidget(purchase_label)
self.purchase_label = BodyLabel("未选择")
purchase_layout.addWidget(self.purchase_label)
purchase_layout.addWidget(self.purchase_label, 1)
purchase_btn = PushButton("选择")
purchase_btn.setMaximumWidth(100)
purchase_btn.clicked.connect(lambda: self.select_image("purchase"))
purchase_layout.addWidget(purchase_btn)
layout.addLayout(purchase_layout)
scroll_layout.addLayout(purchase_layout)
layout.addStretch()
scroll_layout.addStretch()
scroll_area.setWidget(scroll_widget)
layout.addWidget(scroll_area, 1)
# 按钮
# 按钮区域
btn_layout = QHBoxLayout()
btn_layout.setSpacing(12)
btn_layout.addStretch()
cancel_btn = PushButton("取消")
cancel_btn.setMinimumWidth(110)
cancel_btn.clicked.connect(self.reject)
btn_layout.addWidget(cancel_btn)
save_btn = PrimaryPushButton("保存")
save_btn.setMinimumWidth(110)
save_btn.clicked.connect(self.save_transaction)
btn_layout.addWidget(save_btn)
@@ -325,7 +383,17 @@ class RecordViewDialog(QDialog):
self.finance_manager = FinanceManager()
self.setWindowTitle("记录详情")
self.setGeometry(100, 100, 700, 600)
self.setGeometry(100, 100, 750, 650)
self.setMinimumWidth(700)
self.setMinimumHeight(580)
# 设置背景色跟随主题
from qfluentwidgets import theme, Theme
if theme() == Theme.DARK:
self.setStyleSheet("background-color: #232323;")
else:
self.setStyleSheet("background-color: #f7f9fc;")
self.init_ui()
if transaction:
@@ -334,52 +402,117 @@ class RecordViewDialog(QDialog):
def init_ui(self):
"""初始化UI"""
layout = QVBoxLayout(self)
layout.setContentsMargins(20, 20, 20, 20)
layout.setSpacing(15)
layout.setContentsMargins(24, 24, 24, 24)
layout.setSpacing(16)
# 标题区域
title_layout = QVBoxLayout()
title_layout.setSpacing(6)
title_label = TitleLabel("交易记录详情")
title_layout.addWidget(title_label)
desc_label = BodyLabel("查看交易的完整信息和相关文件")
title_layout.addWidget(desc_label)
layout.addLayout(title_layout)
layout.addWidget(HorizontalSeparator())
# 使用ScrollArea实现可滚动的内容区
scroll_area = QScrollArea()
scroll_area.setWidgetResizable(True)
scroll_area.setFrameShape(QScrollArea.NoFrame)
scroll_widget = QWidget()
scroll_layout = QVBoxLayout(scroll_widget)
scroll_layout.setSpacing(12)
# 基本信息
info_layout = QVBoxLayout()
date_layout = QHBoxLayout()
date_layout.addWidget(BodyLabel("日期:"))
date_label = BodyLabel("日期:")
date_label.setMinimumWidth(80)
date_layout.addWidget(date_label)
self.date_label = BodyLabel()
date_layout.addWidget(self.date_label)
date_layout.addWidget(self.date_label, 1)
date_layout.addStretch()
info_layout.addLayout(date_layout)
scroll_layout.addLayout(date_layout)
amount_layout = QHBoxLayout()
amount_layout.addWidget(BodyLabel("金额:"))
amount_label = BodyLabel("金额:")
amount_label.setMinimumWidth(80)
amount_layout.addWidget(amount_label)
self.amount_label = BodyLabel()
amount_layout.addWidget(self.amount_label)
amount_layout.addWidget(self.amount_label, 1)
amount_layout.addStretch()
info_layout.addLayout(amount_layout)
scroll_layout.addLayout(amount_layout)
trader_layout = QHBoxLayout()
trader_layout.addWidget(BodyLabel("交易人:"))
trader_label = BodyLabel("交易人:")
trader_label.setMinimumWidth(80)
trader_layout.addWidget(trader_label)
self.trader_label = BodyLabel()
trader_layout.addWidget(self.trader_label)
trader_layout.addWidget(self.trader_label, 1)
trader_layout.addStretch()
info_layout.addLayout(trader_layout)
scroll_layout.addLayout(trader_layout)
notes_layout = QHBoxLayout()
notes_layout.addWidget(BodyLabel("备注:"))
notes_layout = QVBoxLayout()
notes_title = BodyLabel("备注:")
notes_layout.addWidget(notes_title)
self.notes_label = BodyLabel()
self.notes_label.setWordWrap(True)
notes_layout.addWidget(self.notes_label)
info_layout.addLayout(notes_layout)
scroll_layout.addLayout(notes_layout)
layout.addLayout(info_layout)
layout.addWidget(HorizontalSeparator())
scroll_layout.addWidget(HorizontalSeparator())
# 图片预览
layout.addWidget(SubtitleLabel("相关文件预览"))
scroll_layout.addWidget(SubtitleLabel("相关文件预览"))
preview_layout = QHBoxLayout()
preview_layout.setSpacing(16)
# 发票
invoice_layout = QVBoxLayout()
invoice_layout.addWidget(BodyLabel("发票:"))
self.invoice_preview = BodyLabel("")
self.invoice_preview.setMinimumSize(140, 140)
invoice_layout.addWidget(self.invoice_preview)
preview_layout.addLayout(invoice_layout)
# 支付记录
payment_layout = QVBoxLayout()
payment_layout.addWidget(BodyLabel("支付记录:"))
self.payment_preview = BodyLabel("")
self.payment_preview.setMinimumSize(140, 140)
payment_layout.addWidget(self.payment_preview)
preview_layout.addLayout(payment_layout)
# 购买记录
purchase_layout = QVBoxLayout()
purchase_layout.addWidget(BodyLabel("购买记录:"))
self.purchase_preview = BodyLabel("")
self.purchase_preview.setMinimumSize(140, 140)
purchase_layout.addWidget(self.purchase_preview)
preview_layout.addLayout(purchase_layout)
scroll_layout.addLayout(preview_layout)
scroll_layout.addStretch()
scroll_area.setWidget(scroll_widget)
layout.addWidget(scroll_area, 1)
# 按钮区域
btn_layout = QHBoxLayout()
btn_layout.setSpacing(12)
btn_layout.addStretch()
export_images_btn = PushButton()
export_images_btn.setText("导出图片")
export_images_btn.setMinimumWidth(120)
export_images_btn.clicked.connect(self.on_export_images)
btn_layout.addWidget(export_images_btn)
close_btn = PushButton("关闭")
close_btn.setMinimumWidth(110)
close_btn.clicked.connect(self.reject)
btn_layout.addWidget(close_btn)
layout.addLayout(btn_layout)
self.invoice_preview.setMinimumSize(150, 150)
invoice_layout.addWidget(self.invoice_preview)
preview_layout.addLayout(invoice_layout)

View File

@@ -1,10 +1,10 @@
from PyQt5.QtCore import Qt, QSize
from PyQt5.QtCore import Qt, QSize, QTimer
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QApplication
from contextlib import redirect_stdout
with redirect_stdout(None):
from qfluentwidgets import NavigationItemPosition, FluentWindow, SplashScreen, setThemeColor, NavigationBarPushButton, toggleTheme, setTheme, Theme, NavigationAvatarWidget, NavigationToolButton ,NavigationPushButton
from qfluentwidgets import NavigationItemPosition, FluentWindow, SplashScreen, setThemeColor, NavigationBarPushButton, toggleTheme, setTheme, Theme, NavigationAvatarWidget, NavigationToolButton ,NavigationPushButton, theme
from qfluentwidgets import FluentIcon as FIF
from qfluentwidgets import InfoBar, InfoBarPosition
@@ -69,7 +69,7 @@ class MainWindow(FluentWindow):
self.themeBtn = NavigationPushButton(FIF.BRUSH, "切换主题", False, self.navigationInterface)
self.themeBtn.clicked.connect(lambda: toggleTheme(lazy=True))
self.themeBtn.clicked.connect(self._safe_toggle_theme)
self.navigationInterface.addWidget(
'themeButton',
self.themeBtn,
@@ -77,6 +77,36 @@ class MainWindow(FluentWindow):
NavigationItemPosition.BOTTOM
)
def _safe_toggle_theme(self):
"""安全地切换主题,避免字典迭代异常"""
def safe_toggle():
try:
import sys
from io import StringIO
# 捕获 stderr 以抑制库内的异常消息
old_stderr = sys.stderr
sys.stderr = StringIO()
try:
# 获取当前主题
current_theme = theme()
# 根据当前主题切换到另一个
new_theme = Theme.LIGHT if current_theme == Theme.DARK else Theme.DARK
setTheme(new_theme, save=True, lazy=True)
finally:
# 恢复 stderr
sys.stderr = old_stderr
except Exception as e:
# 其他异常仍然打印,但忽略字典迭代异常
error_msg = str(e)
if "dictionary changed size during iteration" not in error_msg:
print(f"主题切换失败: {e}")
# 在下一个事件循环中执行切换,让 Qt 完成当前事件处理
QTimer.singleShot(50, safe_toggle)
def check_updates_in_background(self):
"""后台检查更新"""
try: