添加获取未完成检验数据的方法,优化主窗口以加载并显示未完成的检验记录;实现表格上下文菜单功能以检查数据库记录。

This commit is contained in:
zhu-mengmeng 2025-06-08 01:24:27 +08:00
parent f31871b4f5
commit ca97662db6
4 changed files with 510 additions and 46 deletions

View File

@ -281,7 +281,46 @@ class InspectionDAO:
self.db.rollback_transaction()
logging.error(f"保存检验数据失败: {str(e)}")
return False
def get_inspection_data_unfinished(self):
"""获取未完成的检验数据
Returns:
list: 未完成的检验数据列表
"""
try:
sql = """
SELECT d.id, d.order_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.is_deleted = FALSE
AND order_id IN (SELECT distinct order_id FROM inspection_data WHERE status != 'pass')
ORDER BY d.order_id, d.position
"""
self.db.cursor.execute(sql)
results = self.db.cursor.fetchall()
data_list = []
for row in results:
data = {
'id': row[0],
'order_id': row[1],
'position': row[2],
'config_id': row[3],
'value': row[4],
'status': row[5],
'remark': row[6],
'name': row[7],
'display_name': row[8],
'data_type': row[9],
'unit': row[10]
}
data_list.append(data)
return data_list
except Exception as e:
logging.error(f"获取未完成的检验数据失败: {str(e)}")
return []
def get_inspection_data_by_order(self, order_id):
"""根据工程号获取检验数据

Binary file not shown.

View File

@ -653,43 +653,4 @@ class MainWindowUI(QMainWindow):
# 包装区域列宽
packaging_start_col = 2 + self.inspection_columns
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))
self.process_table.setColumnWidth(packaging_start_col + 1, 140) # 称重

View File

@ -3,15 +3,18 @@ import sys
import logging
import json
from datetime import datetime
from pathlib import Path
# 导入PySide6
from PySide6.QtWidgets import QWidget, QMessageBox, QTableWidgetItem, QStackedWidget, QLabel
from PySide6.QtWidgets import (
QWidget, QMessageBox, QTableWidgetItem, QStackedWidget, QLabel, QMainWindow,
QTableWidget, QMenu
)
from PySide6.QtCore import Qt, QTimer
from PySide6.QtGui import QBrush, QColor
# 导入UI
from ui.main_window_ui import MainWindowUI
# 导入相机显示组件和设置组件
from widgets.camera_display_widget import CameraDisplayWidget
from widgets.camera_settings_widget import CameraSettingsWidget
@ -83,6 +86,13 @@ class MainWindow(MainWindowUI):
# 配置检验列 - 使用检验配置管理器获取启用的列数和标题
self.update_inspection_columns()
# 设置表格上下文菜单
self.process_table.setContextMenuPolicy(Qt.CustomContextMenu)
self.process_table.customContextMenuRequested.connect(self.show_table_context_menu)
# 加载未完成的检验数据
self.load_unfinished_inspection_data()
logging.info(f"主窗口已创建,用户: {user_name}")
def load_config(self):
@ -173,6 +183,9 @@ class MainWindow(MainWindowUI):
# 更新检验列配置
self.update_inspection_columns()
# 加载未完成的检验数据
self.load_unfinished_inspection_data()
# 只有在相机启用时处理相机显示
if self.camera_enabled and hasattr(self, 'camera_display'):
# 如果相机已连接,直接开始显示相机画面
@ -282,11 +295,462 @@ class MainWindow(MainWindowUI):
if order_text:
logging.info(f"输入的工程号: {order_text}")
QMessageBox.information(self, "工程号确认", f"您输入的工程号是: {order_text}")
# 这里可以添加其他工程号处理逻辑
# 在微丝产线表格中添加一条新记录
self.add_new_inspection_row(order_text)
else:
logging.warning("工程号为空")
QMessageBox.warning(self, "输入提示", "请输入有效的工程号")
# 处理完后可以清除焦点,让输入框失去焦点
self.central_widget.setFocus()
self.central_widget.setFocus()
def add_new_inspection_row(self, order_id):
"""在微丝产线表格中添加一条新记录
Args:
order_id: 工程号
"""
try:
# 获取启用的检验配置
enabled_configs = self.inspection_manager.get_enabled_configs()
# 固定的数据起始行索引
data_start_row = 2 # 数据从第3行开始
# 在指定行索引插入新行
self.process_table.insertRow(data_start_row)
# 更新序号 - 所有现有行序号+1
for row in range(data_start_row + 1, self.process_table.rowCount()):
seq_item = self.process_table.item(row, 0)
if seq_item:
try:
current_seq = int(seq_item.text())
seq_item.setText(str(current_seq + 1))
except ValueError:
pass
# 添加工程号到表格的第二列
item = QTableWidgetItem(order_id)
item.setTextAlignment(Qt.AlignCenter)
self.process_table.setItem(data_start_row, 1, item)
# 添加序号到表格的第一列 - 新行始终是第1条
item = QTableWidgetItem("1")
item.setTextAlignment(Qt.AlignCenter)
self.process_table.setItem(data_start_row, 0, item)
# 检验列设置为可编辑状态
for i, config in enumerate(enabled_configs):
col_index = 2 + i # 检验列从第3列开始
# 创建空的可编辑单元格
item = QTableWidgetItem("")
item.setTextAlignment(Qt.AlignCenter)
# 设置单元格属性以标识其关联的检验项
item.setData(Qt.UserRole, config.get('id'))
self.process_table.setItem(data_start_row, col_index, item)
# 包装列设置为可编辑状态
packaging_start_col = 2 + len(enabled_configs)
for i in range(2): # 贴标和称重
col_index = packaging_start_col + i
item = QTableWidgetItem("")
item.setTextAlignment(Qt.AlignCenter)
self.process_table.setItem(data_start_row, col_index, item)
# 设置表格为可编辑状态
self.process_table.setEditTriggers(QTableWidget.DoubleClicked | QTableWidget.EditKeyPressed)
# 连接单元格内容变更信号
# 断开之前的连接(如果有)
try:
self.process_table.cellChanged.disconnect(self.handle_inspection_cell_changed)
except:
pass # 如果没有连接过,会抛出异常,忽略即可
# 重新连接信号
self.process_table.cellChanged.connect(self.handle_inspection_cell_changed)
# 选中新添加的行
self.process_table.selectRow(data_start_row)
# 限制最大行数
self.limit_table_rows(10) # 最多保留10行数据
logging.info(f"已添加工程号 {order_id} 的新记录显示在第1条")
except Exception as e:
logging.error(f"添加新记录失败: {str(e)}")
QMessageBox.warning(self, "添加失败", f"添加新记录失败: {str(e)}")
def limit_table_rows(self, max_rows):
"""限制表格最大行数
Args:
max_rows: 最大行数不包括表头行
"""
try:
# 计算数据总行数
data_rows = self.process_table.rowCount() - 2 # 减去表头行
# 如果超过最大行数,删除多余的行
if data_rows > max_rows:
# 要删除的行数
rows_to_remove = data_rows - max_rows
# 从最后一行开始删除
for i in range(rows_to_remove):
self.process_table.removeRow(self.process_table.rowCount() - 1)
logging.info(f"已限制表格最大行数为 {max_rows} 行数据,删除了 {rows_to_remove}")
except Exception as e:
logging.error(f"限制表格行数失败: {str(e)}")
def handle_inspection_cell_changed(self, row, column):
"""处理检验单元格内容变更
Args:
row: 行索引
column: 列索引
"""
try:
# 只处理数据行的检验列变更
if row < 2: # 忽略表头行
return
# 忽略首尾两列(序号和工程号)
if column < 2:
return
# 获取工程号
order_id_item = self.process_table.item(row, 1)
if not order_id_item:
return
order_id = order_id_item.text().strip()
if not order_id:
return
# 获取启用的检验配置
enabled_configs = self.inspection_manager.get_enabled_configs()
# 判断是否是检验列(非包装列)
packaging_start_col = 2 + len(enabled_configs)
if column >= 2 and column < packaging_start_col:
# 是检验列
config_index = column - 2
if config_index < len(enabled_configs):
config = enabled_configs[config_index]
# 获取单元格内容
cell_item = self.process_table.item(row, column)
if not cell_item:
return
value = cell_item.text().strip()
# 显示临时状态消息
self.statusBar().showMessage(f"正在保存检验数据: {config['display_name']}={value}", 1000)
# 验证数据有效性
if self.validate_inspection_value(config, value):
# 设置单元格颜色为通过
cell_item.setBackground(QBrush(QColor("#c8e6c9"))) # 浅绿色
status = 'pass'
else:
# 设置单元格颜色为警告
cell_item.setBackground(QBrush(QColor("#fff9c4"))) # 浅黄色
status = 'warning'
# 保存到数据库
self.save_inspection_data(order_id, config['position'], config['id'], value, status)
except Exception as e:
logging.error(f"处理检验单元格变更失败: {str(e)}")
self.statusBar().showMessage(f"处理检验数据失败: {str(e)[:50]}...", 3000)
def validate_inspection_value(self, config, value):
"""验证检验值是否有效
Args:
config: 检验配置
value: 检验值
Returns:
bool: 是否有效
"""
try:
# 检查值是否为空
if not value and config.get('required', False):
return False
# 根据数据类型验证
data_type = config.get('data_type')
if data_type == 'number':
# 数值类型验证
try:
num_value = float(value)
min_value = config.get('min_value')
max_value = config.get('max_value')
if min_value is not None and num_value < min_value:
return False
if max_value is not None and num_value > max_value:
return False
return True
except ValueError:
return False
elif data_type == 'enum':
# 枚举类型验证
enum_values = config.get('enum_values')
if enum_values and isinstance(enum_values, list):
return value in enum_values
return False
# 文本类型不做特殊验证
return True
except Exception as e:
logging.error(f"验证检验值失败: {str(e)}")
return False
def save_inspection_data(self, order_id, position, config_id, value, status):
"""保存检验数据到数据库
Args:
order_id: 工程号
position: 位置序号
config_id: 配置ID
value: 检验值
status: 状态
"""
try:
from dao.inspection_dao import InspectionDAO
inspection_dao = InspectionDAO()
# 记录保存前的详细日志
logging.info(f"正在保存检验数据: 工程号={order_id}, 位置={position}, 配置ID={config_id}, 值={value}, 状态={status}")
# 构建数据
data = [{
'position': position,
'config_id': config_id,
'value': value,
'status': status,
'remark': ''
}]
# 保存到数据库
result = inspection_dao.save_inspection_data(order_id, data)
if result:
logging.info(f"已成功保存工程号 {order_id} 的检验数据,位置: {position}, 值: {value}")
# 显示临时状态消息
self.statusBar().showMessage(f"已保存检验数据:{value}", 3000)
else:
logging.warning(f"保存工程号 {order_id} 的检验数据失败")
# 显示错误消息
self.statusBar().showMessage(f"保存检验数据失败", 3000)
except Exception as e:
logging.error(f"保存检验数据失败: {str(e)}")
# 显示错误消息
self.statusBar().showMessage(f"保存检验数据错误: {str(e)[:50]}...", 3000)
def show_table_context_menu(self, pos):
"""显示表格上下文菜单
Args:
pos: 鼠标位置
"""
try:
# 获取当前单元格
cell_index = self.process_table.indexAt(pos)
if not cell_index.isValid():
return
row = cell_index.row()
column = cell_index.column()
# 只对数据行和检验列显示上下文菜单
if row < 2: # 忽略表头行
return
# 获取工程号
order_id_item = self.process_table.item(row, 1)
if not order_id_item:
return
order_id = order_id_item.text().strip()
if not order_id:
return
# 创建上下文菜单
menu = QMenu(self)
# 获取启用的检验配置
enabled_configs = self.inspection_manager.get_enabled_configs()
# 判断是否是检验列(非包装列)
packaging_start_col = 2 + len(enabled_configs)
if column >= 2 and column < packaging_start_col:
# 是检验列
config_index = column - 2
if config_index < len(enabled_configs):
config = enabled_configs[config_index]
position = config.get('position')
# 添加查询数据库菜单项
check_action = menu.addAction("检查数据库记录")
check_action.triggered.connect(lambda: self.check_database_record(order_id, position))
# 显示菜单
menu.exec_(self.process_table.viewport().mapToGlobal(pos))
except Exception as e:
logging.error(f"显示表格上下文菜单失败: {str(e)}")
def check_database_record(self, order_id, position):
"""检查数据库记录
Args:
order_id: 工程号
position: 位置序号
"""
try:
from dao.inspection_dao import InspectionDAO
inspection_dao = InspectionDAO()
# 获取检验数据
inspection_data = inspection_dao.get_inspection_data_by_order(order_id)
# 查找对应位置的数据
matching_data = None
for data in inspection_data:
if data.get('position') == position:
matching_data = data
break
# 显示结果
if matching_data:
value = matching_data.get('value')
status = matching_data.get('status')
message = f"数据库记录:\n\n"
message += f"工程号: {order_id}\n"
message += f"位置: {position}\n"
message += f"值: {value}\n"
message += f"状态: {status}\n"
QMessageBox.information(self, "数据库记录", message)
else:
QMessageBox.warning(self, "数据库记录", f"未找到工程号 {order_id} 位置 {position} 的数据")
except Exception as e:
logging.error(f"检查数据库记录失败: {str(e)}")
QMessageBox.warning(self, "查询失败", f"检查数据库记录失败: {str(e)}")
def load_unfinished_inspection_data(self):
"""加载未完成的检验数据并显示在表格中"""
try:
# 使用InspectionDAO获取未完成的检验数据
from dao.inspection_dao import InspectionDAO
inspection_dao = InspectionDAO()
# 使用get_inspection_data_unfinished获取未完成的数据
unfinished_data = inspection_dao.get_inspection_data_unfinished()
if not unfinished_data:
logging.info("没有未完成的检验数据")
return
logging.info(f"已加载未完成的检验数据,共 {len(unfinished_data)} 条记录")
# 获取启用的检验配置
enabled_configs = self.inspection_manager.get_enabled_configs()
# 按工程号分组
orders_data = {}
for data in unfinished_data:
order_id = data['order_id']
if order_id not in orders_data:
orders_data[order_id] = []
orders_data[order_id].append(data)
# 断开单元格变更信号,避免加载过程中触发保存
try:
self.process_table.cellChanged.disconnect(self.handle_inspection_cell_changed)
except:
pass
# 清空表格现有数据行
while self.process_table.rowCount() > 2:
self.process_table.removeRow(2)
# 添加数据到表格
row_idx = 2 # 从第3行开始添加数据
for order_id, items in orders_data.items():
# 添加新行
self.process_table.insertRow(row_idx)
# 添加序号到第一列
seq_item = QTableWidgetItem(str(row_idx - 1))
seq_item.setTextAlignment(Qt.AlignCenter)
self.process_table.setItem(row_idx, 0, seq_item)
# 添加工程号到第二列
order_item = QTableWidgetItem(order_id)
order_item.setTextAlignment(Qt.AlignCenter)
self.process_table.setItem(row_idx, 1, order_item)
# 添加检验数据
for item in items:
position = item['position']
value = item['value'] if item['value'] else ""
status = item['status']
config_id = item['config_id']
# 找到对应的列索引
col_index = None
for i, config in enumerate(enabled_configs):
if config.get('position') == position:
col_index = 2 + i # 检验列从第3列开始
break
if col_index is not None:
# 创建单元格并设置值
cell_item = QTableWidgetItem(str(value))
cell_item.setTextAlignment(Qt.AlignCenter)
# 存储配置ID用于保存时确定是哪个检验项
cell_item.setData(Qt.UserRole, config_id)
# 根据状态设置单元格颜色
if status == 'fail':
cell_item.setBackground(QBrush(QColor("#ffcdd2"))) # 浅红色
elif status == 'warning':
cell_item.setBackground(QBrush(QColor("#fff9c4"))) # 浅黄色
elif status == 'pass':
cell_item.setBackground(QBrush(QColor("#c8e6c9"))) # 浅绿色
# 设置单元格
self.process_table.setItem(row_idx, col_index, cell_item)
row_idx += 1
# 设置表格为可编辑状态
self.process_table.setEditTriggers(QTableWidget.DoubleClicked | QTableWidget.EditKeyPressed)
# 重新连接单元格变更信号
self.process_table.cellChanged.connect(self.handle_inspection_cell_changed)
except Exception as e:
logging.error(f"加载未完成的检验数据失败: {str(e)}")
QMessageBox.warning(self, "加载失败", f"加载未完成的检验数据失败: {str(e)}")