From ab82ea004ffa9e2dfd7f36adc5f6b62199d364fc Mon Sep 17 00:00:00 2001 From: Mark Liversedge Date: Fri, 24 Nov 2017 19:32:07 +0000 Subject: [PATCH] ESC or ^C to stop long running Python script .. when a chart script gets caught in a runaway loop let the user interrupt it with the ESC or ^C key. --- src/Charts/PythonChart.cpp | 24 ++---------------------- src/Python/PythonEmbed.cpp | 19 +++++++++++++++++++ src/Python/PythonEmbed.h | 1 + 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/Charts/PythonChart.cpp b/src/Charts/PythonChart.cpp index 64246914e..c9c21967c 100644 --- a/src/Charts/PythonChart.cpp +++ b/src/Charts/PythonChart.cpp @@ -128,17 +128,13 @@ void PythonConsole::keyPressEvent(QKeyEvent *e) case Qt::Key_C: { - fprintf(stderr, "ESC PRESSED\n"); - Qt::KeyboardModifiers kmod = static_cast(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(); - } + python->cancel(); // ESC or ^C needs to clear program and go to next line python->program.clear(); @@ -386,7 +382,6 @@ PythonChart::eventFilter(QObject *, QEvent *e) // is it an ESC key? if (e->type() == QEvent::KeyPress && static_cast(e)->key() == Qt::Key_Escape) { // stop! - fprintf(stderr, "ESC hit!\n"); python->cancel(); return true; } @@ -453,26 +448,11 @@ PythonChart::setState(QString) //if (python && splitter && b != "") splitter->restoreState(QByteArray(b.toLatin1())); } -// from PythonEmbed -extern PyThreadState *mainThreadState; - +// this is executed in its own thread. void PythonChart::execScript(PythonChart *chart) { - //PyGILState_STATE gstate; - //gstate = PyGILState_Ensure(); - //PyThreadState *myThreadState = PyThreadState_New(mainThreadState->interp); - - // switch to me - //PyEval_RestoreThread(myThreadState); - - // exec python->runline(chart->script->toPlainText()); - - // save my state and release lock - // we discard return as we're about to give up anyway - //PyEval_SaveThread(); - //PyGILState_Release(gstate); } void diff --git a/src/Python/PythonEmbed.cpp b/src/Python/PythonEmbed.cpp index 41938b8fd..521fb4e4a 100644 --- a/src/Python/PythonEmbed.cpp +++ b/src/Python/PythonEmbed.cpp @@ -43,6 +43,7 @@ PythonEmbed::~PythonEmbed() PythonEmbed::PythonEmbed(const bool verbose, const bool interactive) : verbose(verbose), interactive(interactive) { loaded = false; + threadid=-1; name = QString("GoldenCheetah"); // tell python our program name @@ -95,6 +96,14 @@ void PythonEmbed::runline(QString line) PyGILState_STATE gstate; gstate = PyGILState_Ensure(); + // Get current thread ID via Python thread functions + PyObject* thread = PyImport_ImportModule("_thread"); + PyObject* get_ident = PyObject_GetAttrString(thread, "get_ident"); + PyObject* ident = PyObject_CallObject(get_ident, 0); + Py_DECREF(get_ident); + threadid = PyLong_AsLong(ident); + Py_DECREF(ident); + // run and generate errors etc messages.clear(); PyRun_SimpleString(line.toStdString().c_str()); @@ -117,9 +126,19 @@ void PythonEmbed::runline(QString line) PyObject_CallFunction(static_cast(clear), NULL); } PyGILState_Release(gstate); + threadid=-1; } void PythonEmbed::cancel() { + if (chart!=NULL && threadid != -1) { + PyGILState_STATE gstate; + gstate = PyGILState_Ensure(); + + // raise an exception to cancel the execution + PyThreadState_SetAsyncExc(threadid, PyExc_KeyboardInterrupt); + + PyGILState_Release(gstate); + } } diff --git a/src/Python/PythonEmbed.h b/src/Python/PythonEmbed.h index 9cfee5800..bb3a7635b 100644 --- a/src/Python/PythonEmbed.h +++ b/src/Python/PythonEmbed.h @@ -64,6 +64,7 @@ class PythonEmbed { bool cancelled; bool loaded; + long threadid; }; #endif