mirror of
https://github.com/GoldenCheetah/GoldenCheetah.git
synced 2026-02-13 16:18:42 +00:00
GenericPlot legend placement and line styles
.. updated to support different linestyles and also legend can now be placed above, below, left or right of the chart and orientated to list series vertically or horizontally. .. the vertical legend is a bit ugly as nothing lines up, will fix up separately.
This commit is contained in:
@@ -161,6 +161,7 @@ GenericLegend::GenericLegend(Context *context, GenericPlot *plot) : context(cont
|
||||
layout = new QHBoxLayout(this);
|
||||
layout->addStretch();
|
||||
|
||||
orientation=Qt::Horizontal;
|
||||
xname="";
|
||||
clickable=true;
|
||||
|
||||
@@ -184,6 +185,33 @@ GenericLegend::addSeries(QString name, QColor color)
|
||||
connect(add, SIGNAL(clicked(QString,bool)), this, SIGNAL(clicked(QString,bool)));
|
||||
}
|
||||
|
||||
void
|
||||
GenericLegend::setOrientation(Qt::Orientation o)
|
||||
{
|
||||
if (orientation == o) return; // already is.
|
||||
|
||||
// set new orientation
|
||||
orientation = o;
|
||||
|
||||
QBoxLayout *newlayout;
|
||||
if (orientation == Qt::Horizontal) newlayout = new QHBoxLayout();
|
||||
else newlayout = new QVBoxLayout();
|
||||
newlayout->addStretch();
|
||||
|
||||
// remove from layout and redo
|
||||
QMapIterator<QString, GenericLegendItem*> i(items);
|
||||
while (i.hasNext()) {
|
||||
i.next();
|
||||
layout->removeWidget(i.value());
|
||||
newlayout->insertWidget(0, i.value());
|
||||
}
|
||||
|
||||
// replace the layout
|
||||
delete layout;
|
||||
setLayout(newlayout);
|
||||
layout = newlayout;
|
||||
|
||||
}
|
||||
void
|
||||
GenericLegend::addX(QString name)
|
||||
{
|
||||
|
||||
@@ -95,20 +95,23 @@ class GenericLegend : public QWidget {
|
||||
void clicked(QString name, bool enabled); // someone clicked on a legend and enabled/disabled it
|
||||
|
||||
public slots:
|
||||
|
||||
void setValue(QPointF value, QString name);
|
||||
void unhover(QString name);
|
||||
void unhoverx();
|
||||
void setClickable(bool x);
|
||||
void setOrientation(Qt::Orientation);
|
||||
|
||||
private:
|
||||
// a label has a unique name, not directly tide to
|
||||
// a series or axis value, it depends...
|
||||
Context *context;
|
||||
GenericPlot *plot;
|
||||
QHBoxLayout *layout;
|
||||
QBoxLayout *layout;
|
||||
QMap<QString,GenericLegendItem*> items;
|
||||
QString xname;
|
||||
bool clickable;
|
||||
Qt::Orientation orientation;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -33,9 +33,10 @@ GenericPlot::GenericPlot(QWidget *parent, Context *context) : QWidget(parent), c
|
||||
barseries=NULL;
|
||||
bottom=left=true;
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout(this);
|
||||
mainLayout = new QVBoxLayout(this);
|
||||
mainLayout->setSpacing(0);
|
||||
mainLayout->setContentsMargins(0,0,0,0);
|
||||
leftLayout = new QHBoxLayout();
|
||||
|
||||
// setup the chart
|
||||
qchart = new QChart();
|
||||
@@ -67,7 +68,9 @@ GenericPlot::GenericPlot(QWidget *parent, Context *context) : QWidget(parent), c
|
||||
|
||||
// add all widgets to the view
|
||||
mainLayout->addWidget(legend);
|
||||
mainLayout->addWidget(chartview);
|
||||
holder=mainLayout;
|
||||
mainLayout->addLayout(leftLayout);
|
||||
leftLayout->addWidget(chartview);
|
||||
|
||||
// watch for colors changing
|
||||
connect(context, SIGNAL(configChanged(qint32)), this, SLOT(configChanged(qint32)));
|
||||
@@ -327,7 +330,7 @@ GenericPlot::plotAreaChanged()
|
||||
}
|
||||
|
||||
bool
|
||||
GenericPlot::initialiseChart(QString title, int type, bool animate)
|
||||
GenericPlot::initialiseChart(QString title, int type, bool animate, int legpos)
|
||||
{
|
||||
// if we changed the type, all series must go
|
||||
if (charttype != type) {
|
||||
@@ -360,6 +363,31 @@ GenericPlot::initialiseChart(QString title, int type, bool animate)
|
||||
// by default they are disabled anyway
|
||||
qchart->setAnimationOptions(animate ? QChart::SeriesAnimations : QChart::NoAnimation);
|
||||
|
||||
// lets move the legend, only support top or bottom for now
|
||||
holder->removeWidget(legend);
|
||||
switch (legpos) {
|
||||
case 0: // bottom
|
||||
legend->setOrientation(Qt::Horizontal);
|
||||
mainLayout->insertWidget(-1, legend); // at end
|
||||
holder=mainLayout;
|
||||
break;
|
||||
case 1: // left
|
||||
legend->setOrientation(Qt::Vertical);
|
||||
leftLayout->insertWidget(0, legend); // at beginning
|
||||
holder=leftLayout;
|
||||
break;
|
||||
case 2: // top
|
||||
legend->setOrientation(Qt::Horizontal);
|
||||
mainLayout->insertWidget(0, legend); // at front
|
||||
holder=mainLayout;
|
||||
break;
|
||||
case 3: // right
|
||||
legend->setOrientation(Qt::Vertical);
|
||||
leftLayout->insertWidget(-1, legend); // at end
|
||||
holder=leftLayout;
|
||||
break;
|
||||
}
|
||||
|
||||
// what kind of selector do we use?
|
||||
if (charttype==GC_CHART_LINE) selector->setMode(GenericSelectTool::XRANGE);
|
||||
else selector->setMode(GenericSelectTool::RECTANGLE);
|
||||
@@ -371,7 +399,7 @@ GenericPlot::initialiseChart(QString title, int type, bool animate)
|
||||
bool
|
||||
GenericPlot::addCurve(QString name, QVector<double> xseries, QVector<double> yseries, QString xname, QString yname,
|
||||
QStringList labels, QStringList colors,
|
||||
int /*line TODO*/, int /*symbol TODO */, int size, QString color, int opacity, bool opengl)
|
||||
int linestyle, int /*symbol TODO */, int size, QString color, int opacity, bool opengl)
|
||||
{
|
||||
// if curve already exists, remove it
|
||||
if (charttype==GC_CHART_LINE || charttype==GC_CHART_SCATTER || charttype==GC_CHART_PIE) {
|
||||
@@ -425,7 +453,7 @@ GenericPlot::addCurve(QString name, QVector<double> xseries, QVector<double> yse
|
||||
// aesthetics
|
||||
add->setBrush(Qt::NoBrush);
|
||||
QPen pen(color);
|
||||
pen.setStyle(Qt::SolidLine);
|
||||
pen.setStyle(static_cast<Qt::PenStyle>(linestyle));
|
||||
pen.setWidth(size);
|
||||
add->setPen(pen);
|
||||
add->setOpacity(double(opacity) / 100.0); // 0-100% to 0.0-1.0 values
|
||||
|
||||
@@ -134,7 +134,7 @@ class GenericPlot : public QWidget {
|
||||
void configChanged(qint32);
|
||||
|
||||
// set chart settings
|
||||
bool initialiseChart(QString title, int type, bool animate);
|
||||
bool initialiseChart(QString title, int type, bool animate, int legendpos);
|
||||
|
||||
// add a curve, associating an axis
|
||||
bool addCurve(QString name, QVector<double> xseries, QVector<double> yseries, QString xname, QString yname,
|
||||
@@ -166,6 +166,9 @@ class GenericPlot : public QWidget {
|
||||
QChartView *chartview;
|
||||
GenericSelectTool *selector;
|
||||
QChart *qchart;
|
||||
QVBoxLayout *mainLayout;
|
||||
QBoxLayout *leftLayout;
|
||||
QBoxLayout *holder; // who has me at the mo?
|
||||
|
||||
// trap widget events and pass to event handler
|
||||
bool eventFilter(QObject *, QEvent *e);
|
||||
|
||||
@@ -274,7 +274,10 @@ void GenericSelectTool::paint(QPainter*painter, const QStyleOptionGraphicsItem *
|
||||
painter->drawText(maxxp-QPointF(0,4), label);
|
||||
label=QString("%1").arg(calc.x.min);
|
||||
painter->drawText(minxp-QPointF(0,4), label);
|
||||
label=QString("%1").arg(calc.x.mean);
|
||||
|
||||
// scatter sees mean, line/timeseries more likely to want dx
|
||||
if (host->charttype == GC_CHART_SCATTER) label=QString("%1").arg(calc.x.mean);
|
||||
else label=QString("%1").arg(calc.x.max - calc.x.min);
|
||||
painter->drawText(avgxp-QPointF(0,4), label);
|
||||
|
||||
//
|
||||
@@ -329,6 +332,7 @@ GenericSelectTool::reset()
|
||||
hoverseries = NULL;
|
||||
hoverpoints.clear();
|
||||
hoveraxis = NULL;
|
||||
hidden.clear();
|
||||
resetSelections();
|
||||
rectchanged=true;
|
||||
update();
|
||||
|
||||
@@ -454,7 +454,7 @@ PythonChart::setWeb(bool x)
|
||||
renderlayout->insertWidget(0,plot);
|
||||
|
||||
// signals to update it
|
||||
connect(this, SIGNAL(emitChart(QString,int,bool)), plot, SLOT(initialiseChart(QString,int,bool)));
|
||||
connect(this, SIGNAL(emitChart(QString,int,bool,int)), plot, SLOT(initialiseChart(QString,int,bool,int)));
|
||||
connect(this, SIGNAL(emitCurve(QString,QVector<double>,QVector<double>,QString,QString,QStringList,QStringList,int,int,int,QString,int,bool)),
|
||||
plot, SLOT( addCurve(QString,QVector<double>,QVector<double>,QString,QString,QStringList,QStringList,int,int,int,QString,int,bool)));
|
||||
connect(this, SIGNAL(emitAxis(QString,bool,int,double,double,int,QString,QString,bool,QStringList)),
|
||||
|
||||
@@ -141,7 +141,7 @@ class PythonChart : public GcChartWindow, public PythonHost {
|
||||
|
||||
signals:
|
||||
void setUrl(QUrl);
|
||||
void emitChart(QString title, int type, bool animate);
|
||||
void emitChart(QString title, int type, bool animate, int legpos);
|
||||
void emitCurve(QString name, QVector<double> xseries, QVector<double> yseries, QString xname, QString yname,
|
||||
QStringList labels, QStringList colors,
|
||||
int line, int symbol, int size, QString color, int opacity, bool opengl);
|
||||
|
||||
@@ -58,9 +58,9 @@ Bindings::webpage(QString url) const
|
||||
}
|
||||
|
||||
bool
|
||||
Bindings::configChart(QString title, int type, bool animate) const
|
||||
Bindings::configChart(QString title, int type, bool animate, int pos) const
|
||||
{
|
||||
python->chart->emitChart(title, type, animate);
|
||||
python->chart->emitChart(title, type, animate, pos);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -114,7 +114,7 @@ class Bindings {
|
||||
bool postProcess(QString processor, PyObject *activity = NULL) const;
|
||||
|
||||
// working with charts
|
||||
bool configChart(QString title, int type, bool animate) const;
|
||||
bool configChart(QString title, int type, bool animate, int pos) const;
|
||||
bool setCurve(QString name, PyObject *xseries, PyObject *yseries, QString xname, QString yname,
|
||||
QStringList labels, QStringList colors,
|
||||
int line, int symbol, int symbolsize, QString color, int opacity, bool opengl) const;
|
||||
|
||||
@@ -388,7 +388,7 @@ public:
|
||||
bool postProcess(QString processor, PyObject *activity = NULL) const;
|
||||
|
||||
// working with qt charts
|
||||
bool configChart(QString title, int type, bool animate) const;
|
||||
bool configChart(QString title, int type, bool animate, int legpos) const;
|
||||
bool setCurve(QString name, PyObject *xseries, PyObject *yseries, QString xname, QString yname,
|
||||
QStringList labels, QStringList colors,
|
||||
int line, int symbol, int symbolsize, QString color, int opacity, bool opengl) const;
|
||||
|
||||
@@ -125,62 +125,64 @@
|
||||
#define sipName_colors &sipStrings_goldencheetah[616]
|
||||
#define sipNameNr_labels 623
|
||||
#define sipName_labels &sipStrings_goldencheetah[623]
|
||||
#define sipNameNr_metric 630
|
||||
#define sipName_metric &sipStrings_goldencheetah[630]
|
||||
#define sipNameNr_legpos 630
|
||||
#define sipName_legpos &sipStrings_goldencheetah[630]
|
||||
#define sipNameNr_metric 637
|
||||
#define sipName_metric &sipStrings_goldencheetah[637]
|
||||
#define sipNameNr_series 515
|
||||
#define sipName_series &sipStrings_goldencheetah[515]
|
||||
#define sipNameNr_season 637
|
||||
#define sipName_season &sipStrings_goldencheetah[637]
|
||||
#define sipNameNr_filter 644
|
||||
#define sipName_filter &sipStrings_goldencheetah[644]
|
||||
#define sipNameNr_result 651
|
||||
#define sipName_result &sipStrings_goldencheetah[651]
|
||||
#define sipNameNr_remove 658
|
||||
#define sipName_remove &sipStrings_goldencheetah[658]
|
||||
#define sipNameNr_append 665
|
||||
#define sipName_append &sipStrings_goldencheetah[665]
|
||||
#define sipNameNr_align 672
|
||||
#define sipName_align &sipStrings_goldencheetah[672]
|
||||
#define sipNameNr_season 644
|
||||
#define sipName_season &sipStrings_goldencheetah[644]
|
||||
#define sipNameNr_filter 651
|
||||
#define sipName_filter &sipStrings_goldencheetah[651]
|
||||
#define sipNameNr_result 658
|
||||
#define sipName_result &sipStrings_goldencheetah[658]
|
||||
#define sipNameNr_remove 665
|
||||
#define sipName_remove &sipStrings_goldencheetah[665]
|
||||
#define sipNameNr_append 672
|
||||
#define sipName_append &sipStrings_goldencheetah[672]
|
||||
#define sipNameNr_align 679
|
||||
#define sipName_align &sipStrings_goldencheetah[679]
|
||||
#define sipNameNr_color 361
|
||||
#define sipName_color &sipStrings_goldencheetah[361]
|
||||
#define sipNameNr_yname 678
|
||||
#define sipName_yname &sipStrings_goldencheetah[678]
|
||||
#define sipNameNr_xname 684
|
||||
#define sipName_xname &sipStrings_goldencheetah[684]
|
||||
#define sipNameNr_title 690
|
||||
#define sipName_title &sipStrings_goldencheetah[690]
|
||||
#define sipNameNr_index 696
|
||||
#define sipName_index &sipStrings_goldencheetah[696]
|
||||
#define sipNameNr_group 702
|
||||
#define sipName_group &sipStrings_goldencheetah[702]
|
||||
#define sipNameNr_xdata 708
|
||||
#define sipName_xdata &sipStrings_goldencheetah[708]
|
||||
#define sipNameNr_sport 714
|
||||
#define sipName_sport &sipStrings_goldencheetah[714]
|
||||
#define sipNameNr_value 720
|
||||
#define sipName_value &sipStrings_goldencheetah[720]
|
||||
#define sipNameNr_build 726
|
||||
#define sipName_build &sipStrings_goldencheetah[726]
|
||||
#define sipNameNr_line 732
|
||||
#define sipName_line &sipStrings_goldencheetah[732]
|
||||
#define sipNameNr_join 737
|
||||
#define sipName_join &sipStrings_goldencheetah[737]
|
||||
#define sipNameNr_name 679
|
||||
#define sipName_name &sipStrings_goldencheetah[679]
|
||||
#define sipNameNr_type 742
|
||||
#define sipName_type &sipStrings_goldencheetah[742]
|
||||
#define sipNameNr_date 747
|
||||
#define sipName_date &sipStrings_goldencheetah[747]
|
||||
#define sipNameNr_log 752
|
||||
#define sipName_log &sipStrings_goldencheetah[752]
|
||||
#define sipNameNr_yname 685
|
||||
#define sipName_yname &sipStrings_goldencheetah[685]
|
||||
#define sipNameNr_xname 691
|
||||
#define sipName_xname &sipStrings_goldencheetah[691]
|
||||
#define sipNameNr_title 697
|
||||
#define sipName_title &sipStrings_goldencheetah[697]
|
||||
#define sipNameNr_index 703
|
||||
#define sipName_index &sipStrings_goldencheetah[703]
|
||||
#define sipNameNr_group 709
|
||||
#define sipName_group &sipStrings_goldencheetah[709]
|
||||
#define sipNameNr_xdata 715
|
||||
#define sipName_xdata &sipStrings_goldencheetah[715]
|
||||
#define sipNameNr_sport 721
|
||||
#define sipName_sport &sipStrings_goldencheetah[721]
|
||||
#define sipNameNr_value 727
|
||||
#define sipName_value &sipStrings_goldencheetah[727]
|
||||
#define sipNameNr_build 733
|
||||
#define sipName_build &sipStrings_goldencheetah[733]
|
||||
#define sipNameNr_line 739
|
||||
#define sipName_line &sipStrings_goldencheetah[739]
|
||||
#define sipNameNr_join 744
|
||||
#define sipName_join &sipStrings_goldencheetah[744]
|
||||
#define sipNameNr_name 686
|
||||
#define sipName_name &sipStrings_goldencheetah[686]
|
||||
#define sipNameNr_type 749
|
||||
#define sipName_type &sipStrings_goldencheetah[749]
|
||||
#define sipNameNr_date 754
|
||||
#define sipName_date &sipStrings_goldencheetah[754]
|
||||
#define sipNameNr_log 759
|
||||
#define sipName_log &sipStrings_goldencheetah[759]
|
||||
#define sipNameNr_max 120
|
||||
#define sipName_max &sipStrings_goldencheetah[120]
|
||||
#define sipNameNr_min 756
|
||||
#define sipName_min &sipStrings_goldencheetah[756]
|
||||
#define sipNameNr_all 760
|
||||
#define sipName_all &sipStrings_goldencheetah[760]
|
||||
#define sipNameNr_url 764
|
||||
#define sipName_url &sipStrings_goldencheetah[764]
|
||||
#define sipNameNr_min 763
|
||||
#define sipName_min &sipStrings_goldencheetah[763]
|
||||
#define sipNameNr_all 767
|
||||
#define sipName_all &sipStrings_goldencheetah[767]
|
||||
#define sipNameNr_url 771
|
||||
#define sipName_url &sipStrings_goldencheetah[771]
|
||||
|
||||
#define sipMalloc sipAPI_goldencheetah->api_malloc
|
||||
#define sipFree sipAPI_goldencheetah->api_free
|
||||
|
||||
@@ -1056,19 +1056,21 @@ static PyObject *meth_Bindings_configChart(PyObject *sipSelf, PyObject *sipArgs,
|
||||
int a0State = 0;
|
||||
int a1;
|
||||
bool a2;
|
||||
int a3;
|
||||
const ::Bindings *sipCpp;
|
||||
|
||||
static const char *sipKwdList[] = {
|
||||
sipName_title,
|
||||
sipName_type,
|
||||
sipName_animate,
|
||||
sipName_legpos,
|
||||
};
|
||||
|
||||
if (sipParseKwdArgs(&sipParseErr, sipArgs, sipKwds, sipKwdList, NULL, "BJ1ib", &sipSelf, sipType_Bindings, &sipCpp, sipType_QString,&a0, &a0State, &a1, &a2))
|
||||
if (sipParseKwdArgs(&sipParseErr, sipArgs, sipKwds, sipKwdList, NULL, "BJ1ibi", &sipSelf, sipType_Bindings, &sipCpp, sipType_QString,&a0, &a0State, &a1, &a2, &a3))
|
||||
{
|
||||
bool sipRes;
|
||||
|
||||
sipRes = sipCpp->configChart(*a0,a1,a2);
|
||||
sipRes = sipCpp->configChart(*a0,a1,a2,a3);
|
||||
sipReleaseType(a0,sipType_QString,a0State);
|
||||
|
||||
return PyBool_FromLong(sipRes);
|
||||
|
||||
@@ -74,6 +74,7 @@ const char sipStrings_goldencheetah[] = {
|
||||
's', 'y', 'm', 'b', 'o', 'l', 0,
|
||||
'c', 'o', 'l', 'o', 'r', 's', 0,
|
||||
'l', 'a', 'b', 'e', 'l', 's', 0,
|
||||
'l', 'e', 'g', 'p', 'o', 's', 0,
|
||||
'm', 'e', 't', 'r', 'i', 'c', 0,
|
||||
's', 'e', 'a', 's', 'o', 'n', 0,
|
||||
'f', 'i', 'l', 't', 'e', 'r', 0,
|
||||
|
||||
@@ -29,8 +29,8 @@ def __GCactivityXdata(name="", activity=None):
|
||||
return rd
|
||||
|
||||
# setting up the chart
|
||||
def __GCsetChart(title="",type=1,animate=False):
|
||||
GC.configChart(title,type,animate)
|
||||
def __GCsetChart(title="",type=1,animate=False,legpos=2):
|
||||
GC.configChart(title,type,animate,legpos)
|
||||
|
||||
# add a curve
|
||||
def __GCsetCurve(name="",x=list(),y=list(),xaxis="x",yaxis="y", labels=list(), colors=list(),line=1,symbol=0,size=15,color="cyan",opacity=0,opengl=True):
|
||||
@@ -52,6 +52,13 @@ GC.setChart=__GCsetChart
|
||||
GC.addCurve=__GCsetCurve
|
||||
GC.setAxis=__GCconfigAxis
|
||||
|
||||
# line style
|
||||
GC_LINE_NONE=0
|
||||
GC_LINE_SOLID=1
|
||||
GC_LINE_DASH=2
|
||||
GC_LINE_DOT=3
|
||||
GC_LINE_DASHDOT=4
|
||||
|
||||
# constants
|
||||
GC_ALIGN_BOTTOM=0
|
||||
GC_ALIGN_LEFT=1
|
||||
|
||||
Reference in New Issue
Block a user