mirror of
https://github.com/jkriege2/JKQtPlotter.git
synced 2024-12-25 01:51:49 +08:00
improved speed example with more options
This commit is contained in:
parent
676b97a3ca
commit
17b93ab580
@ -1,5 +1,8 @@
|
|||||||
# Example (JKQTPlotter): Simple line-graph with live-data (speed test) {#JKQTPlotterSpeedTest}
|
# Example (JKQTPlotter): Simple line-graph with live-data (speed test) {#JKQTPlotterSpeedTest}
|
||||||
This project (see `./examples/speed/`) simply creates a JKQTPlotter widget (as a new window) and adds two line-graph (a sine and a cosine wave).
|
This project (see `./examples/speed/`) creates a JKQTPlotter widget (as a new window) withs two line-graph (a sine and a cosine wave with a random disturbance). It constantly changes the data (a bit) and redraws the plot. The redraw-rate is measured and displayed at the top of the window. Also the example offers several context-menu entries to alter the plot and thus experiment with different factors that affect the actuak plot speed.
|
||||||
|
|
||||||
|
Here follows a basic description of the eample's code. Note however that the actual code is more involved and this description is mostly to give you a first clue, what is going on.
|
||||||
|
|
||||||
Data is stored in two [`std::array<double, NDATA>`](https://en.cppreference.com/w/cpp/container/array) objects (`X`, `Y`, and `Y2`) and the data is added as external pointer to the datastore:
|
Data is stored in two [`std::array<double, NDATA>`](https://en.cppreference.com/w/cpp/container/array) objects (`X`, `Y`, and `Y2`) and the data is added as external pointer to the datastore:
|
||||||
```.cpp
|
```.cpp
|
||||||
// 3. make data available to JKQTPlotter by adding it to the internal datastore.
|
// 3. make data available to JKQTPlotter by adding it to the internal datastore.
|
||||||
@ -7,11 +10,11 @@ Data is stored in two [`std::array<double, NDATA>`](https://en.cppreference.com/
|
|||||||
// the array is added to the datastore. therefore the datastore does not manage
|
// the array is added to the datastore. therefore the datastore does not manage
|
||||||
// the memory, oly uses the data stored in it!
|
// the memory, oly uses the data stored in it!
|
||||||
JKQTPDatastore* ds=getDatastore();
|
JKQTPDatastore* ds=getDatastore();
|
||||||
size_t columnX=ds->addColumn(X.data(), X.size(), "x");
|
size_t columnX=ds->addColumn(X.data(), NDATA, "x");
|
||||||
size_t columnY=ds->addColumn(Y.data(), Y.size(), "y");
|
size_t columnY=ds->addColumn(Y.data(), NDATA, "y");
|
||||||
size_t columnY2=ds->addColumn(Y2.data(), Y2.size(), "y2");
|
size_t columnY2=ds->addColumn(Y2.data(), NDATA, "y2");
|
||||||
```
|
```
|
||||||
The datastore then uses the data from the `std::array` instances, but does not own their memory, i.e. also does not free it. This is useful, when data fro external sources should be used without copying.
|
The datastore then uses the data from the `std::array` instances, i.e. just references the memory managed by the `std::arrays`s.
|
||||||
|
|
||||||
For this example we also don't use axis autoscaling, but set the axes explicitly:
|
For this example we also don't use axis autoscaling, but set the axes explicitly:
|
||||||
```.cpp
|
```.cpp
|
||||||
@ -42,8 +45,8 @@ void SpeedTestPlot::plotNewData()
|
|||||||
// calculate and update FPS-rate in window title
|
// calculate and update FPS-rate in window title
|
||||||
auto tlastalst=t_lastplot;
|
auto tlastalst=t_lastplot;
|
||||||
t_lastplot=std::chrono::system_clock::now();
|
t_lastplot=std::chrono::system_clock::now();
|
||||||
double delta_secs=static_cast<double>(std::chrono::duration_cast<std::chrono::milliseconds>(t_lastplot-tlastalst).count())/1000.0;
|
const double delta_secs=static_cast<double>(std::chrono::duration_cast<std::chrono::milliseconds>(t_lastplot-tlastalst).count())/1000.0;
|
||||||
setWindowTitle(QString("Live Data Speed Test: %2 datapoint, %1 fps").arg(1/delta_secs).arg(NDATA));
|
setWindowTitle(QString("Live Data Speed Test: %2 datapoint, %1 fps").arg(1.0/delta_secs).arg(NDATA));
|
||||||
// enqueue call for next data value
|
// enqueue call for next data value
|
||||||
QTimer::singleShot(1, this, SLOT(plotNewData()));
|
QTimer::singleShot(1, this, SLOT(plotNewData()));
|
||||||
}
|
}
|
||||||
@ -53,7 +56,7 @@ The result looks like this:
|
|||||||
|
|
||||||
![speed1](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/speed.png)
|
![speed1](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/speed.png)
|
||||||
|
|
||||||
There are different facor affecting the replot speed:
|
There are different factors affecting the replot speed:
|
||||||
1. *Anti-Aliasing:* If `JKQTPlotter` uses Anti-Aliasing for plotting, the plots are much nicer, but also about a factor of 3-4 slower. This is due to the increased amount of calculations, necessary in the drawing sub-system of Qt.
|
1. *Anti-Aliasing:* If `JKQTPlotter` uses Anti-Aliasing for plotting, the plots are much nicer, but also about a factor of 3-4 slower. This is due to the increased amount of calculations, necessary in the drawing sub-system of Qt.
|
||||||
You can configrue anti-aliasing with these calls:
|
You can configrue anti-aliasing with these calls:
|
||||||
```.cpp
|
```.cpp
|
||||||
@ -62,8 +65,11 @@ There are different facor affecting the replot speed:
|
|||||||
plot.getPlotter()->setUseAntiAliasingForText(false);
|
plot.getPlotter()->setUseAntiAliasingForText(false);
|
||||||
```
|
```
|
||||||
2. *Number of Graphs:* The number of plots (and also ther type and complexity) is a major imapct factor in the plotting speed. You can switch off a plot with the context menu:<br>![contextmenu_graph_visibility](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/contextmenu_graph_visibility.png)
|
2. *Number of Graphs:* The number of plots (and also ther type and complexity) is a major imapct factor in the plotting speed. You can switch off a plot with the context menu:<br>![contextmenu_graph_visibility](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/contextmenu_graph_visibility.png)
|
||||||
3. *Axis Scales and Plot Appearance:* Replotting is done in two steps: First the plot with the axes, labels etc. is drawn. Then the graphs are draw on top. Therefore a replot is faster, if only the graphs change, because the background (plot) does not have to be replotted.
|
3. *Number of Datapoints:* Of course, the more data has to be plotted, the slower the algorithm.
|
||||||
|
4. *Graph Type:* the speed also depends on how many elements have to be drawn. So drawing lines and symbols is slower than drawing lines-only or symbols-only.
|
||||||
|
5. *Axis Scales and Plot Appearance:* Replotting is done in two steps: First the plot with the axes, labels etc. is drawn. Then the graphs are draw on top. Therefore a replot is faster, if only the graphs change, because the background (plot) does not have to be replotted.
|
||||||
|
|
||||||
|
You can change all these properties by additional context-menu actions in this test-program.
|
||||||
|
|
||||||
The next table summarizes some results for plotting speed under different conditions, obatined with the test program in this directory (conditions: Qt 5.11, 32-bit, MinGW, Release, Win7, Phenom II X4 765, 500 data points):
|
The next table summarizes some results for plotting speed under different conditions, obatined with the test program in this directory (conditions: Qt 5.11, 32-bit, MinGW, Release, Win7, Phenom II X4 765, 500 data points):
|
||||||
|
|
||||||
|
@ -7,9 +7,8 @@
|
|||||||
#include "speedtestplot.h"
|
#include "speedtestplot.h"
|
||||||
#include "jkqtplotter/graphs/jkqtpscatter.h"
|
#include "jkqtplotter/graphs/jkqtpscatter.h"
|
||||||
|
|
||||||
|
|
||||||
SpeedTestPlot::SpeedTestPlot():
|
SpeedTestPlot::SpeedTestPlot():
|
||||||
JKQTPlotter(), dx(1.0/double(NDATA)*4.0*JKQTPSTATISTICS_PI), x0(0)
|
JKQTPlotter(), NDATA(500), dx(1.0/500.0*4.0*JKQTPSTATISTICS_PI), x0(0)
|
||||||
|
|
||||||
{
|
{
|
||||||
// 1. optimize JKQTPlotter for speed (by switching off anti-aliasing)
|
// 1. optimize JKQTPlotter for speed (by switching off anti-aliasing)
|
||||||
@ -19,7 +18,7 @@ SpeedTestPlot::SpeedTestPlot():
|
|||||||
|
|
||||||
|
|
||||||
// 2. now we create data for a simple plot (a sine curve + random[-0.5,0.5])
|
// 2. now we create data for a simple plot (a sine curve + random[-0.5,0.5])
|
||||||
for (size_t i=0; i<NDATA; i++) {
|
for (size_t i=0; i<X.size(); i++) {
|
||||||
const double x=static_cast<double>(i)*dx;
|
const double x=static_cast<double>(i)*dx;
|
||||||
X[i]=x0+x;
|
X[i]=x0+x;
|
||||||
Y[i]=sin(x)+static_cast<double>(std::rand())/static_cast<double>(RAND_MAX + 1u)-0.5;
|
Y[i]=sin(x)+static_cast<double>(std::rand())/static_cast<double>(RAND_MAX + 1u)-0.5;
|
||||||
@ -31,19 +30,19 @@ SpeedTestPlot::SpeedTestPlot():
|
|||||||
// the array is added to the datastore. therefore the datastore does not manage
|
// the array is added to the datastore. therefore the datastore does not manage
|
||||||
// the memory, oly uses the data stored in it!
|
// the memory, oly uses the data stored in it!
|
||||||
JKQTPDatastore* ds=getDatastore();
|
JKQTPDatastore* ds=getDatastore();
|
||||||
size_t columnX=ds->addColumn(X.data(), X.size(), "x");
|
size_t columnX=ds->addColumn(X.data(), NDATA, "x");
|
||||||
size_t columnY=ds->addColumn(Y.data(), Y.size(), "y");
|
size_t columnY=ds->addColumn(Y.data(), NDATA, "y");
|
||||||
size_t columnY2=ds->addColumn(Y2.data(), Y2.size(), "y2");
|
size_t columnY2=ds->addColumn(Y2.data(), NDATA, "y2");
|
||||||
|
|
||||||
// 4. create two graphs in the plot, which plots the dataset X/Y:
|
// 4. create two graphs in the plot, which plots the dataset X/Y:
|
||||||
JKQTPXYLineGraph* graph=new JKQTPXYLineGraph(this);
|
graph=new JKQTPXYLineGraph(this);
|
||||||
graph->setXColumn(columnX);
|
graph->setXColumn(columnX);
|
||||||
graph->setYColumn(columnY);
|
graph->setYColumn(columnY);
|
||||||
graph->setTitle(QObject::tr("live sin() graph"));
|
graph->setTitle(QObject::tr("live sin() graph"));
|
||||||
graph->setLineWidth(1);
|
graph->setLineWidth(1);
|
||||||
addGraph(graph);
|
addGraph(graph);
|
||||||
|
|
||||||
JKQTPXYLineGraph* graph2=new JKQTPXYLineGraph(this);
|
graph2=new JKQTPXYLineGraph(this);
|
||||||
graph2->setXColumn(columnX);
|
graph2->setXColumn(columnX);
|
||||||
graph2->setYColumn(columnY2);
|
graph2->setYColumn(columnY2);
|
||||||
graph2->setTitle(QObject::tr("live cos() graph"));
|
graph2->setTitle(QObject::tr("live cos() graph"));
|
||||||
@ -54,7 +53,7 @@ SpeedTestPlot::SpeedTestPlot():
|
|||||||
setX(X[0], X[NDATA-1]);
|
setX(X[0], X[NDATA-1]);
|
||||||
setY(-2,2);
|
setY(-2,2);
|
||||||
|
|
||||||
actAntiAliase=new QAction("Anti-Aliase");
|
actAntiAliase=new QAction(QObject::tr("Anti-Aliase"));
|
||||||
actAntiAliase->setCheckable(true);
|
actAntiAliase->setCheckable(true);
|
||||||
actAntiAliase->setChecked(false);
|
actAntiAliase->setChecked(false);
|
||||||
connect(actAntiAliase, &QAction::triggered, std::bind([](SpeedTestPlot* p){
|
connect(actAntiAliase, &QAction::triggered, std::bind([](SpeedTestPlot* p){
|
||||||
@ -63,20 +62,53 @@ SpeedTestPlot::SpeedTestPlot():
|
|||||||
p->getPlotter()->setUseAntiAliasingForText(p->actAntiAliase->isChecked());
|
p->getPlotter()->setUseAntiAliasingForText(p->actAntiAliase->isChecked());
|
||||||
}, this));
|
}, this));
|
||||||
|
|
||||||
actTwoGraphs=new QAction("2 Graphs");
|
actTwoGraphs=new QAction(QObject::tr("2 Graphs"));
|
||||||
actTwoGraphs->setCheckable(true);
|
actTwoGraphs->setCheckable(true);
|
||||||
actTwoGraphs->setChecked(true);
|
actTwoGraphs->setChecked(true);
|
||||||
connect(actTwoGraphs, &QAction::triggered, std::bind([](SpeedTestPlot* p, JKQTPXYLineGraph* g){
|
connect(actTwoGraphs, &QAction::triggered, std::bind([](SpeedTestPlot* p, JKQTPXYLineGraph* g){
|
||||||
g->setVisible(p->actTwoGraphs->isChecked());
|
g->setVisible(p->actTwoGraphs->isChecked());
|
||||||
}, this, graph2));
|
}, this, graph2));
|
||||||
|
|
||||||
actFixedXAxis=new QAction("Fixed X-Axis");
|
actFixedXAxis=new QAction(QObject::tr("Fixed X-Axis"));
|
||||||
actFixedXAxis->setCheckable(true);
|
actFixedXAxis->setCheckable(true);
|
||||||
actFixedXAxis->setChecked(false);
|
actFixedXAxis->setChecked(false);
|
||||||
|
|
||||||
|
actLines=new QAction(QObject::tr("Show Graph Lines"));
|
||||||
|
actLines->setCheckable(true);
|
||||||
|
actLines->setChecked(true);
|
||||||
|
connect(actLines, &QAction::toggled, std::bind([](bool enabled, JKQTPXYLineGraph* g, JKQTPXYLineGraph* g2){
|
||||||
|
g->setDrawLine(enabled);
|
||||||
|
g2->setDrawLine(enabled);
|
||||||
|
}, std::placeholders::_1, graph, graph2));
|
||||||
|
|
||||||
|
actSymbols=new QAction(QObject::tr("Show Graph Symbols"));
|
||||||
|
actSymbols->setCheckable(true);
|
||||||
|
actSymbols->setChecked(true);
|
||||||
|
connect(actSymbols, &QAction::toggled, std::bind([](bool enabled, JKQTPXYLineGraph* g, JKQTPXYLineGraph* g2){
|
||||||
|
g->setSymbolType(enabled?JKQTPCross:JKQTPNoSymbol);
|
||||||
|
g2->setSymbolType(enabled?JKQTPCircle:JKQTPNoSymbol);
|
||||||
|
}, std::placeholders::_1, graph, graph2));
|
||||||
|
|
||||||
|
menuSizes=new QMenu(QObject::tr("number of datapoints"), this);
|
||||||
|
QActionGroup* actGroup=new QActionGroup(menuSizes);
|
||||||
|
for (size_t size: {50,100, 500, 1000, 2000, 10000}) {
|
||||||
|
QAction* act=actGroup->addAction(QString::number(size));
|
||||||
|
act->setCheckable(true);
|
||||||
|
act->setChecked(size==500);
|
||||||
|
connect(act, &QAction::toggled, std::bind([](bool enabled,SpeedTestPlot* p, size_t size){
|
||||||
|
if (enabled) {
|
||||||
|
p->updateDataSize(size);
|
||||||
|
}
|
||||||
|
}, std::placeholders::_1, this, size));
|
||||||
|
menuSizes->addAction(act);
|
||||||
|
}
|
||||||
|
|
||||||
addAction(actAntiAliase);
|
addAction(actAntiAliase);
|
||||||
addAction(actTwoGraphs);
|
|
||||||
addAction(actFixedXAxis);
|
addAction(actFixedXAxis);
|
||||||
|
addAction(menuSizes->menuAction());
|
||||||
|
addAction(actTwoGraphs);
|
||||||
|
addAction(actLines);
|
||||||
|
addAction(actSymbols);
|
||||||
|
|
||||||
// show plotter and make it a decent size
|
// show plotter and make it a decent size
|
||||||
show();
|
show();
|
||||||
@ -120,10 +152,44 @@ void SpeedTestPlot::plotNewData()
|
|||||||
redrawPlot();
|
redrawPlot();
|
||||||
|
|
||||||
// calculate and update FPS-rate in window title
|
// calculate and update FPS-rate in window title
|
||||||
auto tlastalst=t_lastplot;
|
const auto tlastalst=t_lastplot;
|
||||||
t_lastplot=std::chrono::system_clock::now();
|
t_lastplot=std::chrono::system_clock::now();
|
||||||
double delta_secs=static_cast<double>(std::chrono::duration_cast<std::chrono::milliseconds>(t_lastplot-tlastalst).count())/1000.0;
|
const double delta_secs=static_cast<double>(std::chrono::duration_cast<std::chrono::milliseconds>(t_lastplot-tlastalst).count())/1000.0;
|
||||||
setWindowTitle(QString("Live Data Speed Test: %2 datapoint, %1 fps").arg(1/delta_secs).arg(NDATA));
|
setWindowTitle(QString("Live Data Speed Test: %2 datapoint, %1 fps").arg(1.0/delta_secs,0,'f',2).arg(NDATA));
|
||||||
// enqueue call for next data value
|
// enqueue call for next data value
|
||||||
QTimer::singleShot(1, this, SLOT(plotNewData()));
|
QTimer::singleShot(1, this, SLOT(plotNewData()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SpeedTestPlot::updateDataSize(size_t newSize)
|
||||||
|
{
|
||||||
|
NDATA=newSize;
|
||||||
|
dx=1.0/double(NDATA)*4.0*JKQTPSTATISTICS_PI;
|
||||||
|
// 2. now we create data for a simple plot (a sine curve + random[-0.5,0.5])
|
||||||
|
for (size_t i=0; i<X.size(); i++) {
|
||||||
|
const double x=static_cast<double>(i)*dx;
|
||||||
|
X[i]=x0+x;
|
||||||
|
Y[i]=sin(x)+static_cast<double>(std::rand())/static_cast<double>(RAND_MAX + 1u)-0.5;
|
||||||
|
Y2[i]=cos(x)+static_cast<double>(std::rand())/static_cast<double>(RAND_MAX + 1u)-0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. make data available to JKQTPlotter by adding it to the internal datastore.
|
||||||
|
// Here the data from the std::array's is not copied, but only the pointer to
|
||||||
|
// the array is added to the datastore. therefore the datastore does not manage
|
||||||
|
// the memory, oly uses the data stored in it!
|
||||||
|
JKQTPDatastore* ds=getDatastore();
|
||||||
|
ds->clear();
|
||||||
|
size_t columnX=ds->addColumn(X.data(), NDATA, "x");
|
||||||
|
size_t columnY=ds->addColumn(Y.data(), NDATA, "y");
|
||||||
|
size_t columnY2=ds->addColumn(Y2.data(), NDATA, "y2");
|
||||||
|
|
||||||
|
// 4. create two graphs in the plot, which plots the dataset X/Y:
|
||||||
|
graph->setXColumn(columnX);
|
||||||
|
graph->setYColumn(columnY);
|
||||||
|
|
||||||
|
graph2->setXColumn(columnX);
|
||||||
|
graph2->setYColumn(columnY2);
|
||||||
|
|
||||||
|
// 6. scale the plot so the graph is contained
|
||||||
|
setX(X[0], X[NDATA]);
|
||||||
|
setY(-2,2);
|
||||||
|
}
|
||||||
|
@ -2,27 +2,35 @@
|
|||||||
#include <array>
|
#include <array>
|
||||||
#include <random>
|
#include <random>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
#include <QMenu>
|
||||||
|
#include <QActionGroup>
|
||||||
#include "jkqtplotter/jkqtplotter.h"
|
#include "jkqtplotter/jkqtplotter.h"
|
||||||
|
|
||||||
|
|
||||||
#define NDATA 500
|
class JKQTPXYLineGraph; // forward
|
||||||
|
|
||||||
class SpeedTestPlot: public JKQTPlotter {
|
class SpeedTestPlot: public JKQTPlotter {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
protected:
|
protected:
|
||||||
std::array<double, NDATA> X, Y, Y2;
|
std::array<double, 10000> X, Y, Y2;
|
||||||
const double dx;
|
double dx;
|
||||||
double x0;
|
double x0;
|
||||||
|
size_t NDATA;
|
||||||
std::chrono::system_clock::time_point t_lastplot;
|
std::chrono::system_clock::time_point t_lastplot;
|
||||||
QAction* actAntiAliase;
|
QAction* actAntiAliase;
|
||||||
QAction* actTwoGraphs;
|
QAction* actTwoGraphs;
|
||||||
QAction* actFixedXAxis;
|
QAction* actFixedXAxis;
|
||||||
|
QAction* actLines;
|
||||||
|
QAction* actSymbols;
|
||||||
|
QMenu* menuSizes;
|
||||||
|
JKQTPXYLineGraph* graph;
|
||||||
|
JKQTPXYLineGraph* graph2;
|
||||||
public:
|
public:
|
||||||
SpeedTestPlot();
|
SpeedTestPlot();
|
||||||
|
|
||||||
virtual ~SpeedTestPlot();
|
virtual ~SpeedTestPlot();
|
||||||
public slots:
|
protected slots:
|
||||||
void plotNewData();
|
void plotNewData();
|
||||||
|
void updateDataSize(size_t newSize);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -616,6 +616,7 @@ void JKQTPXYGraph::intSortData()
|
|||||||
|
|
||||||
|
|
||||||
if (parent==nullptr) return ;
|
if (parent==nullptr) return ;
|
||||||
|
if (sortData==JKQTPXYLineGraph::Unsorted) return ;
|
||||||
|
|
||||||
JKQTPDatastore* datastore=parent->getDatastore();
|
JKQTPDatastore* datastore=parent->getDatastore();
|
||||||
int imin=0;
|
int imin=0;
|
||||||
|
Loading…
Reference in New Issue
Block a user