#include "waveformwidget.h" #include #include #include #include #include #include #include #include #include #include #include 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)); } }