diff --git a/src/Charts/GenericChart.cpp b/src/Charts/GenericChart.cpp index a09674e92..043fbede2 100644 --- a/src/Charts/GenericChart.cpp +++ b/src/Charts/GenericChart.cpp @@ -25,26 +25,47 @@ #include +// +// The generic chart manages collections of genericplots +// if the stack option is selected we create a plot for +// each data series. +// +// If series do not have a common x-axis we create a +// separate plot, with series that share a common x-axis +// sharing the same plot +// +// The layout is horizontal or vertical in which case the +// generic chart will use vbox or hbox layouts. XXX TODO +// +// Charts have a minimum width and height that needs to be +// honoured too, since multiple series stacked can get +// cramped, so these are placed into a scroll area XXX TODO +// GenericChart::GenericChart(QWidget *parent, Context *context) : QWidget(parent), context(context) { // intitialise state info mainLayout = new QVBoxLayout(this); mainLayout->setSpacing(0); mainLayout->setContentsMargins(0,0,0,0); - - plot = new GenericPlot(this,context); - mainLayout->addWidget(plot); } // set chart settings bool GenericChart::initialiseChart(QString title, int type, bool animate, int legendpos, bool stack, int orientation) { - // for now ... - Q_UNUSED(stack) - Q_UNUSED(orientation) + // Remember the settings, we use them for every new plot + this->title = title; + this->type = type; + this->animate = animate; + this->legendpos = legendpos; + this->stack = stack; + this->orientation = orientation; - return plot->initialiseChart(title, type, animate, legendpos); + // store info as its passed to us + newSeries.clear(); + newAxes.clear(); + + return true; } // add a curve, associating an axis @@ -54,7 +75,8 @@ GenericChart::addCurve(QString name, QVector xseries, QVector ys int line, int symbol, int size, QString color, int opacity, bool opengl) { - return plot->addCurve(name, xseries, yseries, xname, yname, labels, colors, line, symbol, size, color, opacity, opengl); + newSeries << GenericSeriesInfo(name, xseries, yseries, xname, yname, labels, colors, line, symbol, size, color, opacity, opengl); + return true; } // configure axis, after curves added @@ -62,12 +84,131 @@ bool GenericChart::configureAxis(QString name, bool visible, int align, double min, double max, int type, QString labelcolor, QString color, bool log, QStringList categories) { - return plot->configureAxis(name, visible, align, min, max, type, labelcolor, color, log, categories); + newAxes << GenericAxisInfo(name, visible, align, min, max, type, labelcolor, color, log, categories); + return true; } // post processing clean up / add decorations / helpers etc void GenericChart::finaliseChart() { - plot->finaliseChart(); + setUpdatesEnabled(false); // lets not open our kimono here + + // ok, so lets work out what we need, run through the curves + // and allocate to a plot, where we have separate plots if + // we have stacked set -or- for each xaxis, remembering to + // add the axisinfo each time too. + QList newPlots; + QStringList xaxes; + for(int i=0; i=0) add.axes << newAxes[ax]; + + // yaxis info + ax=GenericAxisInfo::findAxis(newAxes, newSeries[i].yname); + if (ax>=0) add.axes << newAxes[ax]; + + // add to list + newPlots << add; + xaxes << newSeries[i].xname; + + } else { + + // otherwise add all series to the same plot + // with a common x axis + int index = xaxes.indexOf(newSeries[i].xname); + if (index >=0) { + // woop, we already got this xaxis + newPlots[index].series << newSeries[i]; + + // yaxis info + int ax=GenericAxisInfo::findAxis(newAxes, newSeries[i].yname); + if (ax>=0) newPlots[index].axes << newAxes[ax]; + + } else { + + GenericPlotInfo add(newSeries[i].xname); + add.series << newSeries[i]; + + // xaxis info + int ax=GenericAxisInfo::findAxis(newAxes, newSeries[i].xname); + if (ax>=0) add.axes << newAxes[ax]; + + // yaxis info + ax=GenericAxisInfo::findAxis(newAxes, newSeries[i].yname); + if (ax>=0) add.axes << newAxes[ax]; + + // add to list + newPlots << add; + xaxes << newSeries[i].xname; + } + } + } + + // lets find existing plots or create new ones + // first we mark all existing plots as deleteme + for(int i=0; iaddWidget(newPlots[i].plot); + mainLayout->setStretchFactor(newPlots[i].plot, 10);// make them all the same + } else { + newPlots[i].plot = currentPlots[index].plot; // reuse + currentPlots[index].state = GenericPlotInfo::matched; // don't deleteme ! + } + } + + // delete all the deleteme plots + for(int i=0; iinitialiseChart(title, type, animate, legendpos); + + // add curves + QListIterators(newPlots[i].series); + while(s.hasNext()) { + GenericSeriesInfo p=s.next(); + newPlots[i].plot->addCurve(p.name, p.xseries, p.yseries, p.xname, p.yname, p.labels, p.colors, p.line, p.symbol, p.size, p.color, p.opacity, p.opengl); + } + + // set axis + QListIteratora(newPlots[i].axes); + while(a.hasNext()) { + GenericAxisInfo p=a.next(); + newPlots[i].plot->configureAxis(p.name, p.visible, p.align,p.minx, p.maxx, p.type, p.labelcolorstring, p.axiscolorstring, p.log, p.categories); + } + + newPlots[i].plot->finaliseChart(); + newPlots[i].state = GenericPlotInfo::active; + } + + // set current and wipe rest + currentPlots=newPlots; + + // and zap the state + newAxes.clear(); + newSeries.clear(); + + // and display + setUpdatesEnabled(true); } diff --git a/src/Charts/GenericChart.h b/src/Charts/GenericChart.h index 983e25e12..396b27267 100644 --- a/src/Charts/GenericChart.h +++ b/src/Charts/GenericChart.h @@ -30,6 +30,154 @@ #include "GenericLegend.h" #include "GenericPlot.h" +// keeping track of the series info +class GenericSeriesInfo { + + public: + + GenericSeriesInfo(QString name, QVector xseries, QVector yseries, QString xname, QString yname, + QStringList labels, QStringList colors, + int line, int symbol, int size, QString color, int opacity, bool opengl) : + name(name), xseries(xseries), yseries(yseries), xname(xname), yname(yname), + labels(labels), colors(colors), + line(line), symbol(symbol), size(size), color(color), opacity(opacity), opengl(opengl) + {} + + // properties, from setCurve(...) + QString name; + QVector xseries; + QVector yseries; + QString xname; + QString yname; + QStringList labels; + QStringList colors; + int line; + int symbol; + int size; + QString color; + int opacity; + bool opengl; +}; + +// keeping track of all our plots +class GenericPlotInfo { + + public: + + // when working out what to do with existing plots + enum { init, active, matched, deleteme } state; + + // initial + GenericPlotInfo(QString xaxis) : state(init), plot(NULL), xaxis(xaxis) {} + + bool matches(const GenericPlotInfo &other) const { + // we need to have same series and same xaxis + if (other.xaxis != xaxis) return false; + + // same number of series + if (other.series.count() != series.count()) return false; + + // all my series are in their series? + foreach(GenericSeriesInfo info, series) { + bool found=false; + // is in other? + foreach(GenericSeriesInfo oinfo, other.series) { + if (oinfo.name == info.name && oinfo.yname == info.yname && oinfo.xname == info.xname) + found=true; + } + if (found == false) return false; + } + return true; + } + + static int findPlot(const QListlist, const GenericPlotInfo findme) { + for(int i=0; i series; + QList axes; +}; + +// general axis info +class GenericAxisInfo { +public: + GenericAxisInfo(QString name, bool visible, int align, double min, double max, + int type, QString labelcolor, QString color, bool log, QStringList categories) : + type(static_cast(type)), + name(name), align(static_cast(align)), + minx(min), maxx(max), visible(visible), log(log), + labelcolorstring(labelcolor), axiscolorstring(color), categories(categories) + {} + + enum axisinfoType { CONTINUOUS=0, // Continious range + DATERANGE=1, // Date + TIME=2, // Duration, Time + CATEGORY=3 // labelled with categories + }; + typedef enum axisinfoType AxisInfoType; + + static int findAxis(QListinfos, QString name) { + for (int i=0; imaxx) maxx=x; + if (xmaxy) maxy=y; + if (y series; + + // data is all public to avoid tedious get/set + AxisInfoType type; // what type of axis is this? + QString name; + Qt::Orientations orientation; + Qt::AlignmentFlag align; + double miny, maxy, minx, maxx; // updated as we see points, set the range + bool visible,fixed, log, minorgrid, majorgrid; // settings + QColor labelcolor, axiscolor; // aesthetics + QString labelcolorstring, axiscolorstring; + QStringList categories; +}; + // the chart class GenericChart : public QWidget { @@ -63,15 +211,28 @@ class GenericChart : public QWidget { // legend and selector need acces to these QVBoxLayout *mainLayout; - GenericPlot *plot; // for now... - // layout options + // chart settings + QString title; + int type; + bool animate; + int legendpos; bool stack; // stack series instead of on same chart - Qt::Orientation orientation; // layout horizontal or vertical + int orientation; // layout horizontal or vertical + + // when we get new settings/calls we + // collect together to prepare before + // actually creating plots since we want + // to see the whole picture- try to reuse + // plots as much as possible to avoid + // the cost of creating new ones. + QList newSeries; + QList newAxes; + + // active plots + QList currentPlots; private: Context *context; }; - - #endif diff --git a/src/Charts/GenericPlot.cpp b/src/Charts/GenericPlot.cpp index c81ad24d3..786457552 100644 --- a/src/Charts/GenericPlot.cpp +++ b/src/Charts/GenericPlot.cpp @@ -17,6 +17,7 @@ */ #include "GenericPlot.h" +#include "GenericChart.h" #include "Colors.h" #include "TabView.h" @@ -208,7 +209,7 @@ GenericPlot::setSeriesVisible(QString name, bool visible) // annotations void -GenericPlot::addAnnotation(AnnotationType type, QAbstractSeries*series, double value) +GenericPlot::addAnnotation(AnnotationType , QAbstractSeries*series, double value) { fprintf(stderr, "add annotation line: for %s at value %f\n", series->name().toStdString().c_str(), value); fflush(stderr); diff --git a/src/Charts/GenericPlot.h b/src/Charts/GenericPlot.h index b638c9b4e..48cf887dc 100644 --- a/src/Charts/GenericPlot.h +++ b/src/Charts/GenericPlot.h @@ -54,59 +54,7 @@ class GenericPlot; class GenericLegend; class GenericSelectTool; - -// general axis info -class GenericAxisInfo { -public: - enum axisinfoType { CONTINUOUS=0, // Continious range - DATERANGE=1, // Date - TIME=2, // Duration, Time - CATEGORY=3 // labelled with categories - }; - typedef enum axisinfoType AxisInfoType; - - GenericAxisInfo(Qt::Orientations orientation, QString name) : name(name), orientation(orientation) { - miny=maxy=minx=maxx=0; - fixed=log=false; - visible=minorgrid=majorgrid=true; - type=CONTINUOUS; - axiscolor=labelcolor=GColor(CPLOTMARKER); - } - - void point(double x, double y) { - if (fixed) return; - if (x>maxx) maxx=x; - if (xmaxy) maxy=y; - if (y series; - - // data is all public to avoid tedious get/set - QString name; - Qt::Orientations orientation; - Qt::AlignmentFlag align; - double miny, maxy, minx, maxx; // updated as we see points, set the range - bool visible,fixed, log, minorgrid, majorgrid; // settings - QColor labelcolor, axiscolor; // aesthetics - QStringList categories; - AxisInfoType type; // what type of axis is this? -}; +class GenericAxisInfo; // the chart class GenericPlot : public QWidget { @@ -202,6 +150,4 @@ class GenericPlot : public QWidget { // alternates as axis added bool left, bottom; }; - - #endif