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 4633566..0000000 Binary files a/ui/__pycache__/main_window_ui.cpython-310.pyc and /dev/null differ diff --git a/ui/__pycache__/settings_ui.cpython-310.pyc b/ui/__pycache__/settings_ui.cpython-310.pyc deleted file mode 100644 index c0c7c5f..0000000 Binary files a/ui/__pycache__/settings_ui.cpython-310.pyc and /dev/null differ 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 c2650ab..0000000 Binary files a/widgets/__pycache__/camera_display_widget.cpython-310.pyc and /dev/null differ diff --git a/widgets/__pycache__/camera_settings_widget.cpython-310.pyc b/widgets/__pycache__/camera_settings_widget.cpython-310.pyc deleted file mode 100644 index e74c224..0000000 Binary files a/widgets/__pycache__/camera_settings_widget.cpython-310.pyc and /dev/null differ diff --git a/widgets/__pycache__/main_window.cpython-310.pyc b/widgets/__pycache__/main_window.cpython-310.pyc deleted file mode 100644 index 15657d7..0000000 Binary files a/widgets/__pycache__/main_window.cpython-310.pyc and /dev/null differ diff --git a/widgets/inspection_settings_widget.py b/widgets/inspection_settings_widget.py new file mode 100644 index 0000000..47b7988 --- /dev/null +++ b/widgets/inspection_settings_widget.py @@ -0,0 +1,384 @@ +import logging +import json +from PySide6.QtWidgets import QMessageBox, QLabel +from PySide6.QtCore import QObject, Signal +from PySide6.QtCore import Qt + +from ui.inspection_settings_ui import InspectionSettingsUI +from utils.inspection_config_manager import InspectionConfigManager +from dao.inspection_dao import InspectionDAO + +class InspectionSettingsWidget(InspectionSettingsUI): + """检验设置部件""" + + # 定义信号 + signal_configs_changed = Signal() # 配置变更信号 + + def __init__(self, parent=None): + super().__init__(parent) + self.parent = parent + + # 添加一个明显的标签,方便测试 + self.test_label = QLabel("检验配置已加载", self) + self.test_label.setStyleSheet("color: green; font-size: 16px; font-weight: bold; background-color: #e0f7e0; padding: 5px; border-radius: 5px;") + self.test_label.setAlignment(Qt.AlignCenter) + self.main_layout.insertWidget(0, self.test_label) + + # 初始化检验配置管理器 + self.inspection_manager = InspectionConfigManager.get_instance() + + # 保存配置ID + self.config_ids = [None] * 6 + + # 连接信号和槽 + self.connect_signals() + + # 加载配置 + self.load_configs() + + def connect_signals(self): + """连接信号和槽""" + # 保存按钮 + self.save_button.clicked.connect(self.save_configs) + + # 重置按钮 + self.reset_button.clicked.connect(self.load_configs) + + # 数据类型下拉框 + for i in range(6): + position = i + 1 + combo = self.findChild(QObject, f"data_type_combo_{position}") + if combo: + combo.currentIndexChanged.connect(lambda idx, pos=position: self.update_form_by_data_type(pos)) + + def load_configs(self): + """加载检验配置""" + try: + # 设置表单禁用,避免加载过程中的错误操作 + self.set_form_enabled(False) + + # 重新加载配置 + self.inspection_manager.reload_configs() + + # 获取所有配置(包括禁用的) + configs = self.inspection_manager.get_configs(include_disabled=True) + + # 添加详细日志用于调试 + logging.info(f"加载检验配置成功,共 {len(configs)} 项: {[c.get('name', 'unknown') for c in configs]}") + self.test_label.setText(f"检验配置已加载({len(configs)}项)") + + # 清空当前配置ID + self.config_ids = [None] * 6 + + # 按位置加载配置 + for config in configs: + position = config.get('position') + if 1 <= position <= 6: + self.config_ids[position - 1] = config.get('id') + self.load_config_to_form(position, config) + + # 恢复表单可用 + self.set_form_enabled(True) + + logging.info("已加载检验配置") + except Exception as e: + logging.error(f"加载检验配置失败: {str(e)}") + self.test_label.setText(f"加载检验配置失败: {str(e)}") + self.test_label.setStyleSheet("color: red; font-size: 16px; font-weight: bold; background-color: #ffe0e0; padding: 5px; border-radius: 5px;") + QMessageBox.critical(self, "错误", f"加载检验配置失败: {str(e)}") + # 恢复表单可用 + self.set_form_enabled(True) + + def load_config_to_form(self, position, config): + """将配置加载到表单 + + Args: + position: 位置序号 (1-6) + config: 配置数据 + """ + # 获取分组 + group = self.config_groups[position - 1] + + # 设置启用状态 + group.setChecked(config.get('enabled', False)) + + # 设置表单值 + # 名称 + name_input = self.findChild(QObject, f"name_input_{position}") + if name_input: + name_input.setText(config.get('name', '')) + + # 显示名称 + display_name_input = self.findChild(QObject, f"display_name_input_{position}") + if display_name_input: + display_name_input.setText(config.get('display_name', '')) + + # 数据类型 + data_type_combo = self.findChild(QObject, f"data_type_combo_{position}") + if data_type_combo: + data_type = config.get('data_type', 'text') + index = data_type_combo.findData(data_type) + if index >= 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