diff --git a/CHANGELOG.md b/CHANGELOG.md index df4784ce..71f37857 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,85 +1,86 @@ # Changelog ## 20.08 ### New features * Change entries replacement logic and add possibility to use previous logic via Cantor setting option. + * Add possibility to change plot extension (available variants: jpeg, png, svg and eps (if builded with eps support)) in Octave backend. ## 20.04 ### New features * WorksheetControlItem: special element, for better UX, while user interact with cell. Now, this element handle drag-and-drop and cell selection (details below) * Multiple cells selection: now user can select not one, but many sells via Ctrl+LeftClick. Selection also visualizate on control elements * Actions on selection (first version): now user can apply some actions on selected cells, for example evaulating, deleting, moving. * Possibility to change result collapsing via double click on '>>>' prompt element * Add collapsing of text results with a lot of visible lines (limit of collapsing set in Settings). Double click can collapse/uncollapse collapsed text result. ## 19.12 ### Screenshots * https://imgur.com/frfNeBH * https://imgur.com/IAJ4YAN * https://imgur.com/eBesNdR ### New features * Support for Jupyter Notebook format (.ipynb) * Allow to convert Cantor's native worksheet format to Jupyter notebook and back * Allow to change the type of a worksheet entry via the context menu * Leave the markdown and latex cells in the edit mode if the the user hits the cancel/escape button * Add opportunity to set path to local documentation in Sage backend ### Important bug fixes * Fix rendering of embedded math in Markdown Entry on openSUSE * Show the pointer hand cursor when hovering over a URL in a markdown entry * Make sagemath backend compatible with sagemath built with python3 ## 19.08 ### Screenshots * https://imgur.com/Xpj2EcQ * https://imgur.com/KnXYvFP * https://imgur.com/CmucWdR ### New features * Instead of showing only available and workable backends, Cantor shows all available backends and for non workable shows reason, why this backend doesn't work. * Allow to set the path to custom Julia installations. However, Cantor will work with versions only it was compiled for. * For Markdown and LaTeX entries allow to switch via double click from the rendered result to the original code and back via the evaluation of the entry * Save the results of rendered markdown and LaTeX entries as part of the project. This allows to see the results also on with no support for markdown and latex rendering * Hide "Help" panel on startup. Automatically show this panel when user executes a command entry with a help expression * Add "Recent Files" submenu (https://bugs.kde.org/show_bug.cgi?id=409138) ### Important bug fixes * [R] Fix bug with expression only from comment - now Cantor R backend don't freeze on 'Computing' after running the expression * Save error status and message of Command Entry into .cws (Cantor Worksheet file) - Cantor have lost them on saving before * Reset Command Entry numeration after Backend restart * Close loaded worksheet, if the loading failed (before Cantor show empty broken worksheet) * [Python] Fix bug with non-working interruption (before interrupted only Cantor expression: Python Server still continued to work) * Don't scroll to worksheet's end after the project was loaded * [Julia, Python] Report about server side errors, for example, crashes * [Python] Don't use Qt in pythonserver executable for avoding problems (often crashes) with PyQt5 (https://bugs.kde.org/show_bug.cgi?id=397264, https://bugs.kde.org/show_bug.cgi?id=407362) * [Python] Show Python warnings not as errors, but as text results (https://bugs.kde.org/show_bug.cgi?id=409240) * Add missing context menu to MarkdownEntry * Fix bug with rendering loaded rendered MarkdownEntry as empty * Fix unworking 'Show LaTeX Code' action in Latex Entry context menu * Fix problem with an incorrect window title after closing all tabs ## 19.04 ### New Features * Possibility to hide and show results of command entry via context menu * [Maxima, Octave, Python, R] Add a way to specify the path to the local documentation in the settings. By default, this path didn't specified and Cantor uses online documentation. * Huge improvment of variables managment: better parsing, option to turn on and turn off variable managment, better GUI performance by limitation of value size (too big for showing values replaced by "" text ### Important bug fixes * [Sage] Fix execuation for unsystem Sage installation * [Julia] Fix bug, that suppressing output don't work diff --git a/src/backends/octave/CMakeLists.txt b/src/backends/octave/CMakeLists.txt index 32affe63..5ac78ff2 100644 --- a/src/backends/octave/CMakeLists.txt +++ b/src/backends/octave/CMakeLists.txt @@ -1,41 +1,52 @@ set( OctaveBackend_SRCS octavebackend.cpp octavesession.cpp octaveexpression.cpp octaveextensions.cpp octavehighlighter.cpp octavekeywords.cpp octavecompletionobject.cpp octavesyntaxhelpobject.cpp octavevariablemodel.cpp ) add_subdirectory(scripts) +if (WITH_EPS) + set(DEFAULT_PLOT_FORMAT "eps") + set(EPS_PLOT_FORMAT_CHOICE "") + file(READ with_eps_ui_part.txt EPS_PLOT_FORMAT_UI_ELEMENT) +else (WITH_EPS) + set(DEFAULT_PLOT_FORMAT "png") +endif (WITH_EPS) +message(STATUS ${CMAKE_CURRENT_SOURCE_DIR}) +configure_file(octavebackend.kcfg.in ${CMAKE_CURRENT_BINARY_DIR}/octavebackend.kcfg) +configure_file(settings.ui.in ${CMAKE_CURRENT_BINARY_DIR}/settings.ui) + kconfig_add_kcfg_files(OctaveBackend_SRCS settings.kcfgc) -install(FILES octavebackend.kcfg DESTINATION ${KDE_INSTALL_KCFGDIR}) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/octavebackend.kcfg DESTINATION ${KDE_INSTALL_KCFGDIR}) -ki18n_wrap_ui(OctaveBackend_SRCS settings.ui) +ki18n_wrap_ui(OctaveBackend_SRCS ${CMAKE_CURRENT_BINARY_DIR}/settings.ui) add_backend(octavebackend ${OctaveBackend_SRCS}) target_link_libraries(cantor_octavebackend KF5::KIOCore KF5::ConfigCore KF5::ConfigGui KF5::SyntaxHighlighting ) if(BUILD_TESTING) - add_executable( testoctave testoctave.cpp) + add_executable( testoctave testoctave.cpp octaveexpression.cpp settings.cpp) add_test(NAME testoctave COMMAND testoctave) ecm_mark_as_test(testoctave) target_link_libraries( testoctave Qt5::Test cantorlibs cantortest ) endif(BUILD_TESTING) install(FILES cantor_octave.knsrc DESTINATION ${KDE_INSTALL_CONFDIR} ) diff --git a/src/backends/octave/octavebackend.kcfg b/src/backends/octave/octavebackend.kcfg.in similarity index 81% rename from src/backends/octave/octavebackend.kcfg rename to src/backends/octave/octavebackend.kcfg.in index f5cb46f7..90b129ac 100644 --- a/src/backends/octave/octavebackend.kcfg +++ b/src/backends/octave/octavebackend.kcfg.in @@ -1,28 +1,37 @@ QStandardPaths QUrl::fromLocalFile(QStandardPaths::findExecutable( QLatin1String("octave-cli") )) true true + + + @EPS_PLOT_FORMAT_CHOICE@ + + + + + @DEFAULT_PLOT_FORMAT@ + diff --git a/src/backends/octave/octaveexpression.cpp b/src/backends/octave/octaveexpression.cpp index bd1cd57d..cd70c5cb 100644 --- a/src/backends/octave/octaveexpression.cpp +++ b/src/backends/octave/octaveexpression.cpp @@ -1,226 +1,243 @@ /* 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 "octaveexpression.h" #include "octavesession.h" #include "defaultvariablemodel.h" #include "textresult.h" +#include "epsresult.h" +#include "imageresult.h" #include #include #include #include #include #include #include #include "settings.h" -static const QLatin1String printCommandBegin("cantor_print('"); -static const QLatin1String printCommandEnd("');"); +static const QString printCommandTemplate = QString::fromLatin1("cantor_print('%1', '%2');"); static const QStringList plotCommands({ QLatin1String("plot"), QLatin1String("semilogx"), QLatin1String("semilogy"), QLatin1String("loglog"), QLatin1String("polar"), QLatin1String("contour"), QLatin1String("bar"), QLatin1String("stairs"), QLatin1String("errorbar"), QLatin1String("sombrero"), QLatin1String("hist"), QLatin1String("fplot"), QLatin1String("imshow"), QLatin1String("stem"), QLatin1String("stem3"), QLatin1String("scatter"), QLatin1String("pareto"), QLatin1String("rose"), QLatin1String("pie"), QLatin1String("quiver"), QLatin1String("compass"), QLatin1String("feather"), QLatin1String("pcolor"), QLatin1String("area"), QLatin1String("fill"), QLatin1String("comet"), QLatin1String("plotmatrix"), /* 3d-plots */ QLatin1String("plot3"), QLatin1String("mesh"), QLatin1String("meshc"), QLatin1String("meshz"), QLatin1String("surf"), QLatin1String("surfc"), QLatin1String("surfl"), QLatin1String("surfnorm"), QLatin1String("isosurface"), QLatin1String("isonormals"), QLatin1String("isocaps"), /* 3d-plots defined by a function */ QLatin1String("ezplot3"), QLatin1String("ezmesh"), QLatin1String("ezmeshc"), QLatin1String("ezsurf"), QLatin1String("ezsurfc"), QLatin1String("cantor_plot2d"), QLatin1String("cantor_plot3d")}); +const QStringList OctaveExpression::plotExtensions({ +#ifdef WITH_EPS + QLatin1String("eps"), +#endif + QLatin1String("png"), + QLatin1String("svg"), + QLatin1String("jpeg") +}); OctaveExpression::OctaveExpression(Cantor::Session* session, bool internal): Expression(session, internal) { } OctaveExpression::~OctaveExpression() { if(m_tempFile) { delete m_tempFile; m_tempFile = nullptr; } } void OctaveExpression::interrupt() { qDebug() << "interrupt"; setStatus(Interrupted); } void OctaveExpression::evaluate() { if(m_tempFile) { delete m_tempFile; m_tempFile = nullptr; } qDebug() << "evaluate"; QString cmd = command(); QStringList cmdWords = cmd.split(QRegularExpression(QStringLiteral("\\b")), QString::SkipEmptyParts); if (OctaveSettings::integratePlots() && !cmdWords.contains(QLatin1String("help")) && !cmdWords.contains(QLatin1String("completion_matches"))) { for (const QString& plotCmd : plotCommands) { if (cmdWords.contains(plotCmd)) { qDebug() << "Executing a plot command"; +/* #ifdef WITH_EPS QLatin1String ext(".eps"); #else QLatin1String ext(".png"); #endif - m_tempFile = new QTemporaryFile(QDir::tempPath() + QLatin1String("/cantor_octave-XXXXXX")+ext); +*/ + m_tempFile = new QTemporaryFile(QDir::tempPath() + QLatin1String("/cantor_octave-XXXXXX.")+plotExtensions[OctaveSettings::inlinePlotFormat()]); m_tempFile->open(); qDebug() << "plot temp file" << m_tempFile->fileName(); QFileSystemWatcher* watcher = fileWatcher(); if (!watcher->files().isEmpty()) watcher->removePaths(watcher->files()); watcher->addPath(m_tempFile->fileName()); connect(watcher, &QFileSystemWatcher::fileChanged, this, &OctaveExpression::imageChanged, Qt::UniqueConnection); m_plotPending = true; break; } } } m_finished = false; session()->enqueueExpression(this); } QString OctaveExpression::internalCommand() { QString cmd = command(); if (m_plotPending) { if (!cmd.endsWith(QLatin1Char(';')) && !cmd.endsWith(QLatin1Char(','))) cmd += QLatin1Char(','); - cmd += printCommandBegin + m_tempFile->fileName() + printCommandEnd; + cmd += printCommandTemplate.arg(QFileInfo(m_tempFile->fileName()).suffix()).arg(m_tempFile->fileName()); } // We need remove all comments here, because below we merge all strings to one long string // Otherwise, all code after line with comment will be commented out after merging // So, this small state machine remove all comments // FIXME better implementation QString tmp; // 0 - command mode, 1 - string mode for ', 2 - string mode for ", 3 - comment mode int status = 0; for (int i = 0; i < cmd.size(); i++) { const char ch = cmd[i].toLatin1(); if (status == 0 && (ch == '#' || ch == '%')) status = 3; else if (status == 0 && ch == '\'') status = 1; else if (status == 0 && ch == '"') status = 2; else if (status == 1 && ch == '\'') status = 0; else if (status == 2 && ch == '"') status = 0; else if (status == 3 && ch == '\n') status = 0; if (status != 3) tmp += cmd[i]; } cmd = tmp; cmd.replace(QLatin1String(";\n"), QLatin1String(";")); cmd.replace(QLatin1Char('\n'), QLatin1Char(',')); cmd += QLatin1Char('\n'); return cmd; } void OctaveExpression::parseOutput(const QString& output) { qDebug() << "parseOutput: " << output; if (!output.trimmed().isEmpty()) { // TODO: what about help in comment? printf with '... help ...'? // This must be corrected. if (command().contains(QLatin1String("help"))) { addResult(new Cantor::HelpResult(output)); } else { addResult(new Cantor::TextResult(output)); } } m_finished = true; if (!m_plotPending) setStatus(Done); } void OctaveExpression::parseError(const QString& error) { if (error.startsWith(QLatin1String("warning: "))) { // It's warning, so add as result addResult(new Cantor::TextResult(error)); } else { setErrorMessage(error); setStatus(Error); } } void OctaveExpression::imageChanged() { if(m_tempFile->size() <= 0) return; - OctavePlotResult* newResult = new OctavePlotResult(QUrl::fromLocalFile(m_tempFile->fileName())); + const QUrl& url = QUrl::fromLocalFile(m_tempFile->fileName()); + Cantor::Result* newResult; + if (m_tempFile->fileName().endsWith(QLatin1String(".eps"))) + newResult = new Cantor::EpsResult(url); + else + newResult = new Cantor::ImageResult(url); + bool found = false; for (int i = 0; i < results().size(); i++) if (results()[i]->type() == newResult->type()) { replaceResult(i, newResult); found = true; } if (!found) addResult(newResult); m_plotPending = false; if (m_finished && status() != Expression::Done) { setStatus(Done); } } diff --git a/src/backends/octave/octaveexpression.h b/src/backends/octave/octaveexpression.h index 27f8ae83..4caf1b95 100644 --- a/src/backends/octave/octaveexpression.h +++ b/src/backends/octave/octaveexpression.h @@ -1,61 +1,56 @@ /* 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. */ #ifndef OCTAVEEXPRESSION_H #define OCTAVEEXPRESSION_H #include #include #include -#ifdef WITH_EPS -#include "epsresult.h" -using OctavePlotResult = Cantor::EpsResult; -#else -#include "imageresult.h" -typedef Cantor::ImageResult OctavePlotResult; -#endif - class QTemporaryFile; class OctaveExpression : public Cantor::Expression { Q_OBJECT public: explicit OctaveExpression(Cantor::Session*, bool internal = false); ~OctaveExpression(); void interrupt() override; void evaluate() override; QString internalCommand() override; void parseOutput(const QString&); void parseError(const QString&); void imageChanged(); +public: + const static QStringList plotExtensions; + private: QString m_resultString; bool m_finished = false; bool m_plotPending = false; QTemporaryFile* m_tempFile = nullptr; }; #endif // OCTAVEEXPRESSION_H diff --git a/src/backends/octave/octavevariablemodel.cpp b/src/backends/octave/octavevariablemodel.cpp index 03004297..90cf75c4 100644 --- a/src/backends/octave/octavevariablemodel.cpp +++ b/src/backends/octave/octavevariablemodel.cpp @@ -1,111 +1,116 @@ /* 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) 2018 Nikita Sirgienko */ #include "octavevariablemodel.h" #include "octavesession.h" #include "textresult.h" #include #include "settings.h" using namespace Cantor; OctaveVariableModel::OctaveVariableModel(OctaveSession* session): DefaultVariableModel(session), m_expr(nullptr) { } void OctaveVariableModel::update() { static const QString code = QString::fromLatin1( "printf('__cantor_delimiter_line__\\n');" "__cantor_list__ = who();" "__cantor_split_var__ = split_long_rows(0);" "__cantor_parse_values__ = %1;" "for __cantor_index__ = 1:length(__cantor_list__)" " __cantor_varname__ = char(__cantor_list__{__cantor_index__});" " printf([__cantor_varname__ '\\n']);" " if (__cantor_parse_values__)" " try" " eval(['__cantor_string__ = disp(' __cantor_varname__ ');']);" " printf(__cantor_string__);" " catch" " printf(['' '\\n']);" " end_try_catch;" " endif;" " printf('__cantor_delimiter_line__\\n')" "endfor;" "split_long_rows(__cantor_split_var__);" "clear __cantor_list__;" "clear __cantor_index__;" "clear __cantor_varname__;" "clear __cantor_parse_values__;" "clear __cantor_string__;" "clear __cantor_split_var__;" ); const QString& cmd = code.arg(OctaveSettings::self()->variableManagement() ? QLatin1String("true") : QLatin1String("false")); if (m_expr) return; m_expr = session()->evaluateExpression(cmd, Expression::FinishingBehavior::DoNotDelete, true); connect(m_expr, &Expression::statusChanged, this, &OctaveVariableModel::parseNewVariables); } void OctaveVariableModel::parseNewVariables(Expression::Status status) { switch(status) { case Expression::Status::Done: { static const QLatin1String delimiter("__cantor_delimiter_line__"); - // Result always must be, if we done, so don't check it + if (m_expr->results().isEmpty()) + { + qWarning() << "Octave code for parsing variables finish with done status, but without results"; + break; + } + QString text = static_cast(m_expr->result())->plain(); const QStringList& lines = text.split(delimiter, QString::SkipEmptyParts); QList vars; for (QString line : lines) { line = line.trimmed(); const QString& name = line.section(QLatin1String("\n"), 0, 0); QString value; if (OctaveSettings::self()->variableManagement()) value = line.section(QLatin1String("\n"), 1); vars << Variable{name, value}; } setVariables(vars); break; } case Expression::Status::Error: qWarning() << "Octave code for parsing variables finish with error message: " << m_expr->errorMessage(); break; default: return; } m_expr->deleteLater(); m_expr = nullptr; } diff --git a/src/backends/octave/scripts/CMakeLists.txt b/src/backends/octave/scripts/CMakeLists.txt index c08c37f6..bb1f2bb2 100644 --- a/src/backends/octave/scripts/CMakeLists.txt +++ b/src/backends/octave/scripts/CMakeLists.txt @@ -1,12 +1 @@ -if (WITH_EPS) -set(PLOT_FILE_FORMAT epsc) -set(PLOT_FILE_SUFFIX eps) -else (WITH_EPS) -set(PLOT_FILE_FORMAT png) -set(PLOT_FILE_SUFFIX png) -endif (WITH_EPS) - -configure_file(cantor_print.m.in ${CMAKE_CURRENT_BINARY_DIR}/cantor_print.m) - -install( FILES cantor_eigenvectors.m cantor_plot2d.m cantor_plot3d.m ${CMAKE_CURRENT_BINARY_DIR}/cantor_print.m DESTINATION ${KDE_INSTALL_DATADIR}/cantor/octavebackend ) - +install( FILES cantor_eigenvectors.m cantor_plot2d.m cantor_plot3d.m cantor_print.m DESTINATION ${KDE_INSTALL_DATADIR}/cantor/octavebackend ) diff --git a/src/backends/octave/scripts/cantor_print.m.in b/src/backends/octave/scripts/cantor_print.m similarity index 84% rename from src/backends/octave/scripts/cantor_print.m.in rename to src/backends/octave/scripts/cantor_print.m index d6b89bbb..c473fcfe 100644 --- a/src/backends/octave/scripts/cantor_print.m.in +++ b/src/backends/octave/scripts/cantor_print.m @@ -1,26 +1,26 @@ %{ 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. %} -function cantor_print(filename) +function cantor_print(plot_format, filename) try - print('-d${PLOT_FILE_FORMAT}',filename,'-S480,336','-tight'); + print(strcat('-d', plot_format), filename, '-tight'); catch - print('-d${PLOT_FILE_FORMAT}',filename,'-S480,336'); + print(strcat('-d', plot_format), filename); end_try_catch endfunction diff --git a/src/backends/octave/settings.ui b/src/backends/octave/settings.ui.in similarity index 75% rename from src/backends/octave/settings.ui rename to src/backends/octave/settings.ui.in index 4ef85a71..eef86bca 100644 --- a/src/backends/octave/settings.ui +++ b/src/backends/octave/settings.ui.in @@ -1,95 +1,126 @@ OctaveSettingsBase 0 0 400 300 Path to Octave: Path to local documentation: Integrate Plots in Worksheet Let Cantor follow the creation/destruction of variables Enable Variable Management + + + + + + Inline Plots Intermediate Format: + + + + + + @EPS_PLOT_FORMAT_UI_ELEMENT@ + + + png + + + + + svg + + + + + jpeg + + + + + + Commands to autorun Qt::Vertical 20 40 KUrlRequester QFrame
kurlrequester.h
diff --git a/src/backends/octave/testoctave.cpp b/src/backends/octave/testoctave.cpp index 4aef9022..e397f805 100644 --- a/src/backends/octave/testoctave.cpp +++ b/src/backends/octave/testoctave.cpp @@ -1,321 +1,324 @@ /* 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 "helpresult.h" #include "epsresult.h" #include "completionobject.h" #include "syntaxhelpobject.h" #include "defaultvariablemodel.h" #include "octaveexpression.h" +#include "settings.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()->data().toString() ), 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()->data().toString(); 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()->data().toString()), QLatin1String("ans = 1")); QCOMPARE(cleanOutput(e2->result()->data().toString()), QLatin1String("ans = 2")); QCOMPARE(cleanOutput(e3->result()->data().toString()), QLatin1String("ans = 3")); } void TestOctave::testVariableDefinition() { Cantor::Expression* e = evalExp(QLatin1String("testvar = 1")); QVERIFY(e != nullptr); QVERIFY(e->result() != nullptr); QCOMPARE(cleanOutput(e->result()->data().toString()), 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()->data().toString()), QLatin1String("s = 1234")); } void TestOctave::testCommentExpression() { Cantor::Expression* e = evalExp(QLatin1String("#Only comment")); QVERIFY(e != nullptr); QCOMPARE(e->status(), Cantor::Expression::Status::Done); QCOMPARE(e->results().size(), 0); } void TestOctave::testMultilineCommandWithComment() { Cantor::Expression* e = evalExp(QLatin1String( "a = 2+4 \n" "6/2 % comment\n" "q = 'Str' # comment\n" "b = 4" )); QVERIFY(e != nullptr); QCOMPARE(e->status(), Cantor::Expression::Status::Done); QVERIFY(e->result() != nullptr); Cantor::TextResult* result = static_cast(e->result()); QVERIFY(result != nullptr); QCOMPARE(cleanOutput(result->plain()), QLatin1String( "a = 6\n" "ans = 3\n" "q = Str\n" "b = 4" )); } void TestOctave::testCompletion() { 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::testVariableCreatingFromCodeWithPlot() { QAbstractItemModel* model = session()->variableModel(); QVERIFY(model != nullptr); evalExp(QLatin1String("clear();")); Cantor::Expression* e = evalExp(QLatin1String( "x = -10:0.1:10;\n" "plot (x, sin (x));\n" "xlabel (\"x\");\n" "ylabel (\"sin (x)\");\n" "title (\"Simple 2-D Plot\");\n" )); QVERIFY(e!=nullptr); QCOMPARE(e->status(), Cantor::Expression::Done); QVERIFY(e->result()); - QVERIFY(e->result()->type() == OctavePlotResult::Type); + int plotType = (OctaveExpression::plotExtensions[OctaveSettings::inlinePlotFormat()] == QLatin1String("eps") ? (int)Cantor::EpsResult::Type : (int)Cantor::ImageResult::Type); + QVERIFY(e->result()->type() == plotType); if(session()->status()==Cantor::Session::Running) waitForSignal(session(), SIGNAL(statusChanged(Cantor::Session::Status))); QCOMPARE(1, model->rowCount()); QCOMPARE(model->index(0,0).data().toString(), QLatin1String("x")); } void TestOctave::testVariableCleanupAfterRestart() { 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 ) + int plotType = (OctaveExpression::plotExtensions[OctaveSettings::inlinePlotFormat()] == QLatin1String("eps") ? (int)Cantor::EpsResult::Type : (int)Cantor::ImageResult::Type); + while(e->result()==nullptr||e->result()->type() != plotType ) { QTest::qWait(250); cnt+=250; if(cnt>5000) break; } QVERIFY( e!=nullptr ); QVERIFY( e->result()!=nullptr ); - QCOMPARE( e->result()->type(), (int) OctavePlotResult::Type ); + QCOMPARE( e->result()->type(), plotType ); QVERIFY( !e->result()->data().isNull() ); } void TestOctave::testInvalidSyntax() { Cantor::Expression* e=evalExp( QLatin1String("2+2*+.") ); QVERIFY( e!=nullptr ); QCOMPARE( e->status(), Cantor::Expression::Error ); } void TestOctave::testHelpRequest() { Cantor::Expression* e = evalExp(QLatin1String("help printf")); QVERIFY(e != nullptr); QCOMPARE(e->status(), Cantor::Expression::Status::Done); QVERIFY(e->result() != nullptr); QCOMPARE(e->result()->type(), (int)Cantor::HelpResult::Type); QString text = QString::fromLatin1("Print optional arguments under the control of the template").toHtmlEscaped(); text.replace(QLatin1Char(' '), QLatin1String(" ")); QVERIFY(e->result()->toHtml().contains(text)); } void TestOctave::testSyntaxHelp() { Cantor::SyntaxHelpObject* help = session()->syntaxHelpFor(QLatin1String("abs")); help->fetchSyntaxHelp(); waitForSignal(help, SIGNAL(done())); QString text = QString::fromLatin1("Compute the magnitude").toHtmlEscaped(); text.replace(QLatin1Char(' '), QLatin1String(" ")); QVERIFY(help->toHtml().contains(text)); } void TestOctave::testLoginLogout() { // Logout from session twice and all must works fine session()->logout(); session()->logout(); // Login in session twice and all must works fine session()->login(); session()->login(); } void TestOctave::testRestartWhileRunning() { Cantor::Expression* e1=session()->evaluateExpression(QLatin1String("sleep(5)")); session()->logout(); QCOMPARE(e1->status(), Cantor::Expression::Interrupted); session()->login(); Cantor::Expression* e2=evalExp( QLatin1String("2+2") ); QVERIFY(e2 != nullptr); QVERIFY(e2->result() != nullptr); QCOMPARE(cleanOutput(e2->result()->data().toString() ), QLatin1String("ans = 4")); } QTEST_MAIN( TestOctave ) diff --git a/src/backends/octave/with_eps_ui_part.txt b/src/backends/octave/with_eps_ui_part.txt new file mode 100644 index 00000000..1d264fb9 --- /dev/null +++ b/src/backends/octave/with_eps_ui_part.txt @@ -0,0 +1,5 @@ + + + eps + + \ No newline at end of file