mirror of
https://github.com/GoldenCheetah/GoldenCheetah.git
synced 2026-04-15 05:32:21 +00:00
Python Chart
.. added a python chart type, it doesn't execute code yet. Just a reimplementation of the RChart UX .. next we need to trap output and run code on selection before proceeding to setting an API for Data and Charting
This commit is contained in:
542
src/Charts/PythonChart.cpp
Normal file
542
src/Charts/PythonChart.cpp
Normal file
@@ -0,0 +1,542 @@
|
||||
/*
|
||||
* Copyright (c) 2017 Mark Liversedge (liversedge@gmail.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 51
|
||||
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "PythonChart.h"
|
||||
#include "PythonEmbed.h"
|
||||
//#include "PythonSyntax.h"
|
||||
|
||||
#include "Colors.h"
|
||||
#include "TabView.h"
|
||||
|
||||
// unique identifier for each chart
|
||||
static int id=0;
|
||||
|
||||
PythonConsole::PythonConsole(Context *context, PythonChart *parent)
|
||||
: QTextEdit(parent)
|
||||
, context(context), localEchoEnabled(true), parent(parent)
|
||||
{
|
||||
setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding));
|
||||
setFrameStyle(QFrame::NoFrame);
|
||||
setAcceptRichText(false);
|
||||
document()->setMaximumBlockCount(512); // lets not get carried away!
|
||||
putData(GColor(CPLOTMARKER), QString(tr("Python Console (%1)").arg(python->version)));
|
||||
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;
|
||||
|
||||
// set unique runtimeid name
|
||||
chartid = QString("gc%1").arg(id++);
|
||||
|
||||
configChanged(0);
|
||||
}
|
||||
|
||||
void
|
||||
PythonConsole::configChanged(qint32)
|
||||
{
|
||||
QFont courier("Courier", QFont().pointSize());
|
||||
setFont(courier);
|
||||
QPalette p = palette();
|
||||
p.setColor(QPalette::Base, GColor(CPLOTBACKGROUND));
|
||||
p.setColor(QPalette::Text, GCColor::invertColor(GColor(CPLOTBACKGROUND)));
|
||||
setPalette(p);
|
||||
setStyleSheet(TabView::ourStyleSheet());
|
||||
}
|
||||
|
||||
void
|
||||
PythonConsole::rMessage(QString x)
|
||||
{
|
||||
putData(GColor(CPLOTMARKER), x);
|
||||
}
|
||||
|
||||
void PythonConsole::putData(QColor color, QString string)
|
||||
{
|
||||
// change color...
|
||||
setTextColor(color);
|
||||
putData(string);
|
||||
}
|
||||
|
||||
|
||||
void PythonConsole::putData(QString data)
|
||||
{
|
||||
insertPlainText(QString(data));
|
||||
|
||||
QScrollBar *bar = verticalScrollBar();
|
||||
bar->setValue(bar->maximum());
|
||||
}
|
||||
|
||||
void PythonConsole::setLocalEchoEnabled(bool set)
|
||||
{
|
||||
localEchoEnabled = set;
|
||||
}
|
||||
|
||||
void PythonConsole::keyPressEvent(QKeyEvent *e)
|
||||
{
|
||||
// if running a script we only process ESC
|
||||
if (python && python->canvas) {
|
||||
if (e->type() != QKeyEvent::KeyPress || e->key() != Qt::Key_Escape) return;
|
||||
}
|
||||
|
||||
// otherwise lets go
|
||||
switch (e->key()) {
|
||||
case Qt::Key_Up:
|
||||
if (hpos) {
|
||||
hpos--;
|
||||
setCurrentLine(history[hpos]);
|
||||
}
|
||||
break;
|
||||
|
||||
case Qt::Key_Down:
|
||||
if (hpos < history.count()-1) {
|
||||
hpos++;
|
||||
setCurrentLine(history[hpos]);
|
||||
}
|
||||
break;
|
||||
|
||||
// you can always go to the right
|
||||
case Qt::Key_Right:
|
||||
QTextEdit::keyPressEvent(e);
|
||||
break;
|
||||
|
||||
// you can only delete or move left from past first character
|
||||
case Qt::Key_Left:
|
||||
case Qt::Key_Backspace:
|
||||
if (textCursor().position() - textCursor().block().position() > 2) QTextEdit::keyPressEvent(e);
|
||||
break;
|
||||
|
||||
case Qt::Key_Escape: // R typically uses ESC to cancel
|
||||
case Qt::Key_C:
|
||||
{
|
||||
Qt::KeyboardModifiers kmod = static_cast<QInputEvent*>(e)->modifiers();
|
||||
bool ctrl = (kmod & Qt::ControlModifier) != 0;
|
||||
|
||||
if (e->key() == Qt::Key_Escape || ctrl) {
|
||||
|
||||
// are we doing something?
|
||||
if (python && python->canvas) {
|
||||
python->cancel();
|
||||
}
|
||||
|
||||
// ESC or ^C needs to clear program and go to next line
|
||||
python->program.clear();
|
||||
|
||||
QTextCursor move = textCursor();
|
||||
move.movePosition(QTextCursor::End);
|
||||
setTextCursor(move);
|
||||
|
||||
// new prompt
|
||||
putData("\n");
|
||||
putData(GCColor::invertColor(GColor(CPLOTBACKGROUND)), "> ");
|
||||
|
||||
} else {
|
||||
// normal C just do the usual
|
||||
if (localEchoEnabled) QTextEdit::keyPressEvent(e);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case Qt::Key_Enter:
|
||||
case Qt::Key_Return:
|
||||
{
|
||||
QTextCursor move = textCursor();
|
||||
move.movePosition(QTextCursor::End);
|
||||
setTextCursor(move);
|
||||
|
||||
QString line = currentLine();
|
||||
if (line.length() > 1) line = line.mid(2, line.length()-2);
|
||||
else line = "";
|
||||
|
||||
putData("\n");
|
||||
|
||||
if (line != "") {
|
||||
|
||||
history << line;
|
||||
hpos = history.count();
|
||||
|
||||
// lets run it
|
||||
//qDebug()<<"RUN:" << line;
|
||||
#if 0
|
||||
// set the context for the call - used by all the
|
||||
// R functions to access the athlete data/model etc
|
||||
python->context = context;
|
||||
python->canvas = parent->canvas;
|
||||
python->chart = parent;
|
||||
|
||||
try {
|
||||
|
||||
// replace $$ with chart identifier (to avoid shared data)
|
||||
line = line.replace("$$", chartid);
|
||||
|
||||
// we always get an array of strings
|
||||
// so only print it out if its actually been set
|
||||
SEXP ret = NULL;
|
||||
|
||||
python->cancelled = false;
|
||||
int rc = python->parseEval(line, ret);
|
||||
|
||||
// if this isn't an assignment then print the result
|
||||
// bit hacky, there must be a better way!
|
||||
if(rc == 0 && ret != NULL && !Rf_isNull(ret) && !line.contains("<-") && !line.contains("print"))
|
||||
Rf_PrintValue(ret);
|
||||
|
||||
putData(GColor(CPLOTMARKER), python->messages.join(""));
|
||||
python->messages.clear();
|
||||
|
||||
} catch(std::exception& ex) {
|
||||
|
||||
putData(QColor(Qt::red), QString("%1\n").arg(QString(ex.what())));
|
||||
putData(QColor(Qt::red), python->messages.join(""));
|
||||
python->messages.clear();
|
||||
|
||||
} catch(...) {
|
||||
|
||||
putData(QColor(Qt::red), "error: general exception.\n");
|
||||
putData(QColor(Qt::red), python->messages.join(""));
|
||||
python->messages.clear();
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
// clear context
|
||||
python->context = NULL;
|
||||
python->canvas = NULL;
|
||||
python->chart = NULL;
|
||||
}
|
||||
|
||||
// prompt ">"
|
||||
putData(GCColor::invertColor(GColor(CPLOTBACKGROUND)), "> ");
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if (localEchoEnabled) QTextEdit::keyPressEvent(e);
|
||||
emit getData(e->text().toLocal8Bit());
|
||||
}
|
||||
|
||||
// if we edit or anything reset the history position
|
||||
if (e->key()!= Qt::Key_Up && e->key()!=Qt::Key_Down) hpos = history.count();
|
||||
}
|
||||
|
||||
void
|
||||
PythonConsole::setCurrentLine(QString p)
|
||||
{
|
||||
QTextCursor select = textCursor();
|
||||
|
||||
select.select(QTextCursor::LineUnderCursor);
|
||||
select.removeSelectedText();
|
||||
putData(GCColor::invertColor(GColor(CPLOTBACKGROUND)), "> ");
|
||||
putData(p);
|
||||
}
|
||||
|
||||
QString
|
||||
PythonConsole::currentLine()
|
||||
{
|
||||
return textCursor().block().text().trimmed();
|
||||
}
|
||||
|
||||
void PythonConsole::mousePressEvent(QMouseEvent *e)
|
||||
{
|
||||
Q_UNUSED(e)
|
||||
setFocus();
|
||||
}
|
||||
|
||||
void PythonConsole::mouseDoubleClickEvent(QMouseEvent *e)
|
||||
{
|
||||
Q_UNUSED(e)
|
||||
}
|
||||
|
||||
void PythonConsole::contextMenuEvent(QContextMenuEvent *e)
|
||||
{
|
||||
Q_UNUSED(e)
|
||||
}
|
||||
|
||||
PythonChart::PythonChart(Context *context, bool ridesummary) : GcChartWindow(context), context(context), ridesummary(ridesummary)
|
||||
{
|
||||
setControls(NULL);
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||
mainLayout->setSpacing(0);
|
||||
mainLayout->setContentsMargins(2,0,2,2);
|
||||
setChartLayout(mainLayout);
|
||||
|
||||
// if we failed to startup embedded R properly
|
||||
// then disable the PythonConsole altogether.
|
||||
if (python) {
|
||||
|
||||
// reveal controls
|
||||
QHBoxLayout *rev = new QHBoxLayout();
|
||||
showCon = new QCheckBox(tr("Show Console"), this);
|
||||
showCon->setChecked(true);
|
||||
|
||||
rev->addStretch();
|
||||
rev->addWidget(showCon);
|
||||
rev->addStretch();
|
||||
|
||||
setRevealLayout(rev);
|
||||
|
||||
leftsplitter = new QSplitter(Qt::Vertical, this);
|
||||
leftsplitter->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding));
|
||||
leftsplitter->setHandleWidth(1);
|
||||
|
||||
// LHS
|
||||
script = new QTextEdit(this);
|
||||
script->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding));
|
||||
script->setFrameStyle(QFrame::NoFrame);
|
||||
script->setAcceptRichText(false);
|
||||
QFont courier("Courier", QFont().pointSize());
|
||||
script->setFont(courier);
|
||||
QPalette p = palette();
|
||||
p.setColor(QPalette::Base, GColor(CPLOTBACKGROUND));
|
||||
p.setColor(QPalette::Text, GCColor::invertColor(GColor(CPLOTBACKGROUND)));
|
||||
script->setPalette(p);
|
||||
script->setStyleSheet(TabView::ourStyleSheet());
|
||||
|
||||
// syntax highlighter
|
||||
setScript("##\n## Python program will run on selection.\n##\n");
|
||||
|
||||
leftsplitter->addWidget(script);
|
||||
console = new PythonConsole(context, this);
|
||||
console->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding));
|
||||
leftsplitter->addWidget(console);
|
||||
|
||||
splitter = new QSplitter(Qt::Horizontal, this);
|
||||
splitter->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding));
|
||||
splitter->setHandleWidth(1);
|
||||
mainLayout->addWidget(splitter);
|
||||
|
||||
splitter->addWidget(leftsplitter);
|
||||
|
||||
canvas = new QWidget(this);
|
||||
canvas->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding));
|
||||
splitter->addWidget(canvas);
|
||||
|
||||
// make splitter reasonable
|
||||
QList<int> sizes;
|
||||
sizes << 300 << 500;
|
||||
splitter->setSizes(sizes);
|
||||
|
||||
if (ridesummary) {
|
||||
connect(this, SIGNAL(rideItemChanged(RideItem*)), this, SLOT(runScript()));
|
||||
|
||||
// refresh when comparing
|
||||
connect(context, SIGNAL(compareIntervalsStateChanged(bool)), this, SLOT(runScript()));
|
||||
connect(context, SIGNAL(compareIntervalsChanged()), this, SLOT(runScript()));
|
||||
|
||||
} else {
|
||||
connect(this, SIGNAL(dateRangeChanged(DateRange)), this, SLOT(runScript()));
|
||||
|
||||
// refresh when comparing
|
||||
connect(context, SIGNAL(compareDateRangesStateChanged(bool)), this, SLOT(runScript()));
|
||||
connect(context, SIGNAL(compareDateRangesChanged()), this, SLOT(runScript()));
|
||||
}
|
||||
|
||||
// we apply BOTH filters, so update when either change
|
||||
connect(context, SIGNAL(filterChanged()), this, SLOT(runScript()));
|
||||
connect(context, SIGNAL(homeFilterChanged()), this, SLOT(runScript()));
|
||||
|
||||
// reveal controls
|
||||
connect(showCon, SIGNAL(stateChanged(int)), this, SLOT(showConChanged(int)));
|
||||
|
||||
// config changes
|
||||
connect(context, SIGNAL(configChanged(qint32)), this, SLOT(configChanged(qint32)));
|
||||
configChanged(CONFIG_APPEARANCE);
|
||||
|
||||
// filter ESC so we can stop scripts
|
||||
installEventFilter(this);
|
||||
installEventFilter(console);
|
||||
installEventFilter(splitter);
|
||||
installEventFilter(canvas);
|
||||
|
||||
} else {
|
||||
|
||||
// not starting
|
||||
script = NULL;
|
||||
splitter = NULL;
|
||||
console = NULL;
|
||||
canvas = NULL;
|
||||
showCon = NULL;
|
||||
leftsplitter = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
PythonChart::eventFilter(QObject *, QEvent *e)
|
||||
{
|
||||
// on resize event scale the display
|
||||
if (e->type() == QEvent::Resize) {
|
||||
//canvas->fitInView(canvas->sceneRect(), Qt::KeepAspectRatio);
|
||||
}
|
||||
|
||||
// not running a script
|
||||
if (!python) return false;
|
||||
|
||||
// is it an ESC key?
|
||||
if (e->type() == QEvent::KeyPress && static_cast<QKeyEvent*>(e)->key() == Qt::Key_Escape) {
|
||||
// stop!
|
||||
python->cancel();
|
||||
return true;
|
||||
}
|
||||
// otherwise do nothing
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
PythonChart::configChanged(qint32)
|
||||
{
|
||||
if (!ridesummary) setProperty("color", GColor(CTRENDPLOTBACKGROUND));
|
||||
else setProperty("color", GColor(CPLOTBACKGROUND));
|
||||
|
||||
// tinted palette for headings etc
|
||||
QPalette palette;
|
||||
palette.setBrush(QPalette::Window, QBrush(GColor(CPLOTBACKGROUND)));
|
||||
palette.setColor(QPalette::WindowText, GColor(CPLOTMARKER));
|
||||
palette.setColor(QPalette::Text, GColor(CPLOTMARKER));
|
||||
palette.setColor(QPalette::Base, GCColor::alternateColor(GColor(CPLOTBACKGROUND)));
|
||||
setPalette(palette);
|
||||
}
|
||||
|
||||
void
|
||||
PythonChart::setConsole(bool show)
|
||||
{
|
||||
if (showCon) showCon->setChecked(show);
|
||||
}
|
||||
|
||||
void
|
||||
PythonChart::showConChanged(int state)
|
||||
{
|
||||
if (leftsplitter) leftsplitter->setVisible(state);
|
||||
}
|
||||
|
||||
QString
|
||||
PythonChart::getScript() const
|
||||
{
|
||||
if (python && script) return script->toPlainText();
|
||||
else return text;
|
||||
}
|
||||
void
|
||||
PythonChart::setScript(QString string)
|
||||
{
|
||||
if (python && script) {
|
||||
script->setText(string);
|
||||
//new RSyntax(script->document());
|
||||
}
|
||||
text = string;
|
||||
}
|
||||
|
||||
QString
|
||||
PythonChart::getState() const
|
||||
{
|
||||
//XXX FIXME
|
||||
//if (python && splitter) return QString(splitter->saveState());
|
||||
//else return "";
|
||||
return "";
|
||||
}
|
||||
|
||||
void
|
||||
PythonChart::setState(QString)
|
||||
{
|
||||
//XXX FIXME
|
||||
//if (python && splitter && b != "") splitter->restoreState(QByteArray(b.toLatin1()));
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
PythonChart::runScript()
|
||||
{
|
||||
// don't run until we can be seen!
|
||||
if (!isVisible()) return;
|
||||
|
||||
if (script->toPlainText() != "") {
|
||||
|
||||
// hourglass .. for long running ones this helps user know its busy
|
||||
QApplication::setOverrideCursor(Qt::WaitCursor);
|
||||
|
||||
// turn off updates for a sec
|
||||
setUpdatesEnabled(false);
|
||||
|
||||
// run it !!
|
||||
python->context = context;
|
||||
python->canvas = canvas;
|
||||
python->chart = this;
|
||||
|
||||
// set default page size
|
||||
//python->width = python->height = 0; // sets the canvas to the window size
|
||||
|
||||
// set to defaults with gc applied
|
||||
python->cancelled = false;
|
||||
#if 0
|
||||
python->R->parseEvalQNT("par(par.gc)\n");
|
||||
|
||||
QString line = script->toPlainText();
|
||||
|
||||
try {
|
||||
|
||||
// replace $$ with chart identifier (to avoid shared data)
|
||||
line = line.replace("$$", console->chartid);
|
||||
|
||||
// run it
|
||||
python->R->parseEval(line);
|
||||
|
||||
// output on console
|
||||
if (python->messages.count()) {
|
||||
console->putData("\n");
|
||||
console->putData(GColor(CPLOTMARKER), python->messages.join(""));
|
||||
python->messages.clear();
|
||||
}
|
||||
|
||||
} catch(std::exception& ex) {
|
||||
|
||||
console->putData(QColor(Qt::red), QString("\n%1\n").arg(QString(ex.what())));
|
||||
console->putData(QColor(Qt::red), python->messages.join(""));
|
||||
python->messages.clear();
|
||||
|
||||
// clear
|
||||
canvas->newPage();
|
||||
|
||||
} catch(...) {
|
||||
|
||||
console->putData(QColor(Qt::red), "\nerror: general exception.\n");
|
||||
console->putData(QColor(Qt::red), python->messages.join(""));
|
||||
python->messages.clear();
|
||||
|
||||
// clear
|
||||
canvas->newPage();
|
||||
}
|
||||
#endif
|
||||
// turn off updates for a sec
|
||||
setUpdatesEnabled(true);
|
||||
|
||||
// reset cursor
|
||||
QApplication::restoreOverrideCursor();
|
||||
|
||||
// if the program expects more we clear it, otherwise
|
||||
// weird things can happen!
|
||||
//python->R->program.clear();
|
||||
|
||||
// scale to fit and center on output
|
||||
//canvas->fitInView(canvas->sceneRect(), Qt::KeepAspectRatio);
|
||||
|
||||
// clear context
|
||||
python->context = NULL;
|
||||
python->canvas = NULL;
|
||||
python->chart = NULL;
|
||||
}
|
||||
}
|
||||
129
src/Charts/PythonChart.h
Normal file
129
src/Charts/PythonChart.h
Normal file
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Copyright (c) 2017 Mark Liversedge (liversedge@gmail.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 51
|
||||
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef _GC_PythonChart_h
|
||||
#define _GC_PythonChart_h 1
|
||||
|
||||
#include <PythonEmbed.h>
|
||||
|
||||
#include <QString>
|
||||
#include <QDebug>
|
||||
#include <QColor>
|
||||
#include <QTextEdit>
|
||||
#include <QScrollBar>
|
||||
#include <QCheckBox>
|
||||
#include <QSplitter>
|
||||
#include <QByteArray>
|
||||
#include <string.h>
|
||||
|
||||
#include "GoldenCheetah.h"
|
||||
#include "Context.h"
|
||||
#include "Athlete.h"
|
||||
#include "RCanvas.h"
|
||||
|
||||
class PythonChart;
|
||||
|
||||
// a console widget to type commands and display response
|
||||
class PythonConsole : public QTextEdit {
|
||||
|
||||
Q_OBJECT
|
||||
|
||||
signals:
|
||||
void getData(const QByteArray &data);
|
||||
|
||||
public slots:
|
||||
void configChanged(qint32);
|
||||
void rMessage(QString);
|
||||
|
||||
public:
|
||||
explicit PythonConsole(Context *context, PythonChart *parent = 0);
|
||||
|
||||
void putData(QString data);
|
||||
void putData(QColor color, QString data);
|
||||
void setLocalEchoEnabled(bool set);
|
||||
|
||||
// return the current "line" of text
|
||||
QString currentLine();
|
||||
void setCurrentLine(QString);
|
||||
|
||||
QStringList history;
|
||||
int hpos;
|
||||
QString chartid;
|
||||
|
||||
protected:
|
||||
virtual void keyPressEvent(QKeyEvent *e);
|
||||
virtual void mousePressEvent(QMouseEvent *e);
|
||||
virtual void mouseDoubleClickEvent(QMouseEvent *e);
|
||||
virtual void contextMenuEvent(QContextMenuEvent *e);
|
||||
|
||||
private:
|
||||
Context *context;
|
||||
bool localEchoEnabled;
|
||||
PythonChart *parent;
|
||||
};
|
||||
|
||||
// the chart
|
||||
class PythonChart : public GcChartWindow {
|
||||
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(QString script READ getScript WRITE setScript USER true)
|
||||
Q_PROPERTY(QString state READ getState WRITE setState USER true)
|
||||
Q_PROPERTY(bool showConsole READ showConsole WRITE setConsole USER true)
|
||||
|
||||
public:
|
||||
PythonChart(Context *context, bool ridesummary);
|
||||
|
||||
// reveal
|
||||
bool hasReveal() { return true; }
|
||||
QCheckBox *showCon;
|
||||
|
||||
// receives all the events
|
||||
QTextEdit *script;
|
||||
PythonConsole *console;
|
||||
QWidget *canvas; // not yet!!
|
||||
|
||||
bool showConsole() const { return (showCon ? showCon->isChecked() : true); }
|
||||
void setConsole(bool);
|
||||
|
||||
QString getScript() const;
|
||||
void setScript(QString);
|
||||
|
||||
QString getState() const;
|
||||
void setState(QString);
|
||||
|
||||
public slots:
|
||||
void configChanged(qint32);
|
||||
void showConChanged(int state);
|
||||
void runScript();
|
||||
|
||||
protected:
|
||||
// enable stopping long running scripts
|
||||
bool eventFilter(QObject *, QEvent *e);
|
||||
|
||||
QSplitter *splitter;
|
||||
QSplitter *leftsplitter;
|
||||
|
||||
private:
|
||||
Context *context;
|
||||
QString text; // if Rtool not alive
|
||||
bool ridesummary;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
@@ -59,6 +59,9 @@
|
||||
#ifdef GC_WANT_R
|
||||
#include "RChart.h"
|
||||
#endif
|
||||
#ifdef GC_WANT_PYTHON
|
||||
#include "PythonChart.h"
|
||||
#endif
|
||||
#include "PlanningWindow.h"
|
||||
#ifdef GC_HAVE_OVERVIEW
|
||||
#include "OverviewWindow.h"
|
||||
@@ -78,7 +81,7 @@ GcWindowRegistry* GcWindows;
|
||||
void
|
||||
GcWindowRegistry::initialize()
|
||||
{
|
||||
static GcWindowRegistry GcWindowsInit[33] = {
|
||||
static GcWindowRegistry GcWindowsInit[34] = {
|
||||
// name GcWinID
|
||||
{ VIEW_HOME|VIEW_DIARY, tr("Metric Trends"),GcWindowTypes::LTM },
|
||||
{ VIEW_HOME|VIEW_DIARY, tr("Collection TreeMap"),GcWindowTypes::TreeMap },
|
||||
@@ -100,6 +103,7 @@ GcWindowRegistry::initialize()
|
||||
{ VIEW_ANALYSIS|VIEW_INTERVAL, tr("Map"),GcWindowTypes::RideMapWindow },
|
||||
{ VIEW_ANALYSIS, tr("R Chart"),GcWindowTypes::RConsole },
|
||||
{ VIEW_HOME, tr("R Chart "),GcWindowTypes::RConsoleSeason },
|
||||
{ VIEW_ANALYSIS|VIEW_HOME, tr("Python Chart"),GcWindowTypes::Python },
|
||||
//{ VIEW_ANALYSIS, tr("Bing Map"),GcWindowTypes::BingMap },
|
||||
{ VIEW_ANALYSIS, tr("2d Plot"),GcWindowTypes::Scatter },
|
||||
{ VIEW_ANALYSIS, tr("3d Plot"),GcWindowTypes::Model },
|
||||
@@ -176,6 +180,11 @@ GcWindowRegistry::newGcWindow(GcWinID id, Context *context)
|
||||
#else
|
||||
case GcWindowTypes::RConsole: returning = new GcChartWindow(context); break;
|
||||
case GcWindowTypes::RConsoleSeason: returning = new GcChartWindow(context); break;
|
||||
#endif
|
||||
#ifdef GC_WANT_PYTHON
|
||||
case GcWindowTypes::Python: returning = new PythonChart(context, true); break;
|
||||
#else
|
||||
case GcWindowTypes::Python: returning = new GcChartWindow(context); break;
|
||||
#endif
|
||||
case GcWindowTypes::Distribution: returning = new HistogramWindow(context, true); break;
|
||||
case GcWindowTypes::PerformanceManager:
|
||||
|
||||
@@ -68,7 +68,8 @@ enum gcwinid {
|
||||
RConsoleSeason = 39,
|
||||
SeasonPlan = 40,
|
||||
WebPageWindow = 41,
|
||||
Overview = 42
|
||||
Overview = 42,
|
||||
Python = 43
|
||||
};
|
||||
};
|
||||
typedef enum GcWindowTypes::gcwinid GcWinID;
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Mark Liversedge (liversedge@gmail.com)
|
||||
* Copyright (c) 2017 Mark Liversedge (liversedge@gmail.com)
|
||||
*
|
||||
* Additionally, for the original source used as a basis for this (RInside.cpp)
|
||||
* Released under the same GNU public license.
|
||||
*
|
||||
* Copyright (C) 2009 Dirk Eddelbuettel
|
||||
* Copyright (C) 2010 - 2012 Dirk Eddelbuettel and Romain Francois
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
@@ -66,3 +63,8 @@ PythonEmbed::PythonEmbed(const bool verbose, const bool interactive) : verbose(v
|
||||
|
||||
loaded = true;
|
||||
}
|
||||
|
||||
void
|
||||
PythonEmbed::cancel()
|
||||
{
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Mark Liversedge (liversedge@gmail.com)
|
||||
* Copyright (c) 2017 Mark Liversedge (liversedge@gmail.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
@@ -19,9 +19,13 @@
|
||||
#ifndef GC_PYTHONEMBED_H
|
||||
#define GC_PYTHONEMBED_H
|
||||
|
||||
#include <QWidget>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
|
||||
class Context;
|
||||
class PythonChart;
|
||||
|
||||
class PythonEmbed;
|
||||
extern PythonEmbed *python;
|
||||
|
||||
@@ -33,6 +37,14 @@ class PythonEmbed {
|
||||
PythonEmbed(const bool verbose=false, const bool interactive=false);
|
||||
~PythonEmbed();
|
||||
|
||||
// stop current execution
|
||||
void cancel();
|
||||
|
||||
// context for caller
|
||||
Context *context;
|
||||
PythonChart *chart;
|
||||
QWidget *canvas;
|
||||
|
||||
// the program being constructed/parsed
|
||||
QStringList program;
|
||||
|
||||
@@ -41,6 +53,7 @@ class PythonEmbed {
|
||||
|
||||
bool verbose;
|
||||
bool interactive;
|
||||
bool cancelled;
|
||||
|
||||
bool loaded;
|
||||
};
|
||||
|
||||
@@ -284,8 +284,8 @@ contains(DEFINES, "GC_WANT_PYTHON") {
|
||||
LIBS += $${PYTHONLIBS}
|
||||
|
||||
## Python integration
|
||||
HEADERS += Python/PythonEmbed.h
|
||||
SOURCES += Python/PythonEmbed.cpp
|
||||
HEADERS += Python/PythonEmbed.h Charts/PythonChart.h
|
||||
SOURCES += Python/PythonEmbed.cpp Charts/PythonChart.cpp
|
||||
|
||||
DEFINES += GC_HAVE_PYTHON
|
||||
|
||||
|
||||
Reference in New Issue
Block a user