mirror of
https://github.com/jankae/LibreVNA.git
synced 2026-01-04 07:40:11 +01:00
TraceWidget: support dropping files for importing measurements
This commit is contained in:
parent
0b8e1a7b50
commit
6ea8869f7f
|
|
@ -8,7 +8,7 @@
|
|||
#include <QFileDialog>
|
||||
|
||||
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<Trace*> 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()+"\"");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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";}
|
||||
|
|
|
|||
|
|
@ -352,6 +352,7 @@ std::set<YAxis::Type> 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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 <QDebug>
|
||||
#include <QMenu>
|
||||
#include <QTableView>
|
||||
#include <QDebug>
|
||||
|
||||
|
||||
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<Trace*> 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<Trace*> &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<QString, Trace*> 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* {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
#include "tracemodel.h"
|
||||
#include "scpi.h"
|
||||
#include "Calibration/calibration.h"
|
||||
#include "VNA/Deembedding/deembedding.h"
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 <QFileDialog>
|
||||
#include <QMenu>
|
||||
|
||||
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<Trace*> 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<Trace*> &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<QString, Trace*> 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()+"\"");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Reference in a new issue