Files
CalibratorLauncher/widgets/dualchannelwidget.cpp
2026-01-02 19:20:35 +09:00

534 lines
16 KiB
C++

#include "dualchannelwidget.h"
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QGroupBox>
#include <QScrollArea>
#include <QTimer>
#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 32px;
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 32px;
font-size: 14px;
font-weight: 600;
}
QPushButton:hover {
background-color: #d32f2f;
}
QPushButton:pressed {
background-color: #c62828;
}
QPushButton:disabled {
background-color: #BDBDBD;
}
)";
static const char *VALUE_DISPLAY_STYLE = R"(
QLabel {
background-color: #1a1a2e;
color: #00ff88;
border: 1px solid #333;
border-radius: 8px;
padding: 16px;
font-size: 28px;
font-weight: bold;
font-family: 'Courier New', monospace;
min-width: 200px;
}
)";
DualChannelWidget::DualChannelWidget(QWidget *parent)
: QWidget(parent), m_isOutputting(false)
{
setupUI();
m_measureTimer = new QTimer(this);
connect(m_measureTimer, &QTimer::timeout, this, &DualChannelWidget::updateMeasurement);
}
void DualChannelWidget::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);
// 双通道输出面板
QHBoxLayout *channelsLayout = new QHBoxLayout;
channelsLayout->setSpacing(16);
channelsLayout->addWidget(createChannel1Panel());
channelsLayout->addWidget(createChannel2Panel());
contentLayout->addLayout(channelsLayout);
// 测量显示面板
contentLayout->addWidget(createMeasurementPanel());
// 控制面板
contentLayout->addWidget(createControlPanel());
contentLayout->addStretch();
scrollArea->setWidget(contentWidget);
mainLayout->addWidget(scrollArea, 1);
}
QWidget *DualChannelWidget::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, &DualChannelWidget::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 *DualChannelWidget::createChannel1Panel()
{
QGroupBox *group = new QGroupBox("通道1 - 输出");
group->setStyleSheet(GROUP_STYLE);
QVBoxLayout *layout = new QVBoxLayout(group);
layout->setContentsMargins(16, 24, 16, 16);
layout->setSpacing(16);
// 启用复选框
m_ch1EnableCheck = new QCheckBox("启用通道1");
m_ch1EnableCheck->setChecked(true);
m_ch1EnableCheck->setStyleSheet("font-weight: 600; color: #00aa55;");
layout->addWidget(m_ch1EnableCheck);
// 信号类型选择
QHBoxLayout *typeLayout = new QHBoxLayout;
typeLayout->addWidget(new QLabel("信号类型:"));
m_ch1TypeCombo = new QComboBox;
m_ch1TypeCombo->addItems({"直流电压 (V)", "直流电流 (mA)", "电阻 (Ω)", "频率 (Hz)"});
m_ch1TypeCombo->setStyleSheet(R"(
QComboBox {
background: white;
border: 1px solid #ddd;
border-radius: 6px;
padding: 8px 12px;
min-width: 150px;
}
)");
connect(m_ch1TypeCombo, QOverload<int>::of(&QComboBox::currentIndexChanged),
this, [this](int)
{ onChannel1OutputChanged(); });
typeLayout->addWidget(m_ch1TypeCombo);
typeLayout->addStretch();
layout->addLayout(typeLayout);
// 输出值设置
QHBoxLayout *valueLayout = new QHBoxLayout;
valueLayout->addWidget(new QLabel("输出值:"));
m_ch1ValueSpin = new QDoubleSpinBox;
m_ch1ValueSpin->setRange(-100, 100);
m_ch1ValueSpin->setValue(5.0);
m_ch1ValueSpin->setDecimals(4);
m_ch1ValueSpin->setSuffix(" V");
m_ch1ValueSpin->setStyleSheet(R"(
QDoubleSpinBox {
padding: 8px 12px;
border: 1px solid #ddd;
border-radius: 6px;
background: white;
font-size: 16px;
font-weight: bold;
min-width: 150px;
}
QDoubleSpinBox::up-button, QDoubleSpinBox::down-button {
subcontrol-origin: border;
width: 24px;
border-left: 1px solid #ddd;
background: #f5f5f5;
}
QDoubleSpinBox::up-button { subcontrol-position: top right; border-top-right-radius: 5px; }
QDoubleSpinBox::down-button { subcontrol-position: bottom right; border-bottom-right-radius: 5px; }
QDoubleSpinBox::up-button:hover, QDoubleSpinBox::down-button:hover { background: #e0e0e0; }
QDoubleSpinBox::up-arrow { image: url(:/icons/arrow_up.svg); width: 12px; height: 12px; }
QDoubleSpinBox::down-arrow { image: url(:/icons/arrow_down.svg); width: 12px; height: 12px; }
)");
connect(m_ch1ValueSpin, QOverload<double>::of(&QDoubleSpinBox::valueChanged),
this, [this](double)
{ onChannel1OutputChanged(); });
valueLayout->addWidget(m_ch1ValueSpin);
valueLayout->addStretch();
layout->addLayout(valueLayout);
// 实际测量值显示
QLabel *measureTitle = new QLabel("实际测量:");
measureTitle->setStyleSheet("color: #666;");
layout->addWidget(measureTitle);
m_ch1MeasureLabel = new QLabel("-- V");
m_ch1MeasureLabel->setStyleSheet(VALUE_DISPLAY_STYLE);
m_ch1MeasureLabel->setAlignment(Qt::AlignCenter);
layout->addWidget(m_ch1MeasureLabel);
layout->addStretch();
return group;
}
QWidget *DualChannelWidget::createChannel2Panel()
{
QGroupBox *group = new QGroupBox("通道2 - 输出");
group->setStyleSheet(GROUP_STYLE);
QVBoxLayout *layout = new QVBoxLayout(group);
layout->setContentsMargins(16, 24, 16, 16);
layout->setSpacing(16);
// 启用复选框
m_ch2EnableCheck = new QCheckBox("启用通道2");
m_ch2EnableCheck->setChecked(true);
m_ch2EnableCheck->setStyleSheet("font-weight: 600; color: #d32f2f;");
layout->addWidget(m_ch2EnableCheck);
// 信号类型选择
QHBoxLayout *typeLayout = new QHBoxLayout;
typeLayout->addWidget(new QLabel("信号类型:"));
m_ch2TypeCombo = new QComboBox;
m_ch2TypeCombo->addItems({"直流电压 (V)", "直流电流 (mA)", "电阻 (Ω)", "频率 (Hz)"});
m_ch2TypeCombo->setStyleSheet(R"(
QComboBox {
background: white;
border: 1px solid #ddd;
border-radius: 6px;
padding: 8px 12px;
min-width: 150px;
}
)");
connect(m_ch2TypeCombo, QOverload<int>::of(&QComboBox::currentIndexChanged),
this, [this](int)
{ onChannel2OutputChanged(); });
typeLayout->addWidget(m_ch2TypeCombo);
typeLayout->addStretch();
layout->addLayout(typeLayout);
// 输出值设置
QHBoxLayout *valueLayout = new QHBoxLayout;
valueLayout->addWidget(new QLabel("输出值:"));
m_ch2ValueSpin = new QDoubleSpinBox;
m_ch2ValueSpin->setRange(-100, 100);
m_ch2ValueSpin->setValue(10.0);
m_ch2ValueSpin->setDecimals(4);
m_ch2ValueSpin->setSuffix(" V");
m_ch2ValueSpin->setStyleSheet(R"(
QDoubleSpinBox {
padding: 8px 12px;
border: 1px solid #ddd;
border-radius: 6px;
background: white;
font-size: 16px;
font-weight: bold;
min-width: 150px;
}
QDoubleSpinBox::up-button, QDoubleSpinBox::down-button {
subcontrol-origin: border;
width: 24px;
border-left: 1px solid #ddd;
background: #f5f5f5;
}
QDoubleSpinBox::up-button { subcontrol-position: top right; border-top-right-radius: 5px; }
QDoubleSpinBox::down-button { subcontrol-position: bottom right; border-bottom-right-radius: 5px; }
QDoubleSpinBox::up-button:hover, QDoubleSpinBox::down-button:hover { background: #e0e0e0; }
QDoubleSpinBox::up-arrow { image: url(:/icons/arrow_up.svg); width: 12px; height: 12px; }
QDoubleSpinBox::down-arrow { image: url(:/icons/arrow_down.svg); width: 12px; height: 12px; }
)");
connect(m_ch2ValueSpin, QOverload<double>::of(&QDoubleSpinBox::valueChanged),
this, [this](double)
{ onChannel2OutputChanged(); });
valueLayout->addWidget(m_ch2ValueSpin);
valueLayout->addStretch();
layout->addLayout(valueLayout);
// 实际测量值显示
QLabel *measureTitle = new QLabel("实际测量:");
measureTitle->setStyleSheet("color: #666;");
layout->addWidget(measureTitle);
m_ch2MeasureLabel = new QLabel("-- V");
m_ch2MeasureLabel->setStyleSheet(R"(
QLabel {
background-color: #1a1a2e;
color: #ff6b6b;
border: 1px solid #333;
border-radius: 8px;
padding: 16px;
font-size: 28px;
font-weight: bold;
font-family: 'Courier New', monospace;
min-width: 200px;
}
)");
m_ch2MeasureLabel->setAlignment(Qt::AlignCenter);
layout->addWidget(m_ch2MeasureLabel);
layout->addStretch();
return group;
}
QWidget *DualChannelWidget::createMeasurementPanel()
{
QGroupBox *group = new QGroupBox("采集输入");
group->setStyleSheet(GROUP_STYLE);
QHBoxLayout *layout = new QHBoxLayout(group);
layout->setContentsMargins(16, 24, 16, 16);
layout->setSpacing(32);
// 通道1输入
QVBoxLayout *ch1Layout = new QVBoxLayout;
QLabel *ch1Title = new QLabel("通道1 采集");
ch1Title->setStyleSheet("font-weight: 600; color: #00aa55;");
ch1Layout->addWidget(ch1Title);
m_ch1InputLabel = new QLabel("-- V");
m_ch1InputLabel->setStyleSheet(R"(
QLabel {
background-color: #e8f5e9;
color: #2e7d32;
border: 2px solid #4caf50;
border-radius: 8px;
padding: 12px 24px;
font-size: 24px;
font-weight: bold;
font-family: monospace;
}
)");
m_ch1InputLabel->setAlignment(Qt::AlignCenter);
ch1Layout->addWidget(m_ch1InputLabel);
layout->addLayout(ch1Layout);
// 通道2输入
QVBoxLayout *ch2Layout = new QVBoxLayout;
QLabel *ch2Title = new QLabel("通道2 采集");
ch2Title->setStyleSheet("font-weight: 600; color: #d32f2f;");
ch2Layout->addWidget(ch2Title);
m_ch2InputLabel = new QLabel("-- V");
m_ch2InputLabel->setStyleSheet(R"(
QLabel {
background-color: #ffebee;
color: #c62828;
border: 2px solid #f44336;
border-radius: 8px;
padding: 12px 24px;
font-size: 24px;
font-weight: bold;
font-family: monospace;
}
)");
m_ch2InputLabel->setAlignment(Qt::AlignCenter);
ch2Layout->addWidget(m_ch2InputLabel);
layout->addLayout(ch2Layout);
layout->addStretch();
return group;
}
QWidget *DualChannelWidget::createControlPanel()
{
QGroupBox *group = new QGroupBox("控制");
group->setStyleSheet(GROUP_STYLE);
QHBoxLayout *layout = new QHBoxLayout(group);
layout->setContentsMargins(16, 24, 16, 16);
layout->setSpacing(16);
// 同步控制
m_syncCheck = new QCheckBox("同步输出(两通道同步变化)");
m_syncCheck->setStyleSheet("font-weight: 600;");
connect(m_syncCheck, &QCheckBox::toggled, this, &DualChannelWidget::onSyncToggled);
layout->addWidget(m_syncCheck);
layout->addStretch();
// 开始按钮
m_startBtn = new QPushButton("开始输出");
m_startBtn->setStyleSheet(BUTTON_STYLE);
m_startBtn->setCursor(Qt::PointingHandCursor);
connect(m_startBtn, &QPushButton::clicked, this, &DualChannelWidget::onStartOutput);
layout->addWidget(m_startBtn);
// 停止按钮
m_stopBtn = new QPushButton("停止输出");
m_stopBtn->setStyleSheet(STOP_BUTTON_STYLE);
m_stopBtn->setCursor(Qt::PointingHandCursor);
m_stopBtn->setEnabled(false);
connect(m_stopBtn, &QPushButton::clicked, this, &DualChannelWidget::onStopOutput);
layout->addWidget(m_stopBtn);
return group;
}
void DualChannelWidget::onChannel1OutputChanged()
{
QStringList suffixes = {" V", " mA", " Ω", " Hz"};
m_ch1ValueSpin->setSuffix(suffixes[m_ch1TypeCombo->currentIndex()]);
}
void DualChannelWidget::onChannel2OutputChanged()
{
QStringList suffixes = {" V", " mA", " Ω", " Hz"};
m_ch2ValueSpin->setSuffix(suffixes[m_ch2TypeCombo->currentIndex()]);
}
void DualChannelWidget::onSyncToggled(bool checked)
{
if (checked)
{
// 同步通道2到通道1的设置
m_ch2TypeCombo->setCurrentIndex(m_ch1TypeCombo->currentIndex());
m_ch2ValueSpin->setValue(m_ch1ValueSpin->value());
}
}
void DualChannelWidget::onStartOutput()
{
m_isOutputting = true;
m_startBtn->setEnabled(false);
m_stopBtn->setEnabled(true);
m_measureTimer->start(100);
}
void DualChannelWidget::onStopOutput()
{
m_isOutputting = false;
m_measureTimer->stop();
m_startBtn->setEnabled(true);
m_stopBtn->setEnabled(false);
m_ch1MeasureLabel->setText("-- V");
m_ch2MeasureLabel->setText("-- V");
m_ch1InputLabel->setText("-- V");
m_ch2InputLabel->setText("-- V");
}
void DualChannelWidget::updateMeasurement()
{
static std::random_device rd;
static std::mt19937 gen(rd());
static std::normal_distribution<> noise(0, 0.001);
QStringList units = {"V", "mA", "Ω", "Hz"};
if (m_ch1EnableCheck->isChecked())
{
double value = m_ch1ValueSpin->value() + noise(gen);
m_ch1MeasureLabel->setText(QString("%1 %2")
.arg(value, 0, 'f', 4)
.arg(units[m_ch1TypeCombo->currentIndex()]));
// 模拟采集输入
double inputValue = value * 0.98 + noise(gen) * 10;
m_ch1InputLabel->setText(QString("%1 %2")
.arg(inputValue, 0, 'f', 4)
.arg(units[m_ch1TypeCombo->currentIndex()]));
}
if (m_ch2EnableCheck->isChecked())
{
double value = m_ch2ValueSpin->value() + noise(gen);
m_ch2MeasureLabel->setText(QString("%1 %2")
.arg(value, 0, 'f', 4)
.arg(units[m_ch2TypeCombo->currentIndex()]));
// 模拟采集输入
double inputValue = value * 0.98 + noise(gen) * 10;
m_ch2InputLabel->setText(QString("%1 %2")
.arg(inputValue, 0, 'f', 4)
.arg(units[m_ch2TypeCombo->currentIndex()]));
}
}