Files
dtm-py-all/UI/views/test_result_view.py

451 lines
18 KiB
Python
Raw Normal View History

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
测试结果视图用于查看和筛选测试结果
"""
from PyQt5.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QLabel,
QPushButton, QTableWidget, QTableWidgetItem,
QHeaderView, QLineEdit, QComboBox, QDateTimeEdit, QMessageBox, QGridLayout)
from PyQt5.QtCore import Qt, QDateTime, QSize
import requests
from config import HTTP_API_BASE_URL
class TestResultView(QWidget):
def __init__(self):
super().__init__()
self.api_base_url = HTTP_API_BASE_URL
# 分页参数
self.current_page = 1
self.page_size = 20
self.total_count = 0
# 所有数据(从后端获取)
self.all_data = []
self.filtered_data = []
self.init_ui()
self.load_data()
def init_ui(self):
"""初始化界面"""
layout = QVBoxLayout(self)
layout.setContentsMargins(10, 10, 10, 10)
layout.setSpacing(10)
# 筛选栏
self._create_filter_bar(layout)
# 数据表格
self._create_table(layout)
# 分页控制栏
self._create_pagination_bar(layout)
def _create_filter_bar(self, parent_layout):
"""创建筛选栏自适应1行或2行"""
# 创建筛选容器
self.filter_container = QWidget()
self.filter_layout = QGridLayout(self.filter_container)
self.filter_layout.setSpacing(10)
self.filter_layout.setContentsMargins(0, 0, 0, 0)
# 样品编号
sn_label = QLabel("样品编号:")
self.sn_input = QLineEdit()
self.sn_input.setPlaceholderText("请输入编号")
self.sn_input.setFixedWidth(150)
# 工单
workorder_label = QLabel("工单:")
self.workorder_input = QLineEdit()
self.workorder_input.setPlaceholderText("请输入测试工单")
self.workorder_input.setFixedWidth(200)
# 测试人员
inspector_label = QLabel("测试人员:")
self.inspector_input = QLineEdit()
self.inspector_input.setPlaceholderText("请输入测试人员")
self.inspector_input.setFixedWidth(150)
# 开始时间
self.start_datetime_edit = QDateTimeEdit()
self.start_datetime_edit.setCalendarPopup(True)
self.start_datetime_edit.setDisplayFormat("yyyy-MM-dd HH:mm:ss")
self.start_datetime_edit.setDateTime(QDateTime.currentDateTime().addMonths(-1))
# 至
to_label = QLabel("")
# 结束时间
self.end_datetime_edit = QDateTimeEdit()
self.end_datetime_edit.setCalendarPopup(True)
self.end_datetime_edit.setDisplayFormat("yyyy-MM-dd HH:mm:ss")
self.end_datetime_edit.setDateTime(QDateTime.currentDateTime())
# 查询按钮
search_btn = QPushButton("查询")
search_btn.clicked.connect(self.on_search)
search_btn.setFixedWidth(80)
# 导出结果按钮
export_btn = QPushButton("导出结果")
export_btn.clicked.connect(self.on_export)
export_btn.setFixedWidth(100)
# 将所有控件存储为列表,方便动态调整布局
self.filter_widgets = [
(sn_label, self.sn_input),
(workorder_label, self.workorder_input),
(inspector_label, self.inspector_input),
(self.start_datetime_edit, to_label, self.end_datetime_edit),
(search_btn, export_btn)
]
# 初始化为2行布局
self._arrange_filters_two_rows()
parent_layout.addWidget(self.filter_container)
def _arrange_filters_one_row(self):
"""将筛选条件排列为1行"""
# 清空现有布局
for i in reversed(range(self.filter_layout.count())):
widget = self.filter_layout.itemAt(i).widget()
if widget:
self.filter_layout.removeWidget(widget)
# 1行布局所有控件排在同一行
col = 0
# 样品编号
self.filter_layout.addWidget(self.filter_widgets[0][0], 0, col) # Label
col += 1
self.filter_layout.addWidget(self.filter_widgets[0][1], 0, col) # Input
col += 1
# 工单
self.filter_layout.addWidget(self.filter_widgets[1][0], 0, col)
col += 1
self.filter_layout.addWidget(self.filter_widgets[1][1], 0, col)
col += 1
# 测试人员
self.filter_layout.addWidget(self.filter_widgets[2][0], 0, col)
col += 1
self.filter_layout.addWidget(self.filter_widgets[2][1], 0, col)
col += 1
# 时间范围
self.filter_layout.addWidget(self.filter_widgets[3][0], 0, col) # start_datetime
col += 1
self.filter_layout.addWidget(self.filter_widgets[3][1], 0, col) # "至"
col += 1
self.filter_layout.addWidget(self.filter_widgets[3][2], 0, col) # end_datetime
col += 1
# 按钮
self.filter_layout.addWidget(self.filter_widgets[4][0], 0, col) # 查询
col += 1
self.filter_layout.addWidget(self.filter_widgets[4][1], 0, col) # 导出
col += 1
# 添加弹性空间
self.filter_layout.setColumnStretch(col, 1)
def _arrange_filters_two_rows(self):
"""将筛选条件排列为2行"""
# 清空现有布局
for i in reversed(range(self.filter_layout.count())):
widget = self.filter_layout.itemAt(i).widget()
if widget:
self.filter_layout.removeWidget(widget)
# 第一行:样品编号、工单、测试人员
col = 0
self.filter_layout.addWidget(self.filter_widgets[0][0], 0, col) # 样品编号Label
col += 1
self.filter_layout.addWidget(self.filter_widgets[0][1], 0, col) # 样品编号Input
col += 1
self.filter_layout.addWidget(self.filter_widgets[1][0], 0, col) # 工单Label
col += 1
self.filter_layout.addWidget(self.filter_widgets[1][1], 0, col) # 工单Input
col += 1
self.filter_layout.addWidget(self.filter_widgets[2][0], 0, col) # 测试人员Label
col += 1
self.filter_layout.addWidget(self.filter_widgets[2][1], 0, col) # 测试人员Input
col += 1
self.filter_layout.setColumnStretch(col, 1) # 弹性空间
# 第二行:时间范围和按钮
col = 0
self.filter_layout.addWidget(self.filter_widgets[3][0], 1, col) # start_datetime
col += 1
self.filter_layout.addWidget(self.filter_widgets[3][1], 1, col) # "至"
col += 1
self.filter_layout.addWidget(self.filter_widgets[3][2], 1, col) # end_datetime
col += 1
self.filter_layout.addWidget(self.filter_widgets[4][0], 1, col) # 查询按钮
col += 1
self.filter_layout.addWidget(self.filter_widgets[4][1], 1, col) # 导出按钮
col += 1
self.filter_layout.setColumnStretch(col, 1) # 弹性空间
def _create_table(self, parent_layout):
"""创建数据表格"""
self.table = QTableWidget()
self.table.setColumnCount(18)
self.table.setHorizontalHeaderLabels([
"序号", "样品编号", "项目代码", "项目阶段", "项目方案", "测试周次",
"工单名称", "产品序列号", "测试设备", "测试通道", "跌落方向",
"跌落高度(mm)", "跌落速度(次/Min)", "样品跌落次数", "方向当前次数",
"木板累计跌落次数", "试验开始时间", "试验结束时间"
])
# 表格样式
self.table.setSelectionBehavior(QTableWidget.SelectRows)
self.table.setSelectionMode(QTableWidget.SingleSelection)
self.table.setAlternatingRowColors(True)
self.table.horizontalHeader().setStretchLastSection(True)
self.table.horizontalHeader().setSectionResizeMode(QHeaderView.Interactive)
self.table.verticalHeader().setVisible(False)
self.table.setEditTriggers(QTableWidget.NoEditTriggers)
parent_layout.addWidget(self.table)
def _create_pagination_bar(self, parent_layout):
"""创建分页控制栏"""
pager_layout = QHBoxLayout()
pager_layout.setSpacing(10)
self.page_info_label = QLabel("")
pager_layout.addWidget(self.page_info_label)
pager_layout.addStretch()
# 每页行数选择
page_size_label = QLabel("每页行数:")
pager_layout.addWidget(page_size_label)
self.page_size_combo = QComboBox()
self.page_size_combo.addItems(["10", "20", "50", "100"])
self.page_size_combo.setCurrentText(str(self.page_size))
self.page_size_combo.currentTextChanged.connect(self.on_page_size_changed)
pager_layout.addWidget(self.page_size_combo)
self.prev_btn = QPushButton("上一页")
self.prev_btn.clicked.connect(self.go_prev_page)
pager_layout.addWidget(self.prev_btn)
self.next_btn = QPushButton("下一页")
self.next_btn.clicked.connect(self.go_next_page)
pager_layout.addWidget(self.next_btn)
parent_layout.addLayout(pager_layout)
def resizeEvent(self, event):
"""窗口大小变化时自动调整筛选栏布局"""
super().resizeEvent(event)
if hasattr(self, 'filter_container'):
# 获取当前窗口宽度
width = self.width()
# 根据宽度阈值决定布局:
# 大于1400px时显示1行否则显示2行
if width > 1400:
if not hasattr(self, '_current_layout') or self._current_layout != 'one_row':
self._arrange_filters_one_row()
self._current_layout = 'one_row'
else:
if not hasattr(self, '_current_layout') or self._current_layout != 'two_rows':
self._arrange_filters_two_rows()
self._current_layout = 'two_rows'
def on_search(self):
"""查询按钮点击事件"""
self.current_page = 1
self.load_data()
def load_data(self):
"""从后端加载数据"""
try:
# 构建请求URL
url = f"{self.api_base_url}/dbTableAccess"
# 构建查询参数
params = {
'table': 'testReqResult',
'pageSize': self.page_size,
'currentPage': self.current_page,
}
# 添加时间筛选参数(后端处理)- 格式yyyy/MM/dd HH:mm:ss
start_dt = self.start_datetime_edit.dateTime().toString("yyyy/MM/dd HH:mm:ss")
end_dt = self.end_datetime_edit.dateTime().toString("yyyy/MM/dd HH:mm:ss")
params['date1'] = start_dt
params['date2'] = end_dt
# 添加样品编号筛选(后端处理)
sn_filter = self.sn_input.text().strip()
if sn_filter:
params['SN'] = sn_filter
# 添加工单筛选(后端处理)
workorder_filter = self.workorder_input.text().strip()
if workorder_filter:
params['dutWorkOrder'] = workorder_filter
print(f"[测试结果] 请求参数: {params}") # 调试输出
# 调用后端API
response = requests.get(url, params=params, timeout=10)
response.raise_for_status()
result = response.json()
print(f"[测试结果] 返回数据量: {result.get('length', 0)}, 总数: {result.get('totalCount', 0)}") # 调试输出
if 'data' in result:
self.all_data = result['data']
self.total_count = result.get('totalCount', len(self.all_data))
# 应用前端筛选(测试人员)
self.apply_frontend_filter()
else:
QMessageBox.warning(self, "错误", "获取数据失败")
except Exception as e:
print(f"加载测试结果数据失败: {e}")
QMessageBox.warning(self, "错误", f"加载数据失败:{str(e)}")
def apply_frontend_filter(self):
"""应用前端筛选(测试人员)
注意样品编号工单已由后端筛选这里不再处理
"""
inspector_filter = self.inspector_input.text().strip().lower()
# 筛选数据
filtered = []
for item in self.all_data:
# 测试人员筛选 - 使用 tester 字段
if inspector_filter:
inspector = str(item.get('tester', '')).lower()
if inspector_filter not in inspector:
continue
filtered.append(item)
self.filtered_data = filtered
print(f"[测试结果] 筛选后数据量: {len(filtered)}") # 调试输出
self.refresh_table()
self.update_pagination_info()
def _format_cell_value(self, value):
"""格式化单元格值,将 None/null/空字符串显示为空白"""
if value is None or value == 'None' or value == 'null' or str(value).strip() == '':
return ''
return str(value)
def refresh_table(self):
"""刷新表格显示"""
self.table.setRowCount(len(self.filtered_data))
for idx, item in enumerate(self.filtered_data):
# 序号 - 使用后端返回的 mysequence
self.table.setItem(idx, 0, QTableWidgetItem(self._format_cell_value(item.get('mysequence', idx + 1))))
# 样品编号 - 使用 SN
dut_sn = self._format_cell_value(item.get('SN'))
self.table.setItem(idx, 1, QTableWidgetItem(dut_sn))
# 项目代码 - 使用 dutProject
self.table.setItem(idx, 2, QTableWidgetItem(self._format_cell_value(item.get('dutProject'))))
# 项目阶段 - 使用 dutPhase
self.table.setItem(idx, 3, QTableWidgetItem(self._format_cell_value(item.get('dutPhase'))))
# 项目方案 - 使用 dutProjectType
self.table.setItem(idx, 4, QTableWidgetItem(self._format_cell_value(item.get('dutProjectType'))))
# 测试周次 - 使用 dutWeeks
self.table.setItem(idx, 5, QTableWidgetItem(self._format_cell_value(item.get('dutWeeks'))))
# 工单名称 - 使用 dutWorkOrder
self.table.setItem(idx, 6, QTableWidgetItem(self._format_cell_value(item.get('dutWorkOrder'))))
# 产品序列号 - 蓝色显示 SN
product_sn = self._format_cell_value(item.get('SN'))
product_sn_item = QTableWidgetItem(product_sn)
if product_sn: # 只有有值时才设置蓝色
product_sn_item.setForeground(Qt.blue)
self.table.setItem(idx, 7, product_sn_item)
# 测试设备 - 使用 machine_sn
self.table.setItem(idx, 8, QTableWidgetItem(self._format_cell_value(item.get('machine_sn'))))
# 测试通道 - 使用 station_no
self.table.setItem(idx, 9, QTableWidgetItem(self._format_cell_value(item.get('station_no'))))
# 跌落方向 - 使用 dropDirection
self.table.setItem(idx, 10, QTableWidgetItem(self._format_cell_value(item.get('dropDirection'))))
# 跌落高度(mm) - 使用 dropHeight_int
self.table.setItem(idx, 11, QTableWidgetItem(self._format_cell_value(item.get('dropHeight_int'))))
# 跌落速度(次/Min) - 使用 dropSpeed
self.table.setItem(idx, 12, QTableWidgetItem(self._format_cell_value(item.get('dropSpeed'))))
# 样品跌落次数 - 使用 dropCycles
self.table.setItem(idx, 13, QTableWidgetItem(self._format_cell_value(item.get('dropCycles'))))
# 方向当前次数 - 使用 itemCurrentCycles
self.table.setItem(idx, 14, QTableWidgetItem(self._format_cell_value(item.get('itemCurrentCycles'))))
# 木板累计跌落次数 - 使用 stationDropCycles
self.table.setItem(idx, 15, QTableWidgetItem(self._format_cell_value(item.get('stationDropCycles'))))
# 试验开始时间 - 使用 startTime
self.table.setItem(idx, 16, QTableWidgetItem(self._format_cell_value(item.get('startTime'))))
# 试验结束时间 - 使用 endTime
self.table.setItem(idx, 17, QTableWidgetItem(self._format_cell_value(item.get('endTime'))))
self.table.resizeColumnsToContents()
def update_pagination_info(self):
"""更新分页信息"""
total_pages = max(1, (self.total_count + self.page_size - 1) // self.page_size)
self.page_info_label.setText(
f"{self.total_count} 条,第 {self.current_page}/{total_pages} 页,每页 {self.page_size}"
)
self.prev_btn.setEnabled(self.current_page > 1)
self.next_btn.setEnabled(self.current_page < total_pages)
def on_page_size_changed(self, text):
"""每页行数改变"""
try:
self.page_size = int(text)
except Exception:
self.page_size = 10
self.current_page = 1
self.load_data()
def go_prev_page(self):
"""上一页"""
if self.current_page > 1:
self.current_page -= 1
self.load_data()
def go_next_page(self):
"""下一页"""
total_pages = max(1, (self.total_count + self.page_size - 1) // self.page_size)
if self.current_page < total_pages:
self.current_page += 1
self.load_data()
def on_export(self):
"""导出结果"""
QMessageBox.information(self, "导出", "导出功能开发中...")