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

451 lines
18 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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, "导出", "导出功能开发中...")