From f31871b4f51b22b0d2b715089efdd219f776f6de Mon Sep 17 00:00:00 2001 From: zhu-mengmeng <15588200382@163.com> Date: Sat, 7 Jun 2025 16:44:27 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E6=A3=80=E9=AA=8C=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E5=8A=9F=E8=83=BD=EF=BC=8C=E6=94=AF=E6=8C=81=E5=8A=A8?= =?UTF-8?q?=E6=80=81=E9=85=8D=E7=BD=AE=E6=A3=80=E9=AA=8C=E9=A1=B9=E7=9B=AE?= =?UTF-8?q?=EF=BC=9B=E4=BF=AE=E5=A4=8D=E8=A1=A8=E6=A0=BC=E8=A1=A8=E5=A4=B4?= =?UTF-8?q?=E5=B8=83=E5=B1=80=E9=94=99=E4=BD=8D=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 +- dao/inspection_dao.py | 327 +++++++++++++++ db/schema.sql | 55 +++ ui/__pycache__/main_window_ui.cpython-310.pyc | Bin 16342 -> 0 bytes ui/__pycache__/settings_ui.cpython-310.pyc | Bin 8224 -> 0 bytes ui/inspection_settings_ui.py | 216 ++++++++++ ui/main_window_ui.py | 190 +++------ ui/settings_ui.py | 19 + utils/init_db.py | 78 ++-- utils/inspection_config_manager.py | 155 +++++++ .../camera_display_widget.cpython-310.pyc | Bin 3845 -> 0 bytes .../camera_settings_widget.cpython-310.pyc | Bin 7840 -> 0 bytes .../__pycache__/main_window.cpython-310.pyc | Bin 7400 -> 0 bytes widgets/inspection_settings_widget.py | 384 ++++++++++++++++++ widgets/main_window.py | 84 +++- widgets/settings_widget.py | 58 ++- 16 files changed, 1378 insertions(+), 190 deletions(-) create mode 100644 dao/inspection_dao.py create mode 100644 db/schema.sql delete mode 100644 ui/__pycache__/main_window_ui.cpython-310.pyc delete mode 100644 ui/__pycache__/settings_ui.cpython-310.pyc create mode 100644 ui/inspection_settings_ui.py create mode 100644 utils/inspection_config_manager.py delete mode 100644 widgets/__pycache__/camera_display_widget.cpython-310.pyc delete mode 100644 widgets/__pycache__/camera_settings_widget.cpython-310.pyc delete mode 100644 widgets/__pycache__/main_window.cpython-310.pyc create mode 100644 widgets/inspection_settings_widget.py diff --git a/.gitignore b/.gitignore index 26946e2..fb15c32 100644 --- a/.gitignore +++ b/.gitignore @@ -22,7 +22,7 @@ __pycache__/ Thumbs.db # 缓存文件 -__pycache__/ +*__pycache__/ .pytest_cache/ .coverage htmlcov/ diff --git a/dao/inspection_dao.py b/dao/inspection_dao.py new file mode 100644 index 0000000..8ed46cb --- /dev/null +++ b/dao/inspection_dao.py @@ -0,0 +1,327 @@ +import json +import logging +from datetime import datetime +from utils.sql_utils import SQLUtils + +class InspectionDAO: + """检验项目配置和数据访问对象""" + + def __init__(self): + """初始化数据访问对象""" + self.db = SQLUtils('sqlite', database='db/jtDB.db') + + def __del__(self): + """析构函数,确保数据库连接关闭""" + if hasattr(self, 'db'): + self.db.close() + + def get_all_inspection_configs(self, include_disabled=False): + """获取所有检验项目配置 + + Args: + include_disabled: 是否包含禁用的项目 + + Returns: + list: 检验项目配置列表 + """ + try: + if include_disabled: + sql = """ + SELECT id, position, name, display_name, enabled, required, + data_type, min_value, max_value, enum_values, unit, sort_order + FROM inspection_config + WHERE is_deleted = FALSE + ORDER BY sort_order, position + """ + params = () + else: + sql = """ + SELECT id, position, name, display_name, enabled, required, + data_type, min_value, max_value, enum_values, unit, sort_order + FROM inspection_config + WHERE is_deleted = FALSE AND enabled = TRUE + ORDER BY sort_order, position + """ + params = () + + self.db.cursor.execute(sql, params) + results = self.db.cursor.fetchall() + + configs = [] + for row in results: + config = { + 'id': row[0], + 'position': row[1], + 'name': row[2], + 'display_name': row[3], + 'enabled': bool(row[4]), + 'required': bool(row[5]), + 'data_type': row[6], + 'min_value': row[7], + 'max_value': row[8], + 'enum_values': json.loads(row[9]) if row[9] else None, + 'unit': row[10], + 'sort_order': row[11] + } + configs.append(config) + + return configs + except Exception as e: + logging.error(f"获取检验项目配置失败: {str(e)}") + return [] + + def get_enabled_inspection_configs(self): + """获取已启用的检验项目配置 + + Returns: + list: 已启用的检验项目配置列表 + """ + return self.get_all_inspection_configs(include_disabled=False) + + def get_inspection_config_by_position(self, position): + """根据位置获取检验项目配置 + + Args: + position: 位置序号 (1-6) + + Returns: + dict: 检验项目配置, 未找到则返回None + """ + try: + sql = """ + SELECT id, position, name, display_name, enabled, required, + data_type, min_value, max_value, enum_values, unit, sort_order + FROM inspection_config + WHERE position = ? AND is_deleted = FALSE + """ + params = (position,) + + self.db.cursor.execute(sql, params) + row = self.db.cursor.fetchone() + + if row: + config = { + 'id': row[0], + 'position': row[1], + 'name': row[2], + 'display_name': row[3], + 'enabled': bool(row[4]), + 'required': bool(row[5]), + 'data_type': row[6], + 'min_value': row[7], + 'max_value': row[8], + 'enum_values': json.loads(row[9]) if row[9] else None, + 'unit': row[10], + 'sort_order': row[11] + } + return config + else: + return None + except Exception as e: + logging.error(f"获取检验项目配置失败: {str(e)}") + return None + + def update_inspection_config(self, config_id, data, username='system'): + """更新检验项目配置 + + Args: + config_id: 配置ID + data: 更新数据 + username: 操作用户 + + Returns: + bool: 更新是否成功 + """ + try: + current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S') + + # 构建更新SQL + update_fields = [] + params = [] + + # 可更新的字段 + allowed_fields = [ + 'name', 'display_name', 'enabled', 'required', + 'data_type', 'min_value', 'max_value', 'unit', + 'sort_order', 'enum_values' + ] + + for field in allowed_fields: + if field in data: + # 特殊处理enum_values字段,确保存储为JSON字符串 + if field == 'enum_values' and data[field] is not None: + if isinstance(data[field], list): + update_fields.append(f"{field} = ?") + params.append(json.dumps(data[field])) + elif isinstance(data[field], str): + # 如果已经是字符串,检查是否有效的JSON + try: + json.loads(data[field]) + update_fields.append(f"{field} = ?") + params.append(data[field]) + except: + logging.warning(f"无效的JSON: {data[field]}") + continue + else: + update_fields.append(f"{field} = ?") + params.append(data[field]) + + # 添加更新时间和更新人 + update_fields.append("update_time = ?") + params.append(current_time) + update_fields.append("update_by = ?") + params.append(username) + + # 添加配置ID到参数列表 + params.append(config_id) + + # 构建SQL + sql = f""" + UPDATE inspection_config + SET {', '.join(update_fields)} + WHERE id = ? + """ + + self.db.execute_update(sql, params) + return True + except Exception as e: + logging.error(f"更新检验项目配置失败: {str(e)}") + return False + + def toggle_inspection_config(self, position, enabled, username='system'): + """启用或禁用检验项目配置 + + Args: + position: 位置序号 (1-6) + enabled: 是否启用 + username: 操作用户 + + Returns: + bool: 操作是否成功 + """ + try: + current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S') + + sql = """ + UPDATE inspection_config + SET enabled = ?, update_time = ?, update_by = ? + WHERE position = ? AND is_deleted = FALSE + """ + params = (enabled, current_time, username, position) + + self.db.execute_update(sql, params) + return True + except Exception as e: + logging.error(f"更新检验项目启用状态失败: {str(e)}") + return False + + def save_inspection_data(self, order_id, data, username='system'): + """保存检验数据 + + Args: + order_id: 工程号 + data: 检验数据列表,格式: [{'position': 1, 'config_id': 1, 'value': '合格'}, ...] + username: 操作用户 + + Returns: + bool: 保存是否成功 + """ + try: + current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S') + + self.db.begin_transaction() + + for item in data: + position = item.get('position') + config_id = item.get('config_id') + value = item.get('value') + status = item.get('status', 'pass') + remark = item.get('remark', '') + + # 检查是否已存在该工程号和位置的记录 + check_sql = """ + SELECT id FROM inspection_data + WHERE order_id = ? AND position = ? AND is_deleted = FALSE + """ + check_params = (order_id, position) + + self.db.cursor.execute(check_sql, check_params) + existing = self.db.cursor.fetchone() + + if existing: + # 更新已有记录 + update_sql = """ + UPDATE inspection_data + SET config_id = ?, value = ?, status = ?, remark = ?, + update_time = ?, update_by = ? + WHERE id = ? + """ + update_params = ( + config_id, value, status, remark, + current_time, username, existing[0] + ) + self.db.cursor.execute(update_sql, update_params) + else: + # 插入新记录 + insert_sql = """ + INSERT INTO inspection_data ( + order_id, position, config_id, value, status, remark, + create_time, create_by, is_deleted + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, FALSE) + """ + insert_params = ( + order_id, position, config_id, value, status, remark, + current_time, username + ) + self.db.cursor.execute(insert_sql, insert_params) + + self.db.commit_transaction() + return True + except Exception as e: + self.db.rollback_transaction() + logging.error(f"保存检验数据失败: {str(e)}") + return False + + def get_inspection_data_by_order(self, order_id): + """根据工程号获取检验数据 + + Args: + order_id: 工程号 + + Returns: + list: 检验数据列表 + """ + try: + sql = """ + SELECT d.id, d.position, d.config_id, d.value, d.status, d.remark, + c.name, c.display_name, c.data_type, c.unit + FROM inspection_data d + JOIN inspection_config c ON d.config_id = c.id + WHERE d.order_id = ? AND d.is_deleted = FALSE + ORDER BY d.position + """ + params = (order_id,) + + self.db.cursor.execute(sql, params) + results = self.db.cursor.fetchall() + + data_list = [] + for row in results: + data = { + 'id': row[0], + 'position': row[1], + 'config_id': row[2], + 'value': row[3], + 'status': row[4], + 'remark': row[5], + 'name': row[6], + 'display_name': row[7], + 'data_type': row[8], + 'unit': row[9] + } + data_list.append(data) + + return data_list + except Exception as e: + logging.error(f"获取检验数据失败: {str(e)}") + return [] \ No newline at end of file diff --git a/db/schema.sql b/db/schema.sql new file mode 100644 index 0000000..cdd261e --- /dev/null +++ b/db/schema.sql @@ -0,0 +1,55 @@ +-- 创建检验项目配置表 +CREATE TABLE IF NOT EXISTS inspection_config ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + position INTEGER NOT NULL, -- 位置序号 (1-6) + name VARCHAR(50) NOT NULL, -- 检验项目名称 + display_name VARCHAR(50) NOT NULL, -- 显示名称 + enabled BOOLEAN DEFAULT TRUE, -- 是否启用 + required BOOLEAN DEFAULT FALSE, -- 是否必填 + data_type VARCHAR(20) DEFAULT 'text', -- 数据类型: text, number, enum + min_value FLOAT, -- 最小值 (用于number类型) + max_value FLOAT, -- 最大值 (用于number类型) + enum_values TEXT, -- 枚举值 (用于enum类型, JSON格式存储) + unit VARCHAR(20), -- 单位 + sort_order INTEGER NOT NULL, -- 排序顺序 + create_time TIMESTAMP NOT NULL, + create_by VARCHAR(50) NOT NULL, + update_time TIMESTAMP, + update_by VARCHAR(50), + is_deleted BOOLEAN DEFAULT FALSE, + UNIQUE(position, is_deleted) -- 确保同一位置只有一个激活的配置 +); + +-- 创建检验数据记录表 +CREATE TABLE IF NOT EXISTS inspection_data ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + order_id VARCHAR(50) NOT NULL, -- 关联的工程号 + position INTEGER NOT NULL, -- 位置序号 + config_id INTEGER NOT NULL, -- 关联的配置ID + value TEXT NOT NULL, -- 检验值 (所有类型都用TEXT存储) + status VARCHAR(20) DEFAULT 'pass', -- 状态: pass, fail, warning + remark TEXT, -- 备注 + create_time TIMESTAMP NOT NULL, + create_by VARCHAR(50) NOT NULL, + update_time TIMESTAMP, + update_by VARCHAR(50), + is_deleted BOOLEAN DEFAULT FALSE, + FOREIGN KEY(config_id) REFERENCES inspection_config(id) +); + +-- 创建默认检验项目数据 +INSERT OR IGNORE INTO inspection_config ( + position, name, display_name, enabled, required, data_type, + min_value, max_value, unit, sort_order, create_time, create_by +) VALUES +(1, 'appearance', '外观', TRUE, TRUE, 'enum', NULL, NULL, '', 1, CURRENT_TIMESTAMP, 'system'), +(2, 'diameter', '线径', TRUE, TRUE, 'number', 0, 100, 'mm', 2, CURRENT_TIMESTAMP, 'system'), +(3, 'resistance', '电阻', TRUE, FALSE, 'number', 0, 1000, 'Ω', 3, CURRENT_TIMESTAMP, 'system'), +(4, 'hardness', '硬度', FALSE, FALSE, 'number', 0, 100, 'HRC', 4, CURRENT_TIMESTAMP, 'system'), +(5, 'strength', '强度', FALSE, FALSE, 'number', 0, 1000, 'MPa', 5, CURRENT_TIMESTAMP, 'system'), +(6, 'custom', '自定义', FALSE, FALSE, 'text', NULL, NULL, '', 6, CURRENT_TIMESTAMP, 'system'); + +-- 为外观检验项设置枚举值 +UPDATE inspection_config +SET enum_values = '["合格", "不合格", "需要重检"]' +WHERE name = 'appearance'; \ No newline at end of file diff --git a/ui/__pycache__/main_window_ui.cpython-310.pyc b/ui/__pycache__/main_window_ui.cpython-310.pyc deleted file mode 100644 index 463356674f0d3004bffa11732828289a26a7d67a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16342 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 diff --git a/ui/inspection_settings_ui.py b/ui/inspection_settings_ui.py new file mode 100644 index 0000000..8092740 --- /dev/null +++ b/ui/inspection_settings_ui.py @@ -0,0 +1,216 @@ +from PySide6.QtWidgets import ( + QWidget, QVBoxLayout, QHBoxLayout, QFormLayout, QLabel, + QLineEdit, QCheckBox, QComboBox, QPushButton, QGroupBox, + QTableWidget, QTableWidgetItem, QHeaderView, QAbstractItemView, + QSpinBox, QDoubleSpinBox, QFrame, QScrollArea +) +from PySide6.QtGui import QFont, QBrush, QColor +from PySide6.QtCore import Qt, Signal + +class InspectionSettingsUI(QWidget): + """检验设置UI""" + + def __init__(self, parent=None): + super().__init__(parent) + self.parent = parent + self.init_ui() + + def init_ui(self): + """初始化UI""" + # 设置字体 + self.title_font = QFont("微软雅黑", 14, QFont.Bold) + self.normal_font = QFont("微软雅黑", 11) + self.small_font = QFont("微软雅黑", 9) + + # 设置背景颜色,便于识别 + self.setStyleSheet("background-color: #f5f5f5;") + + # 创建主布局 + self.main_layout = QVBoxLayout(self) + self.main_layout.setContentsMargins(20, 20, 20, 20) + self.main_layout.setSpacing(15) + + # 标题 + self.title_label = QLabel("检验项目配置") + self.title_label.setFont(self.title_font) + self.title_label.setAlignment(Qt.AlignCenter) + self.title_label.setStyleSheet("color: #1a237e; padding: 10px;") + self.main_layout.addWidget(self.title_label) + + # 说明文本 + self.desc_label = QLabel("配置检验二级菜单项目,至少1项,最多6项。启用的项目将显示在微丝产线表格的检验区域。") + self.desc_label.setWordWrap(True) + self.desc_label.setStyleSheet("color: #666666; padding: 0px 10px 10px 10px;") + self.main_layout.addWidget(self.desc_label) + + # 创建滚动区域 + self.scroll_area = QScrollArea() + self.scroll_area.setWidgetResizable(True) + self.scroll_area.setFrameShape(QFrame.NoFrame) + + # 创建滚动区域的内容部件 + self.scroll_widget = QWidget() + self.scroll_layout = QVBoxLayout(self.scroll_widget) + self.scroll_layout.setContentsMargins(0, 0, 0, 0) + self.scroll_layout.setSpacing(15) + + # 创建6个检验项目配置组 + self.config_groups = [] + for i in range(6): + group = self.create_config_group(i + 1) + self.scroll_layout.addWidget(group) + self.config_groups.append(group) + + # 设置滚动区域的部件 + self.scroll_area.setWidget(self.scroll_widget) + self.main_layout.addWidget(self.scroll_area, 1) + + # 底部按钮区域 + self.button_layout = QHBoxLayout() + self.button_layout.setContentsMargins(0, 10, 0, 0) + + self.save_button = QPushButton("保存配置") + self.save_button.setFont(self.normal_font) + self.save_button.setFixedSize(120, 40) + self.save_button.setStyleSheet(""" + QPushButton { + background-color: #4caf50; + color: white; + border: none; + border-radius: 5px; + } + QPushButton:hover { + background-color: #45a049; + } + QPushButton:pressed { + background-color: #3d8b40; + } + """) + + self.reset_button = QPushButton("重置") + self.reset_button.setFont(self.normal_font) + self.reset_button.setFixedSize(120, 40) + self.reset_button.setStyleSheet(""" + QPushButton { + background-color: #f44336; + color: white; + border: none; + border-radius: 5px; + } + QPushButton:hover { + background-color: #e53935; + } + QPushButton:pressed { + background-color: #d32f2f; + } + """) + + self.button_layout.addStretch() + self.button_layout.addWidget(self.reset_button) + self.button_layout.addSpacing(20) + self.button_layout.addWidget(self.save_button) + + self.main_layout.addLayout(self.button_layout) + + def create_config_group(self, position): + """创建检验项目配置组 + + Args: + position: 位置序号 (1-6) + + Returns: + QGroupBox: 配置组 + """ + group = QGroupBox(f"检验项目 {position}") + group.setFont(self.normal_font) + group.setCheckable(True) + group.setChecked(position <= 3) # 默认前3个启用 + + group_layout = QFormLayout(group) + group_layout.setContentsMargins(15, 25, 15, 15) + group_layout.setSpacing(10) + + # 名称 + name_label = QLabel("项目名称:") + name_label.setFont(self.normal_font) + name_input = QLineEdit() + name_input.setFont(self.normal_font) + name_input.setObjectName(f"name_input_{position}") + group_layout.addRow(name_label, name_input) + + # 显示名称 + display_name_label = QLabel("显示名称:") + display_name_label.setFont(self.normal_font) + display_name_input = QLineEdit() + display_name_input.setFont(self.normal_font) + display_name_input.setObjectName(f"display_name_input_{position}") + group_layout.addRow(display_name_label, display_name_input) + + # 数据类型 + data_type_label = QLabel("数据类型:") + data_type_label.setFont(self.normal_font) + data_type_combo = QComboBox() + data_type_combo.setFont(self.normal_font) + data_type_combo.setObjectName(f"data_type_combo_{position}") + data_type_combo.addItem("文本", "text") + data_type_combo.addItem("数值", "number") + data_type_combo.addItem("枚举", "enum") + group_layout.addRow(data_type_label, data_type_combo) + + # 单位 (用于数值类型) + unit_label = QLabel("单位:") + unit_label.setFont(self.normal_font) + unit_input = QLineEdit() + unit_input.setFont(self.normal_font) + unit_input.setObjectName(f"unit_input_{position}") + group_layout.addRow(unit_label, unit_input) + + # 最小值 (用于数值类型) + min_value_label = QLabel("最小值:") + min_value_label.setFont(self.normal_font) + min_value_spin = QDoubleSpinBox() + min_value_spin.setFont(self.normal_font) + min_value_spin.setObjectName(f"min_value_spin_{position}") + min_value_spin.setRange(-999999, 999999) + min_value_spin.setDecimals(2) + min_value_spin.setSingleStep(0.1) + group_layout.addRow(min_value_label, min_value_spin) + + # 最大值 (用于数值类型) + max_value_label = QLabel("最大值:") + max_value_label.setFont(self.normal_font) + max_value_spin = QDoubleSpinBox() + max_value_spin.setFont(self.normal_font) + max_value_spin.setObjectName(f"max_value_spin_{position}") + max_value_spin.setRange(-999999, 999999) + max_value_spin.setDecimals(2) + max_value_spin.setSingleStep(0.1) + max_value_spin.setValue(100) + group_layout.addRow(max_value_label, max_value_spin) + + # 枚举值 (用于枚举类型) + enum_values_label = QLabel("枚举值:") + enum_values_label.setFont(self.normal_font) + enum_values_input = QLineEdit() + enum_values_input.setFont(self.normal_font) + enum_values_input.setObjectName(f"enum_values_input_{position}") + enum_values_input.setPlaceholderText("用逗号分隔,如: 合格,不合格,需重检") + group_layout.addRow(enum_values_label, enum_values_input) + + # 是否必填 + required_check = QCheckBox("必填项") + required_check.setFont(self.normal_font) + required_check.setObjectName(f"required_check_{position}") + group_layout.addRow("", required_check) + + # 保存位置信息 + group.setProperty("position", position) + + return group + + def set_form_enabled(self, enabled): + """设置表单是否可编辑""" + for group in self.config_groups: + group.setEnabled(enabled) + self.save_button.setEnabled(enabled) + self.reset_button.setEnabled(enabled) \ No newline at end of file diff --git a/ui/main_window_ui.py b/ui/main_window_ui.py index 8060154..9168b6a 100644 --- a/ui/main_window_ui.py +++ b/ui/main_window_ui.py @@ -84,26 +84,14 @@ class MainWindowUI(QMainWindow): self.project_table = QTableWidget(4, 4) self.project_table.setHorizontalHeaderLabels(["用电", "数量", "产量", "开机率"]) self.project_table.setVerticalHeaderLabels(["当日", "当月", "当年", "累计"]) + #设置字体 + self.project_table.setFont(self.normal_font) + # 设置垂直表头宽度 + self.project_table.verticalHeader().setFixedWidth(60) self.project_table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) self.project_table.verticalHeader().setSectionResizeMode(QHeaderView.Stretch) self.project_table.setEditTriggers(QTableWidget.NoEditTriggers) # 设置为不可编辑 - - # 设置表格样式 - self.project_table.setStyleSheet(""" - QTableWidget { - border: none; - gridline-color: #dddddd; - } - QHeaderView::section { - background-color: #f0f0f0; - padding: 4px; - border: 1px solid #cccccc; - font-weight: bold; - } - """) - self.project_layout.addWidget(self.project_table) - self.left_layout.addWidget(self.project_frame) # 任务信息区域 - 使用QFrame包裹,添加边框 @@ -162,7 +150,7 @@ class MainWindowUI(QMainWindow): # 订单行 self.order_layout = QHBoxLayout() - self.order_label = QLabel("订单") + self.order_label = QLabel("工程号") self.order_label.setFont(QFont("微软雅黑", 12, QFont.Bold)) self.order_label.setFixedHeight(35) self.order_label.setStyleSheet("padding: 0 5px; color: #333333;") @@ -463,9 +451,9 @@ class MainWindowUI(QMainWindow): self.process_content_layout.setSpacing(0) # 创建表格 - 支持动态配置检验列数 - self.inspection_columns = 3 # 默认3列,可动态配置 - # TODO:后续从数据库中读取 - self.inspection_headers = ["外观", "线径", "电阻", "硬度", "强度"] # 默认检验标题 + self.inspection_columns = 1 # 默认至少显示1列 + # 默认检验标题,实际运行时将通过InspectionConfigManager获取 + self.inspection_headers = ["检验项"] total_columns = 2 + self.inspection_columns + 2 # 上料2列 + 检验N列 + 包装2列 self.process_table = QTableWidget(8, total_columns) # 8行:1行标题区域 + 1行列标题 + 6行数据 @@ -486,9 +474,7 @@ class MainWindowUI(QMainWindow): # 创建表头 - 合并单元格 self.create_process_table_headers() - - # 填充表格内容 - self.fill_process_table_cells() + # 添加表格到布局 self.process_content_layout.addWidget(self.process_table) @@ -540,9 +526,7 @@ class MainWindowUI(QMainWindow): # 添加表格到布局 self.record_layout.addWidget(self.record_table) - - # 填充表格内容 - self.fill_record_table_cells() + # 添加一个通用的单元格创建方法 def create_cell_item(self, text, alignment=Qt.AlignCenter): @@ -559,61 +543,6 @@ class MainWindowUI(QMainWindow): item.setTextAlignment(alignment) return item - def fill_process_table_cells(self): - """填充微丝产线表格单元格""" - # 工序工程数据 - process_data = ["拉丝", "退火", "检验", "包装", "入库", "出库"] - - # 填充工序数据 - for row in range(6): - # 设置序号 - self.process_table.setItem(row + 2, 0, self.create_cell_item(row + 1)) - - # 设置工序工程名称 - self.process_table.setItem(row + 2, 1, self.create_cell_item(process_data[row])) - - # 只为前3行设置数据 - if row < 3: - # 检验区域 - 动态列 - inspection_data = ["合格", f"{0.5 + row * 0.1:.1f}", f"{10 + row * 5}", f"{80 + row * 5}", f"{90 + row * 2}"] - for i in range(min(self.inspection_columns, len(inspection_data))): - self.process_table.setItem(row + 2, 2 + i, self.create_cell_item(inspection_data[i])) - - # 包装区域 - 贴标和称重 - packaging_start_col = 2 + self.inspection_columns - - self.process_table.setItem(row + 2, packaging_start_col, self.create_cell_item("已完成")) - self.process_table.setItem(row + 2, packaging_start_col + 1, self.create_cell_item(f"{50 + row * 10}")) - - def fill_record_table_cells(self): - """填充包装记录表格单元格""" - # 填充序号列 - for row in range(12): - self.record_table.setItem(row + 1, 0, self.create_cell_item(row + 1)) - - # 填充示例数据 - record_data = [ - ["ORD-2025-001", "不锈钢", "0.5mm", "T001", "A001", "50kg"], - ["ORD-2025-001", "不锈钢", "0.6mm", "T001", "A002", "55kg"], - ["ORD-2025-001", "不锈钢", "0.7mm", "T001", "A003", "60kg"], - ] - - # 只填充前3行 - for row in range(3): - for col, value in enumerate(record_data[row]): - self.record_table.setItem(row + 1, col + 1, self.create_cell_item(value)) - - # 设置合计行 - self.record_table.setItem(13, 0, self.create_header_item("合计")) - - # 轴数 - self.record_table.setItem(13, 3, self.create_header_item("轴数")) - self.record_table.setItem(13, 4, self.create_cell_item("0")) - - # 重量 - self.record_table.setItem(13, 5, self.create_header_item("重量")) - self.record_table.setItem(13, 6, self.create_cell_item("0.0")) - def set_inspection_columns(self, columns, headers=None): """设置检验列数和标题 @@ -621,6 +550,9 @@ class MainWindowUI(QMainWindow): columns: 检验列数量 headers: 检验列标题列表,如果为None则使用默认标题 """ + # 确保列数在1-6之间 + columns = max(1, min(6, columns)) + # 保存旧的列数 old_column_count = self.process_table.columnCount() @@ -636,18 +568,30 @@ class MainWindowUI(QMainWindow): if item_r1: del item_r1 + # 清除所有单元格合并 + for row in range(2): + for col in range(old_column_count): + try: + self.process_table.setSpan(row, col, 1, 1) + except: + pass # 忽略错误,可能有些单元格没有合并 + # 更新检验列数 self.inspection_columns = columns # 更新检验标题 if headers is not None and len(headers) >= columns: - self.inspection_headers = headers + self.inspection_headers = headers[:columns] # 只使用前N个标题 elif len(self.inspection_headers) < columns: # 如果当前标题不足,扩展标题列表 current_len = len(self.inspection_headers) for i in range(current_len, columns): self.inspection_headers.append(f"检验项{i+1}") + # 截断多余的标题 + if len(self.inspection_headers) > columns: + self.inspection_headers = self.inspection_headers[:columns] + # 计算总列数 total_columns = 2 + self.inspection_columns + 2 # 上料2列 + 检验N列 + 包装2列 self.process_table.setColumnCount(total_columns) @@ -657,9 +601,6 @@ class MainWindowUI(QMainWindow): # 重新创建表头 self.create_process_table_headers() - - # 重新填充数据 - self.fill_process_table_cells() def create_process_table_headers(self): """创建微丝产线表格的表头,实现合并单元格""" @@ -680,7 +621,7 @@ class MainWindowUI(QMainWindow): # 第二行:列标题 # 上料区域列标题 - material_headers = ["序号", "工序工程"] + material_headers = ["序号", "工程号"] for col, header in enumerate(material_headers): self.process_table.setItem(1, col, self.create_header_item(header)) @@ -703,7 +644,7 @@ class MainWindowUI(QMainWindow): """设置微丝产线表格的列宽 - 支持动态配置检验列""" # 上料区域列宽 self.process_table.setColumnWidth(0, 70) # 序号 - self.process_table.setColumnWidth(1, 190) # 工序工程 + self.process_table.setColumnWidth(1, 190) # 工程号 # 检验区域列宽 for i in range(self.inspection_columns): @@ -714,40 +655,41 @@ class MainWindowUI(QMainWindow): self.process_table.setColumnWidth(packaging_start_col, 140) # 贴标 self.process_table.setColumnWidth(packaging_start_col + 1, 140) # 称重 - def create_process_table_headers(self): - """创建微丝产线表格的表头,实现合并单元格""" - # 第一行:上料、检验、包装标题区域 - - # 上料区域(2列) - self.process_table.setSpan(0, 0, 1, 2) - self.process_table.setItem(0, 0, self.create_header_item("上料")) - - # 检验区域(动态列数) - self.process_table.setSpan(0, 2, 1, self.inspection_columns) - self.process_table.setItem(0, 2, self.create_header_item("检验")) - - # 包装区域(2列) - packaging_start_col = 2 + self.inspection_columns - self.process_table.setSpan(0, packaging_start_col, 1, 2) - self.process_table.setItem(0, packaging_start_col, self.create_header_item("包装")) - - # 第二行:列标题 - # 上料区域列标题 - material_headers = ["序号", "工序工程"] - for col, header in enumerate(material_headers): - self.process_table.setItem(1, col, self.create_header_item(header)) - - # 检验区域列标题 - 可动态配置 - for i in range(self.inspection_columns): - header_text = "" - if i < len(self.inspection_headers): - header_text = self.inspection_headers[i] - else: - header_text = f"检验项{i+1}" # 如果没有定义足够的标题,使用默认标题 - - self.process_table.setItem(1, 2 + i, self.create_header_item(header_text)) - - # 包装区域列标题 - packaging_headers = ["贴标", "称重"] - for i, header in enumerate(packaging_headers): - self.process_table.setItem(1, packaging_start_col + i, self.create_header_item(header)) \ No newline at end of file + # 删除这个重复的方法,下面的是重复定义 + # def create_process_table_headers(self): + # """创建微丝产线表格的表头,实现合并单元格""" + # # 第一行:上料、检验、包装标题区域 + # + # # 上料区域(2列) + # self.process_table.setSpan(0, 0, 1, 2) + # self.process_table.setItem(0, 0, self.create_header_item("上料")) + # + # # 检验区域(动态列数) + # self.process_table.setSpan(0, 2, 1, self.inspection_columns) + # self.process_table.setItem(0, 2, self.create_header_item("检验")) + # + # # 包装区域(2列) + # packaging_start_col = 2 + self.inspection_columns + # self.process_table.setSpan(0, packaging_start_col, 1, 2) + # self.process_table.setItem(0, packaging_start_col, self.create_header_item("包装")) + # + # # 第二行:列标题 + # # 上料区域列标题 + # material_headers = ["序号", "工序工程"] + # for col, header in enumerate(material_headers): + # self.process_table.setItem(1, col, self.create_header_item(header)) + # + # # 检验区域列标题 - 可动态配置 + # for i in range(self.inspection_columns): + # header_text = "" + # if i < len(self.inspection_headers): + # header_text = self.inspection_headers[i] + # else: + # header_text = f"检验项{i+1}" # 如果没有定义足够的标题,使用默认标题 + # + # self.process_table.setItem(1, 2 + i, self.create_header_item(header_text)) + # + # # 包装区域列标题 + # packaging_headers = ["贴标", "称重"] + # for i, header in enumerate(packaging_headers): + # self.process_table.setItem(1, packaging_start_col + i, self.create_header_item(header)) \ No newline at end of file diff --git a/ui/settings_ui.py b/ui/settings_ui.py index 52e8902..2498f67 100644 --- a/ui/settings_ui.py +++ b/ui/settings_ui.py @@ -36,6 +36,7 @@ class SettingsUI(QWidget): # 创建各个选项卡 self.create_camera_tab() self.create_database_tab() + self.create_inspection_tab() self.create_plc_tab() self.create_push_tab() self.create_auth_tab() @@ -293,6 +294,24 @@ class SettingsUI(QWidget): self.tab_widget.addTab(self.database_tab, "数据源设置") + def create_inspection_tab(self): + """创建检验配置选项卡""" + # 检验配置选项卡 + self.inspection_tab = QWidget() + self.inspection_layout = QVBoxLayout(self.inspection_tab) + self.inspection_layout.setContentsMargins(0, 0, 0, 0) + self.inspection_layout.setSpacing(0) + + # 添加一个临时提示标签,表示此处将由InspectionSettingsWidget替换 + self.inspection_placeholder = QLabel("正在加载检验配置...") + self.inspection_placeholder.setFont(self.normal_font) + self.inspection_placeholder.setAlignment(Qt.AlignCenter) + self.inspection_placeholder.setStyleSheet("color: #888888; padding: 20px;") + self.inspection_layout.addWidget(self.inspection_placeholder) + + # 添加到选项卡 + self.tab_widget.addTab(self.inspection_tab, "检验配置") + def create_plc_tab(self): # PLC设置选项卡 self.plc_tab = QWidget() diff --git a/utils/init_db.py b/utils/init_db.py index 2cd4425..ae084ac 100644 --- a/utils/init_db.py +++ b/utils/init_db.py @@ -1,44 +1,62 @@ -from sql_utils import SQLUtils +from utils.sql_utils import SQLUtils import datetime +import os +import logging def init_database(): db = SQLUtils('sqlite', database='db/jtDB.db') - # 创建用户表 - create_table_sql = """ - CREATE TABLE IF NOT EXISTS user ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - username VARCHAR(50) NOT NULL UNIQUE, - password VARCHAR(100) NOT NULL, - create_time TIMESTAMP NOT NULL, - create_by VARCHAR(50) NOT NULL, - update_time TIMESTAMP, - update_by VARCHAR(50), - is_deleted BOOLEAN DEFAULT FALSE - ); - """ - - # 获取当前时间 - current_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') - - # 插入系统用户 - insert_system_user_sql = """ - INSERT OR IGNORE INTO user ( - username, password, create_time, create_by, is_deleted - ) VALUES ( - 'system', '123456', ?, 'system', FALSE - ); - """ - try: db.begin_transaction() - db.execute_query(create_table_sql) + + # 创建用户表 + create_user_table_sql = """ + CREATE TABLE IF NOT EXISTS user ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + username VARCHAR(50) NOT NULL UNIQUE, + password VARCHAR(100) NOT NULL, + create_time TIMESTAMP NOT NULL, + create_by VARCHAR(50) NOT NULL, + update_time TIMESTAMP, + update_by VARCHAR(50), + is_deleted BOOLEAN DEFAULT FALSE + ); + """ + db.execute_query(create_user_table_sql) + + # 获取当前时间 + current_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') + + # 插入系统用户 + insert_system_user_sql = """ + INSERT OR IGNORE INTO user ( + username, password, create_time, create_by, is_deleted + ) VALUES ( + 'system', '123456', ?, 'system', FALSE + ); + """ db.execute_query(insert_system_user_sql, (current_time,)) + + # 检查是否存在schema.sql文件,如果存在则执行其中的SQL + schema_file = 'db/schema.sql' + if os.path.exists(schema_file): + with open(schema_file, 'r', encoding='utf-8') as f: + schema_sql = f.read() + + # 按语句分割并执行SQL + statements = schema_sql.split(';') + for statement in statements: + statement = statement.strip() + if statement: # 跳过空语句 + db.execute_query(statement) + + logging.info("已执行schema.sql中的数据库初始化脚本") + db.commit_transaction() - print("Database initialized successfully!") + logging.info("数据库初始化成功!") except Exception as e: db.rollback_transaction() - print(f"Error initializing database: {str(e)}") + logging.error(f"数据库初始化失败: {str(e)}") finally: db.close() diff --git a/utils/inspection_config_manager.py b/utils/inspection_config_manager.py new file mode 100644 index 0000000..9b07518 --- /dev/null +++ b/utils/inspection_config_manager.py @@ -0,0 +1,155 @@ +import logging +from dao.inspection_dao import InspectionDAO + +class InspectionConfigManager: + """检验配置管理器,用于管理检验项目配置""" + + _instance = None + + @classmethod + def get_instance(cls): + """获取单例实例""" + if cls._instance is None: + cls._instance = InspectionConfigManager() + return cls._instance + + def __init__(self): + """初始化检验配置管理器""" + self.dao = InspectionDAO() + self.configs = [] + self.reload_configs() + + def reload_configs(self): + """重新加载检验配置""" + try: + self.configs = self.dao.get_all_inspection_configs(include_disabled=True) + logging.info(f"已加载{len(self.configs)}个检验项目配置") + return True + except Exception as e: + logging.error(f"加载检验配置失败: {str(e)}") + return False + + def get_configs(self, include_disabled=False): + """获取检验配置列表 + + Args: + include_disabled: 是否包含禁用的项目 + + Returns: + list: 检验配置列表 + """ + if include_disabled: + return self.configs + else: + return [config for config in self.configs if config['enabled']] + + def get_enabled_configs(self): + """获取已启用的检验配置列表 + + Returns: + list: 已启用的检验配置列表 + """ + return self.get_configs(include_disabled=False) + + def get_enabled_count(self): + """获取已启用的检验项目数量 + + Returns: + int: 已启用的检验项目数量 + """ + return len(self.get_enabled_configs()) + + def get_config_by_position(self, position): + """根据位置获取检验配置 + + Args: + position: 位置序号 (1-6) + + Returns: + dict: 检验配置,未找到则返回None + """ + for config in self.configs: + if config['position'] == position: + return config + return None + + def toggle_config(self, position, enabled, username='system'): + """启用或禁用检验配置 + + Args: + position: 位置序号 (1-6) + enabled: 是否启用 + username: 操作用户 + + Returns: + bool: 操作是否成功 + """ + result = self.dao.toggle_inspection_config(position, enabled, username) + if result: + self.reload_configs() + return result + + def update_config(self, config_id, data, username='system'): + """更新检验配置 + + Args: + config_id: 配置ID + data: 更新数据 + username: 操作用户 + + Returns: + bool: 更新是否成功 + """ + result = self.dao.update_inspection_config(config_id, data, username) + if result: + self.reload_configs() + return result + + def get_inspection_headers(self): + """获取检验表头,用于UI显示 + + Returns: + list: 检验表头列表 + """ + enabled_configs = self.get_enabled_configs() + headers = [] + + # 按位置排序 + enabled_configs.sort(key=lambda x: x['position']) + + for config in enabled_configs: + headers.append(config['display_name']) + + return headers + + def get_inspection_column_count(self): + """获取检验列数量 + + Returns: + int: 检验列数量 + """ + return len(self.get_enabled_configs()) + + def save_inspection_data(self, order_id, data, username='system'): + """保存检验数据 + + Args: + order_id: 工程号 + data: 检验数据列表 + username: 操作用户 + + Returns: + bool: 保存是否成功 + """ + return self.dao.save_inspection_data(order_id, data, username) + + def get_inspection_data(self, order_id): + """获取检验数据 + + Args: + order_id: 工程号 + + Returns: + list: 检验数据列表 + """ + return self.dao.get_inspection_data_by_order(order_id) \ No newline at end of file diff --git a/widgets/__pycache__/camera_display_widget.cpython-310.pyc b/widgets/__pycache__/camera_display_widget.cpython-310.pyc deleted file mode 100644 index c2650ab5acb067271b9cc77318cf6528dbee0f56..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3845 zcmZ`+>u(%a6~Fh+?Ck8*jm+KR@d&>~6&h&-xD#Kdw+)DhBXvNN{Fo!!~q zJ6m^GcIDJg0&T#l8k(XcbeE=T5-0){MIj00|Cq0M*3B3Aj%dX>_l}));>>F9+{d}E z^ZT90hNV)T!1Jeb`=@(b2>BN_2A>=>ehjbpHxw>$X+SLXr52TKW-<7(Ks!b{6{u-} zUe8#Wx?vghtd*rGtp{d3XXWa7D^JOB(%7vNuh9MxCHRwbiVw`m1SL7AjB{Ql4LVCa zGBea2_9&EFWI2+STYI!eU8Bc(RIZl|D)nvFHd!}=?e!hj4q0cxBgb$*7VMPYT(GOY z+uBXZi^R>lg(cz^yv(9z?Q!V@sTS|Toyl00#`MXm7H7wg`|gw%#f9;o|0I0rYiUcZnmf0znL;pF0H z$>Q=@jSXB3>stDXXP^?1Mbf2nfGi~u>uNnRM{IqLbTd6*%^aCApq+)U3E!McCdf59 zR?Wv5(E@G8rfvHTKeBCJfW_h>AO{^HavC)+F4rUgc3mks&PvgiAD6<0EvCa++k4Gx zL~#!52W52#Mz|;Zj+c%VR7;Km{JQXh$=GN*9420)an81Dfg^-%UnBoLF#3}4xEQT_ zjj7SukiRk-L9H|GkIwiGR_$3aIxFcVM$@yoO4GLL-q&pNA-Krn@CvlqBF$?i{EAv< z&nD39Tf2UZg7DgdN=9bxhgbXp3RLnuD7j1B^w-ltxwH$El8Vt_GyhTUFR1`Q_#0^kApUYX)ytKmLX3xkk9zxRo!%GbogqM8k|r&@WANV%lkXads?}E!jU2wF7Tr+5@A`Ij)=9cMfJK>jpdC zflC>{crbRp8yc$EgS8PTo`zQpLt$vcEKg0X%!=?UYn|Pjp_1}!11NYUL{x$T+DG-0 z87fAQ^$G^5@Qlk`t;fKVu?f;S+{r~?0W;aI*&~Z|o`PT9sXS^Yx7Ody^#EQNDX5W+ zGhPP2ihFhQ2--ZGWBds0S#VP@5pwh%xaS-@4i+X)lwGF)p2)m6s*|YLK1s>EMgd1D zn~O?w+9JJ3E-SJiabT9}#hJ(93~qj2dqcxpsoE^~`f8!|2Rk{7x6{AS?SJ~)!AXZ&Xf^jq%B5A`>iOGuFaJSppynNmN$asqw^p*rmA`)e zw~tx~{@_Mw_oMf9_b=Q?mfu-D|3Px+&E)3I=l70P%N!ytkjfX}_8W0F2&aI-Q*p-Q zJmhgj$QWX$ZU8)1)Dp4o)%_^e{6@sTg0nNTe&etkXVJ{T$ysHPhZof9W;G2L%W zhH>uT>ou>5fhE>N#H+gGvz3F%E;uYu*TH3PjAbp8+BQ!8%*ys0P$$_lPKIr_ZxDn41CG$(p2{ zp=fvVQvZ{b6^bV+WF1qm4~!1NKG6Lj?1!pZg?ru?FQzD2pyIH(4jTU^+>@ivo$|X{ed=L<5Hzq@X@&Hqw8UR5>!SH2g@8&STDbgh#9+dHP z3I$%2Unby-ff)j{4#7H;`hHBFn9L25f4r+w0%c&JmHvkd{SQ7$ZSSoc{e_ds-*0?= z=6dqh)j?|-s~S>U{2O@XXR-J$7G*3dP+Uu0b4CVgz7zYUW~67{3e8z~1qQoeX3}B0 zIRQ6>`-flsTd>SvJ_6TgGa9eJ^>vkvsH_CZDRW2GdI6>+SC&@KoldTNocv`uS$-d4 z;L5GH(*&va=aqk69`r$X7v8*k>enkb-|D~jyX5RG)PTJ0c34R1LaG5xQ0d}(SiA4y z+n{eAUX>!i#Z3rC`u{plYzF&%8O_75kBow=5{Wbgq}c|f(G{vBFFEz;-3y=(#RlYrqtDqs$o74vJwa?z;dI7(C>1xMT#F$;yX_SmRJ?WQNbfW3laGDT*+*qFpeF#IY&rw~d&`BSaXzi)VEP3DB{}oKRco?X>mgit4YiB9~ zSgrquNypK9Tf5emkf%G~P5KSbu7!|@uaCi8`1f4-DhKNa+4WAv}c3-)$NOGniR6a_Vm8 zOetGA3wNt*pQduV>{LRzkV(8%4xctWV>Z%sJrgu4X-w1jin(GkXT@4EO=CWd_Mvj3 zlFTJ5sa#6-50?ijgSo-VP;LlvBsctoQ9a-o-pHkInFjr3pL9vMyCVP|4&nrtyfu z2H4;ggAIE7FWb2ZW}Y&#Lz|?rsjMl?Lm;7YZRUZ`ig2+|@pvI?iO9((N^HgpMEvBV zp6?fCyoYNSQEr_KMEF!`rdlYU&YC=eLC`%mcj>7>l^%!aK6?I$8psp_iu>vu#tllf zDz!f41wpAg<3D}^Qc^W|v{1!Ec#&SK`{!uEe+P_bkO5&lGiQ5_7xKcFty~10OMgr* z3O;<<%*EKmRb$#@Hgm2-b8!}8;VVWi!6MAXJ;|ahhI@*|SpxR~mSid12bH37Lu`l* zLmp-$ERFjJ+r@U{o@RU4DDJ!17#qiZH`~kh;l796$M)ksDxZ0PWv;lnv8#q-3u2_R@PBZ+^J`!JBK>Z?%@*ZT;xAkMDhV{mqs2=f2Zj+xn+> z*Os4K{qV->-RrA&-(P?Ih1T*vss?L6{BZr%_ckxT)>`_``ioaK7hhccXl1Hsb*Yhf zTVMXy!34%-qiHq(XVVC*roCb`jJ(q@nxPed6!$RQnTgT}vp`*DgGL$JGxd;WKChU?E0bT=C5EAy$%nEj5%TN6h;Z}yQneK1^E`=> zL;@H@fgcpAMNg!P5?1+&0*e@^&#^+_<%_jyz-wh+41lY8#USrXi25Qm&Cw+f?0NZ0 zsoE$lQb0{0&-Zc08b9v^SFBLr>F5&oId0K?$3f!v( zw@nev=Zocn@8|Qkjo%zQ{J8IN|8T{t&K$l_<4+zAz|Eg49iA=WovJhW3;y8?($)OK zdUn2ckGv|MoV&yap^H!9@^^zI%$V)sAGX70`=7Aq@9T%M$+kL+h=rXW@dSq>xcpZ^ z8pfpua)Tg!J^fB)UJTOUki_lQ*6E@$gOP^*eav0N%X=`k=0 z5qvNDig<0#tLAkZB`BqfM6q1+y>^wBbX`2~{GeT>1YI-GfsbDf(fu(wgA>Brnsro?)T8UZRXpe5xg=W=si2MW4D^&dU3(6w(e!4~s z0aDvrQBiNJVZU%5zNB1C>!_zpJ?FN`_d%CD(;*YYr9r-g%O_^g#^1!hL(}H`Cy&UX zd&A)5HjnMWDPc5>OXBnj7{Y1OR3U0shOGqzF+U^A9VWw>rZ(cr^P%`^Ly(J+r1 z-%KtL&W)ziu$VJufQH7570Wc9I;i`GH3q4#=9>{25} zy|A(JT74JZaP6hnTg!j9e*NuE@J9XdDG^~Mf394(BtoSs^Ddswx`3Qgb+J}CTN9yT z*(-2P5Jyz1)+_B0BO(RHP6S>>#9*^y)xz1b$N2A|9UlS7+R_{%p(7Ha<#a6DO zkrR9kmp=*;vtqVmB~1D|rfZLy36ks`4Au59{hS^{Q9z+JMjDFHAr%QjG4n}_N0|lH zlgh0XS|#i{A!g(Ho!0UXS|5DBb?tkbi`Q1~zJbL{>2v*^o2`4#a~gH?|M0^c{}?=U zRI68!lN9Tmg6+{lP~cxg#kOgRG4cw8Wvm1q(|Uwnp!X75`vj?!<(M&RezGqnckJFX zX+kHZCvwvN42GsSYXXsUwRL&>0DLBO8z6faP}-xIcXkF?#~Yrk?8y~aSs!rrHf~Y{5}yR?FK(gy+KDe7!X9Dd8L zAq*<*x(G(ul_x1tYQRc}EjHF9iYJI7D+a|N<~(We{hbz}trmcNh^z-ZSp19a)CzYQ zQ82dKjIuC`Py|wA$`Y4F+a=jpJ%QI)yY~Ioy+svQeoYwO{D&WHy!BRn1gt7;-TB%2 z`^&1`#FVsJ{Qzn=-n!FzdI?Up_U!G|yNj!LU+<1i6Q_l7Tv@*B39VOuy7A6WvN36M zeiWl{dZvh6DDWzooYbQiR2^*okQ&k3h*6b)wHFf|NtM{x zZ#}PU^6|Z;)=RH#-n_DLYf0^;)Gn%Za?GN}nb zSdz2A*$um4jT#F`&Ja}1y+*@H8+(iexLqSOVk}_KiN(@sgqeNAVb04k&0`@rURdQ{ zYVo@9a_i}9t;KuFs_W_v&OEe9Cr7`rCm)!u(~D&O?VdlC4ROLe-$R7*qFkysU*{)L zn2o43E2Q%72sV>>9GZworEpO)QApTlBJUTHRqYamVo)!X^HSRGwV%Pb|BlP2=pM6U zrVIVL=BPb?sISlV-A(lD^JyY&4gZS5F|>uyCG`kRlQe2beNqZc%bCS2=+V}CbYuXN zd92i9>*g!#H=gO}kw1YU`5BOTJHsBH$~t_EN@xkr#kO`ZQe(GOV#?{9gXf84Iz$Go zNr?Z6>sR!~<2I&!|L>ai*6=${nSTo-Ap&sR|tz{yO{!T1iR#+>%mxq+>H zm$DZbo)?H@Iz+zuXCeMK6`ci{|IF`t?d@B(o<)e@*hw{h34ts$Yx+9`+jg?y-`5Ft zPAAbYzhN&}3-*E&;B;mdr(g>q$wk108{tL>UKK$Jd6^ry4VOw9k-a#?OjA&%Kpbtx z8gfIAdZaqDakMaIamGVjPs?OiOPmSKrewq?Q|$$G z60`ibYtO$e<1haz>Za<1vEOc?1L`J2ZFZzzR28Yt?$`@(3bW16`Pa}>W~9PJl>IvG z+_&(oveXVZWWFl)_2xBt<6TtSeYHx*RGgsmAZ*G-vJ*>!QpFPvp;*MWK*9f-Ug8@> zzDeY7KynGS?e8Bo$gTeemrr?-iqzQ0$#e)iI)|c&Yo@WM+m8g8uJlfhMcPqeS|1Md zV3U%74mSS-0_?W~HUmOH2pBRYN7zHM+c1GRyFpP0$LT8ua}dpK8GRg_AUcTVgjAIP zwCjO1D$`MlqCKH6+363YQJJoffH#^0jYuQZz_PypRC5o$F^DvsQfiq9NYtUeA!SOj zJKp2Ay(b%0IFu)`JK#`7zrTVe>S2(zrRQ4D{8)msyMsQ=m6a1a)V6bhzNp=fQn_W4 zK~%8kLC566u#F zd>$2P6X4qqMuSg6ZZ|-eu`@XLq2wTqlwpTxPH(w0=rHsa=xBW;1NRSzFd|^b0Hux* zRWMoo=;hWMKkT+icQBErTYe5F=5?AwOGX>j1?Jqi{6XvO7wHh^%~x=UgWQ0^)7JJ! zWouvQnHA4J9HNIfnXrJiAD{!@L|hwjrh6)bwzHA$z9~$1PAvv z4)OKLK-USW%4gLMWc9sgv5Ti1XKNl)7>o~qh?v|396OJ*6ur9;A&(?Zs!@xcT<s~4)*2`^qp1Lh~`vwFP-OC=>$G6V)=Zf#_DC_6Z!m8NQ-n0Qdd?h;>e%0 zCY-aiT3OhBz~$aYgr>{2LLd<=Mtb9~$9av*!*O~1D9?E1zEoz*{C=9mr->XQLU7`g zbZ}a}ImJ;p_ z9d*auLH84G(xu-)_dq(vsTaNk+5RP8ST$cnkS)$&7mZJD!KFFR7Y-ctoOFFLrrbjx zO+BU#cd>=`1L4e}l?WH+a5}<-Gh4%Af|Er~iX>G}m!x`!9=mi3$I%Z?o($B-CZAIz zR~z`$R^^cz_vAM`srg*|@l(h0kDWPn;^?slMf^l%uEv97q+&7f*rk)f7q*5IiFSb; zf|d%gPrr8i>Vu%Jk5m14X9)S&quz9>D!=Yv>g_s(I<1v+ zUV%>CQ}xm$;)?vJn6H;O<o25o;dlHZl2J-W=BK?ix*AlyGuApH{1ZukTg i*PsQ}#e(XZ_gj`l=h`F<;-WI^e-jb9Q(3}J+y4*I_>Wfr diff --git a/widgets/__pycache__/main_window.cpython-310.pyc b/widgets/__pycache__/main_window.cpython-310.pyc deleted file mode 100644 index 15657d7dc217dcd8afccb086b58bac9ce9d1a567..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7400 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 Rp2Ay1{00wZJeh~o{{x= 0: + data_type_combo.setCurrentIndex(index) + + # 单位 + unit_input = self.findChild(QObject, f"unit_input_{position}") + if unit_input: + unit_input.setText(config.get('unit', '')) + + # 最小值 + min_value_spin = self.findChild(QObject, f"min_value_spin_{position}") + if min_value_spin: + min_value = config.get('min_value') + if min_value is not None: + min_value_spin.setValue(float(min_value)) + else: + min_value_spin.setValue(0) + + # 最大值 + max_value_spin = self.findChild(QObject, f"max_value_spin_{position}") + if max_value_spin: + max_value = config.get('max_value') + if max_value is not None: + max_value_spin.setValue(float(max_value)) + else: + max_value_spin.setValue(100) + + # 枚举值 + enum_values_input = self.findChild(QObject, f"enum_values_input_{position}") + if enum_values_input: + enum_values = config.get('enum_values') + if enum_values: + if isinstance(enum_values, list): + enum_values_input.setText(','.join(enum_values)) + elif isinstance(enum_values, str): + try: + values = json.loads(enum_values) + if isinstance(values, list): + enum_values_input.setText(','.join(values)) + else: + enum_values_input.setText(str(enum_values)) + except: + enum_values_input.setText(str(enum_values)) + else: + enum_values_input.setText('') + + # 是否必填 + required_check = self.findChild(QObject, f"required_check_{position}") + if required_check: + required_check.setChecked(config.get('required', False)) + + # 根据数据类型更新表单 + self.update_form_by_data_type(position) + + def update_form_by_data_type(self, position): + """根据数据类型更新表单项的可用性 + + Args: + position: 位置序号 (1-6) + """ + # 获取数据类型 + data_type_combo = self.findChild(QObject, f"data_type_combo_{position}") + if not data_type_combo: + return + + data_type = data_type_combo.currentData() + + # 获取相关控件 + unit_input = self.findChild(QObject, f"unit_input_{position}") + min_value_spin = self.findChild(QObject, f"min_value_spin_{position}") + max_value_spin = self.findChild(QObject, f"max_value_spin_{position}") + enum_values_input = self.findChild(QObject, f"enum_values_input_{position}") + + # 根据数据类型设置控件可用性 + if data_type == 'number': + # 数值类型:启用单位、最小值、最大值,禁用枚举值 + if unit_input: + unit_input.setEnabled(True) + if min_value_spin: + min_value_spin.setEnabled(True) + if max_value_spin: + max_value_spin.setEnabled(True) + if enum_values_input: + enum_values_input.setEnabled(False) + enum_values_input.setText('') + elif data_type == 'enum': + # 枚举类型:禁用单位、最小值、最大值,启用枚举值 + if unit_input: + unit_input.setEnabled(False) + unit_input.setText('') + if min_value_spin: + min_value_spin.setEnabled(False) + min_value_spin.setValue(0) + if max_value_spin: + max_value_spin.setEnabled(False) + max_value_spin.setValue(0) + if enum_values_input: + enum_values_input.setEnabled(True) + else: # text 或其他 + # 文本类型:禁用所有特殊字段 + if unit_input: + unit_input.setEnabled(False) + unit_input.setText('') + if min_value_spin: + min_value_spin.setEnabled(False) + min_value_spin.setValue(0) + if max_value_spin: + max_value_spin.setEnabled(False) + max_value_spin.setValue(0) + if enum_values_input: + enum_values_input.setEnabled(False) + enum_values_input.setText('') + + def get_form_data(self, position): + """获取表单数据 + + Args: + position: 位置序号 (1-6) + + Returns: + dict: 表单数据 + """ + # 获取分组 + group = self.config_groups[position - 1] + + # 获取控件 + name_input = self.findChild(QObject, f"name_input_{position}") + display_name_input = self.findChild(QObject, f"display_name_input_{position}") + data_type_combo = self.findChild(QObject, f"data_type_combo_{position}") + unit_input = self.findChild(QObject, f"unit_input_{position}") + min_value_spin = self.findChild(QObject, f"min_value_spin_{position}") + max_value_spin = self.findChild(QObject, f"max_value_spin_{position}") + enum_values_input = self.findChild(QObject, f"enum_values_input_{position}") + required_check = self.findChild(QObject, f"required_check_{position}") + + # 获取数据 + enabled = group.isChecked() + name = name_input.text() if name_input else '' + display_name = display_name_input.text() if display_name_input else '' + data_type = data_type_combo.currentData() if data_type_combo else 'text' + unit = unit_input.text() if unit_input and data_type == 'number' else '' + min_value = min_value_spin.value() if min_value_spin and data_type == 'number' else None + max_value = max_value_spin.value() if max_value_spin and data_type == 'number' else None + + # 处理枚举值 + enum_values = None + if enum_values_input and data_type == 'enum': + enum_text = enum_values_input.text().strip() + if enum_text: + enum_values = [v.strip() for v in enum_text.split(',') if v.strip()] + + required = required_check.isChecked() if required_check else False + + # 构建数据 + data = { + 'position': position, + 'name': name, + 'display_name': display_name, + 'enabled': enabled, + 'data_type': data_type, + 'required': required, + 'sort_order': position + } + + # 根据数据类型添加特定字段 + if data_type == 'number': + data['min_value'] = min_value + data['max_value'] = max_value + data['unit'] = unit + elif data_type == 'enum': + data['enum_values'] = enum_values + + return data + + def validate_form(self): + """验证表单数据 + + Returns: + bool: 验证是否通过 + """ + # 检查是否至少启用一个检验项 + enabled_count = 0 + for group in self.config_groups: + if group.isChecked(): + enabled_count += 1 + + if enabled_count == 0: + QMessageBox.warning(self, "验证失败", "请至少启用一个检验项目!") + return False + + # 检查每个启用的检验项是否填写了必要信息 + for i in range(6): + position = i + 1 + group = self.config_groups[i] + + if group.isChecked(): + # 获取控件 + name_input = self.findChild(QObject, f"name_input_{position}") + display_name_input = self.findChild(QObject, f"display_name_input_{position}") + data_type_combo = self.findChild(QObject, f"data_type_combo_{position}") + enum_values_input = self.findChild(QObject, f"enum_values_input_{position}") + + # 验证名称和显示名称 + if not name_input.text().strip(): + QMessageBox.warning(self, "验证失败", f"检验项目 {position} 的项目名称不能为空!") + return False + + if not display_name_input.text().strip(): + QMessageBox.warning(self, "验证失败", f"检验项目 {position} 的显示名称不能为空!") + return False + + # 验证枚举类型的枚举值 + data_type = data_type_combo.currentData() + if data_type == 'enum' and not enum_values_input.text().strip(): + QMessageBox.warning(self, "验证失败", f"检验项目 {position} 是枚举类型,枚举值不能为空!") + return False + + return True + + def save_configs(self): + """保存检验配置""" + try: + # 验证表单 + if not self.validate_form(): + return + + # 设置表单禁用,避免保存过程中的错误操作 + self.set_form_enabled(False) + + # 收集更新数据 + for i in range(6): + position = i + 1 + config_id = self.config_ids[i] + + # 获取表单数据 + data = self.get_form_data(position) + + # 检查配置ID是否存在 + if config_id is not None: + # 更新已有配置 + result = self.inspection_manager.update_config(config_id, data) + if not result: + raise Exception(f"更新检验项目 {position} 失败") + else: + # 新建配置(不应该进入这个分支,因为默认已经创建了6个配置) + logging.warning(f"检验项目 {position} 不存在,需要在初始化时创建") + + # 重新加载配置 + self.inspection_manager.reload_configs() + + # 恢复表单可用 + self.set_form_enabled(True) + + # 显示成功消息 + QMessageBox.information(self, "保存成功", "检验配置已保存成功!") + + # 发送配置变更信号 + self.signal_configs_changed.emit() + + logging.info("已保存检验配置") + except Exception as e: + logging.error(f"保存检验配置失败: {str(e)}") + QMessageBox.critical(self, "错误", f"保存检验配置失败: {str(e)}") + # 恢复表单可用 + self.set_form_enabled(True) \ No newline at end of file diff --git a/widgets/main_window.py b/widgets/main_window.py index 6d341cb..b314b7d 100644 --- a/widgets/main_window.py +++ b/widgets/main_window.py @@ -16,6 +16,9 @@ from ui.main_window_ui import MainWindowUI from widgets.camera_display_widget import CameraDisplayWidget from widgets.camera_settings_widget import CameraSettingsWidget +# 导入检验配置管理器 +from utils.inspection_config_manager import InspectionConfigManager + class MainWindow(MainWindowUI): """主窗口""" @@ -36,6 +39,9 @@ class MainWindow(MainWindowUI): self.config = self.load_config() self.camera_enabled = self.config.get('camera', {}).get('enabled', False) + # 初始化检验配置管理器 + self.inspection_manager = InspectionConfigManager.get_instance() + # 只有在相机启用时创建相机显示组件 if self.camera_enabled: # 创建相机显示组件并添加到上料区 @@ -74,8 +80,8 @@ class MainWindow(MainWindowUI): # 初始化数据 self.initialize_data() - # 配置检验列为5列 - self.set_inspection_columns(5) + # 配置检验列 - 使用检验配置管理器获取启用的列数和标题 + self.update_inspection_columns() logging.info(f"主窗口已创建,用户: {user_name}") @@ -96,6 +102,9 @@ class MainWindow(MainWindowUI): self.main_action.triggered.connect(self.show_main_page) self.settings_action.triggered.connect(self.show_settings_page) + # 工程号输入框回车事件 + self.order_edit.returnPressed.connect(self.handle_order_enter) + # 连接按钮事件 self.input_button.clicked.connect(self.handle_input) self.output_button.clicked.connect(self.handle_output) @@ -134,9 +143,36 @@ class MainWindow(MainWindowUI): item.setTextAlignment(Qt.AlignCenter) # 设置文本居中对齐 self.task_table.setItem(2, col, item) + def update_inspection_columns(self): + """更新检验列配置 - 使用检验配置管理器获取启用的列数和标题""" + try: + # 获取已启用的检验配置 + enabled_configs = self.inspection_manager.get_enabled_configs() + + # 获取启用的列数 + column_count = len(enabled_configs) + if column_count == 0: + # 如果没有启用的列,至少显示一列 + column_count = 1 + headers = ["检验项"] + else: + # 如果有启用的列,使用配置的标题 + headers = [config['display_name'] for config in enabled_configs] + + # 设置检验列 + self.set_inspection_columns(column_count, headers) + logging.info(f"已更新检验列配置:{column_count}列, 标题: {headers}") + except Exception as e: + logging.error(f"更新检验列配置失败: {str(e)}") + # 如果更新失败,使用默认配置 + self.set_inspection_columns(1, ["检验项"]) + def show_main_page(self): self.stacked_widget.setCurrentWidget(self.central_widget) + # 更新检验列配置 + self.update_inspection_columns() + # 只有在相机启用时处理相机显示 if self.camera_enabled and hasattr(self, 'camera_display'): # 如果相机已连接,直接开始显示相机画面 @@ -147,24 +183,15 @@ class MainWindow(MainWindowUI): logging.info("显示主页面") def show_settings_page(self): - # 只有在相机启用时才创建相机设置组件 - if self.camera_enabled: - # 延迟创建相机设置组件 - if self.camera_settings is None: - self.camera_settings = CameraSettingsWidget() - # 连接相机设置信号 - self.camera_settings.signal_camera_connection.connect(self.handle_camera_connection) - self.camera_settings.signal_camera_params_changed.connect(self.handle_camera_params_changed) - self.camera_settings.signal_camera_error.connect(self.handle_camera_error) - # 添加到堆叠部件 - self.stacked_widget.addWidget(self.camera_settings) - - # 切换到设置页面 - self.stacked_widget.setCurrentWidget(self.camera_settings) - else: - # 如果相机未启用,显示提示信息 - QMessageBox.information(self, "设置", "相机功能已在配置文件中禁用,无法打开相机设置页面。") + """显示设置页面""" + # 延迟创建设置组件 + if not hasattr(self, 'settings_widget'): + from widgets.settings_widget import SettingsWidget + self.settings_widget = SettingsWidget(self) + self.stacked_widget.addWidget(self.settings_widget) + # 切换到设置页面 + self.stacked_widget.setCurrentWidget(self.settings_widget) logging.info("显示设置页面") def handle_input(self): @@ -245,4 +272,21 @@ class MainWindow(MainWindowUI): self.camera_display.stop_display() # 接受关闭事件 - event.accept() \ No newline at end of file + event.accept() + + def handle_order_enter(self): + """处理工程号输入框按下回车事件""" + logging.info("工程号输入框按下回车事件") + # 获取当前输入的工程号 + order_text = self.order_edit.text().strip() + + if order_text: + logging.info(f"输入的工程号: {order_text}") + QMessageBox.information(self, "工程号确认", f"您输入的工程号是: {order_text}") + # 这里可以添加其他工程号处理逻辑 + else: + logging.warning("工程号为空") + QMessageBox.warning(self, "输入提示", "请输入有效的工程号") + + # 处理完后可以清除焦点,让输入框失去焦点 + self.central_widget.setFocus() \ No newline at end of file diff --git a/widgets/settings_widget.py b/widgets/settings_widget.py index b575443..d80263a 100644 --- a/widgets/settings_widget.py +++ b/widgets/settings_widget.py @@ -1,19 +1,44 @@ -from PySide6.QtWidgets import QMessageBox +from PySide6.QtWidgets import QMessageBox, QVBoxLayout import logging from ui.settings_ui import SettingsUI from utils.sql_utils import SQLUtils +from widgets.inspection_settings_widget import InspectionSettingsWidget class SettingsWidget(SettingsUI): def __init__(self, parent=None): super().__init__(parent) self.parent = parent + logging.info("正在初始化SettingsWidget") + + # 创建检验设置部件 + logging.info("创建InspectionSettingsWidget实例") + self.inspection_settings = InspectionSettingsWidget() + + # 移除临时占位符标签并添加检验设置部件 + if hasattr(self, 'inspection_placeholder'): + logging.info("移除临时占位符") + self.inspection_layout.removeWidget(self.inspection_placeholder) + self.inspection_placeholder.hide() + self.inspection_placeholder.deleteLater() + else: + logging.warning("未找到临时占位符标签") + + # 检查布局是否可用 + if hasattr(self, 'inspection_layout'): + logging.info("添加检验设置部件到布局") + self.inspection_layout.addWidget(self.inspection_settings) + else: + logging.error("无法找到inspection_layout布局") + # 连接信号和槽 self.connect_signals() # 初始化数据库类型UI状态 self.update_db_ui_state() + logging.info("SettingsWidget初始化完成") + def connect_signals(self): # 数据库类型选择 self.sqlite_radio.toggled.connect(self.update_db_ui_state) @@ -23,6 +48,9 @@ class SettingsWidget(SettingsUI): # 按钮动作 self.test_conn_button.clicked.connect(self.test_connection) self.save_db_button.clicked.connect(self.save_db_settings) + + # 检验配置变更信号 + self.inspection_settings.signal_configs_changed.connect(self.handle_inspection_configs_changed) def update_db_ui_state(self): """根据选择的数据库类型更新UI状态""" @@ -98,25 +126,18 @@ class SettingsWidget(SettingsUI): # 创建数据库连接 db = SQLUtils(db_type, **params) - # 尝试执行简单查询 - if db_type == "sqlite": - db.execute_query("SELECT sqlite_version();") - elif db_type == "pgsql": - db.execute_query("SELECT version();") - elif db_type == "mysql": - db.execute_query("SELECT version();") + # 测试连接 + db.cursor.execute("SELECT 1") - result = db.fetchone() + # 关闭连接 db.close() # 显示成功消息 - QMessageBox.information(self, "连接成功", f"数据库连接测试成功!\n数据库版本: {result[0]}") - - logging.info(f"数据库连接测试成功,类型: {db_type}, 版本: {result[0]}") - + QMessageBox.information(self, "连接成功", "数据库连接测试成功!") + logging.info(f"数据库连接测试成功,类型: {db_type}") except Exception as e: # 显示错误消息 - QMessageBox.critical(self, "连接失败", f"数据库连接测试失败!\n错误: {str(e)}") + QMessageBox.critical(self, "连接失败", f"数据库连接测试失败!\n\n错误: {str(e)}") logging.error(f"数据库连接测试失败,类型: {db_type}, 错误: {str(e)}") def save_db_settings(self): @@ -137,4 +158,11 @@ class SettingsWidget(SettingsUI): settings_info += f"说明: {desc}" QMessageBox.information(self, "设置已保存", f"数据库设置已保存!\n\n{settings_info}") - logging.info(f"数据库设置已保存,类型: {db_type}") \ No newline at end of file + logging.info(f"数据库设置已保存,类型: {db_type}") + + def handle_inspection_configs_changed(self): + """处理检验配置变更""" + logging.info("检验配置已更新") + # 如果有父窗口,通知父窗口更新检验配置 + if self.parent and hasattr(self.parent, 'update_inspection_columns'): + self.parent.update_inspection_columns() \ No newline at end of file