partial reconstruction from qwt to manual plotting

This commit is contained in:
Jan Käberich 2020-11-22 00:41:42 +01:00
parent ef97d54913
commit 25196fbc30
17 changed files with 822 additions and 371 deletions

View File

@ -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 \

View File

@ -41,6 +41,7 @@
#include <QApplication>
#include <QActionGroup>
#include "CustomWidgets/informationbox.h"
#include <QDebug>
SpectrumAnalyzer::SpectrumAnalyzer(AppWindow *window)
: Mode(window, "Spectrum Analyzer"),

View File

@ -6,13 +6,16 @@
<rect>
<x>0</x>
<y>0</y>
<width>224</width>
<height>69</height>
<width>302</width>
<height>76</height>
</rect>
</property>
<property name="windowTitle">
<string>Smithchart Setup</string>
</property>
<property name="modal">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">

View File

@ -1,5 +1,9 @@
#include "traceplot.h"
#include "tracemarker.h"
#include "preferences.h"
#include <QPainter>
#include <QMimeData>
#include <QDebug>
std::set<TracePlot*> TracePlot::plots;
@ -16,6 +20,8 @@ TracePlot::TracePlot(TraceModel &model, QWidget *parent)
// get notified when the span changes
connect(&model, &TraceModel::SpanChanged, this, qOverload<double, double>(&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 *> TracePlot::getPlots()
{
return plots;

View File

@ -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<Trace*, bool> traces;
QMenu *contextmenu;
QTime lastUpdate;
bool markedForDeletion;
static std::set<TracePlot*> 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;

View File

@ -40,15 +40,13 @@ void TraceSmithChart::axisSetupDialog()
QPoint TraceSmithChart::plotToPixel(std::complex<double> 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<double> TraceSmithChart::pixelToPlot(const QPoint &pos)
{
return complex<double>((pos.x() - plotToPixelXOffset) / plotToPixelXScale, (pos.y() - plotToPixelYOffset) / plotToPixelYScale);
QPointF inv = transform.inverted().map(pos);
return complex<double>(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;i<Circles;i++) {
rectangle.adjust(2*smithCoordMax/Circles, smithCoordMax/Circles, 0, -smithCoordMax/Circles);
painter->drawArc(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<double, 5> 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;i<nPoints;i++) {
auto last = trace->sample(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()
{

View File

@ -4,6 +4,7 @@
#include "traceplot.h"
#include <QPen>
#include <QPainterPath>
#include <QTransform>
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<double> S);
std::complex<double> 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;
};

View File

@ -6,6 +6,9 @@
#include "traceimportdialog.h"
#include "traceexportdialog.h"
#include <QFileDialog>
#include <QDrag>
#include <QMimeData>
#include <QDebug>
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<QMouseEvent*>(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<QMouseEvent*>(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;
}

View File

@ -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;

View File

@ -46,6 +46,15 @@
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="dragEnabled">
<bool>true</bool>
</property>
<property name="dragDropMode">
<enum>QAbstractItemView::DragOnly</enum>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::SingleSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>

View File

@ -1,22 +1,25 @@
#include "tracexyplot.h"
#include <QGridLayout>
#include "qwtplotpiecewisecurve.h"
#include "qwt_series_data.h"
//#include "qwtplotpiecewisecurve.h"
//#include "qwt_series_data.h"
#include "trace.h"
#include <cmath>
#include <QFrame>
#include <qwt_plot_canvas.h>
#include <qwt_scale_div.h>
#include <qwt_plot_layout.h>
//#include <qwt_plot_canvas.h>
//#include <qwt_scale_div.h>
//#include <qwt_plot_layout.h>
#include "tracemarker.h"
#include <qwt_symbol.h>
#include <qwt_picker_machine.h>
//#include <qwt_symbol.h>
//#include <qwt_picker_machine.h>
#include "xyplotaxisdialog.h"
#include <preferences.h>
#include <QPainter>
#include "Util/util.h"
#include "unit.h"
using namespace std;
set<TraceXYPlot*> TraceXYPlot::allPlots;
//set<TraceXYPlot*> TraceXYPlot::allPlots;
const set<TraceXYPlot::YAxisType> TraceXYPlot::YAxisTypes = {TraceXYPlot::YAxisType::Disabled,
TraceXYPlot::YAxisType::Magnitude,
@ -54,65 +57,65 @@ static double TimeAxisTransformation(TraceXYPlot::YAxisType type, Trace *t, int
return numeric_limits<double>::quiet_NaN();
}
class QwtTraceSeries : public QwtSeriesData<QPointF> {
public:
QwtTraceSeries(Trace &t, TraceXYPlot::YAxisType Ytype, const TraceXYPlot *plot)
: QwtSeriesData<QPointF>(),
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<QPointF> {
//public:
// QwtTraceSeries(Trace &t, TraceXYPlot::YAxisType Ytype, const TraceXYPlot *plot)
// : QwtSeriesData<QPointF>(),
// 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<double>(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<double>(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<double>(x, XAxis.rangeMin, XAxis.rangeMax, plotAreaLeft, plotAreaLeft + plotAreaWidth));
p.setY(Util::Scale<double>(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;j<nPoints;j++) {
auto last = t->sample(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<double>& 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<double>::lowest();
double min = std::numeric_limits<double>::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<double>::max();
double trace_max = std::numeric_limits<double>::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<double>::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<double>(), QList<double>(), tickList);
plot->setAxisScaleDiv(QwtPlot::xBottom, scalediv);
// QwtScaleDiv scalediv(min, max, QList<double>(), QList<double>(), tickList);
// plot->setAxisScaleDiv(QwtPlot::xBottom, scalediv);
}
}
QwtSeriesData<QPointF> *TraceXYPlot::createQwtSeriesData(Trace &t, int axis)
double TraceXYPlot::transformFrequencyY(std::complex<double> 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<double>::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<QPointF> *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<unsigned int>::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<unsigned int>::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);
//}

View File

@ -3,31 +3,31 @@
#include "traceplot.h"
#include <set>
#include <qwt_plot.h>
#include <qwt_plot_curve.h>
#include <qwt_series_data.h>
#include <qwt_plot_marker.h>
#include <qwt_plot_grid.h>
#include <qwt_plot_picker.h>
//#include <qwt_plot.h>
//#include <qwt_plot_curve.h>
//#include <qwt_series_data.h>
//#include <qwt_plot_marker.h>
//#include <qwt_plot_grid.h>
//#include <qwt_plot_picker.h>
// 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<QPointF> *createQwtSeriesData(Trace &t, int axis);
double transformFrequencyY(std::complex<double> data, YAxisType type);
// QwtSeriesData<QPointF> *createQwtSeriesData(Trace &t, int axis);
std::set<Trace*> tracesAxis[2];
@ -100,6 +104,7 @@ private:
double rangeMin;
double rangeMax;
double rangeDiv;
std::vector<double> ticks;
};
class XAxis {
public:
@ -109,27 +114,28 @@ private:
double rangeMin;
double rangeMax;
double rangeDiv;
std::vector<double> ticks;
};
YAxis YAxis[2];
XAxis XAxis;
using CurveData = struct {
QwtPlotCurve *curve;
QwtSeriesData<QPointF> *data;
};
// using CurveData = struct {
// QwtPlotCurve *curve;
// QwtSeriesData<QPointF> *data;
// };
std::map<Trace*, CurveData> curves[2];
std::map<TraceMarker*, QwtPlotMarker*> markers;
QwtPlot *plot;
QwtPlotGrid *grid;
// std::map<Trace*, CurveData> curves[2];
// std::map<TraceMarker*, QwtPlotMarker*> 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<TraceXYPlot*> allPlots;
// static std::set<TraceXYPlot*> allPlots;
};
#endif // TRACEXYPLOT_H

View File

@ -2,6 +2,9 @@
<ui version="4.0">
<class>XYplotAxisDialog</class>
<widget class="QDialog" name="XYplotAxisDialog">
<property name="windowModality">
<enum>Qt::NonModal</enum>
</property>
<property name="geometry">
<rect>
<x>0</x>
@ -547,7 +550,7 @@
</connection>
</connections>
<buttongroups>
<buttongroup name="Y2group"/>
<buttongroup name="Y1group"/>
<buttongroup name="Y2group"/>
</buttongroups>
</ui>

View File

@ -0,0 +1,13 @@
#ifndef UTILH_H
#define UTILH_H
namespace Util {
template<typename T> 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

View File

@ -43,6 +43,7 @@
#include <QActionGroup>
#include <QErrorMessage>
#include "CustomWidgets/informationbox.h"
#include <QDebug>
VNA::VNA(AppWindow *window)
: Mode(window, "Vector Network Analyzer"),

View File

@ -46,6 +46,7 @@
#include "SpectrumAnalyzer/spectrumanalyzer.h"
#include "Calibration/sourcecaldialog.h"
#include "Calibration/receivercaldialog.h"
#include <QDebug>
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);

View File

@ -3,12 +3,11 @@
#include <QString>
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