diff --git a/Software/PC_Application/LibreVNA-GUI/SpectrumAnalyzer/tracewidgetsa.cpp b/Software/PC_Application/LibreVNA-GUI/SpectrumAnalyzer/tracewidgetsa.cpp index 545ee85..d9d46c9 100644 --- a/Software/PC_Application/LibreVNA-GUI/SpectrumAnalyzer/tracewidgetsa.cpp +++ b/Software/PC_Application/LibreVNA-GUI/SpectrumAnalyzer/tracewidgetsa.cpp @@ -8,7 +8,7 @@ #include TraceWidgetSA::TraceWidgetSA(TraceModel &model, QWidget *parent) - : TraceWidget(model, parent) + : TraceWidget(model, nullptr, nullptr, parent) { } @@ -20,32 +20,3 @@ void TraceWidgetSA::exportDialog() csv->show(); } } - -void TraceWidgetSA::importDialog() -{ - auto filename = QFileDialog::getOpenFileName(nullptr, "Open measurement file", "", "CSV files (*.csv)", nullptr, Preferences::QFileDialogOptions()); - if (!filename.isEmpty()) { - try { - std::vector traces; - QString prefix = QString(); - auto csv = CSV::fromFile(filename); - traces = Trace::createFromCSV(csv); - // contruct prefix from filename - prefix = filename; - // remove any directory names (keep only the filename itself) - int lastSlash = qMax(prefix.lastIndexOf('/'), prefix.lastIndexOf('\\')); - if(lastSlash != -1) { - prefix.remove(0, lastSlash + 1); - } - // remove file type - prefix.truncate(prefix.indexOf('.')); - prefix.append("_"); - auto i = new TraceImportDialog(model, traces, prefix); - if(AppWindow::showGUI()) { - i->show(); - } - } catch(const std::exception &e) { - InformationBox::ShowError("Failed to import file", QString("Attempt to import file ended with error: \"") + e.what()+"\""); - } - } -} diff --git a/Software/PC_Application/LibreVNA-GUI/SpectrumAnalyzer/tracewidgetsa.h b/Software/PC_Application/LibreVNA-GUI/SpectrumAnalyzer/tracewidgetsa.h index 5e24244..52c2201 100644 --- a/Software/PC_Application/LibreVNA-GUI/SpectrumAnalyzer/tracewidgetsa.h +++ b/Software/PC_Application/LibreVNA-GUI/SpectrumAnalyzer/tracewidgetsa.h @@ -9,7 +9,7 @@ public: TraceWidgetSA(TraceModel &model, QWidget *parent = nullptr); public slots: virtual void exportDialog() override; - virtual void importDialog() override; + virtual QStringList supportsImportFileFormats() override {return {"csv"};} protected: virtual QString defaultParameter() override {return "PORT1";} diff --git a/Software/PC_Application/LibreVNA-GUI/Traces/traceaxis.cpp b/Software/PC_Application/LibreVNA-GUI/Traces/traceaxis.cpp index aafdb50..55c457e 100644 --- a/Software/PC_Application/LibreVNA-GUI/Traces/traceaxis.cpp +++ b/Software/PC_Application/LibreVNA-GUI/Traces/traceaxis.cpp @@ -352,6 +352,7 @@ std::set YAxis::getSupported(XAxis::Type type, TraceModel::DataSour } else if(source == TraceModel::DataSource::SA) { switch(type) { case XAxis::Type::Frequency: + case XAxis::Type::TimeZeroSpan: ret.insert(YAxis::Type::Magnitude); ret.insert(YAxis::Type::MagnitudedBuV); break; diff --git a/Software/PC_Application/LibreVNA-GUI/Traces/traceplot.cpp b/Software/PC_Application/LibreVNA-GUI/Traces/traceplot.cpp index e5f567c..94537a6 100644 --- a/Software/PC_Application/LibreVNA-GUI/Traces/traceplot.cpp +++ b/Software/PC_Application/LibreVNA-GUI/Traces/traceplot.cpp @@ -626,7 +626,6 @@ void TracePlot::dragMoveEvent(QDragMoveEvent *event) // transform to relative coordinates from 0 to 1 auto x = (double) pos.x() / (width() - marginLeft - marginRight); auto y = (double) pos.y() / (height() - marginTop - marginBottom); - qDebug() << "x:" << x << "y:" << y; if(y < 0.5) { if(x < y) { dropSection = DropSection::ToTheLeft; diff --git a/Software/PC_Application/LibreVNA-GUI/Traces/tracewidget.cpp b/Software/PC_Application/LibreVNA-GUI/Traces/tracewidget.cpp index b524ded..729a068 100644 --- a/Software/PC_Application/LibreVNA-GUI/Traces/tracewidget.cpp +++ b/Software/PC_Application/LibreVNA-GUI/Traces/tracewidget.cpp @@ -3,6 +3,8 @@ #include "ui_tracewidget.h" #include "traceeditdialog.h" #include "traceimportdialog.h" +#include "ui_s2pImportOptions.h" +#include "CustomWidgets/informationbox.h" #include "tracetouchstoneexport.h" #include "trace.h" #include "unit.h" @@ -16,14 +18,17 @@ #include #include #include +#include - -TraceWidget::TraceWidget(TraceModel &model, QWidget *parent) : +TraceWidget::TraceWidget(TraceModel &model, Calibration *cal, Deembedding *deembed, QWidget *parent) : QWidget(parent), SCPINode("TRACe"), dragTrace(nullptr), ui(new Ui::TraceWidget), - model(model) + model(model), + dropFilename(""), + cal(cal), + deembed(deembed) { ui->setupUi(this); ui->view->setModel(&model); @@ -37,6 +42,7 @@ TraceWidget::TraceWidget(TraceModel &model, QWidget *parent) : ui->remove->setEnabled(current.isValid()); }); installEventFilter(this); + setAcceptDrops(true); createCount = 0; SetupSCPI(); } @@ -119,6 +125,41 @@ bool TraceWidget::eventFilter(QObject *, QEvent *event) return false; } +void TraceWidget::dragEnterEvent(QDragEnterEvent *event) +{ + if(event->mimeData()->hasFormat("text/plain")) { + // might be a file drop + auto data = QString(event->mimeData()->data("text/plain")); + if (data.startsWith("file://")) { + // extract file path/name and type + data = data.trimmed(); + data.remove(0, 7); + if(data.contains(".")) { + auto type = data.split(".").last(); + if (supportsImportFileFormats().contains(type)) { + dropFilename = data; + qDebug() << "prepared to drop file " << dropFilename; + event->accept(); + return; + } + } + } + } + event->ignore(); + dropFilename = ""; + } + +void TraceWidget::dropEvent(QDropEvent *event) +{ + Q_UNUSED(event); + if(dropFilename.size() > 0) { + if(importFile(dropFilename)) { + event->accept(); + } + } + dropFilename = ""; +} + void TraceWidget::on_edit_clicked() { if(ui->view->currentIndex().isValid()) { @@ -159,6 +200,111 @@ void TraceWidget::on_view_clicked(const QModelIndex &index) } } +void TraceWidget::importDialog() +{ + QString supported = "Supported files ("; + for(auto f : supportsImportFileFormats()) { + supported += "*."+f+" "; + } + supported.chop(1); + supported += ")"; + auto filename = QFileDialog::getOpenFileName(nullptr, "Open measurement file", "", supported, nullptr, Preferences::QFileDialogOptions()); + if (!filename.isEmpty()) { + importFile(filename); + } +} + +bool TraceWidget::importFile(QString filename) +{ + if(!filename.contains(".")) { + // no ending, not supported + return false; + } + auto format = filename.split(".").last(); + if(!supportsImportFileFormats().contains(format)) { + // unsupported format + return false; + } + // try to import the file + try { + std::vector traces; + int touchstonePorts = 0; + QString prefix = QString(); + if(filename.endsWith(".csv")) { + auto csv = CSV::fromFile(filename); + traces = Trace::createFromCSV(csv); + } else { + // must be a touchstone file + auto t = Touchstone::fromFile(filename.toStdString()); + traces = Trace::createFromTouchstone(t); + touchstonePorts = t.ports(); + } + // contruct prefix from filename + prefix = filename; + // remove any directory names (keep only the filename itself) + int lastSlash = qMax(prefix.lastIndexOf('/'), prefix.lastIndexOf('\\')); + if(lastSlash != -1) { + prefix.remove(0, lastSlash + 1); + } + // remove file type + prefix.truncate(prefix.indexOf('.')); + prefix.append("_"); + auto i = new TraceImportDialog(model, traces, prefix); + if(AppWindow::showGUI()) { + i->show(); + } + // potential candidate to process via calibration/de-embedding + connect(i, &TraceImportDialog::importFinsished, [=](const std::vector &traces) { + if(traces.size() == touchstonePorts*touchstonePorts) { + // all traces imported, can calculate calibration/de-embedding + bool calAvailable = cal && cal->getNumPoints() > 0; + bool deembedAvailable = deembed && deembed->getOptions().size() > 0; + if(calAvailable || deembedAvailable) { + // check if user wants to apply either one to the imported traces + auto dialog = new QDialog(); + auto ui = new Ui::s2pImportOptions; + ui->setupUi(dialog); + connect(dialog, &QDialog::finished, [=](){ + delete ui; + }); + ui->applyCal->setEnabled(calAvailable); + ui->deembed->setEnabled(deembedAvailable); + bool applyCal = false; + bool applyDeembed = false; + connect(ui->applyCal, &QCheckBox::toggled, [&](bool checked) { + applyCal = checked; + }); + connect(ui->deembed, &QCheckBox::toggled, [&](bool checked) { + applyDeembed = checked; + }); + if(AppWindow::showGUI()) { + dialog->exec(); + } + // assemble trace set + std::map set; + for(int i=1;i<=touchstonePorts;i++) { + for(int j=1;j<=touchstonePorts;j++) { + QString name = "S"+QString::number(i)+QString::number(j); + int index = (i-1)*touchstonePorts+(j-1); + set[name] = traces[index]; + } + } + if(applyCal) { + cal->correctTraces(set); + } + if(applyDeembed) { + deembed->Deembed(set); + } + } + } + }); + return true; + } catch(const std::exception& e) { + InformationBox::ShowError("Failed to import file", QString("Attempt to import file ended with error: \"") + e.what()+"\""); + return false; + } +} + void TraceWidget::SetupSCPI() { auto findTraceFromName = [=](QString name) -> Trace* { diff --git a/Software/PC_Application/LibreVNA-GUI/Traces/tracewidget.h b/Software/PC_Application/LibreVNA-GUI/Traces/tracewidget.h index 223b5cc..d4ffcfa 100644 --- a/Software/PC_Application/LibreVNA-GUI/Traces/tracewidget.h +++ b/Software/PC_Application/LibreVNA-GUI/Traces/tracewidget.h @@ -3,6 +3,8 @@ #include "tracemodel.h" #include "scpi.h" +#include "Calibration/calibration.h" +#include "VNA/Deembedding/deembedding.h" #include @@ -15,28 +17,40 @@ class TraceWidget : public QWidget, public SCPINode Q_OBJECT public: - explicit TraceWidget(TraceModel &model, QWidget *parent = nullptr); + explicit TraceWidget(TraceModel &model, Calibration *cal, Deembedding *deembed, QWidget *parent = nullptr); virtual ~TraceWidget(); +public: + virtual void exportDialog() = 0; + virtual void importDialog(); + protected slots: void on_add_clicked(); void on_remove_clicked(); void on_edit_clicked(); void on_view_doubleClicked(const QModelIndex &index); void on_view_clicked(const QModelIndex &index); - virtual void exportDialog() = 0; - virtual void importDialog() = 0; + virtual QStringList supportsImportFileFormats() = 0; + bool importFile(QString filename); protected: void SetupSCPI(); void contextMenuEvent(QContextMenuEvent *event) override; bool eventFilter(QObject *obj, QEvent *event) override; + void dragEnterEvent(QDragEnterEvent *event) override; + void dropEvent(QDropEvent *event) override; virtual QString defaultParameter() = 0; QPoint dragStartPosition; Trace *dragTrace; Ui::TraceWidget *ui; TraceModel &model; int createCount; + +private: + QString dropFilename; + // These can optionally be applied when importing an s2p file + Calibration *cal; + Deembedding *deembed; }; #endif // TRACEWIDGET_H diff --git a/Software/PC_Application/LibreVNA-GUI/VNA/tracewidgetvna.cpp b/Software/PC_Application/LibreVNA-GUI/VNA/tracewidgetvna.cpp index a7766d2..6327e5c 100644 --- a/Software/PC_Application/LibreVNA-GUI/VNA/tracewidgetvna.cpp +++ b/Software/PC_Application/LibreVNA-GUI/VNA/tracewidgetvna.cpp @@ -4,17 +4,13 @@ #include "Traces/tracetouchstoneexport.h" #include "Traces/tracecsvexport.h" #include "ui_tracewidget.h" -#include "ui_s2pImportOptions.h" -#include "CustomWidgets/informationbox.h" #include "appwindow.h" #include #include -TraceWidgetVNA::TraceWidgetVNA(TraceModel &model, Calibration &cal, Deembedding &deembed, QWidget *parent) - : TraceWidget(model, parent), - cal(cal), - deembed(deembed) +TraceWidgetVNA::TraceWidgetVNA(TraceModel &model, Calibration *cal, Deembedding *deembed, QWidget *parent) + : TraceWidget(model, cal, deembed, parent) { auto exportMenu = new QMenu(); auto exportTouchstoneAction = new QAction("Touchstone"); @@ -54,84 +50,3 @@ void TraceWidgetVNA::exportTouchstone() } } -void TraceWidgetVNA::importDialog() -{ - auto filename = QFileDialog::getOpenFileName(nullptr, "Open measurement file", "", "Touchstone files (*.s1p *.s2p *.s3p *.s4p);;CSV files (*.csv)", nullptr, Preferences::QFileDialogOptions()); - if (!filename.isEmpty()) { - try { - std::vector traces; - int touchstonePorts = 0; - QString prefix = QString(); - if(filename.endsWith(".csv")) { - auto csv = CSV::fromFile(filename); - traces = Trace::createFromCSV(csv); - } else { - // must be a touchstone file - auto t = Touchstone::fromFile(filename.toStdString()); - traces = Trace::createFromTouchstone(t); - touchstonePorts = t.ports(); - } - // contruct prefix from filename - prefix = filename; - // remove any directory names (keep only the filename itself) - int lastSlash = qMax(prefix.lastIndexOf('/'), prefix.lastIndexOf('\\')); - if(lastSlash != -1) { - prefix.remove(0, lastSlash + 1); - } - // remove file type - prefix.truncate(prefix.indexOf('.')); - prefix.append("_"); - auto i = new TraceImportDialog(model, traces, prefix); - if(AppWindow::showGUI()) { - i->show(); - } - // potential candidate to process via calibration/de-embedding - connect(i, &TraceImportDialog::importFinsished, [=](const std::vector &traces) { - if(traces.size() == touchstonePorts*touchstonePorts) { - // all traces imported, can calculate calibration/de-embedding - bool calAvailable = cal.getNumPoints() > 0; - bool deembedAvailable = deembed.getOptions().size() > 0; - if(calAvailable || deembedAvailable) { - // check if user wants to apply either one to the imported traces - auto dialog = new QDialog(); - auto ui = new Ui::s2pImportOptions; - ui->setupUi(dialog); - connect(dialog, &QDialog::finished, [=](){ - delete ui; - }); - ui->applyCal->setEnabled(calAvailable); - ui->deembed->setEnabled(deembedAvailable); - bool applyCal = false; - bool applyDeembed = false; - connect(ui->applyCal, &QCheckBox::toggled, [&](bool checked) { - applyCal = checked; - }); - connect(ui->deembed, &QCheckBox::toggled, [&](bool checked) { - applyDeembed = checked; - }); - if(AppWindow::showGUI()) { - dialog->exec(); - } - // assemble trace set - std::map set; - for(int i=1;i<=touchstonePorts;i++) { - for(int j=1;j<=touchstonePorts;j++) { - QString name = "S"+QString::number(i)+QString::number(j); - int index = (i-1)*touchstonePorts+(j-1); - set[name] = traces[index]; - } - } - if(applyCal) { - cal.correctTraces(set); - } - if(applyDeembed) { - deembed.Deembed(set); - } - } - } - }); - } catch(const std::exception& e) { - InformationBox::ShowError("Failed to import file", QString("Attempt to import file ended with error: \"") + e.what()+"\""); - } - } -} diff --git a/Software/PC_Application/LibreVNA-GUI/VNA/tracewidgetvna.h b/Software/PC_Application/LibreVNA-GUI/VNA/tracewidgetvna.h index 77b5039..32a5599 100644 --- a/Software/PC_Application/LibreVNA-GUI/VNA/tracewidgetvna.h +++ b/Software/PC_Application/LibreVNA-GUI/VNA/tracewidgetvna.h @@ -8,18 +8,15 @@ class TraceWidgetVNA : public TraceWidget { public: - TraceWidgetVNA(TraceModel &model, Calibration &cal, Deembedding &deembed, QWidget *parent = nullptr); + TraceWidgetVNA(TraceModel &model, Calibration *cal, Deembedding *deembed, QWidget *parent = nullptr); public slots: void exportCSV(); void exportTouchstone(); virtual void exportDialog() override {} - virtual void importDialog() override; + virtual QStringList supportsImportFileFormats() override {return {"csv", "s1p", "s2p", "s3p", "s4p"};} protected: virtual QString defaultParameter() override {return "S11";} - // These can optionally be applied when importing an s2p file - Calibration &cal; - Deembedding &deembed; }; #endif // TRACEWIDGETVNA_H diff --git a/Software/PC_Application/LibreVNA-GUI/VNA/vna.cpp b/Software/PC_Application/LibreVNA-GUI/VNA/vna.cpp index c7e64d3..c39d2ad 100644 --- a/Software/PC_Application/LibreVNA-GUI/VNA/vna.cpp +++ b/Software/PC_Application/LibreVNA-GUI/VNA/vna.cpp @@ -596,7 +596,7 @@ VNA::VNA(AppWindow *window, QString name) markerModel = new MarkerModel(traceModel, this); auto tracesDock = new QDockWidget("Traces"); - traceWidget = new TraceWidgetVNA(traceModel, cal, deembedding); + traceWidget = new TraceWidgetVNA(traceModel, &cal, &deembedding); tracesDock->setWidget(traceWidget); window->addDockWidget(Qt::LeftDockWidgetArea, tracesDock); docks.insert(tracesDock);