#include "traceeditdialog.h" #include "ui_traceeditdialog.h" #include "ui_newtracemathdialog.h" #include "Math/tdr.h" #include "appwindow.h" #include #include namespace Ui { class NewTraceMathDialog; } TraceEditDialog::TraceEditDialog(Trace &t, QWidget *parent) : QDialog(parent), ui(new Ui::TraceEditDialog), trace(t) { ui->setupUi(this); ui->vFactor->setPrecision(3); ui->name->setText(t.name()); ui->color->setColor(trace.color()); ui->vFactor->setValue(t.velocityFactor()); ui->impedance->setUnit("Ω"); ui->impedance->setPrecision(3); ui->impedance->setValue(t.getReferenceImpedance()); connect(ui->bLive, &QPushButton::clicked, [=](bool live) { if(live) { ui->stack->setCurrentIndex(0); ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true); } }); connect(ui->bFile, &QPushButton::clicked, [&](bool file) { if(file) { if(t.getFilename().endsWith(".csv")) { ui->stack->setCurrentIndex(2); ui->csvImport->setFile(t.getFilename()); ui->csvImport->selectTrace(t.getFileParameter()); } else { // attempt to parse as touchstone ui->stack->setCurrentIndex(1); ui->touchstoneImport->setFile(t.getFilename()); } } }); connect(ui->color, &ColorPickerButton::colorChanged, [=](const QColor& color){ trace.setColor(color); }); ui->GSource->setId(ui->bLive, 0); ui->GSource->setId(ui->bFile, 1); if(t.isCalibration()) { // prevent editing imported calibration traces (and csv files for now) ui->bLive->setEnabled(false); ui->bFile->setEnabled(false); ui->CLiveType->setEnabled(false); ui->CLiveParam->setEnabled(false); } auto updateTouchstoneFileStatus = [this]() { // remove all options from paramater combo box while(ui->CParameter->count() > 0) { ui->CParameter->removeItem(0); } if (ui->bFile->isChecked() && !ui->touchstoneImport->getStatus()) { ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); } else { ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true); auto touchstone = ui->touchstoneImport->getTouchstone(); for(unsigned int i=0;iCParameter->addItem(name); } } if(trace.getFileParameter() < touchstone.ports()*touchstone.ports()) { ui->CParameter->setCurrentIndex(trace.getFileParameter()); } else { ui->CParameter->setCurrentIndex(0); } } if(ui->touchstoneImport->getFilename().endsWith(".csv")) { // switch to csv import dialog ui->stack->setCurrentIndex(2); ui->csvImport->setFile(ui->touchstoneImport->getFilename()); } }; auto updateCSVFileStatus = [this]() { if (ui->bFile->isChecked() && !ui->csvImport->getStatus()) { ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); } else { ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true); auto touchstone = ui->touchstoneImport->getTouchstone(); } if(!(ui->touchstoneImport->getFilename().endsWith(".csv"))) { // switch to touchstone import dialog ui->stack->setCurrentIndex(1); ui->touchstoneImport->setFile(ui->csvImport->getFilename()); } }; switch(t.liveType()) { case Trace::LivedataType::Overwrite: ui->CLiveType->setCurrentIndex(0); break; case Trace::LivedataType::MaxHold: ui->CLiveType->setCurrentIndex(1); break; case Trace::LivedataType::MinHold: ui->CLiveType->setCurrentIndex(2); break; default: break; } VNAtrace = Trace::isVNAParameter(t.liveParameter()); if(VNAtrace) { ui->CLiveParam->addItem("S11"); ui->CLiveParam->addItem("S12"); ui->CLiveParam->addItem("S21"); ui->CLiveParam->addItem("S22"); } else { ui->CLiveParam->addItem("Port 1"); ui->CLiveParam->addItem("Port 2"); } switch(t.liveParameter()) { case Trace::LiveParameter::S11: ui->CLiveParam->setCurrentIndex(0); break; case Trace::LiveParameter::S12: ui->CLiveParam->setCurrentIndex(1); break; case Trace::LiveParameter::S21: ui->CLiveParam->setCurrentIndex(2); break; case Trace::LiveParameter::S22: ui->CLiveParam->setCurrentIndex(3); break; case Trace::LiveParameter::Port1: ui->CLiveParam->setCurrentIndex(0); break; case Trace::LiveParameter::Port2: ui->CLiveParam->setCurrentIndex(1); break; default: break; } connect(ui->touchstoneImport, &TouchstoneImport::statusChanged, updateTouchstoneFileStatus); connect(ui->touchstoneImport, &TouchstoneImport::filenameChanged, updateTouchstoneFileStatus); connect(ui->csvImport, &CSVImport::filenameChanged, updateCSVFileStatus); if(t.isFromFile()) { ui->bFile->click(); } // setup math part of the GUI auto model = new MathModel(t); ui->view->setModel(model); QHeaderView *headerView = ui->view->horizontalHeader(); headerView->setSectionResizeMode(MathModel::ColIndexDescription, QHeaderView::Stretch); headerView->setSectionResizeMode(MathModel::ColIndexStatus, QHeaderView::ResizeToContents); headerView->setSectionResizeMode(MathModel::ColIndexDomain, QHeaderView::ResizeToContents); connect(ui->view->selectionModel(), &QItemSelectionModel::currentRowChanged, [=](const QModelIndex ¤t, const QModelIndex &previous){ Q_UNUSED(previous) if(!current.isValid()) { ui->bDelete->setEnabled(false); ui->bMoveUp->setEnabled(false); ui->bMoveDown->setEnabled(false); } else { ui->bDelete->setEnabled(true); ui->bMoveUp->setEnabled(current.row() > 1); ui->bMoveDown->setEnabled(current.row() + 1 < model->rowCount()); } }); connect(ui->view, &QTableView::doubleClicked, [&](const QModelIndex &index) { if(index.isValid()) { auto math = t.getMathOperations().at(index.row()).math; math->edit(); } }); connect(ui->bAdd, &QPushButton::clicked, [=](){ auto d = new QDialog(); auto ui = new Ui::NewTraceMathDialog(); ui->setupUi(d); connect(d, &QDialog::rejected, [=](){ delete ui; }); for(int i = 0; i < (int) TraceMath::Type::Last;i++) { auto info = TraceMath::getInfo(static_cast(i)); ui->list->addItem(info.name); if(!info.explanationWidget) { info.explanationWidget = new QWidget(); } ui->stack->addWidget(info.explanationWidget); } // always show the widget for the selected function connect(ui->list, &QListWidget::currentRowChanged, ui->stack, &QStackedWidget::setCurrentIndex); connect(ui->list, &QListWidget::doubleClicked, ui->buttonBox, &QDialogButtonBox::accepted); connect(d, &QDialog::accepted, [=](){ auto type = static_cast(ui->list->currentRow()); delete ui; auto newMath = TraceMath::createMath(type); model->addOperations(newMath); if(newMath.size() == 1) { // any normal math operation added, edit now newMath[0]->edit(); } else { // composite operation added, check which one and edit the correct suboperation switch(type) { case TraceMath::Type::TimeDomainGating: // Automatically select bandpass/lowpass TDR, depending on selected span if(newMath[0]->getInput()->rData().size() > 0) { // Automatically select bandpass/lowpass TDR, depending on selected span auto tdr = (Math::TDR*) newMath[0]; auto fstart = tdr->getInput()->rData().front().x; auto fstop = tdr->getInput()->rData().back().x; if(fstart < fstop / 100.0) { tdr->setMode(Math::TDR::Mode::Lowpass); } else { // lowpass mode would result in very few points in the time domain, switch to bandpass mode tdr->setMode(Math::TDR::Mode::Bandpass); } } // TDR/DFT can be left at default, edit the actual gate newMath[1]->edit(); break; default: break; } } }); ui->list->setCurrentRow(0); ui->stack->setCurrentIndex(0); if(AppWindow::showGUI()) { d->show(); } }); connect(ui->bDelete, &QPushButton::clicked, [=](){ model->deleteRow(ui->view->currentIndex().row()); }); connect(ui->bMoveUp, &QPushButton::clicked, [&](){ auto index = ui->view->currentIndex(); t.swapMathOrder(index.row() - 1); ui->view->setCurrentIndex(index.sibling(index.row() - 1, 0)); }); connect(ui->bMoveDown, &QPushButton::clicked, [&](){ auto index = ui->view->currentIndex(); t.swapMathOrder(index.row()); ui->view->setCurrentIndex(index.sibling(index.row() + 1, 0)); }); } TraceEditDialog::~TraceEditDialog() { delete ui; } void TraceEditDialog::on_buttonBox_accepted() { trace.setName(ui->name->text()); trace.setVelocityFactor(ui->vFactor->value()); if(!trace.isCalibration()) { // only apply changes if it is not a calibration trace if (ui->bFile->isChecked()) { if(ui->stack->currentIndex() == 1) { // touchstone page active auto t = ui->touchstoneImport->getTouchstone(); trace.fillFromTouchstone(t, ui->CParameter->currentIndex()); } else { // CSV page active ui->csvImport->fillTrace(trace); } } else { Trace::LivedataType type = Trace::LivedataType::Overwrite; Trace::LiveParameter param = Trace::LiveParameter::S11; switch(ui->CLiveType->currentIndex()) { case 0: type = Trace::LivedataType::Overwrite; break; case 1: type = Trace::LivedataType::MaxHold; break; case 2: type = Trace::LivedataType::MinHold; break; } if(VNAtrace) { switch(ui->CLiveParam->currentIndex()) { case 0: param = Trace::LiveParameter::S11; break; case 1: param = Trace::LiveParameter::S12; break; case 2: param = Trace::LiveParameter::S21; break; case 3: param = Trace::LiveParameter::S22; break; } } else { switch(ui->CLiveParam->currentIndex()) { case 0: param = Trace::LiveParameter::Port1; break; case 1: param = Trace::LiveParameter::Port2; break; } } trace.fromLivedata(type, param); } } delete this; } MathModel::MathModel(Trace &t, QObject *parent) : QAbstractTableModel(parent), t(t) { } int MathModel::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent); return t.getMathOperations().size(); } int MathModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent); return ColIndexLast; } QVariant MathModel::data(const QModelIndex &index, int role) const { if(!index.isValid()) { return QVariant(); } auto math = t.getMathOperations().at(index.row()); switch(index.column()) { case ColIndexStatus: if(role == Qt::DecorationRole) { switch(math.math->getStatus()) { case TraceMath::Status::Ok: return QApplication::style()->standardIcon(QStyle::SP_DialogApplyButton); case TraceMath::Status::Warning: return QApplication::style()->standardIcon(QStyle::SP_MessageBoxWarning); case TraceMath::Status::Error: return QApplication::style()->standardIcon(QStyle::SP_MessageBoxCritical); } } else if(role == Qt::ToolTipRole) { if(math.math->getStatus() != TraceMath::Status::Ok) { return math.math->getStatusDescription(); } } break; case ColIndexDescription: if(role == Qt::DisplayRole) { return math.math->description(); } // else if(role == Qt::CheckStateRole){ // return math.enabled ? Qt::Checked : Qt::Unchecked; // } break; case ColIndexDomain: if(role == Qt::DisplayRole) { switch(math.math->getDataType()) { case TraceMath::DataType::Time: return "Time"; case TraceMath::DataType::Frequency: return "Frequency"; case TraceMath::DataType::Power: return "Power"; case TraceMath::DataType::TimeZeroSpan: return "Time (Zero Span)"; case TraceMath::DataType::Invalid: default: return "Invalid"; } } } return QVariant(); } QVariant MathModel::headerData(int section, Qt::Orientation orientation, int role) const { if(orientation == Qt::Horizontal && role == Qt::DisplayRole) { switch(section) { case ColIndexStatus: return "Status"; break; case ColIndexDescription: return "Description"; break; case ColIndexDomain: return "Output domain"; break; default: break; } } return QVariant(); } Qt::ItemFlags MathModel::flags(const QModelIndex &index) const { int flags = Qt::NoItemFlags; if(index.row() >= 1) { // the first entry is always the trace itself and not enabled flags |= Qt::ItemIsEnabled | Qt::ItemIsSelectable; } // switch(index.column()) { // case ColIndexDescription: flags |= Qt::ItemIsUserCheckable; break; // default: // break; // } return (Qt::ItemFlags) flags; } void MathModel::addOperation(TraceMath *math) { beginInsertRows(QModelIndex(), t.getMathOperations().size(), t.getMathOperations().size()); t.addMathOperation(math); endInsertRows(); } void MathModel::addOperations(std::vector maths) { beginInsertRows(QModelIndex(), t.getMathOperations().size(), t.getMathOperations().size() + maths.size() - 1); t.addMathOperations(maths); endInsertRows(); } void MathModel::deleteRow(unsigned int row) { beginRemoveRows(QModelIndex(), row, row); t.removeMathOperation(row); endRemoveRows(); }