mirror of
https://github.com/goldenfishs/MRobot.git
synced 2026-03-31 21:07:14 +08:00
添加了开发票
This commit is contained in:
Binary file not shown.
@@ -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()
|
||||
|
||||
@@ -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,
|
||||
"确认删除",
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user