from PySide6.QtWidgets import ( QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QMessageBox, QDialog, QFileDialog ) from PySide6.QtCore import Qt, QSettings import os import glob from src.db_manager import DatabaseManager from src.xml_parser import XmlParser from utils.readMTP import MTPHandler from src.session_manager import SessionManager from utils.logger import get_logger, log_timing_context logger = get_logger("base_page") class BaseInspectionPage(QDialog): def __init__(self, title, parent=None): super().__init__(parent) self.setWindowTitle(title) self.setWindowFlags(self.windowFlags() | Qt.Window | Qt.WindowMaximizeButtonHint) self.settings = QSettings("Tengzhi", "InspectionApp") self.mtp_handler = MTPHandler() self.session = SessionManager() self.page_type = title # 保存页面类型("入检" 或 "手检") self.main_layout = QVBoxLayout(self) self.main_layout.setContentsMargins(0, 0, 0, 0) self.main_layout.setSpacing(0) self.create_toolbar() # Content Area (to be filled by subclasses) self.content_widget = QWidget() self.content_layout = QVBoxLayout(self.content_widget) self.content_layout.setContentsMargins(20, 20, 20, 20) self.content_layout.setSpacing(15) self.content_layout.setAlignment(Qt.AlignTop) self.main_layout.addWidget(self.content_widget, 1) # Stretch = 1 to fill space # Bottom Button Bar self.create_bottom_bar() def create_toolbar(self): toolbar = QWidget() toolbar.setStyleSheet("background-color: #f8f8f8; border-bottom: 1px solid #ddd;") layout = QHBoxLayout(toolbar) layout.setContentsMargins(10, 5, 10, 5) title_label = QPushButton("⚙ 设置读取路径") title_label.setStyleSheet(""" QPushButton { border: none; background-color: transparent; font-family: 'Microsoft YaHei'; font-size: 13px; color: #666; text-align: left; } QPushButton:hover { color: #0086fa; } """) title_label.clicked.connect(self.on_settings) layout.addWidget(title_label) layout.addStretch() self.main_layout.addWidget(toolbar) def on_settings(self): from PySide6.QtWidgets import QFormLayout, QLineEdit, QRadioButton, QButtonGroup dialog = QDialog(self) dialog.setWindowTitle("同步设置") dialog.setFixedWidth(400) layout = QVBoxLayout(dialog) form = QFormLayout() # Mode Selection mode_group = QButtonGroup(dialog) current_mode = self.settings.value("sync_mode", "local") rb_local = QRadioButton("本地路径 (本地硬盘)") rb_mtp = QRadioButton("MTP设备 (连接的PDA)") mode_group.addButton(rb_local) mode_group.addButton(rb_mtp) if current_mode == "local": rb_local.setChecked(True) else: rb_mtp.setChecked(True) form.addRow("同步模式:", rb_local) form.addRow("", rb_mtp) self.edit_local_path = QLineEdit(self.settings.value("xml_path", os.getcwd())) btn_browse = QPushButton("浏览...") btn_browse.clicked.connect(self.browse_local_path) local_path_layout = QHBoxLayout() local_path_layout.addWidget(self.edit_local_path) local_path_layout.addWidget(btn_browse) form.addRow("本地文件夹:", local_path_layout) form.addRow(QWidget()) # Spacer # MTP Settings self.edit_mtp_device = QLineEdit(self.settings.value("mtp_device", "Trizeps VII")) form.addRow("MTP设备名称:", self.edit_mtp_device) self.edit_mtp_path = QLineEdit(self.settings.value("mtp_path", "Storage Card/Program Files/xSort/Results/Analyses")) form.addRow("MTP 内部路径:", self.edit_mtp_path) layout.addLayout(form) # Save Button btn_save = QPushButton("保存配置") btn_save.setStyleSheet("background-color: #0086fa; color: white; padding: 10px; font-weight: bold;") btn_save.clicked.connect(lambda: self.save_settings(dialog, rb_local.isChecked())) layout.addWidget(btn_save) dialog.exec() def browse_local_path(self): directory = QFileDialog.getExistingDirectory(self, "选择本地文件夹", self.edit_local_path.text()) if directory: self.edit_local_path.setText(directory) def save_settings(self, dialog, is_local): self.settings.setValue("sync_mode", "local" if is_local else "mtp") self.settings.setValue("xml_path", self.edit_local_path.text()) self.settings.setValue("mtp_device", self.edit_mtp_device.text()) self.settings.setValue("mtp_path", self.edit_mtp_path.text()) self.show_info("设置成功", "配置已更新。") dialog.accept() def create_bottom_bar(self): button_bar = QWidget() button_bar.setStyleSheet("background-color: #e0e0e0; border-top: 1px solid #999;") button_layout = QHBoxLayout(button_bar) button_layout.setContentsMargins(10, 10, 10, 10) button_layout.setSpacing(10) btn_style = """ QPushButton { background-color: #0086fa; color: white; border-radius: 5px; padding: 15px; font-family: 'Microsoft YaHei'; font-size: 19px; font-weight: bold; } QPushButton:pressed { background-color: #006bbd; } """ # Buttons: [Sync, Submit, Print, Close] self.btn_sync = QPushButton("同步") self.btn_sync.setStyleSheet(btn_style) self.btn_sync.clicked.connect(self.on_sync) self.btn_submit = QPushButton("提交") self.btn_submit.setStyleSheet(btn_style) self.btn_submit.clicked.connect(self.on_submit) self.btn_close = QPushButton("关闭") self.btn_close.setStyleSheet(btn_style) self.btn_close.clicked.connect(self.close) button_layout.addWidget(self.btn_sync) button_layout.addWidget(self.btn_submit) button_layout.addWidget(self.btn_close) self.main_layout.addWidget(button_bar) def on_sync(self): logger.info(f"[{self.page_type}] 开始同步操作") sync_mode = self.settings.value("sync_mode", "local") xml_path = None target_xml_name = "" if sync_mode == "local": xml_dir = self.settings.value("xml_path", "xml_data") if not os.path.exists(xml_dir): self.show_info("同步失败", f"本地路径不存在: {xml_dir}") return xml_files = glob.glob(os.path.join(xml_dir, "*.xml")) if not xml_files: self.show_info("同步失败", "本地目录下未找到 .xml 文件") return latest_xml = max(xml_files, key=os.path.getmtime) xml_path = latest_xml target_xml_name = os.path.basename(latest_xml) logger.info(f"本地模式,选定文件: {target_xml_name}") else: device_name = self.settings.value("mtp_device", "Trizeps VII") remote_path = self.settings.value("mtp_path", "") self.show_info("正在同步", f"正在从设备 [{device_name}] 读取最新数据...") with log_timing_context(f"MTP同步 设备={device_name}", "base_page"): local_cache, filename = self.mtp_handler.get_latest_xml_windows(device_name, remote_path) if not local_cache: self.show_info("同步失败", f"MTP同步错误: {filename}") return xml_path = local_cache target_xml_name = filename # 1.解析xml parser = XmlParser() with log_timing_context("XML解析", "base_page"): parsed_data, msg = parser.parse_file(xml_path) if not parsed_data: self.show_info("同步失败", f"XML解析错误: {msg}") return # 2. 写入数据 db = DatabaseManager() with log_timing_context("DB写入分析数据", "base_page"): success, db_msg = db.insert_analysis_data(parsed_data) if not success: self.show_info("数据库错误", f"插入失败: {db_msg}\n(请检查数据库连接)") return with log_timing_context("DB读取最新样本数据", "base_page"): latest_data = db.get_latest_sample_data() if latest_data: self.update_ui_with_data(latest_data) self.show_info("同步成功", f"已同步 {len(parsed_data)} 条元素数据。\n源文件: {target_xml_name}") else: self.show_info("同步异常", "数据插入成功但无法读取回显。") logger.info(f"[{self.page_type}] 同步操作完成") def update_ui_with_data(self, data): ### 更新UI界面,如果不符合,对不符合的元素字体标红,并且 pass def clear_data(self): """清空界面所有数据(子类实现)""" pass def on_submit(self): """提交检验数据到 e_pz_hjqy_import 表""" logger.info(f"[{self.page_type}] 开始提交检验数据") try: # 1. 收集表单数据 gch = self.inputs.get("batch_no").text().strip() if self.inputs.get("batch_no") else "" heat_number = self.inputs.get("material1").text().strip() if self.inputs.get("material1") else "" heat_number2 = self.inputs.get("info1").text().strip() if self.inputs.get("info1") else "" cz = self.inputs.get("spec1").text().strip() if self.inputs.get("spec1") else "" cz2 = self.inputs.get("spec2").text().strip() if self.inputs.get("spec2") else "" check_information = self.inputs.get("insp_info_1").text().strip() if self.inputs.get("insp_info_1") else "" check_information2 = self.inputs.get("insp_info_2").text().strip() if self.inputs.get("insp_info_2") else "" # 产地/材质 拼接显示时,提交只需要产地 (cd) display_cd = self.inputs.get("material").text().strip() if self.inputs.get("material") else "" cd = display_cd.split('/')[0] if '/' in display_cd else display_cd size = self.inputs.get("spec").text().strip() if self.inputs.get("spec") else "" # 2. 验证必填字段 if not gch: self.show_info("提交失败", "工程号不能为空") return # 3. 收集所有元素数据 element_names = ["C", "Si", "Mn", "P", "S", "Cr", "Ni", "Mo", "Cu", "Ti", "Nb", "V", "Al", "W", "Co", "N", "Fe", "Se"] elements = {} for elem in element_names: if elem in self.inputs: value_str = self.inputs[elem].text().strip() if value_str: try: elements[elem] = float(value_str) except ValueError: pass # 4. 取当前用户信息 user_id = self.session.get_user_id() dept_id = self.session.get_dept_id() data_corp = self.session.get_data_corp() if not user_id: self.show_info("提交失败", "未获取到用户信息,请重新登录") return # 5. 构建提交数据 inspection_data = { "gch": gch, "heat_number": heat_number, "heat_number2": heat_number2, "cz": cz, "cz2": cz2, "check_information": check_information, "check_information2": check_information2, "cd": cd, "size": size, "elements": elements, "user_id": user_id, "dept_id": dept_id, "data_corp": data_corp } # 6. 插入数据库 db = DatabaseManager() with log_timing_context(f"DB提交检验数据 gch={gch}", "base_page"): success, message = db.insert_inspection_data(inspection_data) if success: logger.info(f"[{self.page_type}] 提交成功: {message}") self.show_info("提交成功", message) self.clear_data() else: logger.warning(f"[{self.page_type}] 提交失败: {message}") self.show_info("提交失败", message) except Exception as e: logger.error(f"[{self.page_type}] 提交异常: {e}", exc_info=True) self.show_info("提交异常", f"发生错误: {str(e)}") def show_info(self, title, message): QMessageBox.information(self, title, message)