mirror of
https://github.com/GoldenCheetah/GoldenCheetah.git
synced 2026-02-13 16:18:42 +00:00
Add QChart to Python Chart (3h of 5)
Use legend to select which series are plotted by clicking to show hide a series. Hover just shows a hover background and does not temporarily isolate the series, this may be something to consider for later. Applies to scatter and line charts.
This commit is contained in:
@@ -31,11 +31,16 @@ GenericLegendItem::GenericLegendItem(Context *context, QWidget *parent, QString
|
||||
{
|
||||
|
||||
value=0;
|
||||
enabled=true;
|
||||
hasvalue=false;
|
||||
|
||||
// set height and width, gets reset when configchanges
|
||||
configChanged(0);
|
||||
|
||||
// we want to track our own events - for hover and click
|
||||
installEventFilter(this);
|
||||
setMouseTracking(true);
|
||||
|
||||
// watch for changes...
|
||||
connect(context, SIGNAL(configChanged(qint32)), this, SLOT(configChanged(qint32)));
|
||||
|
||||
@@ -45,8 +50,8 @@ GenericLegendItem::GenericLegendItem(Context *context, QWidget *parent, QString
|
||||
void
|
||||
GenericLegendItem::configChanged(qint32)
|
||||
{
|
||||
static const double gl_margin = 3 * dpiXFactor;
|
||||
static const double gl_spacer = 3 * dpiXFactor;
|
||||
static const double gl_margin = 5 * dpiXFactor;
|
||||
static const double gl_spacer = 2 * dpiXFactor;
|
||||
static const double gl_block = 7 * dpiXFactor;
|
||||
static const double gl_linewidth = 1 * dpiXFactor;
|
||||
|
||||
@@ -62,7 +67,7 @@ GenericLegendItem::configChanged(qint32)
|
||||
+ gl_spacer + fm.boundingRect(valuelabel).width() + gl_margin;
|
||||
|
||||
// maximum height of widget = margin + textheight + spacer + line
|
||||
double height = gl_margin + fm.boundingRect(valuelabel).height() + gl_spacer + gl_linewidth;
|
||||
double height = (gl_margin*2) + fm.boundingRect(valuelabel).height() + gl_spacer + gl_linewidth;
|
||||
|
||||
// now set geometry of widget
|
||||
setFixedWidth(width);
|
||||
@@ -70,15 +75,39 @@ GenericLegendItem::configChanged(qint32)
|
||||
|
||||
// calculate all the rects used by the painter now since static
|
||||
blockrect = QRectF(gl_margin, gl_margin, gl_block, height-gl_margin);
|
||||
linerect = QRectF(gl_margin+gl_block, height-gl_linewidth, width-gl_margin, gl_linewidth);
|
||||
linerect = QRectF(gl_margin+gl_block, gl_spacer+height-gl_linewidth-(gl_margin*2), width-gl_block-(gl_margin*2), gl_linewidth);
|
||||
namerect = QRectF(gl_margin + gl_block + gl_spacer, gl_margin, fm.boundingRect(name).width(), fm.boundingRect(name).height());
|
||||
valuerect =QRectF(namerect.x() + namerect.width() + gl_spacer, gl_margin, fm.boundingRect(valuelabel).width(), fm.boundingRect(valuelabel).height());
|
||||
hoverrect = QRectF(gl_block, 0, width-gl_block,height);
|
||||
|
||||
|
||||
// redraw
|
||||
update();
|
||||
}
|
||||
|
||||
bool
|
||||
GenericLegendItem::eventFilter(QObject *obj, QEvent *e)
|
||||
{
|
||||
if (obj != this) return false;
|
||||
|
||||
switch (e->type()) {
|
||||
case QEvent::MouseButtonRelease: // for now just one event, but may do more later
|
||||
{
|
||||
if (underMouse()) {
|
||||
enabled=!enabled;
|
||||
if (!enabled) hasvalue=false;
|
||||
emit clicked(name, enabled);
|
||||
}
|
||||
}
|
||||
// fall through
|
||||
default:
|
||||
//fprintf(stderr, "event %d on %s\n", e->type(), name.toStdString().c_str()); fflush(stderr);
|
||||
update();
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
GenericLegendItem::paintEvent(QPaintEvent *)
|
||||
{
|
||||
@@ -90,8 +119,18 @@ GenericLegendItem::paintEvent(QPaintEvent *)
|
||||
painter.setPen(Qt::NoPen);
|
||||
painter.drawRect(0,0,geometry().width()-1, geometry().height()-1);
|
||||
|
||||
// block and line
|
||||
painter.setBrush(QBrush(color));
|
||||
// under mouse show
|
||||
if (underMouse()) {
|
||||
QColor mask=GCColor::invertColor(GColor(CPLOTBACKGROUND));
|
||||
mask.setAlphaF(0.1);
|
||||
painter.setBrush(mask);
|
||||
painter.setPen(Qt::NoPen);
|
||||
painter.drawRect(hoverrect);
|
||||
}
|
||||
|
||||
// block and line - gray means disabled
|
||||
if (enabled) painter.setBrush(QBrush(color));
|
||||
else painter.setBrush(QBrush(Qt::gray));
|
||||
painter.setPen(Qt::NoPen);
|
||||
//painter.drawRect(blockrect);
|
||||
painter.drawRect(linerect);
|
||||
@@ -105,7 +144,8 @@ GenericLegendItem::paintEvent(QPaintEvent *)
|
||||
string = Utils::removeDP(string);
|
||||
|
||||
// set pen to series color for now
|
||||
painter.setPen(GCColor::invertColor(GColor(CPLOTBACKGROUND))); // use invert - usually black or white
|
||||
if (enabled) painter.setPen(GCColor::invertColor(GColor(CPLOTBACKGROUND))); // use invert - usually black or white
|
||||
else painter.setPen(Qt::gray);
|
||||
painter.setFont(QFont());
|
||||
|
||||
// series
|
||||
@@ -136,6 +176,9 @@ GenericLegend::addSeries(QString name, QAbstractSeries *series)
|
||||
|
||||
// lets see ya!
|
||||
add->show();
|
||||
|
||||
// connect signals
|
||||
connect(add, SIGNAL(clicked(QString,bool)), this, SIGNAL(clicked(QString,bool)));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -153,6 +196,9 @@ GenericLegend::addX(QString name)
|
||||
|
||||
// remember the x axis
|
||||
xname = name;
|
||||
|
||||
// we don't connect -- there is no such series, its a meta legend item
|
||||
// NOPE: connect(add, SIGNAL(clicked(QString,bool)), this, SIGNAL(clicked(QString,bool)));
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -50,11 +50,17 @@ class GenericLegendItem : public QWidget {
|
||||
public:
|
||||
GenericLegendItem(Context *context, QWidget *parent, QString name, QColor color);
|
||||
|
||||
Q_SIGNALS:
|
||||
void clicked(QString name, bool enabled); // someone clicked on a legend and enabled/disabled it
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject *, QEvent *e);
|
||||
|
||||
public slots:
|
||||
|
||||
void paintEvent(QPaintEvent *event);
|
||||
void setValue(double p) { hasvalue=true; value=p; update(); } // set value to display
|
||||
void noValue() { hasvalue=false; update(); } // no value to display
|
||||
void setValue(double p) { if (enabled) { hasvalue=true; value=p; update(); } } // set value to display
|
||||
void noValue() { if (enabled) { hasvalue=false; update(); } } // no value to display
|
||||
void configChanged(qint32); // context changed
|
||||
|
||||
private:
|
||||
@@ -63,10 +69,11 @@ class GenericLegendItem : public QWidget {
|
||||
QColor color;
|
||||
|
||||
bool hasvalue;
|
||||
bool enabled;
|
||||
double value;
|
||||
|
||||
// geometry for painting fast / updated on config changes
|
||||
QRectF blockrect, namerect, valuerect, linerect;
|
||||
QRectF blockrect, namerect, valuerect, linerect, hoverrect;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -76,6 +76,7 @@ GenericPlot::GenericPlot(QWidget *parent, Context *context) : QWidget(parent), c
|
||||
connect(selector, SIGNAL(hover(QPointF,QString,QAbstractSeries*)), legend, SLOT(hover(QPointF,QString,QAbstractSeries*)));
|
||||
connect(selector, SIGNAL(unhover(QString)), legend, SLOT(unhover(QString)));
|
||||
connect(selector, SIGNAL(unhoverx()), legend, SLOT(unhoverx()));
|
||||
connect(legend, SIGNAL(clicked(QString,bool)), this, SLOT(setSeriesVisible(QString,bool)));
|
||||
|
||||
// config changed...
|
||||
configChanged(0);
|
||||
@@ -211,6 +212,23 @@ GenericPlot::configChanged(qint32)
|
||||
qchart->setBackgroundPen(QPen(GColor(CPLOTMARKER)));
|
||||
}
|
||||
|
||||
void
|
||||
GenericPlot::setSeriesVisible(QString name, bool visible)
|
||||
{
|
||||
// find the curve
|
||||
QAbstractSeries *series = curves.value(name, NULL);
|
||||
|
||||
// does it exist and did it change?
|
||||
if (series && series->isVisible() != visible) {
|
||||
|
||||
// show/hide
|
||||
series->setVisible(visible);
|
||||
|
||||
// tell selector we hid/show a series so it can respond.
|
||||
selector->setSeriesVisible(name, visible);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
GenericPlot::initialiseChart(QString title, int type, bool animate)
|
||||
{
|
||||
|
||||
@@ -124,6 +124,10 @@ class GenericPlot : public QWidget {
|
||||
static QColor seriesColor(QAbstractSeries* series);
|
||||
|
||||
public slots:
|
||||
|
||||
// do we want to see this series?
|
||||
void setSeriesVisible(QString name, bool visible);
|
||||
|
||||
void configChanged(qint32);
|
||||
|
||||
// set chart settings
|
||||
|
||||
@@ -64,6 +64,8 @@ void GenericSelectTool::paint(QPainter*painter, const QStyleOptionGraphicsItem *
|
||||
// current position for each series - we only do first, coz only interested in x axis anyway
|
||||
foreach(QAbstractSeries *series, host->qchart->series()) {
|
||||
|
||||
if (series->isVisible() == false) continue; // ignore invisble curves
|
||||
|
||||
// convert screen position to value for series
|
||||
QPointF v = host->qchart->mapToValue(spos,series);
|
||||
double miny=0;
|
||||
@@ -96,7 +98,7 @@ void GenericSelectTool::paint(QPainter*painter, const QStyleOptionGraphicsItem *
|
||||
QColor invert = GCColor::invertColor(GColor(CPLOTBACKGROUND));
|
||||
painter->setBrush(invert);
|
||||
painter->setPen(invert);
|
||||
QRectF circle(0,0,5*dpiXFactor,5*dpiYFactor);
|
||||
QRectF circle(0,0,gl_linemarker*dpiXFactor,gl_linemarker*dpiYFactor);
|
||||
circle.moveCenter(pos);
|
||||
painter->drawEllipse(circle);
|
||||
painter->setBrush(Qt::NoBrush);
|
||||
@@ -121,7 +123,7 @@ void GenericSelectTool::paint(QPainter*painter, const QStyleOptionGraphicsItem *
|
||||
QColor invert = GCColor::invertColor(GColor(CPLOTBACKGROUND));
|
||||
painter->setBrush(invert);
|
||||
painter->setPen(invert);
|
||||
QRectF circle(0,0,10*dpiXFactor,10*dpiYFactor);
|
||||
QRectF circle(0,0,gl_scattermarker*dpiXFactor,gl_scattermarker*dpiYFactor);
|
||||
circle.moveCenter(hoverpoint);
|
||||
painter->drawEllipse(circle);
|
||||
painter->setBrush(Qt::NoBrush);
|
||||
@@ -131,6 +133,7 @@ void GenericSelectTool::paint(QPainter*painter, const QStyleOptionGraphicsItem *
|
||||
// current position for each series
|
||||
foreach(QAbstractSeries *series, host->qchart->series()) {
|
||||
|
||||
if (series->isVisible() == false) continue; // ignore invisble curves
|
||||
|
||||
// convert screen position to value for series
|
||||
QPointF v = host->qchart->mapToValue(spos,series);
|
||||
@@ -483,6 +486,8 @@ GenericSelectTool::moved(QPointF pos)
|
||||
hoverseries = NULL;
|
||||
foreach(QAbstractSeries *series, host->qchart->series()) {
|
||||
|
||||
if (series->isVisible() == false) continue; // ignore invisble curves
|
||||
|
||||
Quadtree *tree= host->quadtrees.value(series,NULL);
|
||||
if (tree != NULL) {
|
||||
|
||||
@@ -539,7 +544,7 @@ GenericSelectTool::moved(QPointF pos)
|
||||
double nearestx=-9999;
|
||||
foreach(QAbstractSeries *series, host->qchart->series()) {
|
||||
|
||||
if (ignore.contains(series)) continue;
|
||||
if (series->isVisible() == false || ignore.contains(series)) continue;
|
||||
|
||||
// get x value to search
|
||||
double xvalue=host->qchart->mapToValue(spos,series).x();
|
||||
@@ -672,193 +677,222 @@ GenericCalculator::finalise()
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GenericSelectTool::setSeriesVisible(QString name, bool visible)
|
||||
{
|
||||
// a series got hidden or showm, so do whats needed.
|
||||
QString selectionName = QString("%1_select").arg(name);
|
||||
foreach(QAbstractSeries *x, host->qchart->series())
|
||||
if (x->name() == selectionName)
|
||||
x->setVisible(visible);
|
||||
|
||||
// as a special case we clear the hoverpoints.
|
||||
// they will get redisplayed when the cursor moves
|
||||
// but for now this is the quickes and simplest way
|
||||
// to avoid artefacts
|
||||
hoverpoints.clear();
|
||||
hoverpoint=QPointF();
|
||||
|
||||
// hide/show and updatescenes may overlap/get out of sync
|
||||
// so we update scene to be absolutely sure the scene
|
||||
// reflects the current visible settings (and others later)
|
||||
rectchanged = true;
|
||||
updateScene();
|
||||
}
|
||||
|
||||
// selector needs to update the chart for selections
|
||||
void
|
||||
GenericSelectTool::updateScene()
|
||||
{
|
||||
// is the selection active?
|
||||
if (rectchanged) {
|
||||
if (state != GenericSelectTool::INACTIVE && state != DRAGGING) {
|
||||
|
||||
// selection tool is active so set curves gray
|
||||
// and create curves for highlighted points etc
|
||||
QList<QAbstractSeries*> originallist=host->qchart->series();
|
||||
foreach(QAbstractSeries *x, originallist) { // because we update it below (!)
|
||||
// clear incidental state that gets reset when needed
|
||||
stats.clear();
|
||||
|
||||
if (ignore.contains(x)) continue;
|
||||
if (state != GenericSelectTool::INACTIVE && state != DRAGGING) {
|
||||
|
||||
// Run through all the curves, setting them gray
|
||||
// selecting the points that fall into the selection
|
||||
// and creating selection curves and stats for painting
|
||||
// onto the plot
|
||||
//
|
||||
// We duplicate code by curve type (but not chart type)
|
||||
// so we can e.g. put a scatter curve on a line chart
|
||||
// and vice versa later.
|
||||
// selection tool is active so set curves gray
|
||||
// and create curves for highlighted points etc
|
||||
QList<QAbstractSeries*> originallist=host->qchart->series();
|
||||
foreach(QAbstractSeries *x, originallist) { // because we update it below (!)
|
||||
|
||||
switch(x->type()) {
|
||||
case QAbstractSeries::SeriesTypeLine: {
|
||||
QLineSeries *line = static_cast<QLineSeries*>(x);
|
||||
if (x->isVisible() == false || ignore.contains(x)) continue;
|
||||
|
||||
// ignore empty series
|
||||
if (line->count() < 1) continue;
|
||||
// Run through all the curves, setting them gray
|
||||
// selecting the points that fall into the selection
|
||||
// and creating selection curves and stats for painting
|
||||
// onto the plot
|
||||
//
|
||||
// We duplicate code by curve type (but not chart type)
|
||||
// so we can e.g. put a scatter curve on a line chart
|
||||
// and vice versa later.
|
||||
|
||||
// this will be used to plot selected points on the plot
|
||||
QLineSeries *selection =NULL;
|
||||
switch(x->type()) {
|
||||
case QAbstractSeries::SeriesTypeLine: {
|
||||
QLineSeries *line = static_cast<QLineSeries*>(x);
|
||||
|
||||
// the axes for the current series
|
||||
QAbstractAxis *xaxis=NULL, *yaxis=NULL;
|
||||
foreach (QAbstractAxis *ax, x->attachedAxes()) {
|
||||
if (ax->orientation() == Qt::Vertical && yaxis==NULL) yaxis=ax;
|
||||
if (ax->orientation() == Qt::Horizontal && xaxis==NULL) xaxis=ax;
|
||||
}
|
||||
// ignore empty series
|
||||
if (line->count() < 1) continue;
|
||||
|
||||
if ((selection=static_cast<QLineSeries*>(selections.value(x, NULL))) == NULL) {
|
||||
// this will be used to plot selected points on the plot
|
||||
QLineSeries *selection =NULL;
|
||||
|
||||
selection = new QLineSeries();
|
||||
|
||||
// all of this curve cloning should be in a new method xxx todo
|
||||
selection->setUseOpenGL(line->useOpenGL());
|
||||
selection->setPen(line->pen());
|
||||
if (line->useOpenGL())
|
||||
selection->setColor(Qt::gray); // use opengl ignores changing colors
|
||||
else {
|
||||
selection->setColor(line->color());
|
||||
static_cast<QLineSeries*>(x)->setColor(Qt::gray);
|
||||
// the axes for the current series
|
||||
QAbstractAxis *xaxis=NULL, *yaxis=NULL;
|
||||
foreach (QAbstractAxis *ax, x->attachedAxes()) {
|
||||
if (ax->orientation() == Qt::Vertical && yaxis==NULL) yaxis=ax;
|
||||
if (ax->orientation() == Qt::Horizontal && xaxis==NULL) xaxis=ax;
|
||||
}
|
||||
selections.insert(x, selection);
|
||||
ignore.append(selection);
|
||||
|
||||
// add after done all aesthetic for opengl snafus
|
||||
host->qchart->addSeries(selection); // before adding data and axis
|
||||
if ((selection=static_cast<QLineSeries*>(selections.value(x, NULL))) == NULL) {
|
||||
|
||||
// only do when creating it.
|
||||
if (yaxis) selection->attachAxis(yaxis);
|
||||
if (xaxis) selection->attachAxis(xaxis);
|
||||
}
|
||||
selection = new QLineSeries();
|
||||
selection->setName(QString("%1_select").arg(line->name()));
|
||||
|
||||
// lets work out what range of values we need to be
|
||||
// selecting is, reverse since possible to have a backwards
|
||||
// rectangle in the selection tool
|
||||
double minx=0,maxx=0;
|
||||
minx =this->minx(x);
|
||||
maxx =this->maxx(x);
|
||||
if (maxx < minx) { double t=minx; minx=maxx; maxx=t; }
|
||||
// all of this curve cloning should be in a new method xxx todo
|
||||
selection->setUseOpenGL(line->useOpenGL());
|
||||
selection->setPen(line->pen());
|
||||
if (line->useOpenGL())
|
||||
selection->setColor(Qt::gray); // use opengl ignores changing colors
|
||||
else {
|
||||
selection->setColor(line->color());
|
||||
static_cast<QLineSeries*>(x)->setColor(Qt::gray);
|
||||
}
|
||||
selections.insert(x, selection);
|
||||
ignore.append(selection);
|
||||
|
||||
//fprintf(stderr, "xaxis range %f-%f, yaxis range %f-%f, [%s] %d points to check\n", minx,maxx,miny,maxy,scatter->name().toStdString().c_str(), scatter->count());
|
||||
// add after done all aesthetic for opengl snafus
|
||||
host->qchart->addSeries(selection); // before adding data and axis
|
||||
|
||||
// add points to the selection curve and calculate as you go
|
||||
QList<QPointF> points;
|
||||
GenericCalculator calc;
|
||||
calc.initialise();
|
||||
calc.color = selection->color(); // should this go into constructor?! xxx todo
|
||||
calc.xaxis = xaxis;
|
||||
calc.yaxis = yaxis;
|
||||
calc.series = line;
|
||||
for(int i=0; i<line->count(); i++) {
|
||||
QPointF point = line->at(i); // avoid deep copy
|
||||
if (point.x() >= minx && point.x() <= maxx) {
|
||||
if (!points.contains(point)) points << point; // avoid dupes
|
||||
calc.addPoint(point);
|
||||
// only do when creating it.
|
||||
if (yaxis) selection->attachAxis(yaxis);
|
||||
if (xaxis) selection->attachAxis(xaxis);
|
||||
}
|
||||
}
|
||||
calc.finalise();
|
||||
stats.insert(line, calc);
|
||||
|
||||
selection->clear();
|
||||
if (points.count()) selection->append(points);
|
||||
// lets work out what range of values we need to be
|
||||
// selecting is, reverse since possible to have a backwards
|
||||
// rectangle in the selection tool
|
||||
double minx=0,maxx=0;
|
||||
minx =this->minx(x);
|
||||
maxx =this->maxx(x);
|
||||
if (maxx < minx) { double t=minx; minx=maxx; maxx=t; }
|
||||
|
||||
}
|
||||
break;
|
||||
case QAbstractSeries::SeriesTypeScatter: {
|
||||
//fprintf(stderr, "xaxis range %f-%f, yaxis range %f-%f, [%s] %d points to check\n", minx,maxx,miny,maxy,scatter->name().toStdString().c_str(), scatter->count());
|
||||
|
||||
QScatterSeries *scatter = static_cast<QScatterSeries*>(x);
|
||||
|
||||
// ignore empty series
|
||||
if (scatter->count() < 1) continue;
|
||||
|
||||
// this will be used to plot selected points on the plot
|
||||
QScatterSeries *selection =NULL;
|
||||
|
||||
// the axes for the current series
|
||||
QAbstractAxis *xaxis=NULL, *yaxis=NULL;
|
||||
foreach (QAbstractAxis *ax, x->attachedAxes()) {
|
||||
if (ax->orientation() == Qt::Vertical && yaxis==NULL) yaxis=ax;
|
||||
if (ax->orientation() == Qt::Horizontal && xaxis==NULL) xaxis=ax;
|
||||
}
|
||||
|
||||
if ((selection=static_cast<QScatterSeries*>(selections.value(x, NULL))) == NULL) {
|
||||
|
||||
selection = new QScatterSeries();
|
||||
|
||||
// all of this curve cloning should be in a new method xxx todo
|
||||
host->qchart->addSeries(selection); // before adding data and axis
|
||||
selection->setUseOpenGL(scatter->useOpenGL());
|
||||
if (selection->useOpenGL())
|
||||
selection->setColor(Qt::gray); // use opengl ignores changing colors
|
||||
else {
|
||||
selection->setColor(scatter->color());
|
||||
static_cast<QScatterSeries*>(x)->setColor(Qt::gray);
|
||||
// add points to the selection curve and calculate as you go
|
||||
QList<QPointF> points;
|
||||
GenericCalculator calc;
|
||||
calc.initialise();
|
||||
calc.color = selection->color(); // should this go into constructor?! xxx todo
|
||||
calc.xaxis = xaxis;
|
||||
calc.yaxis = yaxis;
|
||||
calc.series = line;
|
||||
for(int i=0; i<line->count(); i++) {
|
||||
QPointF point = line->at(i); // avoid deep copy
|
||||
if (point.x() >= minx && point.x() <= maxx) {
|
||||
if (!points.contains(point)) points << point; // avoid dupes
|
||||
calc.addPoint(point);
|
||||
}
|
||||
}
|
||||
selection->setMarkerSize(scatter->markerSize());
|
||||
selection->setMarkerShape(scatter->markerShape());
|
||||
selection->setPen(scatter->pen());
|
||||
selections.insert(x, selection);
|
||||
ignore.append(selection);
|
||||
calc.finalise();
|
||||
stats.insert(line, calc);
|
||||
|
||||
selection->clear();
|
||||
if (points.count()) selection->append(points);
|
||||
|
||||
// only do when creating it.
|
||||
if (yaxis) selection->attachAxis(yaxis);
|
||||
if (xaxis) selection->attachAxis(xaxis);
|
||||
}
|
||||
|
||||
// lets work out what range of values we need to be
|
||||
// selecting is, reverse since possible to have a backwards
|
||||
// rectangle in the selection tool
|
||||
double miny=0,maxy=0,minx=0,maxx=0;
|
||||
miny =this->miny(x);
|
||||
maxy =this->maxy(x);
|
||||
if (maxy < miny) { double t=miny; miny=maxy; maxy=t; }
|
||||
|
||||
minx =this->minx(x);
|
||||
maxx =this->maxx(x);
|
||||
if (maxx < minx) { double t=minx; minx=maxx; maxx=t; }
|
||||
|
||||
//fprintf(stderr, "xaxis range %f-%f, yaxis range %f-%f, [%s] %d points to check\n", minx,maxx,miny,maxy,scatter->name().toStdString().c_str(), scatter->count());
|
||||
|
||||
// add points to the selection curve and calculate as you go
|
||||
QList<QPointF> points;
|
||||
GenericCalculator calc;
|
||||
calc.initialise();
|
||||
calc.color = selection->color(); // should this go into constructor?! xxx todo
|
||||
calc.xaxis = xaxis;
|
||||
calc.yaxis = yaxis;
|
||||
calc.series = scatter;
|
||||
for(int i=0; i<scatter->count(); i++) {
|
||||
QPointF point = scatter->at(i); // avoid deep copy
|
||||
if (point.y() >= miny && point.y() <= maxy &&
|
||||
point.x() >= minx && point.x() <= maxx) {
|
||||
if (!points.contains(point)) points << point; // avoid dupes
|
||||
calc.addPoint(point);
|
||||
}
|
||||
}
|
||||
calc.finalise();
|
||||
stats.insert(scatter, calc);
|
||||
|
||||
selection->clear();
|
||||
if (points.count()) selection->append(points);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
case QAbstractSeries::SeriesTypeScatter: {
|
||||
|
||||
QScatterSeries *scatter = static_cast<QScatterSeries*>(x);
|
||||
|
||||
// ignore empty series
|
||||
if (scatter->count() < 1) continue;
|
||||
|
||||
// this will be used to plot selected points on the plot
|
||||
QScatterSeries *selection =NULL;
|
||||
|
||||
// the axes for the current series
|
||||
QAbstractAxis *xaxis=NULL, *yaxis=NULL;
|
||||
foreach (QAbstractAxis *ax, x->attachedAxes()) {
|
||||
if (ax->orientation() == Qt::Vertical && yaxis==NULL) yaxis=ax;
|
||||
if (ax->orientation() == Qt::Horizontal && xaxis==NULL) xaxis=ax;
|
||||
}
|
||||
|
||||
if ((selection=static_cast<QScatterSeries*>(selections.value(x, NULL))) == NULL) {
|
||||
|
||||
selection = new QScatterSeries();
|
||||
|
||||
// all of this curve cloning should be in a new method xxx todo
|
||||
host->qchart->addSeries(selection); // before adding data and axis
|
||||
selection->setUseOpenGL(scatter->useOpenGL());
|
||||
if (selection->useOpenGL())
|
||||
selection->setColor(Qt::gray); // use opengl ignores changing colors
|
||||
else {
|
||||
selection->setColor(scatter->color());
|
||||
static_cast<QScatterSeries*>(x)->setColor(Qt::gray);
|
||||
}
|
||||
selection->setMarkerSize(scatter->markerSize());
|
||||
selection->setMarkerShape(scatter->markerShape());
|
||||
selection->setPen(scatter->pen());
|
||||
selection->setName(QString("%1_select").arg(scatter->name()));
|
||||
selections.insert(x, selection);
|
||||
ignore.append(selection);
|
||||
|
||||
// only do when creating it.
|
||||
if (yaxis) selection->attachAxis(yaxis);
|
||||
if (xaxis) selection->attachAxis(xaxis);
|
||||
}
|
||||
|
||||
// lets work out what range of values we need to be
|
||||
// selecting is, reverse since possible to have a backwards
|
||||
// rectangle in the selection tool
|
||||
double miny=0,maxy=0,minx=0,maxx=0;
|
||||
miny =this->miny(x);
|
||||
maxy =this->maxy(x);
|
||||
if (maxy < miny) { double t=miny; miny=maxy; maxy=t; }
|
||||
|
||||
minx =this->minx(x);
|
||||
maxx =this->maxx(x);
|
||||
if (maxx < minx) { double t=minx; minx=maxx; maxx=t; }
|
||||
|
||||
//fprintf(stderr, "xaxis range %f-%f, yaxis range %f-%f, [%s] %d points to check\n", minx,maxx,miny,maxy,scatter->name().toStdString().c_str(), scatter->count());
|
||||
|
||||
// add points to the selection curve and calculate as you go
|
||||
QList<QPointF> points;
|
||||
GenericCalculator calc;
|
||||
calc.initialise();
|
||||
calc.color = selection->color(); // should this go into constructor?! xxx todo
|
||||
calc.xaxis = xaxis;
|
||||
calc.yaxis = yaxis;
|
||||
calc.series = scatter;
|
||||
for(int i=0; i<scatter->count(); i++) {
|
||||
QPointF point = scatter->at(i); // avoid deep copy
|
||||
if (point.y() >= miny && point.y() <= maxy &&
|
||||
point.x() >= minx && point.x() <= maxx) {
|
||||
if (!points.contains(point)) points << point; // avoid dupes
|
||||
calc.addPoint(point);
|
||||
}
|
||||
}
|
||||
calc.finalise();
|
||||
stats.insert(scatter, calc);
|
||||
|
||||
selection->clear();
|
||||
if (points.count()) selection->append(points);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
rectchanged = false;
|
||||
|
||||
} else {
|
||||
|
||||
resetSelections();
|
||||
}
|
||||
|
||||
rectchanged = false;
|
||||
|
||||
} else {
|
||||
|
||||
resetSelections();
|
||||
}
|
||||
}
|
||||
|
||||
// repaint everything
|
||||
@@ -873,7 +907,7 @@ GenericSelectTool::updateScene()
|
||||
|
||||
foreach(QAbstractSeries *x, host->qchart->series()) {
|
||||
|
||||
if (ignore.contains(x)) continue;
|
||||
if (ignore.contains(x)) continue; // we still reset selections for invisible curves
|
||||
|
||||
switch(x->type()) {
|
||||
|
||||
|
||||
@@ -83,6 +83,9 @@ class GenericSelectTool : public QObject, public QGraphicsItem
|
||||
Q_OBJECT
|
||||
Q_INTERFACES(QGraphicsItem)
|
||||
|
||||
static constexpr double gl_linemarker = 7;
|
||||
static constexpr double gl_scattermarker = 10;
|
||||
|
||||
friend class ::GenericPlot;
|
||||
|
||||
public:
|
||||
@@ -95,6 +98,9 @@ class GenericSelectTool : public QObject, public QGraphicsItem
|
||||
// set mode
|
||||
void setMode(SelectionMode mode) { this->mode=mode; }
|
||||
|
||||
// some series was shown or hidden...
|
||||
void setSeriesVisible(QString name, bool visible);
|
||||
|
||||
// is invisible and tiny. we are just an observer
|
||||
bool sceneEventFilter(QGraphicsItem *watched, QEvent *event);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user