diff --git a/src/backends/R/testr.cpp b/src/backends/R/testr.cpp index 86612119..6c034c67 100644 --- a/src/backends/R/testr.cpp +++ b/src/backends/R/testr.cpp @@ -1,88 +1,88 @@ /* 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) 2019 Sirgienko Nikita */ #include "testr.h" #include "session.h" #include "backend.h" #include "expression.h" #include "result.h" #include "completionobject.h" #include "defaultvariablemodel.h" #include QString TestR::backendName() { return QLatin1String("R"); } void TestR::testVariablesCreatingFromCode() { QAbstractItemModel* model = session()->variableModel(); QVERIFY(model != nullptr); Cantor::Expression* e=evalExp(QLatin1String("a1 = 15; b1 = 'S'; d1 = c(1,2,3)")); QVERIFY(e!=nullptr); while(session()->status() != Cantor::Session::Done) waitForSignal(session(), SIGNAL(statusChanged(Cantor::Session::Status))); QCOMPARE(3, model->rowCount()); QCOMPARE(model->index(0,0).data().toString(), QLatin1String("a1")); QCOMPARE(model->index(0,1).data().toString(), QLatin1String("15")); QCOMPARE(model->index(1,0).data().toString(), QLatin1String("b1")); QCOMPARE(model->index(1,1).data().toString(), QLatin1String("S")); QCOMPARE(model->index(2,0).data().toString(), QLatin1String("d1")); QCOMPARE(model->index(2,1).data().toString(), QLatin1String("1, 2, 3")); evalExp(QLatin1String("rm(a1,b1,d1)")); } void TestR::testVariableCleanupAfterRestart() { - Cantor::DefaultVariableModel* model = static_cast(session()->variableModel()); + Cantor::DefaultVariableModel* model = session()->variableModel(); QVERIFY(model != nullptr); while(session()->status() != Cantor::Session::Done) waitForSignal(session(), SIGNAL(statusChanged(Cantor::Session::Status))); QCOMPARE(static_cast(model)->rowCount(), 0); Cantor::Expression* e=evalExp(QLatin1String("h1 = 15; h2 = 'S';")); QVERIFY(e!=nullptr); while(session()->status() != Cantor::Session::Done) waitForSignal(session(), SIGNAL(statusChanged(Cantor::Session::Status))); QCOMPARE(static_cast(model)->rowCount(), 2); session()->logout(); session()->login(); QCOMPARE(static_cast(model)->rowCount(), 0); } QTEST_MAIN( TestR ) diff --git a/src/backends/julia/testjulia.cpp b/src/backends/julia/testjulia.cpp index 61bbe959..b5c233ef 100644 --- a/src/backends/julia/testjulia.cpp +++ b/src/backends/julia/testjulia.cpp @@ -1,294 +1,290 @@ /* * 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) 2016 Ivan Lakhtanov */ #include "testjulia.h" #include "session.h" #include "backend.h" #include "expression.h" #include "result.h" #include "textresult.h" #include "imageresult.h" #include "defaultvariablemodel.h" #include "completionobject.h" QString TestJulia::backendName() { return QLatin1String("julia"); } void TestJulia::testOneLine() { Cantor::Expression *e = evalExp(QLatin1String("2 + 3")); QVERIFY(e != nullptr); QCOMPARE(e->status(), Cantor::Expression::Done); QVERIFY(e->result()->type() == Cantor::TextResult::Type); QCOMPARE(e->result()->data().toString(), QLatin1String("5")); QVERIFY(e->errorMessage().isEmpty()); } void TestJulia::testOneLineWithPrint() { Cantor::Expression *e = evalExp(QLatin1String("print(2 + 3)")); QVERIFY(e != nullptr); QCOMPARE(e->status(), Cantor::Expression::Done); QVERIFY(e->result()->type() == Cantor::TextResult::Type); QCOMPARE(e->result()->data().toString(), QLatin1String("5")); QVERIFY(e->errorMessage().isEmpty()); } void TestJulia::testException() { Cantor::Expression *e = evalExp(QLatin1String("sqrt(-1)")); QVERIFY(e != nullptr); QCOMPARE(e->status(), Cantor::Expression::Error); QVERIFY(e->result()); QVERIFY(e->result()->type() == Cantor::TextResult::Type); QVERIFY( !e->errorMessage().isEmpty() && e->errorMessage().contains(QLatin1String( "sqrt will only return a complex result if called with a " "complex argument." )) ); } void TestJulia::testSyntaxError() { Cantor::Expression *e = evalExp(QLatin1String("for i = 1:10\nprint(i)")); QVERIFY(e != nullptr); QCOMPARE(e->status(), Cantor::Expression::Error); QVERIFY(e->result()->type() == Cantor::TextResult::Type); QVERIFY( !e->errorMessage().isEmpty() && e->errorMessage().contains(QLatin1String( "syntax: incomplete: \"for\" at none:1 requires end" )) ); } void TestJulia::testMultilineCode() { Cantor::Expression *e = evalExp(QLatin1String( "q = 0; # comment\n" "# sdfsdf\n" "for i = 1:10\n" " global q\n" " q += i\n" "end\n" "print(q)" )); QVERIFY(e != nullptr); QCOMPARE(e->status(), Cantor::Expression::Done); QVERIFY(e->result()->type() == Cantor::TextResult::Type); QCOMPARE(e->result()->data().toString(), QLatin1String("55")); QVERIFY(e->errorMessage().isEmpty()); } void TestJulia::testPartialResultOnException() { Cantor::Expression *e = evalExp(QLatin1String( "for i = 1:5\n" " print(i)\n" "end\n" "sqrt(-1)\n" )); QVERIFY(e != nullptr); QCOMPARE(e->status(), Cantor::Expression::Error); QVERIFY(e->result()->type() == Cantor::TextResult::Type); QCOMPARE(e->result()->data().toString(), QLatin1String("12345")); QVERIFY( !e->errorMessage().isEmpty() && e->errorMessage().contains(QLatin1String( "sqrt will only return a complex result if called with a " "complex argument." )) ); } void TestJulia::testInlinePlot() { Cantor::Expression *e = evalExp(QLatin1String( "import GR\n" "GR.plot([-1, -0.75, -0.5, -0.25, 0, 0.25, 0.5, 0.75, 1], sin)" )); QVERIFY(e != nullptr); QCOMPARE(e->status(), Cantor::Expression::Done); QVERIFY(e->result()->type() == Cantor::ImageResult::Type); } void TestJulia::testInlinePlotWithExceptionAndPartialResult() { Cantor::Expression *e = evalExp(QLatin1String( "import GR\n" "print(\"gonna plot\")\n" "sqrt(-1)\n" "GR.plot([-1, -0.75, -0.5, -0.25, 0, 0.25, 0.5, 0.75, 1], sin)\n" )); QVERIFY(e != nullptr); QCOMPARE(e->status(), Cantor::Expression::Error); QVERIFY(e->result()->type() == Cantor::TextResult::Type); QCOMPARE(e->result()->data().toString(), QLatin1String("gonna plot")); QVERIFY( !e->errorMessage().isEmpty() && e->errorMessage().contains(QLatin1String( "sqrt will only return a complex result if called with a " "complex argument." )) ); } void TestJulia::testAddVariablesFromCode() { evalExp(QLatin1String("a = 0; b = 1; c = 2; d = 3\n")); auto variableModel = session()->variableModel(); QStringList variableNames = QString::fromLatin1("a b c d").split(QLatin1String(" ")); for (int i = 0; i < variableNames.size(); i++) { QModelIndexList matchedVariables = variableModel->match( variableModel->index(0, 0), Qt::DisplayRole, QVariant::fromValue(variableNames[i]), -1, Qt::MatchExactly ); QCOMPARE(matchedVariables.size(), 1); auto match = matchedVariables[0]; auto valueIndex = variableModel->index(match.row(), match.column() + 1); QVERIFY( valueIndex.data(Qt::DisplayRole) == QVariant::fromValue(QString::number(i)) ); } } void TestJulia::testAddVariablesFromManager() { - auto variableModel = static_cast( - session()->variableModel() - ); + Cantor::DefaultVariableModel* variableModel = session()->variableModel(); QStringList variableNames = QString::fromLatin1("a b c d").split(QLatin1String(" ")); for (int i = 0; i < variableNames.size(); i++) { variableModel->addVariable(variableNames[i], QString::number(i)); QModelIndexList matchedVariables = variableModel->match( variableModel->index(0, 0), Qt::DisplayRole, QVariant::fromValue(variableNames[i]), -1, Qt::MatchExactly ); QCOMPARE(matchedVariables.size(), 1); auto match = matchedVariables[0]; auto valueIndex = variableModel->index(match.row(), match.column() + 1); QVERIFY( valueIndex.data(Qt::DisplayRole) == QVariant::fromValue(QString::number(i)) ); } } void TestJulia::testRemoveVariables() { - auto variableModel = static_cast( - session()->variableModel() - ); + Cantor::DefaultVariableModel* variableModel = session()->variableModel(); QStringList variableNames = QString::fromLatin1("a b c d").split(QLatin1String(" ")); for (int i = 0; i < variableNames.size(); i++) { variableModel->addVariable(variableNames[i], QString::number(i)); } for (int i = 0; i < variableNames.size(); i += 2) { variableModel->removeVariable(variableNames[i]); } for (int i = 0; i < variableNames.size(); i++) { QModelIndexList matchedVariables = variableModel->match( variableModel->index(0, 0), Qt::DisplayRole, QVariant::fromValue(variableNames[i]), -1, Qt::MatchExactly ); if (i % 2 == 0) { QVERIFY(matchedVariables.isEmpty()); } else { QCOMPARE(matchedVariables.size(), 1); auto match = matchedVariables[0]; auto valueIndex = variableModel->index(match.row(), match.column() + 1); QVERIFY( valueIndex.data(Qt::DisplayRole) == QVariant::fromValue(QString::number(i)) ); } } } void TestJulia::testAutoCompletion() { auto prefix = QLatin1String("ex"); auto completionObject = session()->completionFor(prefix); waitForSignal(completionObject, SIGNAL(fetchingDone())); auto completions = completionObject->completions(); QStringList completionsToCheck; completionsToCheck << QLatin1String("exit"); completionsToCheck << QLatin1String("exponent"); completionsToCheck << QLatin1String("exp"); for (auto completion : completionsToCheck) { QVERIFY(completions.contains(completion)); } for (auto completion : completions) { QVERIFY(completion.startsWith(prefix)); } } void TestJulia::testComplexAutocompletion() { auto prefix = QLatin1String("Base.Ma"); auto completionObject = session()->completionFor(prefix); waitForSignal(completionObject, SIGNAL(fetchingDone())); auto completions = completionObject->completions(); QStringList completionsToCheck; completionsToCheck << QLatin1String("Base.MainInclude"); completionsToCheck << QLatin1String("Base.Math"); completionsToCheck << QLatin1String("Base.Matrix"); for (auto completion : completionsToCheck) { QVERIFY(completions.contains(completion)); } for (auto completion : completions) { QVERIFY(completion.startsWith(prefix)); } } QTEST_MAIN(TestJulia) diff --git a/src/backends/octave/octavesession.cpp b/src/backends/octave/octavesession.cpp index cab68d8b..e89d4959 100644 --- a/src/backends/octave/octavesession.cpp +++ b/src/backends/octave/octavesession.cpp @@ -1,370 +1,369 @@ /* Copyright (C) 2010 Miha Čančula 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 "octavesession.h" #include "octaveexpression.h" #include "octavecompletionobject.h" #include "octavesyntaxhelpobject.h" #include "octavehighlighter.h" #include "result.h" #include "textresult.h" #include "settings.h" #include "octave-backend-config.h" #include "octavehighlighter.h" #include #include #include #include #include #ifndef Q_OS_WIN #include #endif #include "octavevariablemodel.h" const QRegExp OctaveSession::PROMPT_UNCHANGEABLE_COMMAND = QRegExp(QLatin1String("(,|;)+")); OctaveSession::OctaveSession ( Cantor::Backend* backend ) : Session ( backend), m_process(nullptr), m_prompt(QLatin1String("CANTOR_OCTAVE_BACKEND_PROMPT:([0-9]+)> ")), m_subprompt(QLatin1String("CANTOR_OCTAVE_BACKEND_SUBPROMPT:([0-9]+)> ")), m_previousPromptNumber(1), m_watch(nullptr), m_syntaxError(false) { setVariableModel(new OctaveVariableModel(this)); qDebug() << octaveScriptInstallDir; } void OctaveSession::login() { qDebug() << "login"; emit loginStarted(); m_process = new KProcess ( this ); QStringList args; args << QLatin1String("--silent"); args << QLatin1String("--interactive"); args << QLatin1String("--persist"); // Setting prompt and subprompt args << QLatin1String("--eval"); args << QLatin1String("PS1('CANTOR_OCTAVE_BACKEND_PROMPT:\\#> ');"); args << QLatin1String("--eval"); args << QLatin1String("PS2('CANTOR_OCTAVE_BACKEND_SUBPROMPT:\\#> ');"); // Add the cantor script directory to search path args << QLatin1String("--eval"); args << QString::fromLatin1("addpath %1;").arg(octaveScriptInstallDir); // Print the temp dir, used for plot files args << QLatin1String("--eval"); args << QLatin1String("printf('%s\\n', ['____TMP_DIR____ = ' tempdir]);"); // Do not show extra text in help commands args << QLatin1String("--eval"); args << QLatin1String("suppress_verbose_help_message(1);"); if (OctaveSettings::integratePlots()) { // Do not show the popup when plotting, rather only print to a file args << QLatin1String("--eval"); args << QLatin1String("set (0, \"defaultfigurevisible\",\"off\");"); args << QLatin1String("--eval"); args << QLatin1String("graphics_toolkit gnuplot;"); } else { args << QLatin1String("--eval"); args << QLatin1String("set (0, \"defaultfigurevisible\",\"on\");"); } if (OctaveSettings::integratePlots()) { m_watch = new KDirWatch(this); m_watch->setObjectName(QLatin1String("OctaveDirWatch")); connect (m_watch, SIGNAL(dirty(QString)), SLOT(plotFileChanged(QString)) ); } m_process->setProgram ( OctaveSettings::path().toLocalFile(), args ); qDebug() << "starting " << m_process->program(); m_process->setOutputChannelMode ( KProcess::SeparateChannels ); m_process->start(); m_process->waitForStarted(); // Got tmp dir bool loginFinished = false; QString input; while (!loginFinished) { m_process->waitForReadyRead(); input += QString::fromLatin1(m_process->readAllStandardOutput()); qDebug() << "login input: " << input; if (input.contains(QLatin1String("____TMP_DIR____"))) { m_tempDir = input; m_tempDir.remove(0,18); m_tempDir.chop(1); // isolate the tempDir's location qDebug() << "Got temporary file dir:" << m_tempDir; if (m_watch) { m_watch->addDir(m_tempDir, KDirWatch::WatchFiles); } loginFinished = true; } } connect ( m_process, SIGNAL (readyReadStandardOutput()), SLOT (readOutput()) ); connect ( m_process, SIGNAL (readyReadStandardError()), SLOT (readError()) ); connect ( m_process, SIGNAL (error(QProcess::ProcessError)), SLOT (processError()) ); if(!OctaveSettings::self()->autorunScripts().isEmpty()){ QString autorunScripts = OctaveSettings::self()->autorunScripts().join(QLatin1String("\n")); evaluateExpression(autorunScripts, OctaveExpression::DeleteOnFinish, true); forceVariableUpdate(); } changeStatus(Cantor::Session::Done); emit loginDone(); qDebug()<<"login done"; } void OctaveSession::logout() { qDebug()<<"logout"; if(!m_process) return; disconnect(m_process, nullptr, this, nullptr); // if(status()==Cantor::Session::Running) // TODO: terminate the running expressions first m_process->write("exit\n"); qDebug()<<"waiting for octave to finish"; m_process->waitForFinished(); qDebug()<<"octave exit finished"; if(m_process->state() != QProcess::NotRunning) { m_process->kill(); qDebug()<<"octave still running, process kill enforced"; } expressionQueue().clear(); delete m_process; m_process = nullptr; m_tempDir.clear(); m_output.clear(); m_previousPromptNumber = 1; - OctaveVariableModel* model = static_cast(variableModel()); - model->clearVariables(); + variableModel()->clearVariables(); changeStatus(Status::Disable); qDebug()<<"logout done"; } void OctaveSession::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 } foreach (Cantor::Expression* expression, expressionQueue()) expression->setStatus(Cantor::Expression::Interrupted); expressionQueue().clear(); // Cleanup inner state and call octave prompt printing // If we move this code for interruption to Session, we need add function for // cleaning before setting Done status m_output.clear(); m_process->write("\n"); qDebug()<<"done interrupting"; } changeStatus(Cantor::Session::Done); } void OctaveSession::processError() { qDebug() << "processError"; emit error(m_process->errorString()); } Cantor::Expression* OctaveSession::evaluateExpression ( const QString& command, Cantor::Expression::FinishingBehavior finishingBehavior, bool internal ) { qDebug() << "evaluating: " << command; OctaveExpression* expression = new OctaveExpression ( this, internal); expression->setCommand ( command ); expression->setFinishingBehavior ( finishingBehavior ); expression->evaluate(); return expression; } void OctaveSession::runFirstExpression() { OctaveExpression* expression = static_cast(expressionQueue().first()); connect(expression, SIGNAL(statusChanged(Cantor::Expression::Status)), this, SLOT(currentExpressionStatusChanged(Cantor::Expression::Status))); QString command = expression->internalCommand(); expression->setStatus(Cantor::Expression::Computing); if (isDoNothingCommand(command)) expression->setStatus(Cantor::Expression::Done); else { m_process->write ( command.toLocal8Bit() ); } } void OctaveSession::readError() { qDebug() << "readError"; QString error = QString::fromLocal8Bit(m_process->readAllStandardError()); if (!expressionQueue().isEmpty() && !error.isEmpty()) { OctaveExpression* const exp = static_cast(expressionQueue().first()); if (m_syntaxError) { m_syntaxError = false; exp->parseError(i18n("Syntax Error")); } else exp->parseError(error); m_output.clear(); } } void OctaveSession::readOutput() { qDebug() << "readOutput"; while (m_process->bytesAvailable() > 0) { QString line = QString::fromLocal8Bit(m_process->readLine()); qDebug()<<"start parsing " << " " << line; if (line.contains(m_prompt)) { const int promptNumber = m_prompt.cap(1).toInt(); if (!expressionQueue().isEmpty()) { const QString& command = expressionQueue().first()->command(); if (m_previousPromptNumber + 1 == promptNumber || isSpecialOctaveCommand(command)) { if (!expressionQueue().isEmpty()) static_cast(expressionQueue().first())->parseOutput(m_output); } else { // Error command don't increase octave prompt number (usually, but not always) readError(); } } m_previousPromptNumber = promptNumber; m_output.clear(); } else if (line.contains(m_subprompt) && m_subprompt.cap(1).toInt() == m_previousPromptNumber) { // User don't write finished octave statement (for example, write 'a = [1,2, ' only), so // octave print subprompt and waits input finish. m_syntaxError = true; qDebug() << "subprompt catch"; m_process->write(")]'\"\n"); // forse exit from subprompt m_output.clear(); } else m_output += line; } } void OctaveSession::currentExpressionStatusChanged(Cantor::Expression::Status status) { qDebug() << "currentExpressionStatusChanged"; switch (status) { case Cantor::Expression::Done: case Cantor::Expression::Error: finishFirstExpression(); break; default: break; } } void OctaveSession::plotFileChanged(const QString& filename) { if (!QFile::exists(filename) || !filename.split(QLatin1Char('/')).last().contains(QLatin1String("c-ob-"))) { return; } if (!expressionQueue().isEmpty()) { static_cast(expressionQueue().first())->parsePlotFile(filename); } } Cantor::CompletionObject* OctaveSession::completionFor ( const QString& cmd, int index ) { return new OctaveCompletionObject ( cmd, index, this ); } Cantor::SyntaxHelpObject* OctaveSession::syntaxHelpFor ( const QString& cmd ) { return new OctaveSyntaxHelpObject ( cmd, this ); } QSyntaxHighlighter* OctaveSession::syntaxHighlighter ( QObject* parent ) { return new OctaveHighlighter ( parent, this ); } void OctaveSession::runSpecificCommands() { m_process->write("figure(1,'visible','off')"); } bool OctaveSession::isDoNothingCommand(const QString& command) { return PROMPT_UNCHANGEABLE_COMMAND.exactMatch(command) || command.isEmpty(); } bool OctaveSession::isSpecialOctaveCommand(const QString& command) { return command.contains(QLatin1String("completion_matches")); } diff --git a/src/backends/octave/testoctave.cpp b/src/backends/octave/testoctave.cpp index fb54b7a2..45691435 100644 --- a/src/backends/octave/testoctave.cpp +++ b/src/backends/octave/testoctave.cpp @@ -1,221 +1,221 @@ /* 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 Alexander Rieder */ #include "testoctave.h" #include "session.h" #include "backend.h" #include "expression.h" #include "result.h" #include "imageresult.h" #include "textresult.h" #include "epsresult.h" #include "completionobject.h" #include "defaultvariablemodel.h" #include "octaveexpression.h" #include QString TestOctave::backendName() { return QLatin1String("octave"); } void TestOctave::testSimpleCommand() { Cantor::Expression* e=evalExp( QLatin1String("2+2") ); QVERIFY( e!=nullptr ); QVERIFY( e->result()!=nullptr ); QCOMPARE( cleanOutput( e->result()->toHtml() ), QLatin1String("ans = 4") ); } void TestOctave::testMultilineCommand() { Cantor::Expression* e=evalExp( QLatin1String("a = 2+2, b = 3+3") ); QVERIFY( e!=nullptr ); QVERIFY( e->result()!=nullptr ); QString result=e->result()->toHtml(); QCOMPARE( cleanOutput(result ), QLatin1String("a = 4\nb = 6") ); } void TestOctave::testCommandQueue() { Cantor::Expression* e1=session()->evaluateExpression(QLatin1String("0+1")); Cantor::Expression* e2=session()->evaluateExpression(QLatin1String("1+1")); Cantor::Expression* e3=evalExp(QLatin1String("1+2")); QVERIFY(e1!=nullptr); QVERIFY(e2!=nullptr); QVERIFY(e3!=nullptr); QVERIFY(e1->result()); QVERIFY(e2->result()); QVERIFY(e3->result()); QCOMPARE(cleanOutput(e1->result()->toHtml()), QLatin1String("ans = 1")); QCOMPARE(cleanOutput(e2->result()->toHtml()), QLatin1String("ans = 2")); QCOMPARE(cleanOutput(e3->result()->toHtml()), QLatin1String("ans = 3")); } void TestOctave::testVariableDefinition() { Cantor::Expression* e = evalExp(QLatin1String("testvar = 1")); QVERIFY(e != nullptr); QVERIFY(e->result() != nullptr); QCOMPARE(cleanOutput(e->result()->toHtml()), QLatin1String("testvar = 1")); } void TestOctave::testMatrixDefinition() { Cantor::Expression* e = evalExp(QLatin1String( "M = [1, 2, 3;"\ " 4, 5, 6;"\ " 7, 8, 9;]" )); QVERIFY(e != nullptr); QVERIFY(e->result() != nullptr); QCOMPARE(e->result()->type(), (int) Cantor::TextResult::Type); Cantor::TextResult* result = static_cast(e->result()); QCOMPARE(result->plain(), QLatin1String( "M =\n"\ "\n" " 1 2 3\n"\ " 4 5 6\n"\ " 7 8 9" )); } void TestOctave::testSimpleExpressionWithComment() { Cantor::Expression* e = evalExp(QLatin1String("s = 1234 #This is comment")); QVERIFY(e != nullptr); QVERIFY(e->result() != nullptr); QCOMPARE(cleanOutput(e->result()->toHtml()), QLatin1String("s = 1234")); } void TestOctave::testCommentExpression() { // https://bugs.kde.org/show_bug.cgi?id=401893 QSKIP("Skip, until we fix the bug #401893"); Cantor::Expression* e = evalExp(QLatin1String("#Only comment")); QVERIFY(e != nullptr); QCOMPARE(e->status(), Cantor::Expression::Status::Done); QCOMPARE(e->results().size(), 0); } void TestOctave::testCompletion() { QSKIP("Skip, until solve strange fail of this test with anomaly output (double prompt with missing command)"); Cantor::CompletionObject* help = session()->completionFor(QLatin1String("as"), 2); waitForSignal(help, SIGNAL(fetchingDone())); // Checks some completions for this request (but not all) // This correct for Octave 4.2.2 at least (and another versions, I think) const QStringList& completions = help->completions(); qDebug() << completions; QVERIFY(completions.contains(QLatin1String("asin"))); QVERIFY(completions.contains(QLatin1String("asctime"))); QVERIFY(completions.contains(QLatin1String("asec"))); QVERIFY(completions.contains(QLatin1String("assert"))); } void TestOctave::testVariablesCreatingFromCode() { QAbstractItemModel* model = session()->variableModel(); QVERIFY(model != nullptr); evalExp(QLatin1String("clear();")); Cantor::Expression* e=evalExp(QLatin1String("a = 15; b = 'S';")); QVERIFY(e!=nullptr); if(session()->status()==Cantor::Session::Running) waitForSignal(session(), SIGNAL(statusChanged(Cantor::Session::Status))); QCOMPARE(2, model->rowCount()); QCOMPARE(model->index(0,0).data().toString(), QLatin1String("a")); QCOMPARE(model->index(0,1).data().toString(), QLatin1String(" 15")); QCOMPARE(model->index(1,0).data().toString(), QLatin1String("b")); QCOMPARE(model->index(1,1).data().toString(), QLatin1String("S")); } void TestOctave::testVariableCleanupAfterRestart() { - Cantor::DefaultVariableModel* model = static_cast(session()->variableModel()); + Cantor::DefaultVariableModel* model = session()->variableModel(); QVERIFY(model != nullptr); evalExp(QLatin1String("clear();")); Cantor::Expression* e=evalExp(QLatin1String("a = 15; b = 'S';")); QVERIFY(e!=nullptr); if(session()->status()==Cantor::Session::Running) waitForSignal(session(), SIGNAL(statusChanged(Cantor::Session::Status))); QCOMPARE(2, static_cast(model)->rowCount()); session()->logout(); session()->login(); QCOMPARE(0, static_cast(model)->rowCount()); } void TestOctave::testPlot() { Cantor::Expression* e=evalExp( QLatin1String("cantor_plot2d('sin(x)', 'x', -10,10);") ); int cnt=0; //give some time to create the image, but at most 5sec while(e->result()==nullptr||e->result()->type()!=OctavePlotResult::Type ) { QTest::qWait(250); cnt+=250; if(cnt>5000) break; } QVERIFY( e!=nullptr ); QVERIFY( e->result()!=nullptr ); QCOMPARE( e->result()->type(), (int) OctavePlotResult::Type ); QVERIFY( !e->result()->data().isNull() ); } void TestOctave::testInvalidSyntax() { Cantor::Expression* e=evalExp( QLatin1String("2+2*+.") ); QVERIFY( e!=nullptr ); QCOMPARE( e->status(), Cantor::Expression::Error ); } QTEST_MAIN( TestOctave ) diff --git a/src/backends/python2/testpython2.cpp b/src/backends/python2/testpython2.cpp index 63b724c2..1488bebd 100644 --- a/src/backends/python2/testpython2.cpp +++ b/src/backends/python2/testpython2.cpp @@ -1,151 +1,151 @@ /* 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) 2013 Tuukka Verho */ #include "testpython2.h" #include "session.h" #include "backend.h" #include "expression.h" #include "result.h" #include "defaultvariablemodel.h" QString TestPython2::backendName() { return QLatin1String("python2"); } void TestPython2::testImportNumpy() { Cantor::Expression* e = evalExp(QLatin1String("import numpy")); QVERIFY(e != nullptr); QCOMPARE(e->status(), Cantor::Expression::Done); } void TestPython2::testCodeWithComments() { { Cantor::Expression* e = evalExp(QLatin1String("#comment\n1+2")); QVERIFY(e != nullptr); QVERIFY(e->result()->data().toString() == QLatin1String("3")); } { Cantor::Expression* e = evalExp(QLatin1String(" #comment\n1+2")); QVERIFY(e != nullptr); QVERIFY(e->result()->data().toString() == QLatin1String("3")); } } void TestPython2::testSimpleCode() { Cantor::Expression* e=evalExp( QLatin1String("2+2")); QVERIFY( e!=nullptr ); QVERIFY( e->result()!=nullptr ); QString result=e->result()->toHtml(); QCOMPARE( cleanOutput(result ), QLatin1String("4") ); } void TestPython2::testMultilineCode() { Cantor::Expression* e=evalExp(QLatin1String( "a = 2+2\n" "b = 3+3\n" "print a,b" )); QVERIFY( e!=nullptr ); QVERIFY( e->result()!=nullptr ); QString result=e->result()->toHtml(); QCOMPARE( cleanOutput(result ), QLatin1String("4 6") ); evalExp(QLatin1String("del a; del b")); } void TestPython2::testVariablesCreatingFromCode() { QAbstractItemModel* model = session()->variableModel(); QVERIFY(model != nullptr); Cantor::Expression* e=evalExp(QLatin1String("a = 15; b = 'S';")); QVERIFY(e!=nullptr); if(session()->status()==Cantor::Session::Running) waitForSignal(session(), SIGNAL(statusChanged(Cantor::Session::Status))); QCOMPARE(2, model->rowCount()); QCOMPARE(model->index(0,0).data().toString(), QLatin1String("a")); QCOMPARE(model->index(0,1).data().toString(), QLatin1String("15")); QCOMPARE(model->index(1,0).data().toString(), QLatin1String("b")); QCOMPARE(model->index(1,1).data().toString(), QLatin1String("'S'")); evalExp(QLatin1String("del a; del b")); } void TestPython2::testVariableCleanupAfterRestart() { - Cantor::DefaultVariableModel* model = static_cast(session()->variableModel()); + Cantor::DefaultVariableModel* model = session()->variableModel(); QVERIFY(model != nullptr); Cantor::Expression* e=evalExp(QLatin1String("a = 15; b = 'S';")); QVERIFY(e!=nullptr); if(session()->status()==Cantor::Session::Running) waitForSignal(session(), SIGNAL(statusChanged(Cantor::Session::Status))); QCOMPARE(2, static_cast(model)->rowCount()); session()->logout(); session()->login(); QCOMPARE(0, static_cast(model)->rowCount()); } void TestPython2::testDictVariable() { - Cantor::DefaultVariableModel* model = static_cast(session()->variableModel()); + Cantor::DefaultVariableModel* model = session()->variableModel(); QVERIFY(model != nullptr); Cantor::Expression* e=evalExp(QLatin1String("d = {'value': 33}")); QVERIFY(e!=nullptr); if(session()->status()==Cantor::Session::Running) waitForSignal(session(), SIGNAL(statusChanged(Cantor::Session::Status))); QCOMPARE(1, static_cast(model)->rowCount()); QCOMPARE(model->index(0,0).data().toString(), QLatin1String("d")); QCOMPARE(model->index(0,1).data().toString(), QLatin1String("{'value': 33}")); evalExp(QLatin1String("del d")); } QTEST_MAIN(TestPython2) diff --git a/src/backends/python3/testpython3.cpp b/src/backends/python3/testpython3.cpp index 37edf84c..03af0138 100644 --- a/src/backends/python3/testpython3.cpp +++ b/src/backends/python3/testpython3.cpp @@ -1,181 +1,181 @@ /* 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) 2015 Minh Ngo */ #include "testpython3.h" #include "session.h" #include "backend.h" #include "expression.h" #include "imageresult.h" #include "defaultvariablemodel.h" QString TestPython3::backendName() { return QLatin1String("python3"); } void TestPython3::testImportNumpy() { Cantor::Expression* e = evalExp(QLatin1String("import numpy")); QVERIFY(e != nullptr); QCOMPARE(e->status(), Cantor::Expression::Done); } void TestPython3::testCodeWithComments() { { Cantor::Expression* e = evalExp(QLatin1String("#comment\n1+2")); QVERIFY(e != nullptr); QVERIFY(e->result()->data().toString() == QLatin1String("3")); } { Cantor::Expression* e = evalExp(QLatin1String(" #comment\n1+2")); QVERIFY(e != nullptr); QVERIFY(e->result()->data().toString() == QLatin1String("3")); } } void TestPython3::testPython3Code() { { Cantor::Expression* e = evalExp(QLatin1String("print 1 + 2")); QVERIFY(e != nullptr); QVERIFY(!e->errorMessage().isEmpty()); } { Cantor::Expression* e = evalExp(QLatin1String("print(1 + 2)")); QVERIFY(e != nullptr); QVERIFY(e->result()->data().toString() == QLatin1String("3")); } } void TestPython3::testSimplePlot() { Cantor::Expression* e = evalExp(QLatin1String( "import matplotlib\n" "import matplotlib.pyplot as plt\n" "import numpy as np" )); QVERIFY(e != nullptr); QVERIFY(e->errorMessage().isEmpty() == true); //the variable model shouldn't have any entries after the module imports QAbstractItemModel* model = session()->variableModel(); QVERIFY(model != nullptr); QVERIFY(model->rowCount() == 0); //create data for plotting e = evalExp(QLatin1String( "t = np.arange(0.0, 2.0, 0.01)\n" "s = 1 + np.sin(2 * np.pi * t)" )); QVERIFY(e != nullptr); QVERIFY(e->errorMessage().isEmpty() == true); //the variable model should have two entries now QVERIFY(model->rowCount() == 2); //plot the data and check the results e = evalExp(QLatin1String( "plt.plot(t,s)\n" "plt.show()" )); waitForSignal(e, SIGNAL(gotResult())); QVERIFY(e != nullptr); QVERIFY(e->errorMessage().isEmpty() == true); QVERIFY(model->rowCount() == 2); //still only two variables //there must be one single image result QVERIFY(e->results().size() == 1); const Cantor::ImageResult* result = dynamic_cast(e->result()); QVERIFY(result != nullptr); evalExp(QLatin1String("del t; del s")); } void TestPython3::testVariablesCreatingFromCode() { QAbstractItemModel* model = session()->variableModel(); QVERIFY(model != nullptr); Cantor::Expression* e=evalExp(QLatin1String("a = 15; b = 'S';")); QVERIFY(e!=nullptr); if(session()->status()==Cantor::Session::Running) waitForSignal(session(), SIGNAL(statusChanged(Cantor::Session::Status))); QCOMPARE(2, model->rowCount()); QCOMPARE(model->index(0,0).data().toString(), QLatin1String("a")); QCOMPARE(model->index(0,1).data().toString(), QLatin1String("15")); QCOMPARE(model->index(1,0).data().toString(), QLatin1String("b")); QCOMPARE(model->index(1,1).data().toString(), QLatin1String("'S'")); evalExp(QLatin1String("del a; del b")); } void TestPython3::testVariableCleanupAfterRestart() { - Cantor::DefaultVariableModel* model = static_cast(session()->variableModel()); + Cantor::DefaultVariableModel* model = session()->variableModel(); QVERIFY(model != nullptr); Cantor::Expression* e=evalExp(QLatin1String("a = 15; b = 'S';")); QVERIFY(e!=nullptr); if(session()->status()==Cantor::Session::Running) waitForSignal(session(), SIGNAL(statusChanged(Cantor::Session::Status))); QCOMPARE(2, static_cast(model)->rowCount()); session()->logout(); session()->login(); QCOMPARE(0, static_cast(model)->rowCount()); } void TestPython3::testDictVariable() { - Cantor::DefaultVariableModel* model = static_cast(session()->variableModel()); + Cantor::DefaultVariableModel* model = session()->variableModel(); QVERIFY(model != nullptr); Cantor::Expression* e=evalExp(QLatin1String("d = {'value': 33}")); QVERIFY(e!=nullptr); if(session()->status()==Cantor::Session::Running) waitForSignal(session(), SIGNAL(statusChanged(Cantor::Session::Status))); QCOMPARE(1, static_cast(model)->rowCount()); QCOMPARE(model->index(0,0).data().toString(), QLatin1String("d")); QCOMPARE(model->index(0,1).data().toString(), QLatin1String("{'value': 33}")); evalExp(QLatin1String("del d")); } QTEST_MAIN(TestPython3) diff --git a/src/backends/qalculate/qalculateexpression.cpp b/src/backends/qalculate/qalculateexpression.cpp index 40c53cdc..e17a11b3 100644 --- a/src/backends/qalculate/qalculateexpression.cpp +++ b/src/backends/qalculate/qalculateexpression.cpp @@ -1,884 +1,883 @@ /************************************************************************************* * Copyright (C) 2009 by Milian Wolff * * Copyright (C) 2011 by Matteo Agostinelli * * * * 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 #include "textresult.h" #include "helpresult.h" #include "imageresult.h" #include "epsresult.h" #include "settings.h" #include "defaultvariablemodel.h" #include "qalculateexpression.h" #include "qalculatesession.h" #include "qalculatesyntaxhelpobject.h" #include #include #include #include #include #include #include // required for the plotting interface of Qalculator #include #include #include #include #include #include #include #include #include QalculateExpression::QalculateExpression( QalculateSession* session, bool internal) : Cantor::Expression(session, internal), m_tempFile(nullptr) { } QalculateExpression::~QalculateExpression() { if (m_tempFile) delete m_tempFile; } void QalculateExpression::evaluate() { /* Use Api for: * help * plot Use qalc for any other command */ setStatus(Cantor::Expression::Computing); if (command().isEmpty()) { setStatus(Cantor::Expression::Done); return; } QStringList commands = command().split(QLatin1Char('\n')); foreach(const QString& command, commands) { if (command.contains(QLatin1String("help"))) { QalculateSyntaxHelpObject* helper = new QalculateSyntaxHelpObject(command, (QalculateSession*) session()); setResult(new Cantor::HelpResult(helper->answer())); setStatus(Cantor::Expression::Done); return; } else if (command.trimmed().startsWith(QLatin1String("plot")) && (command.indexOf(QLatin1String("plot"))+4 == command.size() || command[command.indexOf(QLatin1String("plot"))+4].isSpace())) { evaluatePlotCommand(); return; } } // we are here because the commands entered by user are regular commands. We would have returned by now otherwise QalculateSession* currentSession = dynamic_cast(session()); currentSession->runExpression(); } void QalculateExpression::parseOutput(QString& output) { output.remove(QLatin1String(">")); output = output.trimmed(); qDebug() << "output from qalc for command: " << command() << " " << output << endl; setResult(new Cantor::TextResult(output)); // update the variable model updateVariables(); setStatus(Cantor::Expression::Done); } void QalculateExpression::updateVariables() { QalculateSession* currentSession = dynamic_cast(session()); QMap &variables = currentSession->variables; - Cantor::DefaultVariableModel* model = static_cast(currentSession->variableModel()); QMap::const_iterator it = variables.constBegin(); while (it != variables.constEnd()) { - model->addVariable(it.key(), it.value()); + currentSession->variableModel()->addVariable(it.key(), it.value()); ++it; } } void QalculateExpression::parseError(QString& error) { error.remove(QLatin1String(">")); error = error.trimmed(); qDebug() << "Error from qalc for command: " << command() << " " << error << endl; setErrorMessage(error); setStatus(Cantor::Expression::Error); } void QalculateExpression::interrupt() { setStatus(Cantor::Expression::Interrupted); } void QalculateExpression::evaluatePlotCommand() { QString argString = command().mid(command().indexOf(QLatin1String("plot"))+4); argString = QLatin1String(unlocalizeExpression(argString).c_str()); argString = argString.trimmed(); QList argumentsList; QStringList arguments; // For error handling KColorScheme scheme(QApplication::palette().currentColorGroup()); const QString errorColor = scheme.foreground(KColorScheme::NegativeText).color().name(); const QString warningColor = scheme.foreground(KColorScheme::NeutralText).color().name(); const QString msgFormat(QLatin1String("%2: %3
\n")); if (!CALCULATOR->canPlot()) { showMessage(i18n("Qalculate reports it cannot print. Is gnuplot installed?"), MESSAGE_ERROR); return; } // Split argString into the arguments int i=0; int j=0; QString arg = QLatin1String(""); while (i < argString.size()) { if (argString[i] == QLatin1Char('"') || argString[i] == QLatin1Char('\'')) { ++j; while(j < argString.size() && argString[j] != argString[i]) { if (argString[j] == QLatin1Char('\\')) { ++j; if (j == argString.size()) continue; // just ignore trailing backslashes } arg += argString[j]; ++j; } if (j == argString.size()) { showMessage(i18n("missing %1", argString[i]), MESSAGE_ERROR); return; } ++j; } else if (argString[i] == QLatin1Char(',')) { argumentsList.append(arguments); arguments.clear(); ++j; } else { while(j < argString.size() && !argString[j].isSpace() && argString[j] != QLatin1Char('=') && argString[j] != QLatin1Char(',')) { if (argString[j] == QLatin1Char('\\')) { ++j; if (j == argString.size()) continue; // just ignore trailing backslashes } arg += argString[j]; ++j; } } if (argString[j] == QLatin1Char('=')) { // Parse things like title="..." as one argument arg += QLatin1Char('='); i = ++j; continue; } if (!arg.isEmpty()) { arguments << arg; arg = QLatin1String(""); } while (j < argString.size() && argString[j].isSpace()) ++j; i = j; } argumentsList.append(arguments); // Parse the arguments and compute the points to be plotted std::vector y_vectors; std::vector x_vectors; std::vector plotDataParameterList; PlotParameters plotParameters; EvaluationOptions eo = evaluationOptions(); /// temporary plotParameters.title = ""; plotParameters.y_label = ""; plotParameters.x_label = ""; plotParameters.filename = ""; plotParameters.filetype = PLOT_FILETYPE_AUTO; plotParameters.color = QalculateSettings::coloredPlot(); plotParameters.auto_y_min = true; plotParameters.auto_x_min = true; plotParameters.auto_x_max = true; plotParameters.auto_y_max = true; plotParameters.y_log = false; plotParameters.x_log = false; plotParameters.grid = QalculateSettings::plotGrid(); plotParameters.linewidth = QalculateSettings::plotLineWidth(); plotParameters.show_all_borders = QalculateSettings::plotBorder(); switch (QalculateSettings::plotLegend()) { case QalculateSettings::LEGEND_NONE: plotParameters.legend_placement = PLOT_LEGEND_NONE; break; case QalculateSettings::LEGEND_TOP_LEFT: plotParameters.legend_placement = PLOT_LEGEND_TOP_LEFT; break; case QalculateSettings::LEGEND_TOP_RIGHT: plotParameters.legend_placement = PLOT_LEGEND_TOP_RIGHT; break; case QalculateSettings::LEGEND_BOTTOM_LEFT: plotParameters.legend_placement = PLOT_LEGEND_BOTTOM_LEFT; break; case QalculateSettings::LEGEND_BOTTOM_RIGHT: plotParameters.legend_placement = PLOT_LEGEND_BOTTOM_RIGHT; break; case QalculateSettings::LEGEND_BELOW: plotParameters.legend_placement = PLOT_LEGEND_BELOW; break; case QalculateSettings::LEGEND_OUTSIDE: plotParameters.legend_placement = PLOT_LEGEND_OUTSIDE; break; } bool plotInline = QalculateSettings::inlinePlot(); MathStructure xMin; MathStructure xMax; xMin.setUndefined(); xMax.setUndefined(); MathStructure stepLength; stepLength.setUndefined(); int steps = QalculateSettings::plotSteps(); QString mustBeNumber = i18n("%1 must be a number."); QString mustBeInteger = i18n("%1 must be a integer."); QString mustBeBoolean = i18n("%1 must be a boolean."); QString invalidOption = i18n("invalid option for %1: %2"); for (int i = 0; i < argumentsList.size(); ++i) { std::string xVariable = "x"; PlotDataParameters* plotDataParams = new PlotDataParameters; plotDataParameterList.push_back(plotDataParams); plotDataParams->title = ""; switch(QalculateSettings::plotSmoothing()) { case QalculateSettings::SMOOTHING_NONE: plotDataParams->smoothing = PLOT_SMOOTHING_NONE; break; case QalculateSettings::SMOOTHING_UNIQUE: plotDataParams->smoothing = PLOT_SMOOTHING_UNIQUE; break; case QalculateSettings::SMOOTHING_CSPLINES: plotDataParams->smoothing = PLOT_SMOOTHING_CSPLINES; break; case QalculateSettings::SMOOTHING_BEZIER: plotDataParams->smoothing = PLOT_SMOOTHING_BEZIER; break; case QalculateSettings::SMOOTHING_SBEZIER: plotDataParams->smoothing = PLOT_SMOOTHING_SBEZIER; break; } switch(QalculateSettings::plotStyle()) { case QalculateSettings::STYLE_LINES: plotDataParams->style = PLOT_STYLE_LINES; break; case QalculateSettings::STYLE_POINTS: plotDataParams->style = PLOT_STYLE_POINTS; break; case QalculateSettings::STYLE_LINES_POINTS: plotDataParams->style = PLOT_STYLE_POINTS_LINES; break; case QalculateSettings::STYLE_BOXES: plotDataParams->style = PLOT_STYLE_BOXES; break; case QalculateSettings::STYLE_HISTOGRAM: plotDataParams->style = PLOT_STYLE_HISTOGRAM; break; case QalculateSettings::STYLE_STEPS: plotDataParams->style = PLOT_STYLE_STEPS; break; case QalculateSettings::STYLE_CANDLESTICKS: plotDataParams->style = PLOT_STYLE_CANDLESTICKS; break; case QalculateSettings::STYLE_DOTS: plotDataParams->style = PLOT_STYLE_DOTS; break; } plotDataParams->yaxis2 = false; plotDataParams->xaxis2 = false; arguments = argumentsList[i]; std::string expression; int lastExpressionEntry = -1; for (int j = 0; j < arguments.size(); ++j) { QString argument = arguments[j]; // PlotParameters if (argument.startsWith(QLatin1String("plottitle="))) plotParameters.title = argument.mid(10).toLatin1().data(); else if (argument.startsWith(QLatin1String("ylabel="))) plotParameters.y_label = argument.mid(7).toLatin1().data(); else if (argument.startsWith(QLatin1String("xlabel="))) plotParameters.x_label = argument.mid(7).toLatin1().data(); else if (argument.startsWith(QLatin1String("filename="))) plotParameters.filename = argument.mid(9).toLatin1().data(); else if (argument.startsWith(QLatin1String("filetype="))) { QString option = argument.mid(9); if (option == QLatin1String("auto")) plotParameters.filetype = PLOT_FILETYPE_AUTO; else if (option == QLatin1String("png")) plotParameters.filetype = PLOT_FILETYPE_PNG; else if (option == QLatin1String("ps")) plotParameters.filetype = PLOT_FILETYPE_PS; else if (option == QLatin1String("eps")) plotParameters.filetype = PLOT_FILETYPE_EPS; else if (option == QLatin1String("latex")) plotParameters.filetype = PLOT_FILETYPE_LATEX; else if (option == QLatin1String("svg")) plotParameters.filetype = PLOT_FILETYPE_SVG; else if (option == QLatin1String("fig")) plotParameters.filetype = PLOT_FILETYPE_FIG; else { QString msg = invalidOption.arg(QLatin1String("filetype"), option); showMessage(msg, MESSAGE_ERROR); deletePlotDataParameters(plotDataParameterList); return; } } else if (argument.startsWith(QLatin1String("font="))) plotParameters.font = argument.mid(5).toLatin1().data(); else if (argument.startsWith(QLatin1String("color="))) { bool ok; plotParameters.color = stringToBool(argument.mid(6), &ok); if (!ok) { showMessage(mustBeBoolean.arg(QLatin1String("color")), MESSAGE_ERROR); deletePlotDataParameters(plotDataParameterList); return; } } else if (argument.startsWith(QLatin1String("ylog="))) { bool ok; plotParameters.y_log = stringToBool(argument.mid(5), &ok); if (!ok) { showMessage(mustBeBoolean.arg(QLatin1String("ylog")), MESSAGE_ERROR); deletePlotDataParameters(plotDataParameterList); return; } } else if (argument.startsWith(QLatin1String("xlog="))) { bool ok; plotParameters.x_log = stringToBool(argument.mid(5), &ok); if (!ok) { showMessage(mustBeBoolean.arg(QLatin1String("xlog")), MESSAGE_ERROR); return; } } else if (argument.startsWith(QLatin1String("ylogbase="))) { MathStructure ylogStr = CALCULATOR->calculate(argument.mid(9).toLatin1().data(), eo); if (checkForCalculatorMessages() & (MSG_WARN|MSG_ERR)) { deletePlotDataParameters(plotDataParameterList); return; } if (ylogStr.isNumber()) { Number ylogNum = ylogStr.number(); plotParameters.y_log_base = ylogNum.floatValue(); } else { showMessage(mustBeNumber.arg(QLatin1String("ylogbase")), MESSAGE_ERROR); deletePlotDataParameters(plotDataParameterList); return; } } else if (argument.startsWith(QLatin1String("xlogbase="))) { MathStructure xlogStr = CALCULATOR->calculate(argument.mid(9).toLatin1().data(), eo); if (checkForCalculatorMessages() & (MSG_WARN|MSG_ERR)) { deletePlotDataParameters(plotDataParameterList); return; } if (xlogStr.isNumber()) { Number xlogNum = xlogStr.number(); plotParameters.x_log_base = xlogNum.floatValue(); } else { showMessage(mustBeNumber.arg(QLatin1String("xlogbase")), MESSAGE_ERROR); deletePlotDataParameters(plotDataParameterList); return; } } else if (argument.startsWith(QLatin1String("grid="))) { bool ok; plotParameters.grid = stringToBool(argument.mid(5), &ok); if (!ok) { showMessage(mustBeBoolean.arg(QLatin1String("grid")), MESSAGE_ERROR); deletePlotDataParameters(plotDataParameterList); return; } } else if (argument.startsWith(QLatin1String("linewidth="))) { MathStructure lineWidthStr = CALCULATOR->calculate(argument.mid(10).toLatin1().data(), eo); Number lineWidthNum; if (lineWidthStr.isNumber() && lineWidthStr.number().isInteger()) { lineWidthNum = lineWidthStr.number(); plotParameters.linewidth = lineWidthNum.intValue(); } else { showMessage(mustBeInteger.arg(QLatin1String("linewidth")), MESSAGE_ERROR); deletePlotDataParameters(plotDataParameterList); return; } } else if (argument.startsWith(QLatin1String("border="))) { bool ok; plotParameters.show_all_borders = stringToBool(argument.mid(7), &ok); if (!ok) { showMessage(mustBeBoolean.arg(QLatin1String("border")), MESSAGE_ERROR); deletePlotDataParameters(plotDataParameterList); return; } } else if (argument.startsWith(QLatin1String("legend="))) { QString option = argument.mid(7); if (option == QLatin1String("none")) plotParameters.legend_placement = PLOT_LEGEND_NONE; else if (option == QLatin1String("top_left")) plotParameters.legend_placement = PLOT_LEGEND_TOP_LEFT; else if (option == QLatin1String("top_right")) plotParameters.legend_placement = PLOT_LEGEND_TOP_RIGHT; else if (option == QLatin1String("bottom_left")) plotParameters.legend_placement = PLOT_LEGEND_BOTTOM_LEFT; else if (option == QLatin1String("bottom_right")) plotParameters.legend_placement = PLOT_LEGEND_BOTTOM_RIGHT; else if (option == QLatin1String("below")) plotParameters.legend_placement = PLOT_LEGEND_BELOW; else if (option == QLatin1String("outside")) plotParameters.legend_placement = PLOT_LEGEND_OUTSIDE; else { QString msg = invalidOption.arg(QLatin1String("legend"), option); showMessage(msg, MESSAGE_ERROR); deletePlotDataParameters(plotDataParameterList); return; } } // PlotDataParameters else if (argument.startsWith(QLatin1String("title="))) { plotDataParams->title = argument.mid(6).toLatin1().data(); } else if (argument.startsWith(QLatin1String("smoothing="))) { QString option = argument.mid(10); if (option == QLatin1String("none")) plotDataParams->smoothing = PLOT_SMOOTHING_NONE; else if (option == QLatin1String("monotonic")) plotDataParams->smoothing = PLOT_SMOOTHING_UNIQUE; else if (option == QLatin1String("csplines")) plotDataParams->smoothing = PLOT_SMOOTHING_CSPLINES; else if (option == QLatin1String("bezier")) plotDataParams->smoothing = PLOT_SMOOTHING_BEZIER; else if (option == QLatin1String("sbezier")) plotDataParams->smoothing = PLOT_SMOOTHING_SBEZIER; else { QString msg = invalidOption.arg(QLatin1String("smoothing"), option); showMessage(msg, MESSAGE_ERROR); deletePlotDataParameters(plotDataParameterList); return; } } else if (argument.startsWith(QLatin1String("style="))) { QString option = argument.mid(6); if (option == QLatin1String("lines")) plotDataParams->style = PLOT_STYLE_LINES; else if (option == QLatin1String("points")) plotDataParams->style = PLOT_STYLE_POINTS; else if (option == QLatin1String("points_lines")) plotDataParams->style = PLOT_STYLE_POINTS_LINES; else if (option == QLatin1String("boxes")) plotDataParams->style = PLOT_STYLE_BOXES; else if (option == QLatin1String("histogram")) plotDataParams->style = PLOT_STYLE_HISTOGRAM; else if (option == QLatin1String("steps")) plotDataParams->style = PLOT_STYLE_STEPS; else if (option == QLatin1String("candlesticks")) plotDataParams->style = PLOT_STYLE_CANDLESTICKS; else if (option == QLatin1String("dots")) plotDataParams->style = PLOT_STYLE_DOTS; else { QString msg = invalidOption.arg(QLatin1String("style"), option); showMessage(msg, MESSAGE_ERROR); deletePlotDataParameters(plotDataParameterList); return; } } else if (argument.startsWith(QLatin1String("xaxis2="))) { bool ok; plotDataParams->xaxis2 = stringToBool(argument.mid(7), &ok); if (!ok) { showMessage(mustBeBoolean.arg(QLatin1String("xaxis2")), MESSAGE_ERROR); deletePlotDataParameters(plotDataParameterList); return; } } else if (argument.startsWith(QLatin1String("yaxis2="))) { bool ok; plotDataParams->yaxis2 = stringToBool(argument.mid(7), &ok); if (!ok) { showMessage(mustBeBoolean.arg(QLatin1String("yaxis2")), MESSAGE_ERROR); deletePlotDataParameters(plotDataParameterList); return; } } // inline, xmin, xmax, step, steps, xvar // Custom options else if (argument.startsWith(QLatin1String("inline="))) { bool ok; plotInline = stringToBool(argument.mid(7), &ok); if (!ok) { showMessage(mustBeBoolean.arg(QLatin1String("inline")), MESSAGE_ERROR); deletePlotDataParameters(plotDataParameterList); return; } } else if (argument.startsWith(QLatin1String("xmin="))) { xMin = CALCULATOR->calculate(argument.mid(5).toLatin1().data(), eo); if (checkForCalculatorMessages() & (MSG_WARN|MSG_ERR)) { deletePlotDataParameters(plotDataParameterList); return; } } else if (argument.startsWith(QLatin1String("xmax="))) { xMax = CALCULATOR->calculate(argument.mid(5).toLatin1().data(), eo); if (checkForCalculatorMessages() & (MSG_WARN|MSG_ERR)) { deletePlotDataParameters(plotDataParameterList); return; } } else if (argument.startsWith(QLatin1String("step="))) { stepLength = CALCULATOR->calculate(argument.mid(5).toLatin1().data(), eo); if (checkForCalculatorMessages() & (MSG_WARN|MSG_ERR)) { deletePlotDataParameters(plotDataParameterList); return; } steps = -1; } else if (argument.startsWith(QLatin1String("steps="))) { MathStructure stepsStr = CALCULATOR->calculate(argument.mid(6).toLatin1().data(), eo); if (checkForCalculatorMessages() & (MSG_WARN|MSG_ERR)) { deletePlotDataParameters(plotDataParameterList); return; } Number stepsNum; if (stepsStr.isNumber() && stepsStr.number().isInteger()) { stepsNum = stepsStr.number(); steps = stepsNum.intValue(); stepLength.setUndefined(); } else { showMessage(mustBeInteger.arg(QLatin1String("steps")), MESSAGE_ERROR); deletePlotDataParameters(plotDataParameterList); return; } } else if (argument.startsWith(QLatin1String("xvar="))) { xVariable = argument.mid(5).toLatin1().data(); } else if (expression.empty()) { expression = argument.toLatin1().data(); lastExpressionEntry = j; } else if (lastExpressionEntry == j-1) { expression += " "; expression += argument.toLatin1().data(); lastExpressionEntry = j; } else { QString msg = i18n("found multiple expressions in one plot command (%1 and %2).", QLatin1String(expression.c_str()), argument); showMessage(msg, MESSAGE_ERROR); deletePlotDataParameters(plotDataParameterList); return; } } if (expression.empty()) continue; if (xMin.isUndefined()) { if (!plotParameters.auto_x_min) xMin = plotParameters.x_min; else xMin = 0.0; } if (xMax.isUndefined()) { if (!plotParameters.auto_x_max) xMax = plotParameters.x_max; else xMax = 10.0; } if (plotDataParams->title.empty()) plotDataParams->title = expression; MathStructure x_vec, y_vec; x_vec.clearVector(); if (!stepLength.isUndefined()) y_vec = CALCULATOR->expressionToPlotVector(expression, xMin, xMax, stepLength, &x_vec, xVariable, eo.parse_options); else y_vec = CALCULATOR->expressionToPlotVector(expression, xMin, xMax, steps, &x_vec, xVariable, eo.parse_options); if (checkForCalculatorMessages() & (MSG_WARN|MSG_ERR)) { deletePlotDataParameters(plotDataParameterList); return; } x_vectors.push_back(x_vec); y_vectors.push_back(y_vec); //PrintOptions po; //y_vec.format(po); //setResult(new Cantor::TextResult(y_vec.print(po).c_str())); //setStatus(Done); //deletePlotDataParameters(plotDataParameterList); //return; } if (plotInline && plotParameters.filename.empty()) { // TODO: get a temporary file name here if (!m_tempFile) { #ifdef WITH_EPS m_tempFile=new QTemporaryFile(QDir::tempPath() + QLatin1String("/cantor_qalculate-XXXXXX.eps" )); #else m_tempFile=new QTemporaryFile(QDir::tempPath() + QLatin1String("/cantor_qalculate-XXXXXX.png")); #endif m_tempFile->open(); } plotParameters.filename = m_tempFile->fileName().toLatin1().data(); plotParameters.filetype = PLOT_FILETYPE_AUTO; } CALCULATOR->plotVectors(&plotParameters, y_vectors, x_vectors, plotDataParameterList); if (checkForCalculatorMessages() & (MSG_WARN|MSG_ERR)) { deletePlotDataParameters(plotDataParameterList); return; } deletePlotDataParameters(plotDataParameterList); if (plotInline) { #ifdef WITH_EPS size_t p = plotParameters.filename.size(); if (plotParameters.filetype == PLOT_FILETYPE_EPS || plotParameters.filetype == PLOT_FILETYPE_PS || (plotParameters.filetype == PLOT_FILETYPE_AUTO && p >= 4 && plotParameters.filename.substr(p-4,4) == ".eps") || (plotParameters.filetype == PLOT_FILETYPE_AUTO && p >= 3 && plotParameters.filename.substr(p-3,3) == ".ps")) setResult(new Cantor::EpsResult(QUrl(QString::fromStdString(plotParameters.filename)))); else setResult(new Cantor::ImageResult(QUrl(QString::fromStdString(plotParameters.filename)))); #else setResult(new Cantor::ImageResult(QUrl::fromLocalFile(QString::fromStdString(plotParameters.filename)))); #endif setStatus(Cantor::Expression::Done); } } void QalculateExpression::showMessage(QString msg, MessageType mtype) { KColorScheme scheme(QApplication::palette().currentColorGroup()); const QString errorColor = scheme.foreground(KColorScheme::NegativeText).color().name(); const QString warningColor = scheme.foreground(KColorScheme::NeutralText).color().name(); const QString msgFormat(QLatin1String("%2: %3
\n")); if(mtype == MESSAGE_ERROR || mtype == MESSAGE_WARNING) { msg.replace(QLatin1String("&"), QLatin1String("&")); msg.replace(QLatin1String(">"), QLatin1String(">")); msg.replace(QLatin1String("<"), QLatin1String("<")); if (mtype == MESSAGE_ERROR) { msg = msgFormat.arg(errorColor, i18n("ERROR"), QLatin1String(msg.toLatin1().data())); } else { msg = msgFormat.arg(errorColor, i18n("WARNING"), QLatin1String(msg.toLatin1().data())); } setErrorMessage(msg); setStatus(Error); } else { KMessageBox::information(QApplication::activeWindow(), msg); } } EvaluationOptions QalculateExpression::evaluationOptions() { EvaluationOptions eo; eo.auto_post_conversion = QalculateSettings::postConversion() ? POST_CONVERSION_BEST : POST_CONVERSION_NONE; eo.keep_zero_units = false; eo.parse_options = parseOptions(); switch (QalculateSettings::structuring()) { case 0: eo.structuring = STRUCTURING_NONE; break; case 1: eo.structuring = STRUCTURING_SIMPLIFY; break; case 2: eo.structuring = STRUCTURING_FACTORIZE; break; } return eo; } ParseOptions QalculateExpression::parseOptions() { ParseOptions po; switch (QalculateSettings::angleUnit()) { case 0: po.angle_unit = ANGLE_UNIT_NONE; break; case 1: po.angle_unit = ANGLE_UNIT_RADIANS; break; case 2: po.angle_unit = ANGLE_UNIT_DEGREES; break; case 3: po.angle_unit = ANGLE_UNIT_GRADIANS; break; } po.base = QalculateSettings::base(); po.comma_as_separator = false; return po; } void QalculateExpression::deletePlotDataParameters (const std::vector& plotDataParameterList) { for(size_t i = 0; i < plotDataParameterList.size(); ++i) delete plotDataParameterList[i]; } bool QalculateExpression::stringToBool(const QString &string, bool *ok) { if (string == QLatin1String("true") || string == QLatin1String("1")) { *ok = true; return true; } else if (string == QLatin1String("false") || string == QLatin1String("0")) { *ok = true; return false; } else { *ok = false; return false; } } int QalculateExpression::checkForCalculatorMessages() { // error handling, most of it copied from qalculate-kde int msgType = MSG_NONE; if ( CALCULATOR->message() ) { QString msg; KColorScheme scheme(QApplication::palette().currentColorGroup()); const QString errorColor = scheme.foreground(KColorScheme::NegativeText).color().name(); const QString warningColor = scheme.foreground(KColorScheme::NeutralText).color().name(); const QString msgFormat(QLatin1String("%2: %3
\n")); MessageType mtype; while(true) { mtype = CALCULATOR->message()->type(); switch(mtype) { case MESSAGE_INFORMATION: msgType |= MSG_INFO; break; case MESSAGE_WARNING: msgType |= MSG_WARN; break; case MESSAGE_ERROR: msgType |= MSG_ERR; break; } if(mtype == MESSAGE_ERROR || mtype == MESSAGE_WARNING) { QString text = QLatin1String(CALCULATOR->message()->message().c_str()); text.replace(QLatin1String("&"), QLatin1String("&")); text.replace(QLatin1String(">"), QLatin1String(">")); text.replace(QLatin1String("<"), QLatin1String("<")); if (mtype == MESSAGE_ERROR) { msg.append(msgFormat.arg(errorColor, i18n("ERROR"), text)); } else { msg.append(msgFormat.arg(errorColor, i18n("WARNING"), text)); } } else { KMessageBox::information(QApplication::activeWindow(), QLatin1String(CALCULATOR->message()->message().c_str())); } if(!CALCULATOR->nextMessage()) break; } if ( !msg.isEmpty() ) { m_message += msg; setErrorMessage(m_message); setStatus(Error); } } return msgType; } std::string QalculateExpression::unlocalizeExpression(QString expr) { // copy'n'pasted from qalculate plasma applet return CALCULATOR->unlocalizeExpression( expr.replace(QChar(0xA3), QLatin1String("GBP")) .replace(QChar(0xA5), QLatin1String("JPY")) .replace(QLatin1String("$"), QLatin1String("USD")) .replace(QChar(0x20AC), QLatin1String("EUR")) .toLatin1().data() ); } QSharedPointer QalculateExpression::printOptions() { QSharedPointer po(new PrintOptions); switch (QalculateSettings::fractionFormat()) { case 0: po->number_fraction_format = FRACTION_DECIMAL; break; case 1: po->number_fraction_format = FRACTION_DECIMAL_EXACT; break; case 2: po->number_fraction_format = FRACTION_FRACTIONAL; break; case 3: po->number_fraction_format = FRACTION_COMBINED; break; } po->indicate_infinite_series = QalculateSettings::indicateInfiniteSeries(); po->use_all_prefixes = QalculateSettings::useAllPrefixes(); po->negative_exponents = QalculateSettings::negativeExponents(); po->lower_case_e = true; po->base = QalculateSettings::base(); po->decimalpoint_sign = QLocale().decimalPoint().toLatin1(); switch (QalculateSettings::minExp()) { case 0: po->min_exp = EXP_NONE; break; case 1: po->min_exp = EXP_PURE; break; case 2: po->min_exp = EXP_SCIENTIFIC; break; case 3: po->min_exp = EXP_PRECISION; break; case 4: po->min_exp = EXP_BASE_3; break; } return po; }