From 25196fbc309c63abb3566777f7e83cf9987dcfaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20K=C3=A4berich?= Date: Sun, 22 Nov 2020 00:41:42 +0100 Subject: [PATCH] partial reconstruction from qwt to manual plotting --- Software/PC_Application/Application.pro | 1 + .../SpectrumAnalyzer/spectrumanalyzer.cpp | 1 + .../PC_Application/Traces/smithchartdialog.ui | 7 +- Software/PC_Application/Traces/traceplot.cpp | 73 +- Software/PC_Application/Traces/traceplot.h | 13 +- .../PC_Application/Traces/tracesmithchart.cpp | 101 ++- .../PC_Application/Traces/tracesmithchart.h | 11 +- .../PC_Application/Traces/tracewidget.cpp | 42 + Software/PC_Application/Traces/tracewidget.h | 6 +- Software/PC_Application/Traces/tracewidget.ui | 9 + .../PC_Application/Traces/tracexyplot.cpp | 808 ++++++++++++------ Software/PC_Application/Traces/tracexyplot.h | 90 +- .../PC_Application/Traces/xyplotaxisdialog.ui | 5 +- Software/PC_Application/Util/util.h | 13 + Software/PC_Application/VNA/vna.cpp | 1 + Software/PC_Application/appwindow.cpp | 3 +- Software/PC_Application/unit.h | 9 +- 17 files changed, 822 insertions(+), 371 deletions(-) create mode 100644 Software/PC_Application/Util/util.h diff --git a/Software/PC_Application/Application.pro b/Software/PC_Application/Application.pro index 4e6fb81..7b5c8e9 100644 --- a/Software/PC_Application/Application.pro +++ b/Software/PC_Application/Application.pro @@ -39,6 +39,7 @@ HEADERS += \ Traces/tracexyplot.h \ Traces/xyplotaxisdialog.h \ Util/qpointervariant.h \ + Util/util.h \ VNA/portextension.h \ VNA/vna.h \ appwindow.h \ diff --git a/Software/PC_Application/SpectrumAnalyzer/spectrumanalyzer.cpp b/Software/PC_Application/SpectrumAnalyzer/spectrumanalyzer.cpp index 166246a..b17e069 100644 --- a/Software/PC_Application/SpectrumAnalyzer/spectrumanalyzer.cpp +++ b/Software/PC_Application/SpectrumAnalyzer/spectrumanalyzer.cpp @@ -41,6 +41,7 @@ #include #include #include "CustomWidgets/informationbox.h" +#include SpectrumAnalyzer::SpectrumAnalyzer(AppWindow *window) : Mode(window, "Spectrum Analyzer"), diff --git a/Software/PC_Application/Traces/smithchartdialog.ui b/Software/PC_Application/Traces/smithchartdialog.ui index eaf61bf..d2e6d94 100644 --- a/Software/PC_Application/Traces/smithchartdialog.ui +++ b/Software/PC_Application/Traces/smithchartdialog.ui @@ -6,13 +6,16 @@ 0 0 - 224 - 69 + 302 + 76 Smithchart Setup + + true + diff --git a/Software/PC_Application/Traces/traceplot.cpp b/Software/PC_Application/Traces/traceplot.cpp index 07116b9..ea65352 100644 --- a/Software/PC_Application/Traces/traceplot.cpp +++ b/Software/PC_Application/Traces/traceplot.cpp @@ -1,5 +1,9 @@ #include "traceplot.h" #include "tracemarker.h" +#include "preferences.h" +#include +#include +#include std::set TracePlot::plots; @@ -16,6 +20,8 @@ TracePlot::TracePlot(TraceModel &model, QWidget *parent) // get notified when the span changes connect(&model, &TraceModel::SpanChanged, this, qOverload(&TracePlot::updateSpan)); plots.insert(this); + + setAcceptDrops(true); } TracePlot::~TracePlot() @@ -42,7 +48,7 @@ void TracePlot::enableTrace(Trace *t, bool enabled) disconnect(t, &Trace::markerRemoved, this, &TracePlot::markerRemoved); } updateContextMenu(); - triggerReplot(); + replot(); } } @@ -78,6 +84,71 @@ void TracePlot::contextMenuEvent(QContextMenuEvent *event) } } +void TracePlot::paintEvent(QPaintEvent *event) +{ + auto pref = Preferences::getInstance(); + QPainter p(this); +// p.setRenderHint(QPainter::Antialiasing); + // fill background + p.setBackground(QBrush(pref.General.graphColors.background)); + p.fillRect(0, 0, width(), height(), QBrush(pref.General.graphColors.background)); + + // show names of active traces + QFont font = p.font(); + font.setPixelSize(12); + p.setFont(font); + int x = 1; + for(auto t : traces) { + if(!t.second || !t.first->isVisible()) { + continue; + } + auto textArea = QRect(x, 0, width() - x, marginTop); + QRect usedArea; + p.setPen(t.first->color()); + p.drawText(textArea, 0, t.first->name() + " ", &usedArea); + x += usedArea.width(); + if(x >= width()) { + // used up all available space + break; + } + } + + p.setViewport(marginLeft, marginTop, width() - marginLeft - marginRight, height() - marginTop - marginBottom); + p.setWindow(0, 0, width() - marginLeft - marginRight, height() - marginTop - marginBottom); + + draw(p); +} + +void TracePlot::dragEnterEvent(QDragEnterEvent *event) +{ + if (event->mimeData()->hasFormat("trace/pointer")) { + auto data = event->mimeData()->data("trace/pointer"); + QDataStream stream(&data, QIODevice::ReadOnly); + quintptr dropPtr; + stream >> dropPtr; + auto trace = (Trace*) dropPtr; + if(supported(trace)) { + event->acceptProposedAction(); + } + } +} + +void TracePlot::dropEvent(QDropEvent *event) +{ + if (event->mimeData()->hasFormat("trace/pointer")) { + auto data = event->mimeData()->data("trace/pointer"); + QDataStream stream(&data, QIODevice::ReadOnly); + quintptr dropPtr; + stream >> dropPtr; + auto trace = (Trace*) dropPtr; + qDebug() << "Dropped" << trace << ", encoded as" << stream; + + if(supported(trace)) { + enableTrace(trace, true); + } + } +} + std::set TracePlot::getPlots() { return plots; diff --git a/Software/PC_Application/Traces/traceplot.h b/Software/PC_Application/Traces/traceplot.h index 2887b6b..7186d8f 100644 --- a/Software/PC_Application/Traces/traceplot.h +++ b/Software/PC_Application/Traces/traceplot.h @@ -29,16 +29,21 @@ protected: // need to be called in derived class constructor void initializeTraceInfo(); void contextMenuEvent(QContextMenuEvent *event) override; + void paintEvent(QPaintEvent *event) override; virtual void updateContextMenu(){}; virtual bool supported(Trace *t) = 0; - virtual void replot(){}; + virtual void replot(){update();}; + virtual void draw(QPainter& p) = 0; std::map traces; QMenu *contextmenu; QTime lastUpdate; bool markedForDeletion; - static std::set plots; + // handle trace drops + void dragEnterEvent(QDragEnterEvent *event) override; + void dropEvent(QDropEvent *event) override; + protected slots: void newTraceAvailable(Trace *t); void traceDeleted(Trace *t); @@ -46,6 +51,10 @@ protected slots: virtual void markerAdded(TraceMarker *m); virtual void markerRemoved(TraceMarker *m); protected: + static constexpr unsigned int marginTop = 20; + static constexpr unsigned int marginBottom = 0; + static constexpr unsigned int marginLeft = 0; + static constexpr unsigned int marginRight = 0; double sweep_fmin, sweep_fmax; TraceModel &model; diff --git a/Software/PC_Application/Traces/tracesmithchart.cpp b/Software/PC_Application/Traces/tracesmithchart.cpp index 0f8204c..ce8452e 100644 --- a/Software/PC_Application/Traces/tracesmithchart.cpp +++ b/Software/PC_Application/Traces/tracesmithchart.cpp @@ -40,15 +40,13 @@ void TraceSmithChart::axisSetupDialog() QPoint TraceSmithChart::plotToPixel(std::complex S) { - QPoint ret; - ret.setX(S.real() * plotToPixelXScale + plotToPixelXOffset); - ret.setY(S.imag() * plotToPixelYScale + plotToPixelYOffset); - return ret; + return transform.map(QPoint(S.real() * smithCoordMax, -S.imag() * smithCoordMax)); } std::complex TraceSmithChart::pixelToPlot(const QPoint &pos) { - return complex((pos.x() - plotToPixelXOffset) / plotToPixelXScale, (pos.y() - plotToPixelYOffset) / plotToPixelYScale); + QPointF inv = transform.inverted().map(pos); + return complex(inv.x()/smithCoordMax, -inv.y()/smithCoordMax); } void TraceSmithChart::mousePressEvent(QMouseEvent *event) @@ -111,43 +109,45 @@ void TraceSmithChart::mouseMoveEvent(QMouseEvent *event) } } -void TraceSmithChart::draw(QPainter * painter, double width_factor) { - painter->setPen(QPen(1.0 * width_factor)); - painter->setBrush(palette().windowText()); - painter->setRenderHint(QPainter::Antialiasing); - -// // Display parameter name -// QFont font = painter->font(); -// font.setPixelSize(48); -// font.setBold(true); -// painter->setFont(font); -// painter->drawText(-512, -512, title); - +void TraceSmithChart::draw(QPainter &p) { + p.setBrush(palette().windowText()); auto pref = Preferences::getInstance(); + // translate coordinate system so that the smith chart sits in the origin has a size of 1 + auto w = p.window(); + p.translate(w.width()/2, w.height()/2); + auto scale = qMin(w.height(), w.width()) / (2.0 * smithCoordMax); + p.scale(scale, scale); + + transform = p.transform(); + // Outer circle - painter->setPen(QPen(pref.General.graphColors.axis, 1.5 * width_factor)); + auto pen = QPen(pref.General.graphColors.axis); + pen.setCosmetic(true); + p.setPen(pen); QRectF rectangle(-smithCoordMax, -smithCoordMax, 2*smithCoordMax, 2*smithCoordMax); - painter->drawArc(rectangle, 0, 5760); + p.drawArc(rectangle, 0, 5760); constexpr int Circles = 6; - painter->setPen(QPen(pref.General.graphColors.divisions, 0.5 * width_factor, Qt::DashLine)); + pen = QPen(pref.General.graphColors.divisions, 0.5, Qt::DashLine); + pen.setCosmetic(true); + p.setPen(pen); for(int i=1;idrawArc(rectangle, 0, 5760); + rectangle.adjust(2.0*smithCoordMax/Circles, smithCoordMax/Circles, 0, -smithCoordMax/Circles); + p.drawArc(rectangle, 0, 5760); } - painter->drawLine(-smithCoordMax, 0, smithCoordMax, 0); + p.drawLine(-smithCoordMax, 0, smithCoordMax, 0); constexpr std::array impedanceLines = {10, 25, 50, 100, 250}; for(auto z : impedanceLines) { z /= ReferenceImpedance; - auto radius = smithCoordMax * 1.0/z; + auto radius = smithCoordMax/z; double span = M_PI - 2 * atan(radius/smithCoordMax); span *= 5760 / (2 * M_PI); - QRectF rectangle(smithCoordMax - radius, -2*radius, 2 * radius, 2 * radius); - painter->drawArc(rectangle, 4320 - span, span); + QRectF rectangle(smithCoordMax - radius, -2 * radius, 2 * radius, 2 * radius); + p.drawArc(rectangle, 4320 - span, span); rectangle = QRectF(smithCoordMax - radius, 0, 2 * radius, 2 * radius); - painter->drawArc(rectangle, 1440, span); + p.drawArc(rectangle, 1440, span); } for(auto t : traces) { @@ -160,7 +160,9 @@ void TraceSmithChart::draw(QPainter * painter, double width_factor) { // trace marked invisible continue; } - painter->setPen(QPen(trace->color(), 1.5 * width_factor)); + pen = QPen(trace->color(), 1.5); + pen.setCosmetic(true); + p.setPen(pen); int nPoints = trace->size(); for(int i=1;isample(i-1); @@ -175,7 +177,7 @@ void TraceSmithChart::draw(QPainter * painter, double width_factor) { last.S *= smithCoordMax; now.S *= smithCoordMax; // draw line - painter->drawLine(std::real(last.S), -std::imag(last.S), std::real(now.S), -std::imag(now.S)); + p.drawLine(std::real(last.S), -std::imag(last.S), std::real(now.S), -std::imag(now.S)); } if(trace->size() > 0) { // only draw markers if the trace has at least one point @@ -187,38 +189,33 @@ void TraceSmithChart::draw(QPainter * painter, double width_factor) { auto coords = m->getData(); coords *= smithCoordMax; auto symbol = m->getSymbol(); - symbol = symbol.scaled(symbol.width()*width_factor, symbol.height()*width_factor); - painter->drawPixmap(coords.real() - symbol.width()/2, -coords.imag() - symbol.height(), symbol); + symbol = symbol.scaled(symbol.width()/scale, symbol.height()/scale); + p.drawPixmap(coords.real() - symbol.width()/2, -coords.imag() - symbol.height(), symbol); } } } } -void TraceSmithChart::replot() -{ - update(); -} +//void TraceSmithChart::paintEvent(QPaintEvent * /* the event */) +//{ +// auto pref = Preferences::getInstance(); +// QPainter painter(this); +// painter.setRenderHint(QPainter::Antialiasing); +// painter.setBackground(QBrush(pref.General.graphColors.background)); +// painter.fillRect(0, 0, width(), height(), QBrush(pref.General.graphColors.background)); -void TraceSmithChart::paintEvent(QPaintEvent * /* the event */) -{ - auto pref = Preferences::getInstance(); - QPainter painter(this); - painter.setRenderHint(QPainter::Antialiasing); - painter.setBackground(QBrush(pref.General.graphColors.background)); - painter.fillRect(0, 0, width(), height(), QBrush(pref.General.graphColors.background)); +// double side = qMin(width(), height()) * screenUsage; - double side = qMin(width(), height()) * screenUsage; +// //painter.setViewport((width()-side)/2, (height()-side)/2, side, side); +// //painter.setWindow(-smithCoordMax, -smithCoordMax, 2*smithCoordMax, 2*smithCoordMax); - painter.setViewport((width()-side)/2, (height()-side)/2, side, side); - painter.setWindow(-smithCoordMax, -smithCoordMax, 2*smithCoordMax, 2*smithCoordMax); +// plotToPixelXOffset = width()/2; +// plotToPixelYOffset = height()/2; +// plotToPixelXScale = side/2; +// plotToPixelYScale = -side/2; - plotToPixelXOffset = width()/2; - plotToPixelYOffset = height()/2; - plotToPixelXScale = side/2; - plotToPixelYScale = -side/2; - - draw(&painter, 2*smithCoordMax/side); -} +// draw(painter, 2*smithCoordMax/side); +//} void TraceSmithChart::updateContextMenu() { diff --git a/Software/PC_Application/Traces/tracesmithchart.h b/Software/PC_Application/Traces/tracesmithchart.h index 3b56748..2e565e8 100644 --- a/Software/PC_Application/Traces/tracesmithchart.h +++ b/Software/PC_Application/Traces/tracesmithchart.h @@ -4,6 +4,7 @@ #include "traceplot.h" #include #include +#include class TraceSmithChart : public TracePlot { @@ -15,18 +16,17 @@ public slots: protected: static constexpr double ReferenceImpedance = 50.0; static constexpr double screenUsage = 0.9; - static constexpr int smithCoordMax = 4096; + static constexpr double smithCoordMax = 4096; QPoint plotToPixel(std::complex S); std::complex pixelToPlot(const QPoint &pos); void mousePressEvent(QMouseEvent *event) override; void mouseMoveEvent(QMouseEvent *event) override; - void paintEvent(QPaintEvent *event) override; + //void paintEvent(QPaintEvent *event) override; virtual void updateContextMenu() override; bool supported(Trace *t) override; - void draw(QPainter * painter, double width_factor); - void replot() override; + virtual void draw(QPainter& painter) override; QPen textPen; QPen chartLinesPen; QPen thinPen; @@ -39,8 +39,7 @@ protected: /// Path for the thick arcs QPainterPath thickArcsPath; - double plotToPixelXOffset, plotToPixelXScale; - double plotToPixelYOffset, plotToPixelYScale; + QTransform transform; TraceMarker *selectedMarker; }; diff --git a/Software/PC_Application/Traces/tracewidget.cpp b/Software/PC_Application/Traces/tracewidget.cpp index 3e63af6..210406e 100644 --- a/Software/PC_Application/Traces/tracewidget.cpp +++ b/Software/PC_Application/Traces/tracewidget.cpp @@ -6,6 +6,9 @@ #include "traceimportdialog.h" #include "traceexportdialog.h" #include +#include +#include +#include TraceWidget::TraceWidget(TraceModel &model, QWidget *parent, bool SA) : QWidget(parent), @@ -16,6 +19,7 @@ TraceWidget::TraceWidget(TraceModel &model, QWidget *parent, bool SA) : ui->setupUi(this); ui->view->setModel(&model); ui->view->setAutoScroll(false); + ui->view->viewport()->installEventFilter(this); installEventFilter(this); createCount = 0; } @@ -54,6 +58,44 @@ bool TraceWidget::eventFilter(QObject *, QEvent *event) model.removeTrace(ui->view->currentIndex().row()); return true; } + } else if(event->type() == QEvent::MouseButtonPress) { + auto mouseEvent = static_cast(event); + if (mouseEvent->button() == Qt::LeftButton) { + auto index = ui->view->indexAt(mouseEvent->pos()); + if(index.isValid()) { + dragStartPosition = mouseEvent->pos(); + dragTrace = model.trace(index.row()); + } else { + dragTrace = nullptr; + } + } + return false; + } else if(event->type() == QEvent::MouseMove) { + auto mouseEvent = static_cast(event); + if (!(mouseEvent->buttons() & Qt::LeftButton)) { + return false; + } + if (!dragTrace) { + return false; + } + if ((mouseEvent->pos() - dragStartPosition).manhattanLength() + < QApplication::startDragDistance()) { + return false; + } + + QDrag *drag = new QDrag(this); + QMimeData *mimeData = new QMimeData; + + QByteArray encodedPointer; + QDataStream stream(&encodedPointer, QIODevice::WriteOnly); + stream << quintptr(dragTrace); + qDebug() << "Dragging" << dragTrace << ", encoded as" << stream; + + mimeData->setData("trace/pointer", encodedPointer); + drag->setMimeData(mimeData); + + drag->exec(Qt::CopyAction); + return true; } return false; } diff --git a/Software/PC_Application/Traces/tracewidget.h b/Software/PC_Application/Traces/tracewidget.h index c34d1fa..6fd8d46 100644 --- a/Software/PC_Application/Traces/tracewidget.h +++ b/Software/PC_Application/Traces/tracewidget.h @@ -22,17 +22,15 @@ public slots: private slots: void on_remove_clicked(); void on_edit_clicked(); - void on_view_doubleClicked(const QModelIndex &index); - void on_view_clicked(const QModelIndex &index); - void on_bImport_clicked(); - void on_bExport_clicked(); private: bool eventFilter(QObject *obj, QEvent *event) override; + QPoint dragStartPosition; + Trace *dragTrace; Ui::TraceWidget *ui; TraceModel &model; int createCount; diff --git a/Software/PC_Application/Traces/tracewidget.ui b/Software/PC_Application/Traces/tracewidget.ui index 2232bd9..ec719a7 100644 --- a/Software/PC_Application/Traces/tracewidget.ui +++ b/Software/PC_Application/Traces/tracewidget.ui @@ -46,6 +46,15 @@ Qt::ScrollBarAlwaysOff + + true + + + QAbstractItemView::DragOnly + + + QAbstractItemView::SingleSelection + QAbstractItemView::SelectRows diff --git a/Software/PC_Application/Traces/tracexyplot.cpp b/Software/PC_Application/Traces/tracexyplot.cpp index e410dba..22a2b3b 100644 --- a/Software/PC_Application/Traces/tracexyplot.cpp +++ b/Software/PC_Application/Traces/tracexyplot.cpp @@ -1,22 +1,25 @@ #include "tracexyplot.h" #include -#include "qwtplotpiecewisecurve.h" -#include "qwt_series_data.h" +//#include "qwtplotpiecewisecurve.h" +//#include "qwt_series_data.h" #include "trace.h" #include #include -#include -#include -#include +//#include +//#include +//#include #include "tracemarker.h" -#include -#include +//#include +//#include #include "xyplotaxisdialog.h" #include +#include +#include "Util/util.h" +#include "unit.h" using namespace std; -set TraceXYPlot::allPlots; +//set TraceXYPlot::allPlots; const set TraceXYPlot::YAxisTypes = {TraceXYPlot::YAxisType::Disabled, TraceXYPlot::YAxisType::Magnitude, @@ -54,65 +57,65 @@ static double TimeAxisTransformation(TraceXYPlot::YAxisType type, Trace *t, int return numeric_limits::quiet_NaN(); } -class QwtTraceSeries : public QwtSeriesData { -public: - QwtTraceSeries(Trace &t, TraceXYPlot::YAxisType Ytype, const TraceXYPlot *plot) - : QwtSeriesData(), - Ytype(Ytype), - plot(plot), - t(t){} - size_t size() const override { - switch(Ytype) { - case TraceXYPlot::YAxisType::Magnitude: - case TraceXYPlot::YAxisType::Phase: - case TraceXYPlot::YAxisType::VSWR: - return t.size(); - case TraceXYPlot::YAxisType::Impulse: - case TraceXYPlot::YAxisType::Step: - case TraceXYPlot::YAxisType::Impedance: - return t.getTDR().size(); - default: - return 0; - } - } - QPointF sample(size_t i) const override { - switch(Ytype) { - case TraceXYPlot::YAxisType::Magnitude: - case TraceXYPlot::YAxisType::Phase: - case TraceXYPlot::YAxisType::VSWR: { - Trace::Data d = t.sample(i); - QPointF p; - p.setX(d.frequency); - p.setY(FrequencyAxisTransformation(Ytype, d.S)); - return p; - } - case TraceXYPlot::YAxisType::Impulse: - case TraceXYPlot::YAxisType::Step: - case TraceXYPlot::YAxisType::Impedance: { - auto sample = t.getTDR()[i]; - QPointF p; - if(plot->XAxis.type == TraceXYPlot::XAxisType::Time) { - p.setX(sample.time); - } else { - p.setX(sample.distance); - } - p.setY(TimeAxisTransformation(Ytype, &t, i)); - return p; - } - default: - return QPointF(); - } +//class QwtTraceSeries : public QwtSeriesData { +//public: +// QwtTraceSeries(Trace &t, TraceXYPlot::YAxisType Ytype, const TraceXYPlot *plot) +// : QwtSeriesData(), +// Ytype(Ytype), +// plot(plot), +// t(t){} +// size_t size() const override { +// switch(Ytype) { +// case TraceXYPlot::YAxisType::Magnitude: +// case TraceXYPlot::YAxisType::Phase: +// case TraceXYPlot::YAxisType::VSWR: +// return t.size(); +// case TraceXYPlot::YAxisType::Impulse: +// case TraceXYPlot::YAxisType::Step: +// case TraceXYPlot::YAxisType::Impedance: +// return t.getTDR().size(); +// default: +// return 0; +// } +// } +// QPointF sample(size_t i) const override { +// switch(Ytype) { +// case TraceXYPlot::YAxisType::Magnitude: +// case TraceXYPlot::YAxisType::Phase: +// case TraceXYPlot::YAxisType::VSWR: { +// Trace::Data d = t.sample(i); +// QPointF p; +// p.setX(d.frequency); +// p.setY(FrequencyAxisTransformation(Ytype, d.S)); +// return p; +// } +// case TraceXYPlot::YAxisType::Impulse: +// case TraceXYPlot::YAxisType::Step: +// case TraceXYPlot::YAxisType::Impedance: { +// auto sample = t.getTDR()[i]; +// QPointF p; +// if(plot->XAxis.type == TraceXYPlot::XAxisType::Time) { +// p.setX(sample.time); +// } else { +// p.setX(sample.distance); +// } +// p.setY(TimeAxisTransformation(Ytype, &t, i)); +// return p; +// } +// default: +// return QPointF(); +// } - } - QRectF boundingRect() const override { - return qwtBoundingRect(*this); - } +// } +// QRectF boundingRect() const override { +// return qwtBoundingRect(*this); +// } -private: - TraceXYPlot::YAxisType Ytype; - const TraceXYPlot *plot; - Trace &t; -}; +//private: +// TraceXYPlot::YAxisType Ytype; +// const TraceXYPlot *plot; +// Trace &t; +//}; TraceXYPlot::TraceXYPlot(TraceModel &model, QWidget *parent) : TracePlot(model, parent), @@ -137,35 +140,37 @@ TraceXYPlot::TraceXYPlot(TraceModel &model, QWidget *parent) XAxis.rangeMin = 0; XAxis.mode = XAxisMode::UseSpan; - plot = new QwtPlot(this); - auto canvas = new QwtPlotCanvas(plot); - canvas->setFrameStyle(QFrame::Plain); - plot->setCanvas(canvas); - plot->setAutoFillBackground(true); - grid = new QwtPlotGrid(); - grid->attach(plot); - setColorFromPreferences(); - auto selectPicker = new XYplotPicker(plot->xBottom, plot->yLeft, QwtPicker::NoRubberBand, QwtPicker::ActiveOnly, plot->canvas()); - selectPicker->setStateMachine(new QwtPickerClickPointMachine); +// plot = new QwtPlot(this); - drawPicker = new XYplotPicker(plot->xBottom, plot->yLeft, QwtPicker::NoRubberBand, QwtPicker::ActiveOnly, plot->canvas()); - drawPicker->setStateMachine(new QwtPickerDragPointMachine); - drawPicker->setTrackerPen(QPen(Qt::white)); +// auto canvas = new QwtPlotCanvas(plot); +// canvas->setFrameStyle(QFrame::Plain); +// plot->setCanvas(canvas); +// plot->setAutoFillBackground(true); +// grid = new QwtPlotGrid(); +// grid->attach(plot); +// setColorFromPreferences(); - // Marker selection - connect(selectPicker, SIGNAL(selected(QPointF)), this, SLOT(clicked(QPointF)));; - // Marker movement - connect(drawPicker, SIGNAL(moved(QPointF)), this, SLOT(moved(QPointF))); +// auto selectPicker = new XYplotPicker(plot->xBottom, plot->yLeft, QwtPicker::NoRubberBand, QwtPicker::ActiveOnly, plot->canvas()); +// selectPicker->setStateMachine(new QwtPickerClickPointMachine); - auto layout = new QGridLayout; - layout->addWidget(plot); - layout->setContentsMargins(0, 0, 0, 0); - setLayout(layout); - plot->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); +// drawPicker = new XYplotPicker(plot->xBottom, plot->yLeft, QwtPicker::NoRubberBand, QwtPicker::ActiveOnly, plot->canvas()); +// drawPicker->setStateMachine(new QwtPickerDragPointMachine); +// drawPicker->setTrackerPen(QPen(Qt::white)); + +// // Marker selection +// connect(selectPicker, SIGNAL(selected(QPointF)), this, SLOT(clicked(QPointF)));; +// // Marker movement +// connect(drawPicker, SIGNAL(moved(QPointF)), this, SLOT(moved(QPointF))); + +// auto layout = new QGridLayout; +// layout->addWidget(plot); +// layout->setContentsMargins(0, 0, 0, 0); +// setLayout(layout); +// plot->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); initializeTraceInfo(); - setAutoFillBackground(true); +// setAutoFillBackground(true); // Setup default axis setYAxis(0, YAxisType::Magnitude, false, false, -120, 20, 10); @@ -174,18 +179,18 @@ TraceXYPlot::TraceXYPlot(TraceModel &model, QWidget *parent) updateSpan(0, 6000000000); setXAxis(XAxisType::Frequency, XAxisMode::UseSpan, 0, 6000000000, 600000000); - allPlots.insert(this); +// allPlots.insert(this); } TraceXYPlot::~TraceXYPlot() { - for(int axis = 0;axis < 2;axis++) { - for(auto pd : curves[axis]) { - delete pd.second.curve; - } - } - delete drawPicker; - allPlots.erase(this); +// for(int axis = 0;axis < 2;axis++) { +// for(auto pd : curves[axis]) { +// delete pd.second.curve; +// } +// } +// delete drawPicker; +// allPlots.erase(this); } void TraceXYPlot::setYAxis(int axis, TraceXYPlot::YAxisType type, bool log, bool autorange, double min, double max, double div) @@ -213,16 +218,16 @@ void TraceXYPlot::setYAxis(int axis, TraceXYPlot::YAxisType type, bool log, bool for(auto t : tracesAxis[axis]) { // supported but needs an adjusted QwtSeriesData - auto td = curves[axis][t]; - td.data = createQwtSeriesData(*t, axis); +// auto td = curves[axis][t]; +// td.data = createQwtSeriesData(*t, axis); // call to setSamples deletes old QwtSeriesData - td.curve->setSamples(td.data); +// td.curve->setSamples(td.data); if(axis == 0) { // update marker data - auto marker = t->getMarkers(); - for(auto m : marker) { - markerDataChanged(m); - } +// auto marker = t->getMarkers(); +// for(auto m : marker) { +// markerDataChanged(m); +// } } if(isTDRtype(type)) { t->addTDRinterest(); @@ -234,14 +239,15 @@ void TraceXYPlot::setYAxis(int axis, TraceXYPlot::YAxisType type, bool log, bool YAxis[axis].rangeMin = min; YAxis[axis].rangeMax = max; YAxis[axis].rangeDiv = div; + updateAxisTicks(); // enable/disable y axis - auto qwtaxis = axis == 0 ? QwtPlot::yLeft : QwtPlot::yRight; - plot->enableAxis(qwtaxis, type != YAxisType::Disabled); - if(autorange) { - plot->setAxisAutoScale(qwtaxis, true); - } else { - plot->setAxisScale(qwtaxis, min, max, div); - } +// auto qwtaxis = axis == 0 ? QwtPlot::yLeft : QwtPlot::yRight; +// plot->enableAxis(qwtaxis, type != YAxisType::Disabled); +// if(autorange) { +// plot->setAxisAutoScale(qwtaxis, true); +// } else { +// plot->setAxisScale(qwtaxis, min, max, div); +// } updateContextMenu(); replot(); } @@ -253,6 +259,7 @@ void TraceXYPlot::setXAxis(XAxisType type, XAxisMode mode, double min, double ma XAxis.rangeMin = min; XAxis.rangeMax = max; XAxis.rangeDiv = div; + updateAxisTicks(); } void TraceXYPlot::enableTrace(Trace *t, bool enabled) @@ -264,13 +271,19 @@ void TraceXYPlot::enableTrace(Trace *t, bool enabled) } } -void TraceXYPlot::updateGraphColors() +void TraceXYPlot::updateSpan(double min, double max) { - for(auto p : allPlots) { - p->setColorFromPreferences(); - } + TracePlot::updateSpan(min, max); + updateAxisTicks(); } +//void TraceXYPlot::updateGraphColors() +//{ +// for(auto p : allPlots) { +// p->setColorFromPreferences(); +// } +//} + bool TraceXYPlot::isTDRtype(TraceXYPlot::YAxisType type) { switch(type) { @@ -335,10 +348,276 @@ bool TraceXYPlot::supported(Trace *) return true; } -void TraceXYPlot::replot() +void TraceXYPlot::draw(QPainter &p) { - updateXAxis(); - plot->replot(); + p.setBrush(palette().windowText()); + auto pref = Preferences::getInstance(); + + constexpr int yAxisSpace = 50; + constexpr int yAxisDisabledSpace = 10; + constexpr int xAxisSpace = 30; + auto w = p.window(); + auto pen = QPen(pref.General.graphColors.axis, 0); + pen.setCosmetic(true); + p.setPen(pen); + int plotAreaLeft = YAxis[0].type == YAxisType::Disabled ? yAxisDisabledSpace : yAxisSpace; + int plotAreaWidth = w.width(); + int plotAreaBottom = w.height() - xAxisSpace; + if(YAxis[0].type != YAxisType::Disabled) { + plotAreaWidth -= yAxisSpace; + } else { + plotAreaWidth -= yAxisDisabledSpace; + } + if(YAxis[1].type != YAxisType::Disabled) { + plotAreaWidth -= yAxisSpace; + } else { + plotAreaWidth -= yAxisDisabledSpace; + } + p.drawRect(plotAreaLeft, 0, plotAreaWidth, w.height()-xAxisSpace); + + // draw axis types + QString labelX; + switch(XAxis.type) { + case XAxisType::Frequency: labelX = "Frequency"; break; + case XAxisType::Time: labelX = "Time"; break; + case XAxisType::Distance: labelX = "Distance"; break; + } + auto font = p.font(); + font.setPixelSize(AxisLabelSize); + p.setFont(font); + p.drawText(QRect(0, w.height()-AxisLabelSize*1.5, w.width(), AxisLabelSize*1.5), Qt::AlignHCenter, labelX); + // draw X ticks + // this only works for evenly distributed ticks: + auto max = qMax(abs(XAxis.ticks.front()), abs(XAxis.ticks.back())); + auto step = abs(XAxis.ticks[0] - XAxis.ticks[1]); + int significantDigits = floor(log10(max)) - floor(log10(step)) + 1; + bool displayFullFreq = significantDigits <= 5; + constexpr int displayLastDigits = 4; + if(!displayFullFreq) { + auto fullFreq = Unit::ToString(XAxis.ticks.front(), "", " kMG", significantDigits); + + auto front = fullFreq; + front.truncate(fullFreq.size() - displayLastDigits); + auto back = fullFreq; + back.remove(0, front.size()); + back.append(".."); + p.setPen(QPen(QColor("orange"))); + QRect bounding; + p.drawText(QRect(2, plotAreaBottom + AxisLabelSize + 5, w.width(), AxisLabelSize), 0, front, &bounding); + p.setPen(pref.General.graphColors.axis); + p.drawText(QRect(bounding.x() + bounding.width(), plotAreaBottom + AxisLabelSize + 5, w.width(), AxisLabelSize), 0, back); + } + + for(auto t : XAxis.ticks) { + auto xCoord = Util::Scale(t, XAxis.ticks.front(), XAxis.ticks.back(), plotAreaLeft, plotAreaLeft + plotAreaWidth); + auto tickValue = Unit::ToString(t, "", "fpnum kMG", significantDigits); + p.setPen(QPen(pref.General.graphColors.axis, 1)); + if(displayFullFreq) { + p.drawText(QRect(xCoord - 40, plotAreaBottom + 5, 80, AxisLabelSize), Qt::AlignHCenter, tickValue); + } else { + tickValue.remove(0, tickValue.size() - displayLastDigits); + QRect bounding; + p.drawText(QRect(xCoord - 40, plotAreaBottom + 5, 80, AxisLabelSize), Qt::AlignHCenter, tickValue, &bounding); + p.setPen(QPen(QColor("orange"))); + p.drawText(QRect(0, plotAreaBottom + 5, bounding.x() - 1, AxisLabelSize), Qt::AlignRight, ".."); + p.setPen(QPen(pref.General.graphColors.axis, 1)); + } + p.drawLine(xCoord, plotAreaBottom, xCoord, plotAreaBottom + 2); + if(xCoord != plotAreaLeft && xCoord != plotAreaLeft + plotAreaWidth) { + p.setPen(QPen(pref.General.graphColors.divisions, 0.5, Qt::DashLine)); + p.drawLine(xCoord, 0, xCoord, plotAreaBottom); + } + } + + for(int i=0;i<2;i++) { + if(YAxis[i].type == YAxisType::Disabled) { + continue; + } + QString labelY; + p.setPen(QPen(pref.General.graphColors.axis, 1)); + switch(YAxis[i].type) { + case YAxisType::Magnitude: labelY = "Magnitude"; break; + case YAxisType::Phase: labelY = "Phase"; break; + case YAxisType::VSWR: labelY = "VSWR"; break; + case YAxisType::Impulse: labelY = "Impulse Response"; break; + case YAxisType::Step: labelY = "Step Response"; break; + case YAxisType::Impedance: labelY = "Impedance"; break; + } + auto xStart = i == 0 ? 0 : w.width() - AxisLabelSize * 1.5; + p.save(); + p.translate(xStart, w.height()-xAxisSpace); + p.rotate(-90); + p.drawText(QRect(0, 0, w.height()-xAxisSpace, AxisLabelSize*1.5), Qt::AlignHCenter, labelY); + p.restore(); + // draw ticks + if(YAxis[0].type != YAxisType::Disabled) { + // this only works for evenly distributed ticks: + auto max = qMax(abs(YAxis[i].ticks.front()), abs(YAxis[i].ticks.back())); + auto step = abs(YAxis[i].ticks[0] - YAxis[i].ticks[1]); + int significantDigits = floor(log10(max)) - floor(log10(step)) + 1; + for(auto t : YAxis[i].ticks) { + auto yCoord = Util::Scale(t, YAxis[i].rangeMax, YAxis[i].rangeMin, 0, w.height() - xAxisSpace); + p.setPen(QPen(pref.General.graphColors.axis, 1)); + // draw tickmark on axis + auto tickStart = i == 0 ? plotAreaLeft : plotAreaLeft + plotAreaWidth; + auto tickLen = i == 0 ? -2 : 2; + p.drawLine(tickStart, yCoord, tickStart + tickLen, yCoord); + auto tickValue = Unit::ToString(t, "", "fpnum kMG", significantDigits); + if(i == 0) { + p.drawText(QRectF(0, yCoord - AxisLabelSize/2 - 2, tickStart + 2 * tickLen, AxisLabelSize), Qt::AlignRight, tickValue); + } else { + p.drawText(QRectF(tickStart + 2 * tickLen + 2, yCoord - AxisLabelSize/2 - 2, yAxisSpace, AxisLabelSize), Qt::AlignLeft, tickValue); + } + + // tick lines + if(yCoord == 0 || yCoord == w.height() - xAxisSpace) { + // skip tick lines right on the plot borders + continue; + } + if(i == 0) { + // only draw tick lines for primary axis + p.setPen(QPen(pref.General.graphColors.divisions, 0.5, Qt::DashLine)); + p.drawLine(plotAreaLeft, yCoord, plotAreaLeft + plotAreaWidth, yCoord); + } + } + } + + auto toPlotCoordinates = [=](double x, double y, const class YAxis& ax) -> QPointF { + QPointF p; + p.setX(Util::Scale(x, XAxis.rangeMin, XAxis.rangeMax, plotAreaLeft, plotAreaLeft + plotAreaWidth)); + p.setY(Util::Scale(y, ax.rangeMin, ax.rangeMax, plotAreaBottom, 0)); + return p; + }; + + // plot traces + p.setClipRect(QRect(plotAreaLeft + 1, 1, plotAreaWidth - 2, plotAreaBottom - 1)); + switch(XAxis.type) { + case XAxisType::Frequency: + for(auto t : tracesAxis[i]) { + if(!t->isVisible()) { + continue; + } + pen = QPen(t->color(), 1.5); + pen.setCosmetic(true); + if(i == 1) { + pen.setStyle(Qt::DotLine); + } else { + pen.setStyle(Qt::SolidLine); + } + p.setPen(pen); + int nPoints = t->size(); + for(int j=1;jsample(j-1); + auto now = t->sample(j); + + auto yLast = transformFrequencyY(last.S, YAxis[i].type); + auto yNow = transformFrequencyY(now.S, YAxis[i].type); + + if(isnan(yLast) || isnan(yNow) || isinf(yLast) || isinf(yNow)) { + continue; + } + + // scale to plot coordinates + auto p1 = toPlotCoordinates(last.frequency, yLast, YAxis[i]); + auto p2 = toPlotCoordinates(now.frequency, yNow, YAxis[i]); + // draw line + p.drawLine(p1, p2); + } + } + break; + case XAxisType::Time: + case XAxisType::Distance: + // TODO + break; + } + p.setClipping(false); + } +} + +void TraceXYPlot::updateAxisTicks() +{ + auto createEvenlySpacedTicks = [](vector& ticks, double start, double stop, double step) { + ticks.clear(); + for(double tick = start;tick <= stop;tick+= step) { + ticks.push_back(tick); + } + }; + + if(XAxis.mode == XAxisMode::Manual) { + createEvenlySpacedTicks(XAxis.ticks, XAxis.rangeMin, XAxis.rangeMax, XAxis.rangeDiv); + } else { + XAxis.ticks.clear(); + // automatic mode, figure out limits + double max = std::numeric_limits::lowest(); + double min = std::numeric_limits::max(); + if(XAxis.mode == XAxisMode::UseSpan) { + min = sweep_fmin; + max = sweep_fmax; + } else if(XAxis.mode == XAxisMode::FitTraces) { + for(auto t : traces) { + bool enabled = (tracesAxis[0].find(t.first) != tracesAxis[0].end() + || tracesAxis[1].find(t.first) != tracesAxis[1].end()); + auto trace = t.first; + if(enabled && trace->isVisible()) { + // this trace is currently displayed + double trace_min = std::numeric_limits::max(); + double trace_max = std::numeric_limits::lowest(); + switch(XAxis.type) { + case XAxisType::Frequency: + trace_min = trace->minFreq(); + trace_max = trace->maxFreq(); + break; + case XAxisType::Time: + trace_min = trace->getTDR().front().time; + trace_max = trace->getTDR().back().time; + break; + case XAxisType::Distance: + trace_min = trace->getTDR().front().distance; + trace_max = trace->getTDR().back().distance; + break; + } + if(trace_min < min) { + min = trace_min; + } + if(trace_max > max) { + max = trace_max; + } + } + } + } + if(min >= max) { + // still at initial values, no traces are active, leave axis unchanged + return; + } + XAxis.rangeMin = min; + XAxis.rangeMax = max; + constexpr int minDivisions = 8; + double max_div_step = (max - min) / minDivisions; + int zeros = floor(log10(max_div_step)); + double decimals_shift = pow(10, zeros); + max_div_step /= decimals_shift; + if(max_div_step >= 5) { + max_div_step = 5; + } else if(max_div_step >= 2) { + max_div_step = 2; + } else { + max_div_step = 1; + } + auto div_step = max_div_step * decimals_shift; + XAxis.rangeDiv = div_step; + // round min up to next multiple of div_step + auto start_div = ceil(min / div_step) * div_step; + for(double tick = start_div;tick <= max;tick += div_step) { + XAxis.ticks.push_back(tick); + } + } + + for(int i=0;i<2;i++) { + if(!YAxis[i].autorange) { + createEvenlySpacedTicks(YAxis[i].ticks, YAxis[i].rangeMin, YAxis[i].rangeMax, YAxis[i].rangeDiv); + } + } + triggerReplot(); } QString TraceXYPlot::AxisTypeToName(TraceXYPlot::YAxisType type) @@ -354,21 +633,24 @@ QString TraceXYPlot::AxisTypeToName(TraceXYPlot::YAxisType type) void TraceXYPlot::enableTraceAxis(Trace *t, int axis, bool enabled) { + if(axis == 0) { + traces[t] = enabled; + } bool alreadyEnabled = tracesAxis[axis].find(t) != tracesAxis[axis].end(); if(alreadyEnabled != enabled) { if(enabled) { tracesAxis[axis].insert(t); - CurveData cd; - cd.data = createQwtSeriesData(*t, axis); - cd.curve = new QwtPlotPiecewiseCurve(); - cd.curve->attach(plot); - cd.curve->setYAxis(axis == 0 ? QwtPlot::yLeft : QwtPlot::yRight); - cd.curve->setSamples(cd.data); - curves[axis][t] = cd; +// CurveData cd; +// cd.data = createQwtSeriesData(*t, axis); +// cd.curve = new QwtPlotPiecewiseCurve(); +// cd.curve->attach(plot); +// cd.curve->setYAxis(axis == 0 ? QwtPlot::yLeft : QwtPlot::yRight); +// cd.curve->setSamples(cd.data); +// curves[axis][t] = cd; // connect signals connect(t, &Trace::dataChanged, this, &TraceXYPlot::triggerReplot); - connect(t, &Trace::colorChanged, this, &TraceXYPlot::traceColorChanged); - connect(t, &Trace::visibilityChanged, this, &TraceXYPlot::traceColorChanged); +// connect(t, &Trace::colorChanged, this, &TraceXYPlot::traceColorChanged); +// connect(t, &Trace::visibilityChanged, this, &TraceXYPlot::traceColorChanged); connect(t, &Trace::visibilityChanged, this, &TraceXYPlot::triggerReplot); if(axis == 0) { connect(t, &Trace::markerAdded, this, &TraceXYPlot::markerAdded); @@ -381,25 +663,25 @@ void TraceXYPlot::enableTraceAxis(Trace *t, int axis, bool enabled) if(isTDRtype(YAxis[axis].type)) { t->addTDRinterest(); } - traceColorChanged(t); +// traceColorChanged(t); } else { if(isTDRtype(YAxis[axis].type)) { t->removeTDRinterest(); } tracesAxis[axis].erase(t); // clean up and delete - if(curves[axis].find(t) != curves[axis].end()) { - delete curves[axis][t].curve; - curves[axis].erase(t); - } +// if(curves[axis].find(t) != curves[axis].end()) { +// delete curves[axis][t].curve; +// curves[axis].erase(t); +// } int otherAxis = axis == 0 ? 1 : 0; - if(curves[otherAxis].find(t) == curves[otherAxis].end()) { - // this trace is not used anymore, disconnect from notifications - disconnect(t, &Trace::dataChanged, this, &TraceXYPlot::triggerReplot); - disconnect(t, &Trace::colorChanged, this, &TraceXYPlot::traceColorChanged); - disconnect(t, &Trace::visibilityChanged, this, &TraceXYPlot::traceColorChanged); - disconnect(t, &Trace::visibilityChanged, this, &TraceXYPlot::triggerReplot); - } +// if(curves[otherAxis].find(t) == curves[otherAxis].end()) { +// // this trace is not used anymore, disconnect from notifications +// disconnect(t, &Trace::dataChanged, this, &TraceXYPlot::triggerReplot); +// disconnect(t, &Trace::colorChanged, this, &TraceXYPlot::traceColorChanged); +// disconnect(t, &Trace::visibilityChanged, this, &TraceXYPlot::traceColorChanged); +// disconnect(t, &Trace::visibilityChanged, this, &TraceXYPlot::triggerReplot); +// } if(axis == 0) { disconnect(t, &Trace::markerAdded, this, &TraceXYPlot::markerAdded); disconnect(t, &Trace::markerRemoved, this, &TraceXYPlot::markerRemoved); @@ -434,7 +716,7 @@ bool TraceXYPlot::supported(Trace *t, TraceXYPlot::YAxisType type) void TraceXYPlot::updateXAxis() { if(XAxis.mode == XAxisMode::Manual) { - plot->setAxisScale(QwtPlot::xBottom, XAxis.rangeMin, XAxis.rangeMax, XAxis.rangeDiv); +// plot->setAxisScale(QwtPlot::xBottom, XAxis.rangeMin, XAxis.rangeMax, XAxis.rangeDiv); } else { // automatic mode, figure out limits double max = std::numeric_limits::lowest(); @@ -497,135 +779,151 @@ void TraceXYPlot::updateXAxis() for(double tick = start_div;tick <= max;tick += div_step) { tickList.append(tick); } - QwtScaleDiv scalediv(min, max, QList(), QList(), tickList); - plot->setAxisScaleDiv(QwtPlot::xBottom, scalediv); +// QwtScaleDiv scalediv(min, max, QList(), QList(), tickList); +// plot->setAxisScaleDiv(QwtPlot::xBottom, scalediv); } } -QwtSeriesData *TraceXYPlot::createQwtSeriesData(Trace &t, int axis) +double TraceXYPlot::transformFrequencyY(std::complex data, TraceXYPlot::YAxisType type) { - return new QwtTraceSeries(t, YAxis[axis].type, this); -} - -void TraceXYPlot::traceColorChanged(Trace *t) -{ - for(int axis = 0;axis < 2;axis++) { - if(curves[axis].find(t) != curves[axis].end()) { - // trace active, change the pen color - if(t->isVisible()) { - if(axis == 0) { - curves[axis][t].curve->setPen(t->color()); - } else { - curves[axis][t].curve->setPen(t->color(), 1.0, Qt::DashLine); - } - for(auto m : t->getMarkers()) { - if(markers.count(m)) { - markers[m]->attach(plot); - } - } - } else { - curves[axis][t].curve->setPen(t->color(), 0.0, Qt::NoPen); - for(auto m : t->getMarkers()) { - if(markers.count(m)) { - markers[m]->detach(); - } - } - } + switch(type) { + case YAxisType::Magnitude: + return 20*log10(abs(data)); + case YAxisType::Phase: + return arg(data) * 180.0 / M_PI; + case YAxisType::VSWR: + if(abs(data) < 1.0) { + return (1+abs(data)) / (1-abs(data)); } + break; } + return numeric_limits::quiet_NaN(); } -void TraceXYPlot::markerAdded(TraceMarker *m) -{ - if(markers.count(m)) { - return; - } - auto qwtMarker = new QwtPlotMarker; - markers[m] = qwtMarker; - markerSymbolChanged(m); - connect(m, &TraceMarker::symbolChanged, this, &TraceXYPlot::markerSymbolChanged); - connect(m, &TraceMarker::dataChanged, this, &TraceXYPlot::markerDataChanged); - markerDataChanged(m); - qwtMarker->attach(plot); - triggerReplot(); -} +//QwtSeriesData *TraceXYPlot::createQwtSeriesData(Trace &t, int axis) +//{ +// return new QwtTraceSeries(t, YAxis[axis].type, this); +//} -void TraceXYPlot::markerRemoved(TraceMarker *m) -{ - disconnect(m, &TraceMarker::symbolChanged, this, &TraceXYPlot::markerSymbolChanged); - disconnect(m, &TraceMarker::dataChanged, this, &TraceXYPlot::markerDataChanged); - if(markers.count(m)) { - markers[m]->detach(); - delete markers[m]; - markers.erase(m); - } - triggerReplot(); -} +//void TraceXYPlot::traceColorChanged(Trace *t) +//{ +// for(int axis = 0;axis < 2;axis++) { +// if(curves[axis].find(t) != curves[axis].end()) { +// // trace active, change the pen color +// if(t->isVisible()) { +// if(axis == 0) { +// curves[axis][t].curve->setPen(t->color()); +// } else { +// curves[axis][t].curve->setPen(t->color(), 1.0, Qt::DashLine); +// } +// for(auto m : t->getMarkers()) { +// if(markers.count(m)) { +// markers[m]->attach(plot); +// } +// } +// } else { +// curves[axis][t].curve->setPen(t->color(), 0.0, Qt::NoPen); +// for(auto m : t->getMarkers()) { +// if(markers.count(m)) { +// markers[m]->detach(); +// } +// } +// } +// } +// } +//} -void TraceXYPlot::markerDataChanged(TraceMarker *m) -{ - auto qwtMarker = markers[m]; - qwtMarker->setXValue(m->getFrequency()); - qwtMarker->setYValue(FrequencyAxisTransformation(YAxis[0].type, m->getData())); - triggerReplot(); -} +//void TraceXYPlot::markerAdded(TraceMarker *m) +//{ +// if(markers.count(m)) { +// return; +// } +// auto qwtMarker = new QwtPlotMarker; +// markers[m] = qwtMarker; +// markerSymbolChanged(m); +// connect(m, &TraceMarker::symbolChanged, this, &TraceXYPlot::markerSymbolChanged); +// connect(m, &TraceMarker::dataChanged, this, &TraceXYPlot::markerDataChanged); +// markerDataChanged(m); +// qwtMarker->attach(plot); +// triggerReplot(); +//} -void TraceXYPlot::markerSymbolChanged(TraceMarker *m) -{ - auto qwtMarker = markers[m]; - qwtMarker->setSymbol(nullptr); +//void TraceXYPlot::markerRemoved(TraceMarker *m) +//{ +// disconnect(m, &TraceMarker::symbolChanged, this, &TraceXYPlot::markerSymbolChanged); +// disconnect(m, &TraceMarker::dataChanged, this, &TraceXYPlot::markerDataChanged); +// if(markers.count(m)) { +// markers[m]->detach(); +// delete markers[m]; +// markers.erase(m); +// } +// triggerReplot(); +//} - QwtSymbol *sym=new QwtSymbol; - sym->setPixmap(m->getSymbol()); - sym->setPinPoint(QPointF(m->getSymbol().width()/2, m->getSymbol().height())); - qwtMarker->setSymbol(sym); - triggerReplot(); -} +//void TraceXYPlot::markerDataChanged(TraceMarker *m) +//{ +// auto qwtMarker = markers[m]; +// qwtMarker->setXValue(m->getFrequency()); +// qwtMarker->setYValue(FrequencyAxisTransformation(YAxis[0].type, m->getData())); +// triggerReplot(); +//} -void TraceXYPlot::clicked(const QPointF pos) -{ - auto clickPoint = drawPicker->plotToPixel(pos); - unsigned int closestDistance = numeric_limits::max(); - TraceMarker *closestMarker = nullptr; - for(auto m : markers) { - if(!m.first->isMovable()) { - continue; - } - auto markerPoint = drawPicker->plotToPixel(m.second->value()); - auto yDiff = abs(markerPoint.y() - clickPoint.y()); - auto xDiff = abs(markerPoint.x() - clickPoint.x()); - unsigned int distance = xDiff * xDiff + yDiff * yDiff; - if(distance < closestDistance) { - closestDistance = distance; - closestMarker = m.first; - } - } - if(closestDistance <= 400) { - selectedMarker = closestMarker; - selectedCurve = curves[0][selectedMarker->trace()].curve; - } else { - selectedMarker = nullptr; - selectedCurve = nullptr; - } -} +//void TraceXYPlot::markerSymbolChanged(TraceMarker *m) +//{ +// auto qwtMarker = markers[m]; +// qwtMarker->setSymbol(nullptr); -void TraceXYPlot::moved(const QPointF pos) -{ - if(!selectedMarker || !selectedCurve) { - return; - } - selectedMarker->setFrequency(pos.x()); -} +// QwtSymbol *sym=new QwtSymbol; +// sym->setPixmap(m->getSymbol()); +// sym->setPinPoint(QPointF(m->getSymbol().width()/2, m->getSymbol().height())); +// qwtMarker->setSymbol(sym); +// triggerReplot(); +//} -void TraceXYPlot::setColorFromPreferences() -{ - auto pref = Preferences::getInstance(); - plot->setCanvasBackground(pref.General.graphColors.background); - auto pal = plot->palette(); - pal.setColor(QPalette::Window, pref.General.graphColors.background); - pal.setColor(QPalette::WindowText, pref.General.graphColors.axis); - pal.setColor(QPalette::Text, pref.General.graphColors.axis); - plot->setPalette(pal); - grid->setPen(pref.General.graphColors.divisions); -} +//void TraceXYPlot::clicked(const QPointF pos) +//{ +// auto clickPoint = drawPicker->plotToPixel(pos); +// unsigned int closestDistance = numeric_limits::max(); +// TraceMarker *closestMarker = nullptr; +// for(auto m : markers) { +// if(!m.first->isMovable()) { +// continue; +// } +// auto markerPoint = drawPicker->plotToPixel(m.second->value()); +// auto yDiff = abs(markerPoint.y() - clickPoint.y()); +// auto xDiff = abs(markerPoint.x() - clickPoint.x()); +// unsigned int distance = xDiff * xDiff + yDiff * yDiff; +// if(distance < closestDistance) { +// closestDistance = distance; +// closestMarker = m.first; +// } +// } +// if(closestDistance <= 400) { +// selectedMarker = closestMarker; +// selectedCurve = curves[0][selectedMarker->trace()].curve; +// } else { +// selectedMarker = nullptr; +// selectedCurve = nullptr; +// } +//} + +//void TraceXYPlot::moved(const QPointF pos) +//{ +// if(!selectedMarker || !selectedCurve) { +// return; +// } +// selectedMarker->setFrequency(pos.x()); +//} + +//void TraceXYPlot::setColorFromPreferences() +//{ +// auto pref = Preferences::getInstance(); +// plot->setCanvasBackground(pref.General.graphColors.background); +// auto pal = plot->palette(); +// pal.setColor(QPalette::Window, pref.General.graphColors.background); +// pal.setColor(QPalette::WindowText, pref.General.graphColors.axis); +// pal.setColor(QPalette::Text, pref.General.graphColors.axis); +// plot->setPalette(pal); +// grid->setPen(pref.General.graphColors.divisions); +//} diff --git a/Software/PC_Application/Traces/tracexyplot.h b/Software/PC_Application/Traces/tracexyplot.h index 14fd142..8f1c645 100644 --- a/Software/PC_Application/Traces/tracexyplot.h +++ b/Software/PC_Application/Traces/tracexyplot.h @@ -3,31 +3,31 @@ #include "traceplot.h" #include -#include -#include -#include -#include -#include -#include +//#include +//#include +//#include +//#include +//#include +//#include -// Derived plotpicker, exposing transformation functions -class XYplotPicker : public QwtPlotPicker { - Q_OBJECT -public: - XYplotPicker(int xAxis, int yAxis, RubberBand rubberBand, DisplayMode trackerMode, QWidget *w) - : QwtPlotPicker(xAxis, yAxis, rubberBand, trackerMode, w) {}; - QPoint plotToPixel(const QPointF &pos) { - return transform(pos); - } - QPointF pixelToPlot(const QPoint &pos) { - return invTransform(pos); - } -}; +//// Derived plotpicker, exposing transformation functions +//class XYplotPicker : public QwtPlotPicker { +// Q_OBJECT +//public: +// XYplotPicker(int xAxis, int yAxis, RubberBand rubberBand, DisplayMode trackerMode, QWidget *w) +// : QwtPlotPicker(xAxis, yAxis, rubberBand, trackerMode, w) {}; +// QPoint plotToPixel(const QPointF &pos) { +// return transform(pos); +// } +// QPointF pixelToPlot(const QPoint &pos) { +// return invTransform(pos); +// } +//}; class TraceXYPlot : public TracePlot { friend class XYplotAxisDialog; - friend class QwtTraceSeries; +// friend class QwtTraceSeries; Q_OBJECT public: TraceXYPlot(TraceModel &model, QWidget *parent = nullptr); @@ -60,9 +60,10 @@ public: void setYAxis(int axis, YAxisType type, bool log, bool autorange, double min, double max, double div); void setXAxis(XAxisType type, XAxisMode mode, double min, double max, double div); void enableTrace(Trace *t, bool enabled) override; + void updateSpan(double min, double max) override; // Applies potentially changed colors to all XY-plots - static void updateGraphColors(); +// static void updateGraphColors(); bool isTDRtype(YAxisType type); public slots: @@ -71,24 +72,27 @@ public slots: protected: virtual void updateContextMenu() override; virtual bool supported(Trace *t) override; - void replot() override; + virtual void draw(QPainter &p) override; private slots: - void traceColorChanged(Trace *t); - void markerAdded(TraceMarker *m) override; - void markerRemoved(TraceMarker *m) override; - void markerDataChanged(TraceMarker *m); - void markerSymbolChanged(TraceMarker *m); + void updateAxisTicks(); +// void traceColorChanged(Trace *t); +// void markerAdded(TraceMarker *m) override; +// void markerRemoved(TraceMarker *m) override; +// void markerDataChanged(TraceMarker *m); +// void markerSymbolChanged(TraceMarker *m); - void clicked(const QPointF pos); - void moved(const QPointF pos); +// void clicked(const QPointF pos); +// void moved(const QPointF pos); private: - void setColorFromPreferences(); + static constexpr int AxisLabelSize = 10; +// void setColorFromPreferences(); QString AxisTypeToName(YAxisType type); void enableTraceAxis(Trace *t, int axis, bool enabled); bool supported(Trace *t, YAxisType type); void updateXAxis(); - QwtSeriesData *createQwtSeriesData(Trace &t, int axis); + double transformFrequencyY(std::complex data, YAxisType type); +// QwtSeriesData *createQwtSeriesData(Trace &t, int axis); std::set tracesAxis[2]; @@ -100,6 +104,7 @@ private: double rangeMin; double rangeMax; double rangeDiv; + std::vector ticks; }; class XAxis { public: @@ -109,27 +114,28 @@ private: double rangeMin; double rangeMax; double rangeDiv; + std::vector ticks; }; YAxis YAxis[2]; XAxis XAxis; - using CurveData = struct { - QwtPlotCurve *curve; - QwtSeriesData *data; - }; +// using CurveData = struct { +// QwtPlotCurve *curve; +// QwtSeriesData *data; +// }; - std::map curves[2]; - std::map markers; - QwtPlot *plot; - QwtPlotGrid *grid; +// std::map curves[2]; +// std::map markers; +// QwtPlot *plot; +// QwtPlotGrid *grid; TraceMarker *selectedMarker; - QwtPlotCurve *selectedCurve; +// QwtPlotCurve *selectedCurve; - XYplotPicker *drawPicker; +// XYplotPicker *drawPicker; // keep track of all created plots for changing colors - static std::set allPlots; +// static std::set allPlots; }; #endif // TRACEXYPLOT_H diff --git a/Software/PC_Application/Traces/xyplotaxisdialog.ui b/Software/PC_Application/Traces/xyplotaxisdialog.ui index faf48f0..be949d6 100644 --- a/Software/PC_Application/Traces/xyplotaxisdialog.ui +++ b/Software/PC_Application/Traces/xyplotaxisdialog.ui @@ -2,6 +2,9 @@ XYplotAxisDialog + + Qt::NonModal + 0 @@ -547,7 +550,7 @@ - + diff --git a/Software/PC_Application/Util/util.h b/Software/PC_Application/Util/util.h new file mode 100644 index 0000000..4170687 --- /dev/null +++ b/Software/PC_Application/Util/util.h @@ -0,0 +1,13 @@ +#ifndef UTILH_H +#define UTILH_H + +namespace Util { + template T Scale(T value, T from_low, T from_high, T to_low, T to_high) { + value -= from_low; + value *= (to_high - to_low) / (from_high - from_low); + value += to_low; + return value; + } +} + +#endif // UTILH_H diff --git a/Software/PC_Application/VNA/vna.cpp b/Software/PC_Application/VNA/vna.cpp index 64680b3..cfadbcc 100644 --- a/Software/PC_Application/VNA/vna.cpp +++ b/Software/PC_Application/VNA/vna.cpp @@ -43,6 +43,7 @@ #include #include #include "CustomWidgets/informationbox.h" +#include VNA::VNA(AppWindow *window) : Mode(window, "Vector Network Analyzer"), diff --git a/Software/PC_Application/appwindow.cpp b/Software/PC_Application/appwindow.cpp index ec39160..a5bc610 100644 --- a/Software/PC_Application/appwindow.cpp +++ b/Software/PC_Application/appwindow.cpp @@ -46,6 +46,7 @@ #include "SpectrumAnalyzer/spectrumanalyzer.h" #include "Calibration/sourcecaldialog.h" #include "Calibration/receivercaldialog.h" +#include using namespace std; @@ -107,7 +108,7 @@ AppWindow::AppWindow(QWidget *parent) connect(ui->actionPreferences, &QAction::triggered, [=](){ Preferences::getInstance().edit(); // settings might have changed, update necessary stuff - TraceXYPlot::updateGraphColors(); +// TraceXYPlot::updateGraphColors(); }); connect(ui->actionAbout, &QAction::triggered, [=](){ auto commit = QString(GITHASH); diff --git a/Software/PC_Application/unit.h b/Software/PC_Application/unit.h index c80d1fb..3bc4677 100644 --- a/Software/PC_Application/unit.h +++ b/Software/PC_Application/unit.h @@ -3,12 +3,11 @@ #include -class Unit +namespace Unit { -public: - static double FromString(QString string, QString unit = QString(), QString prefixes = " "); - static QString ToString(double value, QString unit = QString(), QString prefixes = " ", int precision = 6); - static double SIPrefixToFactor(char prefix); + double FromString(QString string, QString unit = QString(), QString prefixes = " "); + QString ToString(double value, QString unit = QString(), QString prefixes = " ", int precision = 6); + double SIPrefixToFactor(char prefix); }; #endif // UNIT_H