687 lines
28 KiB
C++
687 lines
28 KiB
C++
#include "reportgenerator.h"
|
||
#include <QFile>
|
||
#include <QTextStream>
|
||
#include <QLocale>
|
||
|
||
ReportGenerator::ReportGenerator()
|
||
{
|
||
}
|
||
|
||
void ReportGenerator::setData(const SpectrumReportData &data)
|
||
{
|
||
m_data = data;
|
||
}
|
||
|
||
QString ReportGenerator::formatScientific(double value, int precision) const
|
||
{
|
||
if (value == 0.0) {
|
||
return QString("0.0000E+00");
|
||
}
|
||
|
||
// 格式化为科学计数法,类似 2.7056E-01
|
||
QString result = QString::number(value, 'E', precision);
|
||
|
||
// 确保指数部分有两位
|
||
QStringList parts = result.split('E');
|
||
if (parts.size() == 2) {
|
||
QString mantissa = parts[0];
|
||
QString exponent = parts[1];
|
||
|
||
// 处理指数符号和位数
|
||
QChar sign = exponent[0];
|
||
QString num = exponent.mid(1);
|
||
bool ok;
|
||
int expNum = num.toInt(&ok);
|
||
if (ok) {
|
||
exponent = QString("%1%2").arg(sign).arg(QString::number(expNum).rightJustified(2, '0'));
|
||
}
|
||
result = QString("%1E%2").arg(mantissa).arg(exponent);
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
QString ReportGenerator::formatDecimal(double value, int precision) const
|
||
{
|
||
return QString::number(value, 'f', precision);
|
||
}
|
||
|
||
QString ReportGenerator::formatDateTime(const QDateTime &dt) const
|
||
{
|
||
return dt.toString("yyyy/M/d hh:mm:ss");
|
||
}
|
||
|
||
QString ReportGenerator::generateHtmlHeader() const
|
||
{
|
||
return R"(
|
||
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<title>符合能谱分析报告</title>
|
||
<style>
|
||
body {
|
||
font-family: "SimSun", "宋体", serif;
|
||
font-size: 12px;
|
||
margin: 20px;
|
||
line-height: 1.6;
|
||
}
|
||
.report-title {
|
||
text-align: center;
|
||
font-size: 18px;
|
||
font-weight: bold;
|
||
margin-bottom: 20px;
|
||
}
|
||
.section-title {
|
||
font-weight: bold;
|
||
font-size: 14px;
|
||
margin-top: 15px;
|
||
margin-bottom: 10px;
|
||
border-bottom: 1px solid #333;
|
||
padding-bottom: 5px;
|
||
}
|
||
table {
|
||
width: 100%;
|
||
border-collapse: collapse;
|
||
margin-bottom: 10px;
|
||
}
|
||
table, th, td {
|
||
border: 1px solid #333;
|
||
}
|
||
th, td {
|
||
padding: 6px 8px;
|
||
text-align: left;
|
||
font-size: 12px;
|
||
}
|
||
th {
|
||
background-color: #f0f0f0;
|
||
font-weight: bold;
|
||
}
|
||
.param-table td:first-child {
|
||
width: 150px;
|
||
font-weight: bold;
|
||
}
|
||
.peak-table {
|
||
font-size: 11px;
|
||
}
|
||
.peak-table th {
|
||
text-align: center;
|
||
background-color: #e8e8e8;
|
||
}
|
||
.peak-table td {
|
||
text-align: center;
|
||
}
|
||
.signature-section {
|
||
margin-top: 30px;
|
||
text-align: right;
|
||
}
|
||
.signature-line {
|
||
margin-bottom: 8px;
|
||
}
|
||
.spectrum-file-header {
|
||
text-align: center;
|
||
font-weight: bold;
|
||
margin-bottom: 10px;
|
||
}
|
||
.legend {
|
||
margin-top: 10px;
|
||
font-size: 11px;
|
||
color: #555;
|
||
}
|
||
.legend-item {
|
||
margin-bottom: 3px;
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
)";
|
||
}
|
||
|
||
QString ReportGenerator::generateMeasurementSection() const
|
||
{
|
||
QString html;
|
||
html += QString("<div class='spectrum-file-header'>能谱文件:%1</div>")
|
||
.arg(m_data.measurement.spectrumFile);
|
||
|
||
html += "<div class='section-title'>测量参数</div>";
|
||
html += "<table class='param-table'>";
|
||
html += QString("<tr><td>开始时间:</td><td>%1</td>"
|
||
"<td>活时间(S):</td><td>%2</td></tr>")
|
||
.arg(formatDateTime(m_data.measurement.startTime))
|
||
.arg(formatDecimal(m_data.measurement.liveTime, 0));
|
||
html += QString("<tr><td>实时间(S):</td><td>%1</td>"
|
||
"<td>死时间:</td><td>%2 %</td></tr>")
|
||
.arg(formatDecimal(m_data.measurement.realTime, 0))
|
||
.arg(formatDecimal(m_data.measurement.deadTime, 2));
|
||
html += "</table>";
|
||
|
||
return html;
|
||
}
|
||
|
||
QString ReportGenerator::generateCalibrationSection() const
|
||
{
|
||
QString html;
|
||
html += "<div class='section-title'>刻度</div>";
|
||
html += "<table class='param-table'>";
|
||
html += QString("<tr><td>文件:</td><td>%1</td>"
|
||
"<td>刻度标准源:</td><td>%2</td></tr>")
|
||
.arg(m_data.calibration.fileName)
|
||
.arg(m_data.calibration.standardSource);
|
||
html += "</table>";
|
||
|
||
// 能量刻度
|
||
html += "<div style='margin-left: 20px; margin-top: 10px;'>";
|
||
html += "<div style='font-weight: bold;'>能量刻度</div>";
|
||
html += "<table class='param-table'>";
|
||
html += QString("<tr><td>生成时间:</td><td>%1</td>"
|
||
"<td>截距:</td><td>%2 keV</td></tr>")
|
||
.arg(formatDateTime(m_data.calibration.energy.generateTime))
|
||
.arg(formatDecimal(m_data.calibration.energy.intercept, 3));
|
||
html += QString("<tr><td>斜率:</td><td>%1 keV/ch</td>"
|
||
"<td>二次项系数:</td><td>%2 keV/(ch^2)</td></tr>")
|
||
.arg(formatDecimal(m_data.calibration.energy.slope, 3))
|
||
.arg(formatScientific(m_data.calibration.energy.quadratic, 3));
|
||
html += "</table>";
|
||
html += "</div>";
|
||
|
||
// 效率刻度
|
||
html += "<div style='margin-left: 20px; margin-top: 10px;'>";
|
||
html += "<div style='font-weight: bold;'>效率刻度</div>";
|
||
html += "<table class='param-table'>";
|
||
html += QString("<tr><td>生成时间:</td><td>%1</td>"
|
||
"<td>拐点能量(keV):</td><td>%2</td></tr>")
|
||
.arg(formatDateTime(m_data.calibration.efficiency.generateTime))
|
||
.arg(formatDecimal(m_data.calibration.efficiency.inflectionEnergy, 2));
|
||
html += QString("<tr><td>拐点以上:</td><td>%1</td>"
|
||
"<td>误差:</td><td>%2%</td></tr>")
|
||
.arg(m_data.calibration.efficiency.aboveInflection)
|
||
.arg(formatDecimal(m_data.calibration.efficiency.aboveError, 2));
|
||
html += QString("<tr><td>拐点以下:</td><td>%1</td>"
|
||
"<td>误差:</td><td>%2%</td></tr>")
|
||
.arg(m_data.calibration.efficiency.belowInflection)
|
||
.arg(formatDecimal(m_data.calibration.efficiency.belowError, 2));
|
||
html += QString("<tr><td>效率对数方程式:</td><td colspan='3'>%1</td></tr>")
|
||
.arg(m_data.calibration.efficiency.logEquation);
|
||
html += "</table>";
|
||
html += "</div>";
|
||
|
||
return html;
|
||
}
|
||
|
||
QString ReportGenerator::generateNuclideLibSection() const
|
||
{
|
||
QString html;
|
||
html += "<div class='section-title'>核素库</div>";
|
||
html += "<table class='param-table'>";
|
||
html += QString("<tr><td>文件:</td><td>%1</td>"
|
||
"<td>峰匹配宽度(keV):</td><td>%2 * FWHM</td></tr>")
|
||
.arg(m_data.nuclideLib.fileName)
|
||
.arg(formatDecimal(m_data.nuclideLib.peakMatchWidth, 3));
|
||
html += "</table>";
|
||
|
||
return html;
|
||
}
|
||
|
||
QString ReportGenerator::generateAnalysisSection() const
|
||
{
|
||
QString html;
|
||
html += "<div class='section-title'>分析参数</div>";
|
||
html += "<table class='param-table'>";
|
||
html += QString("<tr><td>起始能量(keV):</td><td>%1</td>"
|
||
"<td>终止能量(keV):</td><td>%2</td></tr>")
|
||
.arg(formatDecimal(m_data.analysis.startEnergy, 2))
|
||
.arg(formatDecimal(m_data.analysis.endEnergy, 2));
|
||
html += QString("<tr><td>统计误差限值:</td><td>%1%</td>"
|
||
"<td>活度比例系数:</td><td>%2</td></tr>")
|
||
.arg(formatDecimal(m_data.analysis.statErrorLimit, 3))
|
||
.arg(formatScientific(m_data.analysis.activityRatio, 4));
|
||
html += QString("<tr><td>探测限计算方法:</td><td colspan='3'>%1</td></tr>")
|
||
.arg(m_data.analysis.mdaMethod);
|
||
html += "</table>";
|
||
|
||
return html;
|
||
}
|
||
|
||
QString ReportGenerator::generatePeakTableSection() const
|
||
{
|
||
QString html;
|
||
html += "<div class='section-title'>能 谱 峰 一 览 表</div>";
|
||
html += "<table class='peak-table'>";
|
||
|
||
// 表头 - 第一行
|
||
html += "<tr>";
|
||
html += "<th rowspan='2'>核素</th>";
|
||
html += "<th rowspan='2'>平均活度(贝克Bq)</th>";
|
||
html += "<th rowspan='2'>不确定度</th>";
|
||
html += "<th colspan='4'>峰</th>";
|
||
html += "<th rowspan='2'>备注</th>";
|
||
html += "</tr>";
|
||
|
||
// 表头 - 第二行
|
||
html += "<tr>";
|
||
html += "<th>能量keV</th>"; // 峰子项1
|
||
html += "<th>活度(贝克Bq)</th>"; // 峰子项2
|
||
html += "<th>不确定度</th>"; // 峰子项3
|
||
html += "<th>最小可探测活度(MDA)(贝克Bq)</th>"; // 峰子项4
|
||
html += "</tr>";
|
||
|
||
// 数据行(8列,与表头对应)
|
||
for (const auto& peak : m_data.peaks) {
|
||
html += "<tr>";
|
||
html += QString("<td>%1</td>").arg(peak.nuclide);
|
||
html += QString("<td>%1</td>").arg(formatScientific(peak.avgActivity, 4));
|
||
html += QString("<td>%1%</td>").arg(formatDecimal(peak.avgUncertainty, 2));
|
||
html += QString("<td>%1</td>").arg(formatDecimal(peak.energy, 2));
|
||
html += QString("<td>%1</td>").arg(formatScientific(peak.peakActivity, 3));
|
||
html += QString("<td>%1%</td>").arg(formatDecimal(peak.peakUncertainty, 2));
|
||
html += QString("<td>%1</td>").arg(formatScientific(peak.mda, 3));
|
||
html += QString("<td>%1</td>").arg(peak.remark);
|
||
html += "</tr>";
|
||
}
|
||
|
||
html += "</table>";
|
||
|
||
// 峰提示码说明
|
||
html += "<div class='legend'>";
|
||
html += "<div style='font-weight: bold; margin-bottom: 5px;'>峰提示码说明:</div>";
|
||
html += "<div class='legend-item'>G - γ射线</div>";
|
||
html += "<div class='legend-item'>X - X射线</div>";
|
||
html += "<div class='legend-item'>ec - 内转换电子</div>";
|
||
html += "</div>";
|
||
|
||
return html;
|
||
}
|
||
|
||
QString ReportGenerator::generateSignatureSection() const
|
||
{
|
||
QString html;
|
||
html += "<div class='signature-section'>";
|
||
html += QString("<div class='signature-line'>分析员:%1</div>")
|
||
.arg(m_data.signature.analyst);
|
||
html += QString("<div class='signature-line'>审查员:%1</div>")
|
||
.arg(m_data.signature.reviewer);
|
||
html += QString("<div class='signature-line'>实验室:%1</div>")
|
||
.arg(m_data.signature.laboratory);
|
||
html += QString("<div class='signature-line'>日 期:%1</div>")
|
||
.arg(m_data.signature.reportDate.toString("yyyy年MM月dd日"));
|
||
html += "</div>";
|
||
|
||
return html;
|
||
}
|
||
|
||
QString ReportGenerator::generateHtmlFooter() const
|
||
{
|
||
return "</body>\n</html>";
|
||
}
|
||
|
||
QString ReportGenerator::generateHtmlReport() const
|
||
{
|
||
QString html;
|
||
html += generateHtmlHeader();
|
||
html += "<div class='report-title'>符合能谱分析报告</div>";
|
||
html += generateMeasurementSection();
|
||
html += generateCalibrationSection();
|
||
html += generateNuclideLibSection();
|
||
html += generateAnalysisSection();
|
||
html += generatePeakTableSection();
|
||
html += generateSignatureSection();
|
||
html += generateHtmlFooter();
|
||
|
||
return html;
|
||
}
|
||
|
||
QString ReportGenerator::generateTextReport() const
|
||
{
|
||
QString text;
|
||
QTextStream stream(&text);
|
||
|
||
stream << QStringLiteral(u"符合能谱分析报告\n");
|
||
stream << "========================================\n\n";
|
||
|
||
stream << QStringLiteral(u"能谱文件: ") << m_data.measurement.spectrumFile << "\n\n";
|
||
|
||
stream << QStringLiteral(u"测量参数\n");
|
||
stream << "----------------------------------------\n";
|
||
stream << QStringLiteral(u" 开始时间: ") << formatDateTime(m_data.measurement.startTime) << "\n";
|
||
stream << QStringLiteral(u" 活时间: ") << formatDecimal(m_data.measurement.liveTime, 0) << "\n";
|
||
stream << QStringLiteral(u" 实时间: ") << formatDecimal(m_data.measurement.realTime, 0) << "\n";
|
||
stream << QStringLiteral(u" 死时间: ") << formatDecimal(m_data.measurement.deadTime, 2) << " %\n\n";
|
||
|
||
stream << QStringLiteral(u"刻度\n");
|
||
stream << "----------------------------------------\n";
|
||
stream << QStringLiteral(u" 文件: ") << m_data.calibration.fileName << "\n\n";
|
||
|
||
stream << QStringLiteral(u" 能量刻度\n");
|
||
stream << QStringLiteral(u" 生成时间: ") << formatDateTime(m_data.calibration.energy.generateTime) << "\n";
|
||
stream << QStringLiteral(u" 截距: ") << formatDecimal(m_data.calibration.energy.intercept, 3) << " keV\n";
|
||
stream << QStringLiteral(u" 斜率: ") << formatDecimal(m_data.calibration.energy.slope, 3) << " keV/ch\n";
|
||
stream << QStringLiteral(u" 二次项系数: ") << formatScientific(m_data.calibration.energy.quadratic, 3) << " keV/(ch^2)\n\n";
|
||
|
||
stream << QStringLiteral(u" 效率刻度\n");
|
||
stream << QStringLiteral(u" 生成时间: ") << formatDateTime(m_data.calibration.efficiency.generateTime) << "\n";
|
||
stream << QStringLiteral(u" 拐点能量: " )<< formatDecimal(m_data.calibration.efficiency.inflectionEnergy, 2) << " keV\n";
|
||
stream << QStringLiteral(u" 拐点以上: ") << m_data.calibration.efficiency.aboveInflection
|
||
<< QStringLiteral(u" 误差 = ") << formatDecimal(m_data.calibration.efficiency.aboveError, 2) << " %\n";
|
||
stream << QStringLiteral(u" 拐点以下: ") << m_data.calibration.efficiency.belowInflection
|
||
<< QStringLiteral(u" 误差 = ") << formatDecimal(m_data.calibration.efficiency.belowError, 2) << " %\n";
|
||
stream << QStringLiteral(u" 效率对数方程式: ") << m_data.calibration.efficiency.logEquation << "\n\n";
|
||
|
||
stream << QStringLiteral(u"核素库\n");
|
||
stream << "----------------------------------------\n";
|
||
stream << QStringLiteral(u" 文件名: ") << m_data.nuclideLib.fileName << "\n";
|
||
stream << QStringLiteral(u" 峰匹配宽度: ") << formatDecimal(m_data.nuclideLib.peakMatchWidth, 3) << " * FWHM (keV)\n\n";
|
||
|
||
stream << QStringLiteral(u"分析参数\n");
|
||
stream << QStringLiteral(u"----------------------------------------\n");
|
||
stream << QStringLiteral(u" 起始能量: " )<< formatDecimal(m_data.analysis.startEnergy, 2) << " keV\n";
|
||
stream << QStringLiteral(u" 终止能量: ") << formatDecimal(m_data.analysis.endEnergy, 2) << " keV\n";
|
||
stream << QStringLiteral(u" 统计误差限值: ") << formatDecimal(m_data.analysis.statErrorLimit, 3) << "%\n";
|
||
stream << QStringLiteral(u" 活度比例系数: ") << formatScientific(m_data.analysis.activityRatio, 4) << "\n";
|
||
stream << QStringLiteral(u" 探测限计算方法: " )<< m_data.analysis.mdaMethod << "\n\n";
|
||
|
||
stream << QStringLiteral(u"********************* 能 谱 峰 一 览 表 ******************\n\n");
|
||
stream << QString("%1 %2 %3 %4 %5 %6 %7\n")
|
||
.arg("核素", -10)
|
||
.arg("平均活度(Bq)", -15)
|
||
.arg("能量(keV)", -12)
|
||
.arg("峰活度(Bq)", -15)
|
||
.arg("MDA(Bq)", -15)
|
||
.arg("提示码", -8)
|
||
.arg("备注", -6);
|
||
stream << "------------------------------------------------------------------------\n";
|
||
|
||
for (const auto& peak : m_data.peaks) {
|
||
stream << QString("%1 %2 %3 %4 %5 %6 %7\n")
|
||
.arg(peak.nuclide, -10)
|
||
.arg(formatScientific(peak.avgActivity, 4), -15)
|
||
.arg(formatDecimal(peak.energy, 2), -12)
|
||
.arg(formatScientific(peak.peakActivity, 3), -15)
|
||
.arg(formatScientific(peak.mda, 3), -15)
|
||
.arg(peak.peakCode, -8)
|
||
.arg(peak.remark, -6);
|
||
}
|
||
|
||
stream << "\n\n";
|
||
stream << QStringLiteral(u"分析员: " )<< m_data.signature.analyst << "\n";
|
||
stream << QStringLiteral(u"审查员: " )<< m_data.signature.reviewer << "\n";
|
||
stream << QStringLiteral(u"实验室: " )<< m_data.signature.laboratory << "\n";
|
||
stream << QStringLiteral(u"日 期: ") << m_data.signature.reportDate.toString("yyyy年MM月dd日") << "\n";
|
||
|
||
return text;
|
||
}
|
||
|
||
bool ReportGenerator::saveToFile(const QString &filePath) const
|
||
{
|
||
QFile file(filePath);
|
||
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
||
return false;
|
||
}
|
||
|
||
QTextStream out(&file);
|
||
out.setCodec("UTF-8");
|
||
|
||
if (filePath.endsWith(".html", Qt::CaseInsensitive) ||
|
||
filePath.endsWith(".htm", Qt::CaseInsensitive)) {
|
||
out << generateHtmlReport();
|
||
} else {
|
||
out << generateTextReport();
|
||
}
|
||
|
||
file.close();
|
||
return true;
|
||
}
|
||
|
||
QString ReportGenerator::generateWordReport() const
|
||
{
|
||
QString html;
|
||
QTextStream stream(&html);
|
||
|
||
stream << "<html xmlns:o='urn:schemas-microsoft-com:office:office'\n";
|
||
stream << " xmlns:w='urn:schemas-microsoft-com:office:word'\n";
|
||
stream << " xmlns='http://www.w3.org/TR/REC-html40'>\n";
|
||
stream << "<head>\n";
|
||
stream << "<meta charset='UTF-8'>\n";
|
||
stream << "<title>符合能谱分析报告</title>\n";
|
||
stream << "<!--[if gte mso 9]>\n";
|
||
stream << "<xml>\n";
|
||
stream << "<w:WordDocument>\n";
|
||
stream << "<w:View>Print</w:View>\n";
|
||
stream << "<w:Zoom>100</w:Zoom>\n";
|
||
stream << "<w:DoNotOptimizeForBrowser/>\n";
|
||
stream << "</w:WordDocument>\n";
|
||
stream << "</xml>\n";
|
||
stream << "<![endif]-->\n";
|
||
stream << "<style>\n";
|
||
stream << " body {\n";
|
||
stream << " font-family: 'SimSun', '宋体', serif;\n";
|
||
stream << " font-size: 12pt;\n";
|
||
stream << " line-height: 1.5;\n";
|
||
stream << " margin: 2.54cm 3.17cm;\n";
|
||
stream << " }\n";
|
||
stream << " .report-title {\n";
|
||
stream << " text-align: center;\n";
|
||
stream << " font-size: 18pt;\n";
|
||
stream << " font-weight: bold;\n";
|
||
stream << " margin-bottom: 20pt;\n";
|
||
stream << " }\n";
|
||
stream << " .section-title {\n";
|
||
stream << " font-weight: bold;\n";
|
||
stream << " font-size: 14pt;\n";
|
||
stream << " margin-top: 15pt;\n";
|
||
stream << " margin-left: 0pt;\n";
|
||
stream << " margin-bottom: 8pt;\n";
|
||
stream << " border-bottom: 1px solid #000;\n";
|
||
stream << " padding-bottom: 3pt;\n";
|
||
stream << " }\n";
|
||
stream << " table {\n";
|
||
stream << " width: 100%;\n";
|
||
stream << " border-collapse: collapse;\n";
|
||
stream << " margin-bottom: 8pt;\n";
|
||
stream << " font-size: 11pt;\n";
|
||
stream << " }\n";
|
||
stream << " table, th, td {\n";
|
||
stream << " border: 1px solid #000;\n";
|
||
stream << " }\n";
|
||
stream << " th, td {\n";
|
||
stream << " padding: 5pt 8pt;\n";
|
||
stream << " text-align: left;\n";
|
||
stream << " }\n";
|
||
stream << " th {\n";
|
||
stream << " background-color: #f0f0f0;\n";
|
||
stream << " font-weight: bold;\n";
|
||
stream << " text-align: center;\n";
|
||
stream << " }\n";
|
||
stream << " .param-table td:first-child {\n";
|
||
stream << " width: 140pt;\n";
|
||
stream << " font-weight: bold;\n";
|
||
stream << " }\n";
|
||
stream << " .peak-table {\n";
|
||
stream << " font-size: 10pt;\n";
|
||
stream << " }\n";
|
||
stream << " .peak-table td {\n";
|
||
stream << " text-align: center;\n";
|
||
stream << " }\n";
|
||
stream << " .signature-section {\n";
|
||
stream << " margin-top: 30pt;\n";
|
||
stream << " text-align: right;\n";
|
||
stream << " font-size: 12pt;\n";
|
||
stream << " }\n";
|
||
stream << " .signature-line {\n";
|
||
stream << " margin-bottom: 8pt;\n";
|
||
stream << " }\n";
|
||
stream << " .spectrum-file-header {\n";
|
||
stream << " text-align: center;\n";
|
||
stream << " font-weight: bold;\n";
|
||
stream << " font-size: 13pt;\n";
|
||
stream << " margin-bottom: 10pt;\n";
|
||
stream << " }\n";
|
||
stream << " .calibration-sub {\n";
|
||
stream << " margin-left: 20pt;\n";
|
||
stream << " margin-top: 8pt;\n";
|
||
stream << " }\n";
|
||
stream << " .calibration-sub-title {\n";
|
||
stream << " font-weight: bold;\n";
|
||
stream << " margin-bottom: 5pt;\n";
|
||
stream << " }\n";
|
||
stream << " .legend {\n";
|
||
stream << " margin-top: 8pt;\n";
|
||
stream << " font-size: 10pt;\n";
|
||
stream << " color: #333;\n";
|
||
stream << " }\n";
|
||
stream << " .legend-title {\n";
|
||
stream << " font-weight: bold;\n";
|
||
stream << " margin-bottom: 3pt;\n";
|
||
stream << " }\n";
|
||
stream << "</style>\n";
|
||
stream << "</head>\n";
|
||
stream << "<body>\n";
|
||
|
||
// 报告标题
|
||
stream << QStringLiteral(u"<div class='report-title'>符合能谱分析报告</div>\n");
|
||
|
||
// 能谱文件
|
||
stream << QStringLiteral(u"<div class='spectrum-file-header'>能谱文件:%1</div>\n")
|
||
.arg(m_data.measurement.spectrumFile);
|
||
|
||
// 测量参数
|
||
stream << QStringLiteral(u"<div class='section-title'>测量参数</div>\n");
|
||
stream << "<table class='param-table'>\n";
|
||
stream << QStringLiteral(u"<tr><td>开始时间:</td><td>%1</td><td>活时间(S):</td><td>%2</td></tr>\n")
|
||
.arg(formatDateTime(m_data.measurement.startTime))
|
||
.arg(formatDecimal(m_data.measurement.liveTime, 0));
|
||
stream << QStringLiteral(u"<tr><td>实时间(S):</td><td>%1</td><td>死时间:</td><td>%2 %</td></tr>\n")
|
||
.arg(formatDecimal(m_data.measurement.realTime, 0))
|
||
.arg(formatDecimal(m_data.measurement.deadTime, 2));
|
||
stream << "</table>\n";
|
||
|
||
// 刻度
|
||
stream << QStringLiteral(u"<div class='section-title'>刻度</div>\n");
|
||
stream << "<table class='param-table'>\n";
|
||
stream << QStringLiteral(u"<tr><td>文件:</td><td>%1</td><td>刻度标准源:</td><td>%2</td></tr>\n")
|
||
.arg(m_data.calibration.fileName)
|
||
.arg(m_data.calibration.standardSource);
|
||
stream << "</table>\n";
|
||
|
||
// 能量刻度
|
||
stream << "<div class='calibration-sub'>\n";
|
||
stream << QStringLiteral(u"<div class='calibration-sub-title'>能量刻度</div>\n");
|
||
stream << "<table class='param-table'>\n";
|
||
stream << QStringLiteral(u"<tr><td>生成时间:</td><td>%1</td><td>截距:</td><td>%2 keV</td></tr>\n")
|
||
.arg(formatDateTime(m_data.calibration.energy.generateTime))
|
||
.arg(formatDecimal(m_data.calibration.energy.intercept, 3));
|
||
stream << QStringLiteral(u"<tr><td>斜率:</td><td>%1 keV/ch</td><td>二次项系数:</td><td>%2 keV/(ch^2)</td></tr>\n")
|
||
.arg(formatDecimal(m_data.calibration.energy.slope, 3))
|
||
.arg(formatScientific(m_data.calibration.energy.quadratic, 3));
|
||
stream << "</table>\n";
|
||
stream << "</div>\n";
|
||
|
||
// 效率刻度
|
||
stream << "<div class='calibration-sub'>\n";
|
||
stream << QStringLiteral(u"<div class='calibration-sub-title'>效率刻度</div>\n");
|
||
stream << "<table class='param-table'>\n";
|
||
stream << QStringLiteral(u"<tr><td>生成时间:</td><td>%1</td><td>拐点能量(keV):</td><td>%2</td></tr>\n")
|
||
.arg(formatDateTime(m_data.calibration.efficiency.generateTime))
|
||
.arg(formatDecimal(m_data.calibration.efficiency.inflectionEnergy, 2));
|
||
stream << QStringLiteral(u"<tr><td>拐点以上:</td><td>%1</td><td>误差:</td><td>%2%</td></tr>\n")
|
||
.arg(m_data.calibration.efficiency.aboveInflection)
|
||
.arg(formatDecimal(m_data.calibration.efficiency.aboveError, 2));
|
||
stream << QStringLiteral(u"<tr><td>拐点以下:</td><td>%1</td><td>误差:</td><td>%2%</td></tr>\n")
|
||
.arg(m_data.calibration.efficiency.belowInflection)
|
||
.arg(formatDecimal(m_data.calibration.efficiency.belowError, 2));
|
||
stream << QStringLiteral(u"<tr><td>效率对数方程式:</td><td colspan='3'>%1</td></tr>\n")
|
||
.arg(m_data.calibration.efficiency.logEquation);
|
||
stream << "</table>\n";
|
||
stream << "</div>\n";
|
||
|
||
// 核素库
|
||
stream << QStringLiteral(u"<div class='section-title'>核素库</div>\n");
|
||
stream << "<table class='param-table'>\n";
|
||
stream << QStringLiteral(u"<tr><td>文件:</td><td>%1</td><td>峰匹配宽度(keV):</td><td>%2 * FWHM</td></tr>\n")
|
||
.arg(m_data.nuclideLib.fileName)
|
||
.arg(formatDecimal(m_data.nuclideLib.peakMatchWidth, 3));
|
||
stream << "</table>\n";
|
||
|
||
// 分析参数
|
||
stream << QStringLiteral(u"<div class='section-title'>分析参数</div>\n");
|
||
stream << "<table class='param-table'>\n";
|
||
stream << QStringLiteral(u"<tr><td>起始能量(keV):</td><td>%1</td><td>终止能量(keV):</td><td>%2</td></tr>\n")
|
||
.arg(formatDecimal(m_data.analysis.startEnergy, 2))
|
||
.arg(formatDecimal(m_data.analysis.endEnergy, 2));
|
||
stream << QStringLiteral(u"<tr><td>统计误差限值:</td><td>%1%</td><td>活度比例系数:</td><td>%2</td></tr>\n")
|
||
.arg(formatDecimal(m_data.analysis.statErrorLimit, 3))
|
||
.arg(formatScientific(m_data.analysis.activityRatio, 4));
|
||
stream << QStringLiteral(u"<tr><td>探测限计算方法:</td><td colspan='3'>%1</td></tr>\n")
|
||
.arg(m_data.analysis.mdaMethod);
|
||
stream << "</table>\n";
|
||
|
||
// 能谱峰一览表
|
||
stream << QStringLiteral(u"<div class='section-title'>能 谱 峰 一 览 表</div>\n");
|
||
stream << QStringLiteral(u"<table class='peak-table'>\n");
|
||
stream << QStringLiteral(u"<tr>\n");
|
||
stream << QStringLiteral(u"<th rowspan='2'>核素</th>\n");
|
||
stream << QStringLiteral(u"<th rowspan='2' colspan='1'>平均活度(贝克Bq)</th>\n");
|
||
stream << QStringLiteral(u"<th rowspan='2' colspan='1'>不确定度</th>\n");
|
||
stream << QStringLiteral(u"<th colspan='4'>峰</th>\n");
|
||
stream << QStringLiteral(u"<th rowspan='2'>备注</th>\n");
|
||
stream << QStringLiteral(u"</tr>\n");
|
||
stream << QStringLiteral(u"<tr>\n");
|
||
stream << QStringLiteral(u"<th>能量keV</th>\n");
|
||
stream << QStringLiteral(u"<th>活度(贝克Bq)</th>\n");
|
||
stream << QStringLiteral(u"<th>不确定度</th>\n");
|
||
stream << QStringLiteral(u"<th>最小可探测活度(MDA)(贝克Bq)</th>\n");
|
||
stream << QStringLiteral(u"</tr>\n");
|
||
|
||
for (const auto& peak : m_data.peaks) {
|
||
stream << QStringLiteral(u"<tr>\n");
|
||
stream << QStringLiteral(u"<td>%1</td>\n").arg(peak.nuclide);
|
||
stream << QStringLiteral(u"<td>%1</td>\n").arg(formatScientific(peak.avgActivity, 4));
|
||
stream << QStringLiteral(u"<td>%1%</td>\n").arg(formatDecimal(peak.avgUncertainty, 2));
|
||
stream << QStringLiteral(u"<td>%1</td>\n").arg(formatDecimal(peak.energy, 2));
|
||
stream << QStringLiteral(u"<td>%1</td>\n").arg(formatScientific(peak.peakActivity, 3));
|
||
stream << QStringLiteral(u"<td>%1%</td>\n").arg(formatDecimal(peak.peakUncertainty, 2));
|
||
stream << QStringLiteral(u"<td>%1</td>\n").arg(formatScientific(peak.mda, 3));
|
||
stream << QStringLiteral(u"<td>%1</td>\n").arg(peak.remark);
|
||
stream << QStringLiteral(u"</tr>\n");
|
||
}
|
||
|
||
stream << QStringLiteral(u"</table>\n");
|
||
|
||
// 峰提示码说明
|
||
stream << QStringLiteral(u"<div class='legend'>\n");
|
||
stream << QStringLiteral(u"<div class='legend-title'>峰提示码说明:</div>\n");
|
||
stream << QStringLiteral(u"<div>G - γ射线</div>\n");
|
||
stream << QStringLiteral(u"<div>X - X射线</div>\n");
|
||
stream << QStringLiteral(u"<div>ec - 内转换电子</div>\n");
|
||
stream << QStringLiteral(u"</div>\n");
|
||
// 签名
|
||
stream << "<div class='signature-section'>\n";
|
||
stream << QStringLiteral(u"<div class='signature-line'>分析员:%1</div>\n")
|
||
.arg(m_data.signature.analyst);
|
||
stream << QStringLiteral(u"<div class='signature-line'>审查员:%1</div>\n")
|
||
.arg(m_data.signature.reviewer);
|
||
stream << QStringLiteral(u"<div class='signature-line'>实验室:%1</div>\n")
|
||
.arg(m_data.signature.laboratory);
|
||
stream << QStringLiteral(u"<div class='signature-line'>日 期:%1</div>\n")
|
||
.arg(m_data.signature.reportDate.toString("yyyy年MM月dd日"));
|
||
stream << "</div>\n";
|
||
|
||
stream << "</body>\n";
|
||
stream << "</html>\n";
|
||
|
||
return html;
|
||
}
|
||
|
||
bool ReportGenerator::saveToWordFile(const QString &filePath) const
|
||
{
|
||
QFile file(filePath);
|
||
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
||
return false;
|
||
}
|
||
|
||
QTextStream out(&file);
|
||
out.setCodec("UTF-8");
|
||
out << generateWordReport();
|
||
file.close();
|
||
|
||
return true;
|
||
} |