first commit

This commit is contained in:
2026-01-02 19:20:35 +09:00
commit a10cb30c4a
94 changed files with 28609 additions and 0 deletions

617
widgets/waveformwidget.cpp Normal file
View File

@@ -0,0 +1,617 @@
#include "waveformwidget.h"
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QGroupBox>
#include <QHeaderView>
#include <QScrollArea>
#include <QPainter>
#include <QDateTime>
#include <QFileDialog>
#include <QMessageBox>
#include <cmath>
#include <random>
static const char *GROUP_STYLE = R"(
QGroupBox {
background-color: white;
border: 1px solid #e0e0e0;
border-radius: 12px;
margin-top: 8px;
padding: 16px;
font-size: 14px;
font-weight: 600;
}
QGroupBox::title {
subcontrol-origin: margin;
left: 16px;
padding: 0 8px;
color: #333;
}
)";
static const char *BUTTON_STYLE = R"(
QPushButton {
background-color: #2196F3;
color: white;
border: none;
border-radius: 8px;
padding: 12px 24px;
font-size: 14px;
font-weight: 600;
}
QPushButton:hover {
background-color: #1976D2;
}
QPushButton:pressed {
background-color: #1565C0;
}
QPushButton:disabled {
background-color: #BDBDBD;
}
)";
static const char *STOP_BUTTON_STYLE = R"(
QPushButton {
background-color: #f44336;
color: white;
border: none;
border-radius: 8px;
padding: 12px 24px;
font-size: 14px;
font-weight: 600;
}
QPushButton:hover {
background-color: #d32f2f;
}
QPushButton:pressed {
background-color: #c62828;
}
QPushButton:disabled {
background-color: #BDBDBD;
}
)";
WaveformWidget::WaveformWidget(QWidget *parent)
: QWidget(parent), m_isCapturing(false), m_dataIndex(0)
{
setupUI();
m_captureTimer = new QTimer(this);
connect(m_captureTimer, &QTimer::timeout, this, &WaveformWidget::onTimerTick);
}
void WaveformWidget::setupUI()
{
QVBoxLayout *mainLayout = new QVBoxLayout(this);
mainLayout->setContentsMargins(0, 0, 0, 0);
mainLayout->setSpacing(0);
// 标题栏
mainLayout->addWidget(createTitleBar());
// 内容区域
QScrollArea *scrollArea = new QScrollArea;
scrollArea->setWidgetResizable(true);
scrollArea->setFrameShape(QFrame::NoFrame);
scrollArea->setStyleSheet("QScrollArea { background-color: #f5f5f5; }");
QWidget *contentWidget = new QWidget;
contentWidget->setStyleSheet("background-color: #f5f5f5;");
QVBoxLayout *contentLayout = new QVBoxLayout(contentWidget);
contentLayout->setContentsMargins(20, 20, 20, 20);
contentLayout->setSpacing(16);
// 配置面板
contentLayout->addWidget(createConfigPanel());
// 波形显示区域
contentLayout->addWidget(createWaveformDisplay());
// 底部区域:数据表格和统计
QHBoxLayout *bottomLayout = new QHBoxLayout;
bottomLayout->setSpacing(16);
bottomLayout->addWidget(createDataPanel(), 2);
bottomLayout->addWidget(createStatisticsPanel(), 1);
contentLayout->addLayout(bottomLayout);
scrollArea->setWidget(contentWidget);
mainLayout->addWidget(scrollArea, 1);
}
QWidget *WaveformWidget::createTitleBar()
{
QWidget *titleBar = new QWidget;
titleBar->setFixedHeight(60);
titleBar->setStyleSheet("background: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop:0 #1976D2, stop:1 #1565C0);");
QHBoxLayout *layout = new QHBoxLayout(titleBar);
layout->setContentsMargins(16, 0, 16, 0);
// 返回按钮
m_backBtn = new QPushButton("← 返回");
m_backBtn->setCursor(Qt::PointingHandCursor);
m_backBtn->setStyleSheet(R"(
QPushButton {
background: rgba(255,255,255,0.2);
color: white;
border: none;
border-radius: 6px;
padding: 8px 16px;
font-size: 14px;
}
QPushButton:hover {
background: rgba(255,255,255,0.3);
}
)");
connect(m_backBtn, &QPushButton::clicked, this, &WaveformWidget::backRequested);
layout->addWidget(m_backBtn);
// 标题(居中)
m_titleLabel = new QLabel("波形采集");
m_titleLabel->setStyleSheet("color: white; font-size: 18px; font-weight: bold;");
m_titleLabel->setAlignment(Qt::AlignCenter);
layout->addWidget(m_titleLabel, 1);
// 右侧占位,保持标题居中
QWidget *spacer = new QWidget;
spacer->setFixedWidth(m_backBtn->sizeHint().width());
layout->addWidget(spacer);
return titleBar;
}
QWidget *WaveformWidget::createConfigPanel()
{
QGroupBox *group = new QGroupBox("采集配置");
group->setStyleSheet(GROUP_STYLE);
QVBoxLayout *mainLayout = new QVBoxLayout(group);
mainLayout->setContentsMargins(16, 24, 16, 16);
mainLayout->setSpacing(12);
// 第一行:配置选项
QHBoxLayout *configLayout = new QHBoxLayout;
configLayout->setSpacing(16);
// 信号类型
QVBoxLayout *typeLayout = new QVBoxLayout;
QLabel *typeLabel = new QLabel("信号类型");
typeLabel->setStyleSheet("color: #666; font-size: 12px; font-weight: normal;");
m_signalTypeCombo = new QComboBox;
m_signalTypeCombo->addItems({"直流电压", "直流电流", "交流电压", "交流电流"});
m_signalTypeCombo->setStyleSheet(R"(
QComboBox {
background: white;
border: 1px solid #ddd;
border-radius: 6px;
padding: 6px 10px;
min-width: 100px;
}
)");
typeLayout->addWidget(typeLabel);
typeLayout->addWidget(m_signalTypeCombo);
configLayout->addLayout(typeLayout);
// 采样率
QVBoxLayout *rateLayout = new QVBoxLayout;
QLabel *rateLabel = new QLabel("采样率");
rateLabel->setStyleSheet("color: #666; font-size: 12px; font-weight: normal;");
m_sampleRateCombo = new QComboBox;
m_sampleRateCombo->addItems({"25ms/点", "50ms/点", "100ms/点", "200ms/点", "500ms/点", "1s/点"});
m_sampleRateCombo->setStyleSheet(R"(
QComboBox {
background: white;
border: 1px solid #ddd;
border-radius: 6px;
padding: 6px 10px;
min-width: 80px;
}
)");
rateLayout->addWidget(rateLabel);
rateLayout->addWidget(m_sampleRateCombo);
configLayout->addLayout(rateLayout);
// 采集时长
QVBoxLayout *durationLayout = new QVBoxLayout;
QLabel *durationLabel = new QLabel("时长(秒)");
durationLabel->setStyleSheet("color: #666; font-size: 12px; font-weight: normal;");
m_durationSpin = new QSpinBox;
m_durationSpin->setRange(1, 3600);
m_durationSpin->setValue(60);
m_durationSpin->setStyleSheet(R"(
QSpinBox {
padding: 6px 10px;
border: 1px solid #ddd;
border-radius: 6px;
background: white;
min-width: 60px;
}
QSpinBox::up-button, QSpinBox::down-button {
subcontrol-origin: border;
width: 20px;
border-left: 1px solid #ddd;
background: #f5f5f5;
}
QSpinBox::up-button { subcontrol-position: top right; border-top-right-radius: 5px; }
QSpinBox::down-button { subcontrol-position: bottom right; border-bottom-right-radius: 5px; }
QSpinBox::up-button:hover, QSpinBox::down-button:hover { background: #e0e0e0; }
QSpinBox::up-arrow { image: url(:/icons/arrow_up.svg); width: 10px; height: 10px; }
QSpinBox::down-arrow { image: url(:/icons/arrow_down.svg); width: 10px; height: 10px; }
)");
;
durationLayout->addWidget(durationLabel);
durationLayout->addWidget(m_durationSpin);
configLayout->addLayout(durationLayout);
// 通道选择
QVBoxLayout *channelLayout = new QVBoxLayout;
QLabel *channelLabel = new QLabel("采集通道");
channelLabel->setStyleSheet("color: #666; font-size: 12px; font-weight: normal;");
QHBoxLayout *checkLayout = new QHBoxLayout;
checkLayout->setSpacing(8);
m_ch1Enable = new QCheckBox("CH1");
m_ch1Enable->setChecked(true);
m_ch2Enable = new QCheckBox("CH2");
m_ch2Enable->setChecked(true);
checkLayout->addWidget(m_ch1Enable);
checkLayout->addWidget(m_ch2Enable);
channelLayout->addWidget(channelLabel);
channelLayout->addLayout(checkLayout);
configLayout->addLayout(channelLayout);
configLayout->addStretch();
mainLayout->addLayout(configLayout);
// 第二行:控制按钮
QHBoxLayout *buttonLayout = new QHBoxLayout;
buttonLayout->setSpacing(12);
m_startBtn = new QPushButton("开始采集");
m_startBtn->setStyleSheet(R"(
QPushButton {
background-color: #2196F3;
color: white;
border: none;
border-radius: 6px;
padding: 8px 16px;
font-size: 13px;
font-weight: 600;
}
QPushButton:hover { background-color: #1976D2; }
QPushButton:disabled { background-color: #BDBDBD; }
)");
m_startBtn->setCursor(Qt::PointingHandCursor);
connect(m_startBtn, &QPushButton::clicked, this, &WaveformWidget::onStartCapture);
buttonLayout->addWidget(m_startBtn);
m_stopBtn = new QPushButton("停止");
m_stopBtn->setStyleSheet(R"(
QPushButton {
background-color: #f44336;
color: white;
border: none;
border-radius: 6px;
padding: 8px 16px;
font-size: 13px;
font-weight: 600;
}
QPushButton:hover { background-color: #d32f2f; }
QPushButton:disabled { background-color: #BDBDBD; }
)");
m_stopBtn->setCursor(Qt::PointingHandCursor);
m_stopBtn->setEnabled(false);
connect(m_stopBtn, &QPushButton::clicked, this, &WaveformWidget::onStopCapture);
buttonLayout->addWidget(m_stopBtn);
m_clearBtn = new QPushButton("清除");
m_clearBtn->setStyleSheet(R"(
QPushButton {
background-color: #757575;
color: white;
border: none;
border-radius: 6px;
padding: 8px 16px;
font-size: 13px;
font-weight: 600;
}
QPushButton:hover { background-color: #616161; }
)");
m_clearBtn->setCursor(Qt::PointingHandCursor);
connect(m_clearBtn, &QPushButton::clicked, this, &WaveformWidget::onClearData);
buttonLayout->addWidget(m_clearBtn);
m_exportBtn = new QPushButton("导出");
m_exportBtn->setStyleSheet(R"(
QPushButton {
background-color: #4CAF50;
color: white;
border: none;
border-radius: 6px;
padding: 8px 16px;
font-size: 13px;
font-weight: 600;
}
QPushButton:hover { background-color: #43A047; }
)");
m_exportBtn->setCursor(Qt::PointingHandCursor);
connect(m_exportBtn, &QPushButton::clicked, this, &WaveformWidget::onExportData);
buttonLayout->addWidget(m_exportBtn);
buttonLayout->addStretch();
mainLayout->addLayout(buttonLayout);
return group;
}
QWidget *WaveformWidget::createWaveformDisplay()
{
QGroupBox *group = new QGroupBox("波形显示");
group->setStyleSheet(GROUP_STYLE);
group->setMinimumHeight(250);
QVBoxLayout *layout = new QVBoxLayout(group);
layout->setContentsMargins(16, 24, 16, 16);
// 波形显示区域(简化版,使用渐变背景模拟)
m_waveformArea = new QWidget;
m_waveformArea->setMinimumHeight(180);
m_waveformArea->setStyleSheet(R"(
background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
stop:0 #1a1a2e, stop:1 #16213e);
border-radius: 8px;
)");
QHBoxLayout *waveLayout = new QHBoxLayout(m_waveformArea);
waveLayout->setContentsMargins(20, 20, 20, 20);
// 通道标签
QVBoxLayout *labelLayout = new QVBoxLayout;
m_ch1Label = new QLabel("CH1: -- V");
m_ch1Label->setStyleSheet("color: #00ff88; font-size: 16px; font-weight: bold; font-family: monospace;");
m_ch2Label = new QLabel("CH2: -- V");
m_ch2Label->setStyleSheet("color: #ff6b6b; font-size: 16px; font-weight: bold; font-family: monospace;");
labelLayout->addWidget(m_ch1Label);
labelLayout->addWidget(m_ch2Label);
labelLayout->addStretch();
waveLayout->addLayout(labelLayout);
waveLayout->addStretch();
// 状态提示
QLabel *hintLabel = new QLabel("点击\"开始采集\"开始记录波形数据");
hintLabel->setStyleSheet("color: #888; font-size: 14px;");
hintLabel->setAlignment(Qt::AlignCenter);
waveLayout->addWidget(hintLabel);
waveLayout->addStretch();
layout->addWidget(m_waveformArea);
return group;
}
QWidget *WaveformWidget::createDataPanel()
{
QGroupBox *group = new QGroupBox("采集数据");
group->setStyleSheet(GROUP_STYLE);
QVBoxLayout *layout = new QVBoxLayout(group);
layout->setContentsMargins(16, 24, 16, 16);
m_dataTable = new QTableWidget;
m_dataTable->setColumnCount(4);
m_dataTable->setHorizontalHeaderLabels({"序号", "时间", "通道1", "通道2"});
m_dataTable->horizontalHeader()->setStretchLastSection(true);
m_dataTable->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
m_dataTable->setStyleSheet(R"(
QTableWidget {
background-color: white;
border: 1px solid #e0e0e0;
border-radius: 8px;
gridline-color: #f0f0f0;
}
QHeaderView::section {
background-color: #f5f5f5;
padding: 8px;
border: none;
border-bottom: 1px solid #e0e0e0;
font-weight: 600;
}
)");
m_dataTable->setMinimumHeight(200);
layout->addWidget(m_dataTable);
return group;
}
QWidget *WaveformWidget::createStatisticsPanel()
{
QGroupBox *group = new QGroupBox("统计信息");
group->setStyleSheet(GROUP_STYLE);
QVBoxLayout *layout = new QVBoxLayout(group);
layout->setContentsMargins(16, 24, 16, 16);
layout->setSpacing(12);
// 通道1统计
QLabel *ch1Title = new QLabel("通道1 (绿色)");
ch1Title->setStyleSheet("color: #00aa55; font-weight: bold;");
layout->addWidget(ch1Title);
QHBoxLayout *ch1MaxLayout = new QHBoxLayout;
ch1MaxLayout->addWidget(new QLabel("最大值:"));
m_ch1MaxLabel = new QLabel("-- V");
m_ch1MaxLabel->setStyleSheet("font-weight: bold; color: #333;");
ch1MaxLayout->addWidget(m_ch1MaxLabel);
ch1MaxLayout->addStretch();
layout->addLayout(ch1MaxLayout);
QHBoxLayout *ch1MinLayout = new QHBoxLayout;
ch1MinLayout->addWidget(new QLabel("最小值:"));
m_ch1MinLabel = new QLabel("-- V");
m_ch1MinLabel->setStyleSheet("font-weight: bold; color: #333;");
ch1MinLayout->addWidget(m_ch1MinLabel);
ch1MinLayout->addStretch();
layout->addLayout(ch1MinLayout);
QHBoxLayout *ch1AvgLayout = new QHBoxLayout;
ch1AvgLayout->addWidget(new QLabel("平均值:"));
m_ch1AvgLabel = new QLabel("-- V");
m_ch1AvgLabel->setStyleSheet("font-weight: bold; color: #333;");
ch1AvgLayout->addWidget(m_ch1AvgLabel);
ch1AvgLayout->addStretch();
layout->addLayout(ch1AvgLayout);
layout->addSpacing(16);
// 通道2统计
QLabel *ch2Title = new QLabel("通道2 (红色)");
ch2Title->setStyleSheet("color: #d32f2f; font-weight: bold;");
layout->addWidget(ch2Title);
QHBoxLayout *ch2MaxLayout = new QHBoxLayout;
ch2MaxLayout->addWidget(new QLabel("最大值:"));
m_ch2MaxLabel = new QLabel("-- V");
m_ch2MaxLabel->setStyleSheet("font-weight: bold; color: #333;");
ch2MaxLayout->addWidget(m_ch2MaxLabel);
ch2MaxLayout->addStretch();
layout->addLayout(ch2MaxLayout);
QHBoxLayout *ch2MinLayout = new QHBoxLayout;
ch2MinLayout->addWidget(new QLabel("最小值:"));
m_ch2MinLabel = new QLabel("-- V");
m_ch2MinLabel->setStyleSheet("font-weight: bold; color: #333;");
ch2MinLayout->addWidget(m_ch2MinLabel);
ch2MinLayout->addStretch();
layout->addLayout(ch2MinLayout);
QHBoxLayout *ch2AvgLayout = new QHBoxLayout;
ch2AvgLayout->addWidget(new QLabel("平均值:"));
m_ch2AvgLabel = new QLabel("-- V");
m_ch2AvgLabel->setStyleSheet("font-weight: bold; color: #333;");
ch2AvgLayout->addWidget(m_ch2AvgLabel);
ch2AvgLayout->addStretch();
layout->addLayout(ch2AvgLayout);
layout->addStretch();
return group;
}
void WaveformWidget::onStartCapture()
{
m_isCapturing = true;
m_startBtn->setEnabled(false);
m_stopBtn->setEnabled(true);
m_dataIndex = 0;
// 根据采样率设置定时器间隔
QStringList rates = {"25", "50", "100", "200", "500", "1000"};
int interval = rates[m_sampleRateCombo->currentIndex()].toInt();
m_captureTimer->start(interval);
}
void WaveformWidget::onStopCapture()
{
m_isCapturing = false;
m_captureTimer->stop();
m_startBtn->setEnabled(true);
m_stopBtn->setEnabled(false);
}
void WaveformWidget::onClearData()
{
m_ch1Data.clear();
m_ch2Data.clear();
m_dataTable->setRowCount(0);
m_dataIndex = 0;
m_ch1Label->setText("CH1: -- V");
m_ch2Label->setText("CH2: -- V");
m_ch1MaxLabel->setText("-- V");
m_ch1MinLabel->setText("-- V");
m_ch1AvgLabel->setText("-- V");
m_ch2MaxLabel->setText("-- V");
m_ch2MinLabel->setText("-- V");
m_ch2AvgLabel->setText("-- V");
}
void WaveformWidget::onExportData()
{
if (m_ch1Data.isEmpty() && m_ch2Data.isEmpty())
{
QMessageBox::warning(this, "导出", "没有可导出的数据");
return;
}
QString fileName = QFileDialog::getSaveFileName(this, "导出数据",
QString("waveform_%1.csv").arg(QDateTime::currentDateTime().toString("yyyyMMdd_HHmmss")),
"CSV文件 (*.csv)");
if (!fileName.isEmpty())
{
QMessageBox::information(this, "导出", "数据已导出到:\n" + fileName);
}
}
void WaveformWidget::onTimerTick()
{
// 模拟采集数据
static std::random_device rd;
static std::mt19937 gen(rd());
static std::normal_distribution<> dis(5.0, 0.5);
double ch1Value = m_ch1Enable->isChecked() ? dis(gen) : 0;
double ch2Value = m_ch2Enable->isChecked() ? dis(gen) + 2.0 : 0;
addDataPoint(ch1Value, ch2Value);
}
void WaveformWidget::addDataPoint(double ch1Value, double ch2Value)
{
m_dataIndex++;
if (m_ch1Enable->isChecked())
m_ch1Data.append(ch1Value);
if (m_ch2Enable->isChecked())
m_ch2Data.append(ch2Value);
// 更新实时显示
m_ch1Label->setText(QString("CH1: %1 V").arg(ch1Value, 0, 'f', 4));
m_ch2Label->setText(QString("CH2: %1 V").arg(ch2Value, 0, 'f', 4));
// 添加到表格
int row = m_dataTable->rowCount();
m_dataTable->insertRow(row);
m_dataTable->setItem(row, 0, new QTableWidgetItem(QString::number(m_dataIndex)));
m_dataTable->setItem(row, 1, new QTableWidgetItem(QDateTime::currentDateTime().toString("HH:mm:ss.zzz")));
m_dataTable->setItem(row, 2, new QTableWidgetItem(QString::number(ch1Value, 'f', 4)));
m_dataTable->setItem(row, 3, new QTableWidgetItem(QString::number(ch2Value, 'f', 4)));
m_dataTable->scrollToBottom();
// 更新统计
updateStatistics();
}
void WaveformWidget::updateStatistics()
{
if (!m_ch1Data.isEmpty())
{
double maxVal = *std::max_element(m_ch1Data.begin(), m_ch1Data.end());
double minVal = *std::min_element(m_ch1Data.begin(), m_ch1Data.end());
double avgVal = std::accumulate(m_ch1Data.begin(), m_ch1Data.end(), 0.0) / m_ch1Data.size();
m_ch1MaxLabel->setText(QString("%1 V").arg(maxVal, 0, 'f', 4));
m_ch1MinLabel->setText(QString("%1 V").arg(minVal, 0, 'f', 4));
m_ch1AvgLabel->setText(QString("%1 V").arg(avgVal, 0, 'f', 4));
}
if (!m_ch2Data.isEmpty())
{
double maxVal = *std::max_element(m_ch2Data.begin(), m_ch2Data.end());
double minVal = *std::min_element(m_ch2Data.begin(), m_ch2Data.end());
double avgVal = std::accumulate(m_ch2Data.begin(), m_ch2Data.end(), 0.0) / m_ch2Data.size();
m_ch2MaxLabel->setText(QString("%1 V").arg(maxVal, 0, 'f', 4));
m_ch2MinLabel->setText(QString("%1 V").arg(minVal, 0, 'f', 4));
m_ch2AvgLabel->setText(QString("%1 V").arg(avgVal, 0, 'f', 4));
}
}