mirror of
https://github.com/GoldenCheetah/GoldenCheetah.git
synced 2026-02-13 16:18:42 +00:00
Add QT Charts to R Chart
.. mostly complete, following the Python charts lead on the API and basic UX. .. some nits left in there; pie/bar chart categories and labels and an open question on whether/how we let users set the axis color / leave it alone.
This commit is contained in:
@@ -22,6 +22,7 @@
|
||||
|
||||
#include "Colors.h"
|
||||
#include "TabView.h"
|
||||
#include "GenericChart.h"
|
||||
|
||||
// unique identifier for each chart
|
||||
static int id=0;
|
||||
@@ -275,7 +276,14 @@ void RConsole::contextMenuEvent(QContextMenuEvent *e)
|
||||
|
||||
RChart::RChart(Context *context, bool ridesummary) : GcChartWindow(context), context(context), ridesummary(ridesummary)
|
||||
{
|
||||
setControls(NULL);
|
||||
// settings - just choosing output type
|
||||
plotOnChartSetting = new QCheckBox(tr("Use GC charts"), this);
|
||||
plotOnChartSetting->setChecked(false);// by default we use R graphics device (legacy charts)
|
||||
QWidget *c=new QWidget(this);
|
||||
QVBoxLayout *cl = new QVBoxLayout(c);
|
||||
cl->addWidget(plotOnChartSetting);
|
||||
cl->addStretch();
|
||||
setControls(c);
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||
mainLayout->setSpacing(0);
|
||||
@@ -338,7 +346,16 @@ RChart::RChart(Context *context, bool ridesummary) : GcChartWindow(context), con
|
||||
|
||||
canvas = new RCanvas(context, this);
|
||||
canvas->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding));
|
||||
splitter->addWidget(canvas);
|
||||
|
||||
chart = new GenericChart(this, context);
|
||||
chart->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding));
|
||||
|
||||
stack = new QStackedWidget(this);
|
||||
stack->addWidget(canvas);
|
||||
stack->addWidget(chart);
|
||||
stack->setCurrentIndex(0);
|
||||
|
||||
splitter->addWidget(stack);
|
||||
|
||||
// make splitter reasonable
|
||||
QList<int> sizes;
|
||||
@@ -371,6 +388,9 @@ RChart::RChart(Context *context, bool ridesummary) : GcChartWindow(context), con
|
||||
// reveal controls
|
||||
connect(showCon, SIGNAL(stateChanged(int)), this, SLOT(showConChanged(int)));
|
||||
|
||||
// output to graphics device or qt chart
|
||||
connect(plotOnChartSetting, SIGNAL(stateChanged(int)), this, SLOT(plotOnChartChanged()));
|
||||
|
||||
// config changes
|
||||
connect(context, SIGNAL(configChanged(qint32)), this, SLOT(configChanged(qint32)));
|
||||
configChanged(CONFIG_APPEARANCE);
|
||||
@@ -393,6 +413,27 @@ RChart::RChart(Context *context, bool ridesummary) : GcChartWindow(context), con
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
RChart::plotOnChart() const
|
||||
{
|
||||
return plotOnChartSetting->isChecked();
|
||||
}
|
||||
|
||||
void
|
||||
RChart::setPlotOnChart(bool x)
|
||||
{
|
||||
plotOnChartSetting->setChecked(x);
|
||||
}
|
||||
|
||||
void
|
||||
RChart::plotOnChartChanged()
|
||||
{
|
||||
|
||||
// now deal with that by adjusting which is visible.
|
||||
if (plotOnChartSetting->isChecked()) stack->setCurrentIndex(1); // generic chart
|
||||
else stack->setCurrentIndex(0); // r graphics device
|
||||
}
|
||||
|
||||
bool
|
||||
RChart::eventFilter(QObject *, QEvent *e)
|
||||
{
|
||||
@@ -536,6 +577,9 @@ RChart::runScript()
|
||||
canvas->newPage();
|
||||
}
|
||||
|
||||
// finalise the chart (even if not on show)
|
||||
chart->finaliseChart();
|
||||
|
||||
// turn off updates for a sec
|
||||
setUpdatesEnabled(true);
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include <QCheckBox>
|
||||
#include <QSplitter>
|
||||
#include <QByteArray>
|
||||
#include <QStackedWidget>
|
||||
#include <string.h>
|
||||
|
||||
#include "GoldenCheetah.h"
|
||||
@@ -36,6 +37,7 @@
|
||||
#include "RCanvas.h"
|
||||
|
||||
class RChart;
|
||||
class GenericChart;
|
||||
|
||||
// a console widget to type commands and display response
|
||||
class RConsole : public QTextEdit {
|
||||
@@ -83,6 +85,7 @@ class RChart : public GcChartWindow {
|
||||
|
||||
Q_PROPERTY(QString script READ getScript WRITE setScript USER true)
|
||||
Q_PROPERTY(QString state READ getState WRITE setState USER true)
|
||||
Q_PROPERTY(bool plotOnChart READ plotOnChart WRITE setPlotOnChart USER true)
|
||||
Q_PROPERTY(bool showConsole READ showConsole WRITE setConsole USER true)
|
||||
|
||||
public:
|
||||
@@ -96,6 +99,7 @@ class RChart : public GcChartWindow {
|
||||
QTextEdit *script;
|
||||
RConsole *console;
|
||||
RCanvas *canvas;
|
||||
GenericChart *chart;
|
||||
|
||||
bool showConsole() const { return (showCon ? showCon->isChecked() : true); }
|
||||
void setConsole(bool);
|
||||
@@ -106,17 +110,23 @@ class RChart : public GcChartWindow {
|
||||
QString getState() const;
|
||||
void setState(QString);
|
||||
|
||||
bool plotOnChart() const;
|
||||
void setPlotOnChart(bool x);
|
||||
|
||||
public slots:
|
||||
void configChanged(qint32);
|
||||
void showConChanged(int state);
|
||||
void plotOnChartChanged();
|
||||
void runScript();
|
||||
|
||||
protected:
|
||||
// enable stopping long running scripts
|
||||
bool eventFilter(QObject *, QEvent *e);
|
||||
|
||||
QStackedWidget *stack;
|
||||
QSplitter *splitter;
|
||||
QSplitter *leftsplitter;
|
||||
QCheckBox *plotOnChartSetting;
|
||||
|
||||
private:
|
||||
Context *context;
|
||||
|
||||
226
src/R/RTool.cpp
226
src/R/RTool.cpp
@@ -36,6 +36,7 @@
|
||||
#include "Zones.h"
|
||||
#include "HrZones.h"
|
||||
#include "PaceZones.h"
|
||||
#include "GenericChart.h"
|
||||
|
||||
// Structure used to register routines has changed in v3.4 of R
|
||||
//
|
||||
@@ -132,6 +133,9 @@ RTool::RTool()
|
||||
{ "GC.season.meanmax", (DL_FUNC) &RTool::seasonMeanmax, 0,0 },
|
||||
{ "GC.season.peaks", (DL_FUNC) &RTool::seasonPeaks, 0,0 },
|
||||
{ "GC.season.measures", (DL_FUNC) &RTool::measures, 0,0 },
|
||||
{ "GC.chart.set", (DL_FUNC) &RTool::setChart, 0,0 },
|
||||
{ "GC.chart.addCurve", (DL_FUNC) &RTool::addCurve, 0,0 },
|
||||
{ "GC.chart.configureAxis", (DL_FUNC) &RTool::configureAxis, 0,0 },
|
||||
{ NULL, NULL, 0,0 }
|
||||
};
|
||||
|
||||
@@ -156,6 +160,9 @@ RTool::RTool()
|
||||
{ "GC.season.meanmax", (DL_FUNC) &RTool::seasonMeanmax, 0,0,0 },
|
||||
{ "GC.season.peaks", (DL_FUNC) &RTool::seasonPeaks, 0,0,0 },
|
||||
{ "GC.season.measures", (DL_FUNC) &RTool::measures, 0,0,0 },
|
||||
{ "GC.chart.set", (DL_FUNC) &RTool::setChart, 0,0,0 },
|
||||
{ "GC.chart.addCurve", (DL_FUNC) &RTool::addCurve, 0,0,0 },
|
||||
{ "GC.chart.configureAxis", (DL_FUNC) &RTool::configureAxis, 0,0,0 },
|
||||
{ NULL, NULL, 0,0,0 }
|
||||
};
|
||||
|
||||
@@ -192,6 +199,9 @@ RTool::RTool()
|
||||
{ "GC.season.pmc", (DL_FUNC) &RTool::pmc, 2 },
|
||||
// return a data.frame of measure fields (all=FALSE, group="Body")
|
||||
{ "GC.season.measures", (DL_FUNC) &RTool::measures, 2 },
|
||||
{ "GC.chart.set", (DL_FUNC) &RTool::setChart, 6 },
|
||||
{ "GC.chart.addCurve", (DL_FUNC) &RTool::addCurve, 13 },
|
||||
{ "GC.chart.configureAxis", (DL_FUNC) &RTool::configureAxis, 10 },
|
||||
{ NULL, NULL, 0 }
|
||||
};
|
||||
|
||||
@@ -290,10 +300,33 @@ RTool::RTool()
|
||||
"GC.metrics <- function(all=FALSE, filter=\"\", compare=FALSE) { .Call(\"GC.season.metrics\", all, filter, compare) }\n"
|
||||
"GC.pmc <- function(all=FALSE, metric=\"BikeStress\") { .Call(\"GC.season.pmc\", all, metric) }\n"
|
||||
|
||||
// charts
|
||||
"GC.setChart <- function(title=\"\", type=1, animate=FALSE, legpos=2, stack=FALSE, orientation=2) { .Call(\"GC.chart.set\", title, type, animate, legpos ,stack, orientation)}\n"
|
||||
"GC.addCurve <- function(name=\"curve\", xseries=c(), yseries=c(), xname=\"xaxis\", yname=\"yaxis\", min=-1, max=-1, labels=c(), colors=c(), line=1,symbol=0,size=2,color=\"red\",opacity=100,opengl=TRUE) { .Call(\"GC.chart.addCurve\", name, xseries, yseries, xname, yname, labels, colors, line, symbol, size, color, opacity, opengl)}\n"
|
||||
"GC.setAxis <- function(name=\"xaxis\",visible=TRUE, align=-1, min=-1, max=-1, type=0, labelcolor=\"\", color=\"\", log=FALSE, categories=c()) { .Call(\"GC.chart.configureAxis\", name, visible, align, min, max, type, labelcolor,color,log,categories)}\n"
|
||||
|
||||
// constants
|
||||
"GC.HORIZONTAL<-1\n"
|
||||
"GC.VERTICAL<-2\n"
|
||||
"GC.ALIGN.TOP<-2\n"
|
||||
"GC.ALIGN.BOTTOM<-0\n"
|
||||
"GC.ALIGN.LEFT<-1\n"
|
||||
"GC.ALIGN.RIGHT<-3\n"
|
||||
"GC.LINE.NONE<-0\n"
|
||||
"GC.LINE.SOLID<-1\n"
|
||||
"GC.LINE.DASH<-2\n"
|
||||
"GC.LINE.DOT<-3\n"
|
||||
"GC.LINE.DASHDOT<-4\n"
|
||||
"GC.CHART.LINE<-1\n"
|
||||
"GC.CHART.SCATTER<-2\n"
|
||||
"GC.CHART.BAR<-3\n"
|
||||
"GC.CHART.PIE<-4\n"
|
||||
|
||||
// version and build
|
||||
"GC.version <- function() { return(\"%1\") }\n"
|
||||
"GC.build <- function() { return(%2) }\n"
|
||||
"par.default <- par()\n")
|
||||
"par.default <- par()\n"
|
||||
)
|
||||
.arg(VERSION_STRING)
|
||||
.arg(VERSION_LATEST)
|
||||
.arg("https://cloud.r-project.org/"));
|
||||
@@ -3950,3 +3983,194 @@ RTool::dfForActivityXData(RideFile*f, QString name)
|
||||
// return a valid result
|
||||
return ans;
|
||||
}
|
||||
|
||||
//
|
||||
// Working with Generic Charts
|
||||
//
|
||||
SEXP
|
||||
RTool::setChart(SEXP title, SEXP type, SEXP animate, SEXP legpos, SEXP stack, SEXP orientation)
|
||||
{
|
||||
|
||||
if (rtool == NULL || rtool->context == NULL || rtool->chart == NULL) return Rf_allocVector(INTSXP, 0);
|
||||
|
||||
GenericChartInfo info;
|
||||
|
||||
// title
|
||||
PROTECT(title=Rf_coerceVector(title, STRSXP));
|
||||
info.title=QString(CHAR(STRING_ELT(title,0)));
|
||||
UNPROTECT(1);
|
||||
|
||||
// type
|
||||
PROTECT(type=Rf_coerceVector(type,INTSXP));
|
||||
info.type=INTEGER(type)[0];
|
||||
UNPROTECT(1);
|
||||
|
||||
// animation
|
||||
animate = Rf_coerceVector(animate, LGLSXP);
|
||||
info.animate = LOGICAL(animate)[0];
|
||||
|
||||
// legend position
|
||||
PROTECT(legpos=Rf_coerceVector(legpos,INTSXP));
|
||||
info.legendpos=INTEGER(legpos)[0];
|
||||
UNPROTECT(1);
|
||||
|
||||
// stack
|
||||
stack = Rf_coerceVector(stack, LGLSXP);
|
||||
info.stack = LOGICAL(stack)[0];
|
||||
|
||||
// type
|
||||
PROTECT(orientation=Rf_coerceVector(orientation,INTSXP));
|
||||
info.orientation=INTEGER(orientation)[0];
|
||||
UNPROTECT(1);
|
||||
|
||||
// call generic chart
|
||||
rtool->chart->chart->initialiseChart(info.title, info.type, info.animate, info.legendpos, info.stack, info.orientation);
|
||||
|
||||
// return 0
|
||||
return Rf_allocVector(INTSXP,0);
|
||||
}
|
||||
|
||||
SEXP
|
||||
RTool::addCurve(SEXP name, SEXP xseries, SEXP yseries, SEXP xname, SEXP yname, SEXP labels, SEXP colors,
|
||||
SEXP line, SEXP symbol, SEXP size, SEXP color, SEXP opacity, SEXP opengl)
|
||||
{
|
||||
Q_UNUSED(labels) //XXX todo
|
||||
Q_UNUSED(colors) //XXX todo
|
||||
|
||||
if (rtool == NULL || rtool->context == NULL || rtool->chart == NULL) return Rf_allocVector(INTSXP, 0);
|
||||
|
||||
GenericSeriesInfo info;
|
||||
|
||||
// name
|
||||
PROTECT(name=Rf_coerceVector(name, STRSXP));
|
||||
info.name=QString(CHAR(STRING_ELT(name,0)));
|
||||
UNPROTECT(1);
|
||||
|
||||
// xseries
|
||||
PROTECT(xseries=Rf_coerceVector(xseries,REALSXP));
|
||||
long vs=Rf_length(xseries);
|
||||
info.xseries.resize(vs);
|
||||
for(int i=0; i<vs; i++) info.xseries[i]=REAL(xseries)[i];
|
||||
UNPROTECT(1);
|
||||
|
||||
// yseries
|
||||
PROTECT(yseries=Rf_coerceVector(yseries,REALSXP));
|
||||
vs=Rf_length(yseries);
|
||||
info.yseries.resize(vs);
|
||||
for(int i=0; i<vs; i++) info.yseries[i]=REAL(yseries)[i];
|
||||
UNPROTECT(1);
|
||||
|
||||
// yname
|
||||
PROTECT(yname=Rf_coerceVector(yname, STRSXP));
|
||||
info.yname=QString(CHAR(STRING_ELT(yname,0)));
|
||||
UNPROTECT(1);
|
||||
|
||||
// xname
|
||||
PROTECT(xname=Rf_coerceVector(xname, STRSXP));
|
||||
info.xname=QString(CHAR(STRING_ELT(xname,0)));
|
||||
UNPROTECT(1);
|
||||
|
||||
// labels
|
||||
// XXX todo
|
||||
|
||||
// colors
|
||||
// XXX todo
|
||||
|
||||
// line
|
||||
PROTECT(line=Rf_coerceVector(line,INTSXP));
|
||||
info.line=INTEGER(line)[0];
|
||||
UNPROTECT(1);
|
||||
|
||||
// symbol
|
||||
PROTECT(symbol=Rf_coerceVector(symbol,INTSXP));
|
||||
info.symbol=INTEGER(symbol)[0];
|
||||
UNPROTECT(1);
|
||||
|
||||
// size
|
||||
PROTECT(size=Rf_coerceVector(size,REALSXP));
|
||||
info.size=REAL(size)[0];
|
||||
UNPROTECT(1);
|
||||
|
||||
// color
|
||||
PROTECT(color=Rf_coerceVector(color, STRSXP));
|
||||
info.color=QString(CHAR(STRING_ELT(color,0)));
|
||||
UNPROTECT(1);
|
||||
|
||||
// opacity
|
||||
PROTECT(opacity=Rf_coerceVector(opacity,INTSXP));
|
||||
info.opacity=INTEGER(opacity)[0];
|
||||
UNPROTECT(1);
|
||||
|
||||
// opengl
|
||||
opengl = Rf_coerceVector(opengl, LGLSXP);
|
||||
info.opengl = LOGICAL(opengl)[0];
|
||||
|
||||
// add to chart
|
||||
rtool->chart->chart->addCurve(info.name, info.xseries, info.yseries, info.xname, info.yname, info.labels, info.colors, info.line, info.symbol, info.size, info.color, info.opacity, info.opengl);
|
||||
|
||||
// return 0
|
||||
return Rf_allocVector(INTSXP,0);
|
||||
}
|
||||
|
||||
SEXP
|
||||
RTool::configureAxis(SEXP name, SEXP visible, SEXP align, SEXP min, SEXP max,
|
||||
SEXP type, SEXP labelcolor, SEXP axiscolor, SEXP log, SEXP categories)
|
||||
{
|
||||
Q_UNUSED(align) // we always pass -1 for now
|
||||
Q_UNUSED(categories) // XXX TODO
|
||||
|
||||
fprintf(stderr, "configure axis...\n"); fflush(stderr);
|
||||
|
||||
if (rtool == NULL || rtool->context == NULL || rtool->chart == NULL) return Rf_allocVector(INTSXP, 0);
|
||||
|
||||
GenericAxisInfo info;
|
||||
|
||||
// name
|
||||
PROTECT(name=Rf_coerceVector(name, STRSXP));
|
||||
info.name=QString(CHAR(STRING_ELT(name,0)));
|
||||
UNPROTECT(1);
|
||||
|
||||
// visible
|
||||
visible = Rf_coerceVector(visible, LGLSXP);
|
||||
info.visible = LOGICAL(visible)[0];
|
||||
|
||||
// align- ignore !
|
||||
|
||||
// min
|
||||
PROTECT(min=Rf_coerceVector(min,REALSXP));
|
||||
info.minx=REAL(min)[0];
|
||||
UNPROTECT(1);
|
||||
|
||||
// max
|
||||
PROTECT(max=Rf_coerceVector(max,REALSXP));
|
||||
info.maxx=REAL(max)[0];
|
||||
UNPROTECT(1);
|
||||
|
||||
// type
|
||||
PROTECT(type=Rf_coerceVector(type,INTSXP));
|
||||
info.type=static_cast<GenericAxisInfo::AxisInfoType>(INTEGER(type)[0]);
|
||||
UNPROTECT(1);
|
||||
|
||||
// labelcolor string
|
||||
PROTECT(labelcolor=Rf_coerceVector(labelcolor, STRSXP));
|
||||
info.labelcolor=QString(CHAR(STRING_ELT(labelcolor,0)));
|
||||
UNPROTECT(1);
|
||||
|
||||
// color string
|
||||
PROTECT(axiscolor=Rf_coerceVector(axiscolor, STRSXP));
|
||||
info.axiscolor=QString(CHAR(STRING_ELT(axiscolor,0)));
|
||||
UNPROTECT(1);
|
||||
|
||||
// log scale
|
||||
log = Rf_coerceVector(log, LGLSXP);
|
||||
info.log = LOGICAL(log)[0];
|
||||
|
||||
// categories
|
||||
//XXX todo
|
||||
|
||||
// add to chart -- XXX need to think on how to set axis colors -- or if we even should allow it
|
||||
rtool->chart->chart->configureAxis(info.name, info.visible, -1 /*info.align*/, info.minx, info.maxx, info.type, "" /*info.labelcolor.name()*/, ""/*info.axiscolor.name()*/, info.log, info.categories);
|
||||
|
||||
// return 0
|
||||
return Rf_allocVector(INTSXP,0);
|
||||
}
|
||||
|
||||
@@ -69,6 +69,13 @@ class RTool {
|
||||
static SEXP pmc(SEXP all, SEXP metric);
|
||||
static SEXP measures(SEXP all, SEXP group);
|
||||
|
||||
// charts
|
||||
static SEXP setChart(SEXP title, SEXP type, SEXP animate, SEXP legpos, SEXP stack, SEXP orientation);
|
||||
static SEXP addCurve(SEXP name, SEXP xseries, SEXP yseries, SEXP xname, SEXP yname, SEXP labels, SEXP colors,
|
||||
SEXP line, SEXP symbol, SEXP size, SEXP color, SEXP opacity, SEXP opengl);
|
||||
static SEXP configureAxis(SEXP name, SEXP visible, SEXP align, SEXP min, SEXP max,
|
||||
SEXP type, SEXP labelcolor, SEXP color, SEXP log, SEXP categories);
|
||||
|
||||
bool starting;
|
||||
bool failed;
|
||||
bool cancelled;
|
||||
|
||||
Reference in New Issue
Block a user