certflow.views.common package

Submodules

Module contents

视图通用层包

集中放置跨视图复用的 UI 基础设施(非具体业务窗体)。 与 ``views/bases``(视图基类契约)的区别:本包提供的是应用级、 被多个 View 共享的「呈现规则」管理器,如主题样式与界面文案国际化。

归属判定:本包内模块均强依赖 PySide6,属 UI 层设施, 不可下沉至 handlers / services 等下层(下层需保持无 Qt 可导入)。

Exports:

I18nManager: 国际化管理器,提供中/英文界面文案切换 ThemeManager: 主题管理器,提供浅色/深色 QSS 全局切换 safe_qdate: 校验式 QDate 构造,非法参数返回 None parse_qdate: 校验式 QDate 解析,解析失败返回 None

class certflow.views.common.I18nManager(parent=None)[源代码]

基类:QObject

国际化管理器 — 中/英文切换

维护核心 UI 文本的翻译字典,语言切换时发出信号通知各 View 刷新。

参数:

parent (QObject | None)

language_changed

语言变更信号,携带新语言代码 (zh_CN / en_US)

TRANSLATIONS: dict[str, dict[str, str]] = {'-- 没有历史文件,请浏览 --': '-- No history, please browse --', '-- 选择历史文件 --': '-- Select History File --', 'CertFlow - 阀门证书管理系统': 'CertFlow - Valve Certificate Management System', 'English': 'English', 'X 偏移 (向右为正):': 'X Offset (right positive):', 'Y 偏移 (向下为正):': 'Y Offset (down positive):', 'dark (深色)': 'dark (Dark)', 'en_US (English)': 'en_US (English)', 'light (浅色)': 'light (Light)', 'zh_CN (简体中文)': 'zh_CN (Simplified Chinese)', '⚙️ 偏好设置': '⚙️ Preferences', '⚙️ 系统设置': '⚙️ Settings', '✅ 系统就绪': '✅ System Ready', '✓ 是': '✓ Yes', '✗ 否': '✗ No', '个值': ' values', '个子节点': ' sub-nodes', '个字段': ' fields', '个对象': ' objects', '个扫描件': 'scan file(s)', '个文件': 'file(s)', '主题:': 'Theme:', '产品名称': 'Product Name', '产品型号': 'Product Model', '介质': 'Medium', '份...': 'copy/copies...', '份数': 'Copies', '保存失败': 'Save Failed', '保存成功': 'Save Successful', '保存设置': 'Save Settings', '值': 'Value', '值 / 说明': 'Value / Description', '全选': 'Select All', '关闭': 'Close', '分钟': ' min', '切换界面显示语言,保存后立即生效': 'Switch the interface display language, takes effect immediately after saving', '切换界面颜色主题,保存后立即生效。也可点击导航栏右侧的 🌙/☀️ 按钮快速切换': 'Switch the interface color theme, takes effect immediately after saving. You can also click the 🌙/☀️ button on the right side of the navigation bar to quickly toggle', '列映射': 'Column Mapping', '列表': 'List', '刷新': 'Refresh', '剩余': 'remaining', '单张打印': 'Single Print', '压力': 'Pressure', '取消': 'Cancel', '取消全选': 'Deselect All', '变更': 'Changes', '否': 'No', '启用后,系统会定时自动保存打印视图中正在编辑的合格证数据到数据库,防止意外退出丢失记录': 'When enabled, the system periodically auto-saves the certificate data being edited in the print view to the database, preventing data loss on unexpected exit', '启用自动保存': 'Enable Auto Save', '字段': 'Field', '导入': 'Import', '导入中...': 'Importing...', '导入完成': 'Import Completed', '导入对话框中「最近文件」列表的最大保留条数,超出后自动裁剪': "Maximum number of entries kept in the 'Recent Files' list in the import dialog, older entries are trimmed automatically", '导入数据前弹出确认对话框,防止误操作覆盖已有销售计划记录': 'Show a confirmation dialog before importing data to prevent accidentally overwriting existing sales plan records', '导入配置': 'Import Configuration', '导出': 'Export', '就绪': 'Ready', '工作表': 'Worksheet', '已加载': 'Loaded', '已发送第一张打印': 'First print sent', '已完成': 'Completed', '已生成': 'Generated', '常规': 'Regular', '张': 'cards', '张合格证打印,是否生成扫描件?': 'certificate(s) printed. Generate scans?', '张,是否继续?': 'cards, continue?', '总记录': 'Total Records', '恢复默认': 'Restore Defaults', '手动输入': 'Manual Input', '打印历史': 'Print History', '打印合格证': 'Print Certificates', '打印坐标:': 'Print Coordinates:', '打印失败': 'Print Failed', '打印字体:': 'Print Font:', '打印完成': 'Print Completed', '打印机': 'Printer', '打印选中': 'Print Selected', '扫描件生成': 'Scan Generation', '扫描件生成完成!\n共': 'Scan generation completed!\nTotal', '批量参数:': 'Batch Parameters:', '批量打印': 'Batch Print', '批量打印完成,共': 'Batch print completed, total', '批量打印已取消': 'Batch print cancelled', '报告生成': 'Report Generation', '报告类型': 'Report Type', '提示': 'Notice', '搜索': 'Search', '操作符': 'Operator', '数据起始行': 'Data Start Row', '新增': 'New', '无可打印的编号': 'No serial numbers to print', '是': 'Yes', '最大保留数量:': 'Max Recent Files:', '最近文件': 'Recent Files', '材质报告': 'Material Report', '条记录': 'record(s)', '查询': 'Query', '检验标准': 'Inspection Standard', '模板': 'Template', '正在打印': 'Printing', '浏览...': 'Browse...', '添加条件': 'Add Condition', '清除历史': 'Clear History', '清除条件': 'Clear Conditions', '温度': 'Temperature', '生成': 'Generate', '生成 PDF': 'Generate PDF', '生成完成': 'Generation Completed', '生成扫描件': 'Generate Scans', '生成报告': 'Generate Reports', '界面': 'Interface', '确定': 'OK', '确定要将所有设置恢复为默认值吗?\n此操作不会保存,需点击「保存设置」生效。': "Are you sure you want to restore all settings to defaults?\nThis will not save automatically, click 'Save Settings' to apply.", '确认': 'Confirm', '确认恢复': 'Confirm Restore', '确认批量打印': 'Confirm Batch Print', '空列表': 'empty list', '空字符串': 'empty string', '简体中文': 'Simplified Chinese', '粗体': 'Bold', '系统级打印配置 (来自 config.yaml,只读)': 'System Print Config (from config.yaml, read-only)', '系统设置': 'Settings', '纸张尺寸:': 'Paper Size:', '结束编号': 'End Serial', '编号模式': 'Serial Number Mode', '自动保存的时间间隔,仅在「启用自动保存」开启时生效': "Auto-save time interval, only effective when 'Enable Auto Save' is on", '自动保存间隔:': 'Auto Save Interval:', '自动生成': 'Auto Generate', '节点': 'Node', '表头行': 'Header Row', '覆盖前确认': 'Confirm Before Overwrite', '规格': 'Specification', '警告': 'Warning', '设置已保存到 userconfig.yaml': 'Settings saved to userconfig.yaml', '试压报告': 'Pressure Test Report', '语言': 'Language', '语言:': 'Language:', '起始编号': 'Start Serial', '跳过': 'Skipped', '跳过行': 'Skip Rows', '输出目录': 'Output Directory', '进度': 'Progress', '选择文件': 'Select File', '选择目录': 'Select Directory', '通用': 'General', '重复跳过': 'Duplicates Skipped', '重置': 'Reset', '错误': 'Error', '项': ' items', '预览': 'Preview', '高级筛选': 'Advanced Filter', '默认份数:': 'Default Copies:', '默认打印机:': 'Default Printer:', '🏭 CertFlow': '🏭 CertFlow', '📄 报告生成': '📄 Generate Reports', '📋 以下为 config.yaml 全部配置节点的只读展示,修改请直接编辑 config.yaml 文件后重启。': '📋 Read-only view of all config.yaml nodes. Edit the config.yaml file directly and restart to apply changes.', '📋 计划导入': '📋 Import Plans', '📋 配置总览': '📋 Config Overview', '📜 打印历史': '📜 Print History', '📸 扫描件生成': '📸 Generate Scans', '🔍 查询与补充': '🔍 Query & Supplement', '🔧 打印机物理偏移 (mm) 用户可调,保存到 userconfig.yaml': '🔧 Printer Physical Offset (mm) adjustable, saved to userconfig.yaml', '🖨 打印合格证': '🖨 Print Certificates', '🖨️ 打印设置': '🖨️ Print Settings'}
property current_language: str

