From 9effcfeef134e45993f048f0e30e2faa3616b1ae Mon Sep 17 00:00:00 2001 From: Mark Liversedge Date: Thu, 14 Apr 2016 21:16:13 +0100 Subject: [PATCH] R Console exceptions .. don't crash when cannot initialise (e.g. when RInside not available or R is not installed) .. handle messaging via signals to trap 'late' messages from the R Runtime --- src/Charts/RChart.cpp | 38 +++++++++++++-------- src/Charts/RChart.h | 34 +------------------ src/Charts/RTool.cpp | 79 +++++++++++++++++++++++++++---------------- src/Charts/RTool.h | 30 ++++++++++++++++ src/Core/Context.h | 4 +++ 5 files changed, 108 insertions(+), 77 deletions(-) diff --git a/src/Charts/RChart.cpp b/src/Charts/RChart.cpp index 51bd3d5cf..ba2af8584 100644 --- a/src/Charts/RChart.cpp +++ b/src/Charts/RChart.cpp @@ -33,6 +33,7 @@ RConsole::RConsole(Context *context, QWidget *parent) putData(GCColor::invertColor(GColor(CPLOTBACKGROUND)), "\n> "); connect(context, SIGNAL(configChanged(qint32)), this, SLOT(configChanged(qint32))); + connect(context, SIGNAL(rMessage(QString)), this, SLOT(rMessage(QString))); // history position hpos=0; @@ -52,6 +53,11 @@ RConsole::configChanged(qint32) setStyleSheet(TabView::ourStyleSheet()); } +void +RConsole::rMessage(QString x) +{ + putData(GColor(CPLOTMARKER), x); +} void RConsole::putData(QColor color, QString string) { @@ -133,7 +139,7 @@ void RConsole::keyPressEvent(QKeyEvent *e) SEXP ret = rtool->R->parseEval(line.toStdString()); // if this isn't an assignment then print the result - if(!line.contains("<-")) Rcpp::print(ret); + if(!Rf_isNull(ret) && !line.contains("<-")) Rcpp::print(ret); QStringList &response = rtool->callbacks->getConsoleOutput(); putData(GColor(CPLOTMARKER), response.join("")); @@ -216,20 +222,24 @@ RChart::RChart(Context *context) : GcChartWindow(context) mainLayout->setContentsMargins(2,0,2,2); setChartLayout(mainLayout); - splitter = new QSplitter(Qt::Horizontal, this); - splitter->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); - splitter->setHandleWidth(1); - mainLayout->addWidget(splitter); + // if we failed to startup the RInside properly + // then disable the RConsole altogether. + if (rtool->R) { + splitter = new QSplitter(Qt::Horizontal, this); + splitter->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); + splitter->setHandleWidth(1); + mainLayout->addWidget(splitter); - console = new RConsole(context, this); - splitter->addWidget(console); - QWidget *surface = new QSvgWidget(this); - surface->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); - splitter->addWidget(surface); + console = new RConsole(context, this); + splitter->addWidget(console); + QWidget *surface = new QSvgWidget(this); + surface->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); + splitter->addWidget(surface); - QPalette p = palette(); - p.setColor(QPalette::Base, GColor(CPLOTBACKGROUND)); - p.setColor(QPalette::Text, GCColor::invertColor(GColor(CPLOTBACKGROUND))); - surface->setPalette(p); + QPalette p = palette(); + p.setColor(QPalette::Base, GColor(CPLOTBACKGROUND)); + p.setColor(QPalette::Text, GCColor::invertColor(GColor(CPLOTBACKGROUND))); + surface->setPalette(p); + } } diff --git a/src/Charts/RChart.h b/src/Charts/RChart.h index 09d49653b..51bb03c45 100644 --- a/src/Charts/RChart.h +++ b/src/Charts/RChart.h @@ -33,39 +33,6 @@ #include "Context.h" #include "Athlete.h" -class RInside; -class RCallbacks; -class QString; - -extern RInside *gc_RInside; -extern RCallbacks *gc_RCallbacks; -extern QString gc_RVersion; -extern Context *gc_RContext; - -// global singleton catches output from R interpreter -// first come first served on output -class RCallbacks : public Callbacks { - public: - // see inst/includes/Callbacks.h for a list of all overrideable methods - virtual void WriteConsole(const std::string& line, int type) { - //qDebug()<<"Console>>" <set_callbacks(callbacks); + bool failed = false; + try { - // lets get the version early for the about dialog - R->parseEvalNT("print(R.version.string)"); - QStringList &strings = callbacks->getConsoleOutput(); - if (strings.count() == 3) { - QRegExp exp("^.*([0-9]+\\.[0-9]+\\.[0-9]+).*$"); - if (exp.exactMatch(strings[1])) version = exp.cap(1); - else version = strings[1]; + R = new RInside(argc,argv); + callbacks = new RCallbacks; + R->set_callbacks(callbacks); + + // lets get the version early for the about dialog + R->parseEvalNT("print(R.version.string)"); + QStringList &strings = callbacks->getConsoleOutput(); + if (strings.count() == 3) { + QRegExp exp("^.*([0-9]+\\.[0-9]+\\.[0-9]+).*$"); + if (exp.exactMatch(strings[1])) version = exp.cap(1); + else version = strings[1]; + } + strings.clear(); + + // set the "GC" object and methods + context = NULL; + (*R)["GC.version"] = VERSION_STRING; + (*R)["GC.build"] = VERSION_LATEST; + + // Access into the GC data + (*R)["GC.activities"] = Rcpp::InternalFunction(RTool::activities); + (*R)["GC.activity"] = Rcpp::InternalFunction(RTool::activity); + + // TBD + // GC.metrics - list of activities and their metrics + // GC.activity - single activity and its data series + // GC.seasons - configured seasons + // GC.season - currently selected season + // GC.config - configuration (zones, units etc) + + // the following are already set in RChart on a per call basis + // "GC.athlete" "GC.athlete.home" + + } catch(std::exception& ex) { + + qDebug()<<"RInside error:" << ex.what(); + failed = true; + + } catch(...) { + + failed = true; } - strings.clear(); - - // set the "GC" object and methods - context = NULL; - (*R)["GC.version"] = VERSION_STRING; - (*R)["GC.build"] = VERSION_LATEST; - - // Access into the GC data - (*R)["GC.activities"] = Rcpp::InternalFunction(RTool::activities); - (*R)["GC.activity"] = Rcpp::InternalFunction(RTool::activity); - - // TBD - // GC.metrics - list of activities and their metrics - // GC.activity - single activity and its data series - // GC.seasons - configured seasons - // GC.season - currently selected season - // GC.config - configuration (zones, units etc) - - // the following are already set in RChart on a per call basis - // "GC.athlete" "GC.athlete.home" + // ack, disable R runtime + if (failed) { + qDebug() << "RInside/Rcpp failed to start, RConsole disabled."; + version = "none"; + R = NULL; + } } Rcpp::DatetimeVector diff --git a/src/Charts/RTool.h b/src/Charts/RTool.h index f4c214663..52d2c1cfb 100644 --- a/src/Charts/RTool.h +++ b/src/Charts/RTool.h @@ -21,6 +21,7 @@ #ifndef _GC_RTool_h +class RCallbacks; class RTool { public: @@ -35,5 +36,34 @@ class RTool { static Rcpp::DatetimeVector activities(); }; +// there is a global instance created in main extern RTool *rtool; + +// global singleton catches output from R interpreter +// first come first served on output +class RCallbacks : public Callbacks { + + public: + // see inst/includes/Callbacks.h for a list of all overrideable methods + virtual void WriteConsole(const std::string& line, int type) { + //qDebug()<<"Console>>" <context) rtool->context->notifyRMessage(QString::fromStdString(line)); + else strings << QString::fromStdString(line); + }; + + //virtual void ShowMessage(const char* message) { + //qDebug()<<"M:" << QString(message); + //strings << QString(message); + //} + + //virtual bool has_ShowMessage() { return true; } + virtual bool has_WriteConsole() { return true; } + + QStringList &getConsoleOutput() { + return strings; + } + private: + QStringList strings; +}; + #endif diff --git a/src/Core/Context.h b/src/Core/Context.h index 0b1622088..95a6fe344 100644 --- a/src/Core/Context.h +++ b/src/Core/Context.h @@ -190,6 +190,7 @@ class Context : public QObject void notifyRefreshEnd() { emit refreshEnd(); } void notifyRefreshUpdate(QDate date) { emit refreshUpdate(date); } + void notifyRMessage(QString x) { emit rMessage(x); } void notifyCompareIntervals(bool state); void notifyCompareIntervalsChanged(); @@ -261,6 +262,9 @@ class Context : public QObject void pause(); void stop(); + // R messages + void rMessage(QString); + // comparing things void compareIntervalsStateChanged(bool); void compareIntervalsChanged();