diff --git a/src/backends/maxima/maximaexpression.cpp b/src/backends/maxima/maximaexpression.cpp index 33d37847..9ec21eb1 100644 --- a/src/backends/maxima/maximaexpression.cpp +++ b/src/backends/maxima/maximaexpression.cpp @@ -1,810 +1,819 @@ /* 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. --- Copyright (C) 2009-2012 Alexander Rieder */ #include "maximaexpression.h" #include #include "maximasession.h" #include "textresult.h" #include "epsresult.h" #include "imageresult.h" #include "helpresult.h" #include "latexresult.h" #include "settings.h" #include #include #include #include #include #include #include #include MaximaExpression::MaximaExpression( Cantor::Session* session, bool internal ) : Cantor::Expression(session, internal), m_tempFile(nullptr), m_isHelpRequest(false), m_isPlot(false), m_plotResult(nullptr), m_plotResultIndex(-1), m_gotErrorContent(false) { } +MaximaExpression::~MaximaExpression() { + if(m_tempFile) + delete m_tempFile; +} + void MaximaExpression::evaluate() { //until we get the real output Id from maxima, set it to invalid setId(-1); m_isHelpRequest=false; - m_isPlot=false; m_gotErrorContent=false; + if(m_tempFile) - m_tempFile->deleteLater(); - m_tempFile=nullptr; - m_plotResult = nullptr; - m_plotResultIndex = -1; + { + delete m_tempFile; + m_tempFile = nullptr; + m_isPlot = false; + m_plotResult = nullptr; + m_plotResultIndex = -1; + } + //check if this is a ?command if(command().startsWith(QLatin1String("??")) || command().startsWith(QLatin1String("describe(")) || command().startsWith(QLatin1String("example(")) || command().startsWith(QLatin1String(":lisp(cl-info::info-exact"))) m_isHelpRequest=true; if(command().contains(QRegExp(QLatin1String("(?:plot2d|plot3d|contour_plot)\\s*\\([^\\)]"))) && MaximaSettings::self()->integratePlots() && !command().contains(QLatin1String("ps_file"))) { m_isPlot=true; #ifdef WITH_EPS m_tempFile=new QTemporaryFile(QDir::tempPath() + QLatin1String("/cantor_maxima-XXXXXX.eps" )); #else m_tempFile=new QTemporaryFile(QDir::tempPath() + QLatin1String("/cantor_maxima-XXXXXX.png")); #endif m_tempFile->open(); m_fileWatch.removePaths(m_fileWatch.files()); m_fileWatch.addPath(m_tempFile->fileName()); connect(&m_fileWatch, &QFileSystemWatcher::fileChanged, this, &MaximaExpression::imageChanged, Qt::UniqueConnection); } const QString& cmd=command(); bool isComment = true; int commentLevel = 0; bool inString = false; for (int i = 0; i < cmd.size(); ++i) { if (cmd[i] == QLatin1Char('\\')) { ++i; // skip the next character if (commentLevel == 0 && !inString) { isComment = false; } } else if (cmd[i] == QLatin1Char('"') && commentLevel == 0) { inString = !inString; isComment = false; } else if (cmd.mid(i,2) == QLatin1String("/*") && !inString) { ++commentLevel; ++i; } else if (cmd.mid(i,2) == QLatin1String("*/") && !inString) { if (commentLevel == 0) { qDebug() << "Comments mismatched!"; setErrorMessage(i18n("Error: Too many */")); setStatus(Cantor::Expression::Error); return; } ++i; --commentLevel; } else if (isComment && commentLevel == 0 && !cmd[i].isSpace()) { isComment = false; } } if (commentLevel > 0) { qDebug() << "Comments mismatched!"; setErrorMessage(i18n("Error: Too many /*")); setStatus(Cantor::Expression::Error); return; } if (inString) { qDebug() << "String not closed"; setErrorMessage(i18n("Error: expected \" before ;")); setStatus(Cantor::Expression::Error); return; } if(isComment) { setStatus(Cantor::Expression::Done); return; } session()->enqueueExpression(this); } void MaximaExpression::interrupt() { qDebug()<<"interrupting"; setStatus(Cantor::Expression::Interrupted); } QString MaximaExpression::internalCommand() { QString cmd=command(); if(m_isPlot) { if(!m_tempFile) { qDebug()<<"plotting without tempFile"; return QString(); } QString fileName = m_tempFile->fileName(); #ifdef WITH_EPS const QString psParam=QLatin1String("[gnuplot_ps_term_command, \"set size 1.0, 1.0; set term postscript eps color solid \"]"); const QString plotParameters = QLatin1String("[ps_file, \"")+ fileName+QLatin1String("\"],")+psParam; #else const QString plotParameters = QLatin1String("[gnuplot_term, \"png size 500,340\"], [gnuplot_out_file, \"")+fileName+QLatin1String("\"]"); #endif cmd.replace(QRegExp(QLatin1String("((plot2d|plot3d|contour_plot)\\s*\\(.*)\\)([;\n]|$)")), QLatin1String("\\1, ")+plotParameters+QLatin1String(");")); } if (!cmd.endsWith(QLatin1Char('$'))) { if (!cmd.endsWith(QLatin1String(";"))) cmd+=QLatin1Char(';'); } //replace all newlines with spaces, as maxima isn't sensitive about //whitespaces, and without newlines the whole command //is executed at once, without outputting an input //prompt after each line cmd.replace(QLatin1Char('\n'), QLatin1Char(' ')); //lisp-quiet doesn't print a prompt after the command //is completed, which causes the parsing to hang. //replace the command with the non-quiet version cmd.replace(QRegExp(QLatin1String("^:lisp-quiet")), QLatin1String(":lisp")); return cmd; } void MaximaExpression::forceDone() { qDebug()<<"forcing Expression state to DONE"; setResult(nullptr); setStatus(Cantor::Expression::Done); } void MaximaExpression::addInformation(const QString& information) { qDebug()<<"adding information"; QString inf=information; if(!inf.endsWith(QLatin1Char(';'))) inf+=QLatin1Char(';'); Cantor::Expression::addInformation(inf); dynamic_cast(session())->sendInputToProcess(inf+QLatin1Char('\n')); } //The maxima backend is modified, so that it outputs //xml-style tags around outputs, input prompts etc. //the following are some simple helper functions to facilitate parsing inline void skipWhitespaces(int* idx, const QString& txt) { for(;*idx < txt.size() && (txt[*idx]).isSpace();++(*idx)); } QStringRef readXmlOpeningTag(int* idx, const QString& txt, bool* isComplete=nullptr) { qDebug()<<"trying to read an opening tag"; if (*idx >= txt.size()) return QStringRef(); skipWhitespaces(idx, txt); if(isComplete) *isComplete=false; if(txt[*idx]!=QLatin1Char('<')) { qDebug()<<"This is NOT AN OPENING TAG."<')) { if(isComplete) *isComplete=true; break; }else length++; } return QStringRef(&txt, startIndex, length); } QStringRef readXmlTagContent(int* idx, const QString& txt, const QStringRef& name, bool* isComplete=nullptr) { bool readingClosingTag=false; int contentStartIdx=*idx; int contentLength=0; int currentTagStartIdx=-1; int currentTagLength=0; if(isComplete) *isComplete=false; while(*idx0&&txt[(*idx)-1]==QLatin1Char('<')) { //remove the opening < contentLength--; currentTagStartIdx=*idx+1; currentTagLength=0; readingClosingTag=true; } else if(readingClosingTag) { if(c==QLatin1Char('>')) { const QStringRef currentTagName(&txt, currentTagStartIdx, currentTagLength); if(currentTagName==name) { //eat up the closing > ++(*idx); if(isComplete) (*isComplete)=true; break; } readingClosingTag=false; }else currentTagLength++; } else contentLength++; ++(*idx); } if(contentStartIdx+contentLength>txt.size()) { qDebug()<<"something is wrong with the content-length "<"), idx); int idx2=out.indexOf(QLatin1String(""), idx); idx1=(idx1==-1) ? out.size():idx1; idx2=(idx2==-1) ? out.size():idx2; int newIdx=qMin(idx1, idx2); if(newIdx>idx) { const QString& err=out.mid(idx, newIdx-idx); if(!err.isEmpty()) m_gotErrorContent=true; errorBuffer+=err; qDebug()<<"the unmatched part of the output is: "<0) { textBuffer.append(QLatin1String("\n")); latexBuffer.append(QLatin1String("\n")); } result=parseResult(&idx, out, textBuffer, latexBuffer); numResults++; qDebug()<<"got "<type()==Cantor::TextResult::Type) m_errorBuffer.prepend(dynamic_cast(result)->plain()+QLatin1String("\n")); else if(result->type()==Cantor::LatexResult::Type) m_errorBuffer.prepend(dynamic_cast(result)->plain()+QLatin1String("\n")); } Cantor::TextResult* result=new Cantor::TextResult(m_errorBuffer); setResult(result); setStatus(Cantor::Expression::Done); }else if(m_isHelpRequest) //Help Messages are also provided in the errorBuffer. { Cantor::HelpResult* result=new Cantor::HelpResult(m_errorBuffer); setResult(result); setStatus(Cantor::Expression::Done); }else { if(result) { qDebug()<<"result: "<toHtml(); if(result->type()==Cantor::TextResult::Type) m_errorBuffer.prepend(dynamic_cast(result)->plain()+QLatin1String("\n")); else if(result->type()==Cantor::LatexResult::Type) m_errorBuffer.prepend(dynamic_cast(result)->plain()+QLatin1String("\n")); } qDebug()<<"errorBuffer: "< element wasn't read completely, there //is no point in trying to render it. Use text for //incomplete results. if(!isLatexComplete ||(latexBuffer.trimmed().isEmpty()&&latex.isEmpty()) ||m_isHelpRequest||isInternal()) { qDebug()<<"using text"; result=new Cantor::TextResult(textBuffer); }else { qDebug()<<"using latex"; //strip away the latex code for the label. //it is contained in an \mbox{} call int i; int pcount=0; for(i=latex.indexOf(QLatin1String("\\mbox{"))+5;isetFormat(Cantor::TextResult::LatexFormat); } } return result; } /*! example output for the simple expression '5+5': latex mode - "\n(%o1) 10\n\\mbox{\\tt\\red(\\mathrm{\\%o1}) \\black}10\n(%i2) \n" text mode - "\n(%o1) 10\n\n(%i2) \n" */ bool MaximaExpression::parseOutput(QString& out) { const int promptStart = out.indexOf(QLatin1String("")); const int promptEnd = out.indexOf(QLatin1String("")); const QString prompt = out.mid(promptStart + 15, promptEnd - promptStart - 15).simplified(); //check whether the result is part of the promt - this is the case when additional input is required from the user if (prompt.contains(QLatin1String(""))) { //text part of the output const int textContentStart = prompt.indexOf(QLatin1String("")); const int textContentEnd = prompt.indexOf(QLatin1String("")); QString textContent = prompt.mid(textContentStart + 13, textContentEnd - textContentStart - 13).trimmed(); qDebug()<<"asking for additional input for " << textContent; emit needsAdditionalInformation(textContent); return true; } qDebug()<<"new input label: " << prompt; QString errorContent; //parse the results int resultStart = out.indexOf(QLatin1String("")); if (resultStart != -1) errorContent += out.mid(0, resultStart); while (resultStart != -1) { int resultEnd = out.indexOf(QLatin1String(""), resultStart + 15); const QString resultContent = out.mid(resultStart + 15, resultEnd - resultStart - 15); parseResult(resultContent); //search for the next openning tag after the current closing tag resultStart = out.indexOf(QLatin1String(""), resultEnd + 16); } //parse the error message, the part outside of the tags int lastResultEnd = out.lastIndexOf(QLatin1String("")); if (lastResultEnd != -1) lastResultEnd += 16; else lastResultEnd = 0; errorContent += out.mid(lastResultEnd, promptStart - lastResultEnd).trimmed(); if (errorContent.isEmpty()) { // For plots we set Done status in imageChanged if (!m_isPlot || m_plotResult) setStatus(Cantor::Expression::Done); } else { qDebug() << "error content: " << errorContent; if (out.contains(QLatin1String("cantor-value-separator"))) { //when fetching variables, in addition to the actual result with variable names and values, //Maxima also write out the names of the variables to the error buffer. //we don't interpret this as an error. setStatus(Cantor::Expression::Done); } else if(m_isHelpRequest) //help messages are also part of the error output { Cantor::HelpResult* result = new Cantor::HelpResult(errorContent); addResult(result); setStatus(Cantor::Expression::Done); } else { errorContent = errorContent.replace(QLatin1String("\n\n"), QLatin1String("
")); errorContent = errorContent.replace(QLatin1String("\n"), QLatin1String("
")); setErrorMessage(errorContent); setStatus(Cantor::Expression::Error); } } return true; } void MaximaExpression::parseResult(const QString& resultContent) { qDebug()<<"result content: " << resultContent; //text part of the output const int textContentStart = resultContent.indexOf(QLatin1String("")); const int textContentEnd = resultContent.indexOf(QLatin1String("")); QString textContent = resultContent.mid(textContentStart + 13, textContentEnd - textContentStart - 13).trimmed(); qDebug()<<"text content: " << textContent; //output label can be a part of the text content -> determine it const QRegExp regex = QRegExp(MaximaSession::MaximaOutputPrompt.pattern()); const int index = regex.indexIn(textContent); QString outputLabel; if (index != -1) // No match, so output don't contain output label outputLabel = textContent.mid(index, regex.matchedLength()).trimmed(); qDebug()<<"output label: " << outputLabel; //extract the expression id bool ok; QString idString = outputLabel.mid(3, outputLabel.length()-4); int id = idString.toInt(&ok); ok ? setId(id) : setId(-1); qDebug()<<"expression id: " << this->id(); //remove the output label from the text content textContent = textContent.remove(outputLabel).trimmed(); //determine the actual result Cantor::Result* result = nullptr; const int latexContentStart = resultContent.indexOf(QLatin1String("")); //Handle system maxima output for plotting commands if (m_isPlot && textContent.endsWith(QString::fromLatin1("\"%1\"]").arg(m_tempFile->fileName()))) { m_plotResultIndex = results().size(); // Gnuplot could generate plot before we parse text output from maxima and after // If we already have plot result, just add it // Else set info message, and replace it by real result in imageChanged function later if (m_plotResult) result = m_plotResult; else result = new Cantor::TextResult(i18n("Waiting for the plot result")); } else if (latexContentStart != -1) { //latex output is available const int latexContentEnd = resultContent.indexOf(QLatin1String("")); QString latexContent = resultContent.mid(latexContentStart + 14, latexContentEnd - latexContentStart - 14).trimmed(); qDebug()<<"latex content: " << latexContent; Cantor::TextResult* textResult; //replace the \mbox{} environment, if available, by the eqnarray environment if (latexContent.indexOf(QLatin1String("\\mbox{")) != -1) { int i; int pcount=0; for(i = latexContent.indexOf(QLatin1String("\\mbox{"))+5; i < latexContent.size(); ++i) { if(latexContent[i]==QLatin1Char('{')) pcount++; else if(latexContent[i]==QLatin1Char('}')) pcount--; if(pcount==0) break; } QString modifiedLatexContent = latexContent.mid(i+1); if(modifiedLatexContent.trimmed().isEmpty()) { //empty content in the \mbox{} environment (e.g. for print() outputs), use the latex string outside of the \mbox{} environment modifiedLatexContent = latexContent.left(latexContent.indexOf(QLatin1String("\\mbox{"))); } modifiedLatexContent.prepend(QLatin1String("\\begin{eqnarray*}")); modifiedLatexContent.append(QLatin1String("\\end{eqnarray*}")); textResult = new Cantor::TextResult(modifiedLatexContent, textContent); qDebug()<<"modified latex content: " << modifiedLatexContent; } else { //no \mbox{} available, use what we've got. textResult = new Cantor::TextResult(latexContent, textContent); } textResult->setFormat(Cantor::TextResult::LatexFormat); result = textResult; } else { //no latex output is availabe, the actual result is part of the textContent string result = new Cantor::TextResult(textContent); } addResult(result); } void MaximaExpression::parseError(const QString& out) { m_errorBuffer.append(out); } void MaximaExpression::imageChanged() { if(m_tempFile->size()>0) { #ifdef WITH_EPS m_plotResult = new Cantor::EpsResult( QUrl::fromLocalFile(m_tempFile->fileName()) ); #else m_plotResult = new Cantor::ImageResult( QUrl::fromLocalFile(m_tempFile->fileName()) ); #endif // Check, that we already parse maxima output for this plot, and if not, keep it up to this moment // If it's true, replace text info result by real plot and set status as Done if (m_plotResultIndex != -1) { replaceResult(m_plotResultIndex, m_plotResult); setStatus(Cantor::Expression::Done); } } } diff --git a/src/backends/maxima/maximaexpression.h b/src/backends/maxima/maximaexpression.h index 517e273c..baefa600 100644 --- a/src/backends/maxima/maximaexpression.h +++ b/src/backends/maxima/maximaexpression.h @@ -1,74 +1,75 @@ /* 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. --- Copyright (C) 2009-2012 Alexander Rieder */ #ifndef _MAXIMAEXPRESSION_H #define _MAXIMAEXPRESSION_H #include "expression.h" #include #include class QTemporaryFile; class MaximaExpression : public Cantor::Expression { Q_OBJECT public: explicit MaximaExpression(Cantor::Session*, bool internal = false); + ~MaximaExpression() override; void evaluate() override; void interrupt() override; void addInformation(const QString&) override; bool needsLatexResult(); //returns the command that should be send to //the Maxima process, it's different from the //command() for example to allow plot embedding QString internalCommand(); //Forces the status of this Expression to done void forceDone(); //reads from @param out until a prompt indicates that a new expression has started bool parseOutput(QString&); bool parseOutputOld(QString&); void parseError(const QString&); private Q_SLOTS: void imageChanged(); private: void parseResult(const QString&); Cantor::Result* parseResult(int* idx,QString& out,QString& textBuffer,QString& latexBuffer); QTemporaryFile *m_tempFile; QFileSystemWatcher m_fileWatch; bool m_isHelpRequest; bool m_isPlot; Cantor::Result* m_plotResult; int m_plotResultIndex; QString m_errorBuffer; bool m_gotErrorContent; }; #endif /* _MAXIMAEXPRESSION_H */ diff --git a/src/backends/maxima/maximasession.cpp b/src/backends/maxima/maximasession.cpp index 39cf330e..b822e5d4 100644 --- a/src/backends/maxima/maximasession.cpp +++ b/src/backends/maxima/maximasession.cpp @@ -1,347 +1,347 @@ /* 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. --- Copyright (C) 2009-2012 Alexander Rieder Copyright (C) 2017-2018 Alexander Semke (alexander.semke@web.de) */ #include "maximasession.h" #include "maximaexpression.h" #include "maximacompletionobject.h" #include "maximasyntaxhelpobject.h" #include "maximahighlighter.h" #include "maximavariablemodel.h" #include "result.h" #include "settings.h" #include #include #include #include #include #ifndef Q_OS_WIN #include #endif //NOTE: the \\s in the expressions is needed, because Maxima seems to sometimes insert newlines/spaces between the letters //maybe this is caused by some behaviour if the Prompt is split into multiple "readStdout" calls //the Expressions are encapsulated in () to allow capturing for the text const QRegExp MaximaSession::MaximaOutputPrompt=QRegExp(QLatin1String("(\\(\\s*%\\s*o\\s*[0-9\\s]*\\))")); //Text, maxima outputs, before any output MaximaSession::MaximaSession( Cantor::Backend* backend ) : Session(backend), m_process(nullptr), m_variableModel(new MaximaVariableModel(this)), m_justRestarted(false) { } void MaximaSession::login() { qDebug()<<"login"; if (m_process) return; //TODO: why do we call login() again?!? emit loginStarted(); QStringList arguments; arguments << QLatin1String("--quiet"); //Suppress Maxima start-up message const QString initFile = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QLatin1String("cantor/maximabackend/cantor-initmaxima.lisp")); arguments << QLatin1String("--init-lisp=") + initFile; //Set the name of the Lisp initialization file m_process = new QProcess(this); m_process->start(MaximaSettings::self()->path().toLocalFile(), arguments); m_process->waitForStarted(); m_process->waitForReadyRead(); qDebug()<readAllStandardOutput(); connect(m_process, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(restartMaxima())); connect(m_process, SIGNAL(readyReadStandardOutput()), this, SLOT(readStdOut())); connect(m_process, SIGNAL(readyReadStandardError()), this, SLOT(readStdErr())); connect(m_process, SIGNAL(error(QProcess::ProcessError)), this, SLOT(reportProcessError(QProcess::ProcessError))); if(!MaximaSettings::self()->autorunScripts().isEmpty()){ QString autorunScripts = MaximaSettings::self()->autorunScripts().join(QLatin1String(";")); autorunScripts.append(QLatin1String(";kill(labels)")); // Reset labels after running autorun scripts evaluateExpression(autorunScripts, MaximaExpression::DeleteOnFinish, true); } changeStatus(Session::Done); emit loginDone(); qDebug()<<"login done"; } void MaximaSession::logout() { qDebug()<<"logout"; if(!m_process) return; disconnect(m_process, nullptr, this, nullptr); // if(status()==Cantor::Session::Running) //TODO: terminate the running expressions first write(QLatin1String("quit();\n")); qDebug()<<"waiting for maxima to finish"; m_process->waitForFinished(); qDebug()<<"maxima exit finished"; if(m_process->state() != QProcess::NotRunning) { m_process->kill(); qDebug()<<"maxima still running, process kill enforced"; } expressionQueue().clear(); delete m_process; m_process = nullptr; changeStatus(Status::Disable); qDebug()<<"logout done"; } Cantor::Expression* MaximaSession::evaluateExpression(const QString& cmd, Cantor::Expression::FinishingBehavior behave, bool internal) { qDebug() << "evaluating: " << cmd; MaximaExpression* expr = new MaximaExpression(this, internal); expr->setFinishingBehavior(behave); expr->setCommand(cmd); expr->evaluate(); return expr; } void MaximaSession::readStdErr() { qDebug()<<"reading stdErr"; if (!m_process) return; QString out=QLatin1String(m_process->readAllStandardError()); if(expressionQueue().size()>0) { MaximaExpression* expr = static_cast(expressionQueue().first()); expr->parseError(out); } } void MaximaSession::readStdOut() { QString out = QLatin1String(m_process->readAllStandardOutput()); m_cache += out; //collect the multi-line output until Maxima has finished the calculation and returns a new promt if ( !out.contains(QLatin1String("")) ) return; if(expressionQueue().isEmpty()) { //queue is empty, interrupt was called, nothing to do here qDebug()<(expressionQueue().first()); if (!expr) return; //should never happen qDebug()<<"output: " << m_cache; expr->parseOutput(m_cache); m_cache.clear(); } void MaximaSession::killLabels() { Cantor::Expression* e=evaluateExpression(QLatin1String("kill(labels);"), Cantor::Expression::DeleteOnFinish, true); //TODO: what for? connect(e, SIGNAL(statusChanged(Cantor::Expression::Status)), this, SIGNAL(ready())); } void MaximaSession::reportProcessError(QProcess::ProcessError e) { qDebug()<<"process error"<command())&&!exp2.exactMatch(expression->command())) { m_variableModel->checkForNewFunctions(); m_variableModel->checkForNewVariables(); }else { changeStatus(Cantor::Session::Done); } }else { runFirstExpression(); } } } void MaximaSession::runFirstExpression() { qDebug()<<"running next expression"; if (!m_process) return; if(!expressionQueue().isEmpty()) { MaximaExpression* expr = static_cast(expressionQueue().first()); QString command=expr->internalCommand(); connect(expr, SIGNAL(statusChanged(Cantor::Expression::Status)), this, SLOT(currentExpressionChangedStatus(Cantor::Expression::Status))); expr->setStatus(Cantor::Expression::Computing); if(command.isEmpty()) { qDebug()<<"empty command"; expr->forceDone(); } else { m_cache.clear(); write(command + QLatin1Char('\n')); } } } void MaximaSession::interrupt() { if(!expressionQueue().isEmpty()) { qDebug()<<"interrupting " << expressionQueue().first()->command(); if(m_process->state() != QProcess::NotRunning) { #ifndef Q_OS_WIN const int pid=m_process->pid(); kill(pid, SIGINT); #else ; //TODO: interrupt the process on windows #endif } expressionQueue().first()->interrupt(); expressionQueue().removeFirst(); foreach (Cantor::Expression* expression, expressionQueue()) expression->setStatus(Cantor::Expression::Done); expressionQueue().clear(); qDebug()<<"done interrupting"; } changeStatus(Cantor::Session::Done); m_cache.clear(); } void MaximaSession::sendInputToProcess(const QString& input) { write(input); } void MaximaSession::restartMaxima() { qDebug()<<"restarting maxima cooldown: "<write(exp.toUtf8()); }