当前语言

tr(text)[源代码]

翻译文本

参数:

text (str) -- 源文本(简体中文)

返回:

翻译后的文本;若当前为中文或找不到翻译则返回原文

返回类型:

str

staticMetaObject = PySide6.QtCore.QMetaObject("I18nManager" inherits "QObject": Methods:   #4 type=Signal, signature=language_changed(QString), parameters=QString )
switch_language(language)[源代码]

切换应用语言

参数:

language (str) -- 语言代码,可选 "zh_CN" 或 "en_US"

返回:

切换成功时发出 language_changed 信号

返回类型:

None

class certflow.views.common.ThemeManager(app, parent=None)[源代码]

基类:QObject

主题管理器 — 浅色/深色主题切换

维护两套完整的 QSS (Qt Style Sheets),通过替换 application-level stylesheet 实现全局主题切换。

参数:
  • app (QApplication)

  • parent (QObject | None)

LIGHT_THEME = '\n    /* ===== 全局 ===== */\n    QMainWindow, QDialog, QWidget {\n        background-color: #f5f6fa;\n        color: #2c3e50;\n        font-family: "Microsoft YaHei", "SimHei", sans-serif;\n    }\n\n    /* ===== 导航栏 ===== */\n    QWidget#navBar {\n        background-color: #2c3e50;\n    }\n    QWidget#navBar QPushButton#navLogo {\n        background-color: transparent;\n        color: #ecf0f1;\n        border: none;\n        padding: 8px 16px;\n        font-weight: bold;\n        font-size: 16px;\n    }\n    QWidget#navBar QPushButton {\n        background-color: transparent;\n        color: #ecf0f1;\n        border: none;\n        padding: 8px 16px;\n        font-size: 14px;\n    }\n    QWidget#navBar QPushButton:hover {\n        background-color: #34495e;\n    }\n    QWidget#navBar QPushButton:checked {\n        background-color: #1abc9c;\n        color: white;\n    }\n    QWidget#navBar QPushButton#themeToggleBtn {\n        background-color: transparent;\n        color: #ecf0f1;\n        border: 1px solid #4a6278;\n        border-radius: 4px;\n        padding: 4px 12px;\n        font-size: 16px;\n    }\n    QWidget#navBar QPushButton#themeToggleBtn:hover {\n        background-color: #34495e;\n    }\n\n    /* ===== 主按钮 ===== */\n    QPushButton {\n        background-color: #2c7be5;\n        color: white;\n        border: none;\n        padding: 6px 16px;\n        border-radius: 4px;\n        font-size: 13px;\n    }\n    QPushButton:hover {\n        background-color: #1a68d1;\n    }\n    QPushButton:pressed {\n        background-color: #1557b0;\n    }\n    QPushButton:disabled {\n        background-color: #adb5bd;\n        color: #6c757d;\n    }\n\n    /* ===== 次要按钮 ===== */\n    QPushButton[secondary="true"] {\n        background-color: #6c757d;\n        color: white;\n    }\n    QPushButton[secondary="true"]:hover {\n        background-color: #5a6268;\n    }\n\n    /* ===== 成功按钮 ===== */\n    QPushButton[success="true"] {\n        background-color: #28a745;\n        color: white;\n    }\n    QPushButton[success="true"]:hover {\n        background-color: #218838;\n    }\n\n    /* ===== 输入框 ===== */\n    QLineEdit, QSpinBox, QDoubleSpinBox, QComboBox, QDateEdit {\n        background-color: white;\n        color: #2c3e50;\n        border: 1px solid #ced4da;\n        border-radius: 4px;\n        padding: 4px 8px;\n        font-size: 13px;\n    }\n    QLineEdit:focus, QSpinBox:focus, QDoubleSpinBox:focus, QComboBox:focus {\n        border-color: #2c7be5;\n    }\n    QComboBox::drop-down {\n        border: none;\n        padding-right: 8px;\n    }\n    QComboBox QAbstractItemView {\n        background-color: white;\n        color: #2c3e50;\n        selection-background-color: #2c7be5;\n        selection-color: white;\n    }\n\n    /* ===== 表格 ===== */\n    QTableWidget, QTableView {\n        background-color: white;\n        alternate-background-color: #f8f9fa;\n        color: #2c3e50;\n        gridline-color: #dee2e6;\n        border: 1px solid #dee2e6;\n        font-size: 13px;\n        selection-background-color: #cce5ff;\n        selection-color: #2c3e50;\n    }\n    QHeaderView::section {\n        background-color: #e9ecef;\n        color: #495057;\n        padding: 6px;\n        border: 1px solid #dee2e6;\n        font-weight: bold;\n    }\n\n    /* ===== 分组框 ===== */\n    QGroupBox {\n        border: 1px solid #dee2e6;\n        border-radius: 6px;\n        margin-top: 8px;\n        padding-top: 16px;\n        font-weight: bold;\n        color: #495057;\n    }\n    QGroupBox::title {\n        subcontrol-origin: margin;\n        left: 12px;\n        padding: 0 6px;\n    }\n\n    /* ===== 标签页 ===== */\n    QTabWidget::pane {\n        border: 1px solid #dee2e6;\n        background-color: white;\n    }\n    QTabBar::tab {\n        background-color: #e9ecef;\n        color: #495057;\n        padding: 8px 20px;\n        border: 1px solid #dee2e6;\n        border-bottom: none;\n        border-top-left-radius: 4px;\n        border-top-right-radius: 4px;\n    }\n    QTabBar::tab:selected {\n        background-color: white;\n        color: #2c7be5;\n        font-weight: bold;\n    }\n    QTabBar::tab:hover:!selected {\n        background-color: #dee2e6;\n    }\n\n    /* ===== 滚动条 ===== */\n    QScrollBar:vertical {\n        background: #f5f6fa;\n        width: 10px;\n        border-radius: 5px;\n    }\n    QScrollBar::handle:vertical {\n        background: #ced4da;\n        border-radius: 5px;\n        min-height: 20px;\n    }\n    QScrollBar::handle:vertical:hover {\n        background: #adb5bd;\n    }\n    QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical {\n        height: 0;\n    }\n\n    /* ===== 复选框/单选框 ===== */\n    QCheckBox, QRadioButton {\n        color: #2c3e50;\n        spacing: 6px;\n    }\n    QCheckBox::indicator {\n        width: 16px;\n        height: 16px;\n    }\n\n    /* ===== 标签 ===== */\n    QLabel {\n        color: #2c3e50;\n    }\n\n    /* ===== 进度条 ===== */\n    QProgressBar {\n        background-color: #e9ecef;\n        border: none;\n        border-radius: 4px;\n        height: 8px;\n        text-align: center;\n    }\n    QProgressBar::chunk {\n        background-color: #2c7be5;\n        border-radius: 4px;\n    }\n\n    /* ===== 树形控件 ===== */\n    QTreeWidget {\n        background-color: white;\n        alternate-background-color: #f8f9fa;\n        color: #2c3e50;\n        border: 1px solid #dee2e6;\n    }\n    QTreeWidget::item:selected {\n        background-color: #cce5ff;\n        color: #2c3e50;\n    }\n\n    /* ===== 状态栏 ===== */\n    QStatusBar {\n        background-color: #e9ecef;\n        color: #6c757d;\n    }\n    '
DARK_THEME = '\n    /* ===== 全局 ===== */\n    QMainWindow, QDialog, QWidget {\n        background-color: #1e1e2e;\n        color: #cdd6f4;\n        font-family: "Microsoft YaHei", "SimHei", sans-serif;\n    }\n\n    /* ===== 导航栏 ===== */\n    QWidget#navBar {\n        background-color: #11111b;\n    }\n    QWidget#navBar QPushButton#navLogo {\n        background-color: transparent;\n        color: #bac2de;\n        border: none;\n        padding: 8px 16px;\n        font-weight: bold;\n        font-size: 16px;\n    }\n    QWidget#navBar QPushButton {\n        background-color: transparent;\n        color: #bac2de;\n        border: none;\n        padding: 8px 16px;\n        font-size: 14px;\n    }\n    QWidget#navBar QPushButton:hover {\n        background-color: #313244;\n    }\n    QWidget#navBar QPushButton:checked {\n        background-color: #89b4fa;\n        color: #1e1e2e;\n    }\n    QWidget#navBar QPushButton#themeToggleBtn {\n        background-color: transparent;\n        color: #bac2de;\n        border: 1px solid #45475a;\n        border-radius: 4px;\n        padding: 4px 12px;\n        font-size: 16px;\n    }\n    QWidget#navBar QPushButton#themeToggleBtn:hover {\n        background-color: #313244;\n    }\n\n    /* ===== 主按钮 ===== */\n    QPushButton {\n        background-color: #89b4fa;\n        color: #1e1e2e;\n        border: none;\n        padding: 6px 16px;\n        border-radius: 4px;\n        font-size: 13px;\n    }\n    QPushButton:hover {\n        background-color: #74a8f7;\n    }\n    QPushButton:pressed {\n        background-color: #5e9cf5;\n    }\n    QPushButton:disabled {\n        background-color: #45475a;\n        color: #6c7086;\n    }\n\n    /* ===== 次要按钮 ===== */\n    QPushButton[secondary="true"] {\n        background-color: #585b70;\n        color: #cdd6f4;\n    }\n    QPushButton[secondary="true"]:hover {\n        background-color: #6c7086;\n    }\n\n    /* ===== 成功按钮 ===== */\n    QPushButton[success="true"] {\n        background-color: #a6e3a1;\n        color: #1e1e2e;\n    }\n    QPushButton[success="true"]:hover {\n        background-color: #94e2d5;\n    }\n\n    /* ===== 输入框 ===== */\n    QLineEdit, QSpinBox, QDoubleSpinBox, QComboBox, QDateEdit {\n        background-color: #313244;\n        color: #cdd6f4;\n        border: 1px solid #45475a;\n        border-radius: 4px;\n        padding: 4px 8px;\n        font-size: 13px;\n    }\n    QLineEdit:focus, QSpinBox:focus, QDoubleSpinBox:focus, QComboBox:focus {\n        border-color: #89b4fa;\n    }\n    QComboBox::drop-down {\n        border: none;\n        padding-right: 8px;\n    }\n    QComboBox QAbstractItemView {\n        background-color: #313244;\n        color: #cdd6f4;\n        selection-background-color: #89b4fa;\n        selection-color: #1e1e2e;\n    }\n\n    /* ===== 表格 ===== */\n    QTableWidget, QTableView {\n        background-color: #1e1e2e;\n        alternate-background-color: #313244;\n        color: #cdd6f4;\n        gridline-color: #45475a;\n        border: 1px solid #45475a;\n        font-size: 13px;\n        selection-background-color: #45475a;\n        selection-color: #cdd6f4;\n    }\n    QHeaderView::section {\n        background-color: #313244;\n        color: #bac2de;\n        padding: 6px;\n        border: 1px solid #45475a;\n        font-weight: bold;\n    }\n\n    /* ===== 分组框 ===== */\n    QGroupBox {\n        border: 1px solid #45475a;\n        border-radius: 6px;\n        margin-top: 8px;\n        padding-top: 16px;\n        font-weight: bold;\n        color: #bac2de;\n    }\n    QGroupBox::title {\n        subcontrol-origin: margin;\n        left: 12px;\n        padding: 0 6px;\n    }\n\n    /* ===== 标签页 ===== */\n    QTabWidget::pane {\n        border: 1px solid #45475a;\n        background-color: #1e1e2e;\n    }\n    QTabBar::tab {\n        background-color: #313244;\n        color: #6c7086;\n        padding: 8px 20px;\n        border: 1px solid #45475a;\n        border-bottom: none;\n        border-top-left-radius: 4px;\n        border-top-right-radius: 4px;\n    }\n    QTabBar::tab:selected {\n        background-color: #1e1e2e;\n        color: #89b4fa;\n        font-weight: bold;\n    }\n    QTabBar::tab:hover:!selected {\n        background-color: #45475a;\n    }\n\n    /* ===== 滚动条 ===== */\n    QScrollBar:vertical {\n        background: #1e1e2e;\n        width: 10px;\n        border-radius: 5px;\n    }\n    QScrollBar::handle:vertical {\n        background: #45475a;\n        border-radius: 5px;\n        min-height: 20px;\n    }\n    QScrollBar::handle:vertical:hover {\n        background: #585b70;\n    }\n    QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical {\n        height: 0;\n    }\n\n    /* ===== 复选框/单选框 ===== */\n    QCheckBox, QRadioButton {\n        color: #cdd6f4;\n        spacing: 6px;\n    }\n    QCheckBox::indicator {\n        width: 16px;\n        height: 16px;\n    }\n\n    /* ===== 标签 ===== */\n    QLabel {\n        color: #cdd6f4;\n    }\n\n    /* ===== 进度条 ===== */\n    QProgressBar {\n        background-color: #313244;\n        border: none;\n        border-radius: 4px;\n        height: 8px;\n        text-align: center;\n    }\n    QProgressBar::chunk {\n        background-color: #89b4fa;\n        border-radius: 4px;\n    }\n\n    /* ===== 树形控件 ===== */\n    QTreeWidget {\n        background-color: #1e1e2e;\n        alternate-background-color: #313244;\n        color: #cdd6f4;\n        border: 1px solid #45475a;\n    }\n    QTreeWidget::item:selected {\n        background-color: #45475a;\n        color: #cdd6f4;\n    }\n\n    /* ===== 状态栏 ===== */\n    QStatusBar {\n        background-color: #313244;\n        color: #6c7086;\n    }\n    '
property current_theme: str

当前主题名称

apply_theme(theme)[源代码]

应用指定主题

参数:

theme (str) -- 主题名称,可选 "light" 或 "dark"

返回:

切换成功时更新 _current_theme 并替换全局样式表

返回类型:

None

staticMetaObject = PySide6.QtCore.QMetaObject("ThemeManager" inherits "QObject": )
certflow.views.common.parse_qdate(text, fmt='yyyy-MM-dd')[源代码]

用指定格式解析日期字符串为 QDate,失败时返回 None

QDate.fromString() 解析失败时会返回无效 QDate(setDate 后显示 1752-09-14),这里显式校验 isValid() 后再返回。

参数:
  • text (str) -- 日期字符串,如 "2026-07-01"

  • fmt (str) -- Qt 日期格式,默认 "yyyy-MM-dd"

返回:

有效的 QDate 实例,解析失败时返回 None

返回类型:

QDate | None

certflow.views.common.safe_qdate(year, month, day)[源代码]

以校验方式构造 QDate,避免静默回退到 Qt 的零日期(1752-09-14)

QDate(year, month, day) 在参数非法(如 2 月 30 日、year 越界)时会 返回一个「无效」日期,而 QDateEdit.setDate() 收到无效日期后默认显示 1752-09-14。本方法在构造后显式校验 isValid(),失败时返回 None。

参数:
  • year (int) -- 年份

  • month (int) -- 月份 (1-12)

  • day (int) -- 日 (1-31,需符合该月实际天数)

返回:

有效的 QDate 实例,参数非法时返回 None

返回类型:

QDate | None