[education/cantor] src/backends: Tag backend plot outputs

Alexander Semke <[email protected]>
Newsgroups gmane.comp.kde.cvs
Message-ID <[email protected]>
Git commit ff5f71dd3912c31be076553cf0f4f9bbd63055ca by Alexander Semke, on behalf of Nanhao Lv.
Committed on 26/07/2026 at 19:50.
Pushed by asemke into branch 'master'.

Tag backend plot outputs

M  +6    -2    src/backends/R/rexpression.cpp
M  +3    -1    src/backends/julia/juliaexpression.cpp
M  +63   -9    src/backends/maxima/maximaexpression.cpp
M  +5    -0    src/backends/maxima/maximaexpression.h
M  +1    -0    src/backends/octave/octaveexpression.cpp
M  +2    -0    src/backends/python/pythonexpression.cpp
M  +3    -1    src/backends/qalculate/qalculateexpression.cpp
M  +6    -2    src/backends/sage/sageexpression.cpp
M  +3    -1    src/backends/scilab/scilabexpression.cpp

https://invent.kde.org/education/cantor/-/commit/ff5f71dd3912c31be076553cf0f4f9bbd63055ca

diff --git a/src/backends/R/rexpression.cpp b/src/backends/R/rexpression.cpp
index 426feefb..4c194683 100644
--- a/src/backends/R/rexpression.cpp
+++ b/src/backends/R/rexpression.cpp
@@ -73,13 +73,17 @@ void RExpression::showFilesAsResult(const QStringList& files)
         qDebug()<<"MimeType: "<<type.name();
         if(type.name() == QLatin1String("application/pdf"))
         {
-            setResult(new Cantor::ImageResult(QUrl::fromLocalFile(file)));
+            auto* result = new Cantor::ImageResult(QUrl::fromLocalFile(file));
+            result->setRole(Cantor::Result::Role::Plot);
+            setResult(result);
             setStatus(Cantor::Expression::Done);
         }
         else
             if (type.name().contains(QLatin1String("image")))
         {
-            setResult(new Cantor::ImageResult(QUrl::fromLocalFile(file)));
+            auto* result = new Cantor::ImageResult(QUrl::fromLocalFile(file));
+            result->setRole(Cantor::Result::Role::Plot);
+            setResult(result);
             setStatus(Cantor::Expression::Done);
         }
         else if(type.inherits(QLatin1String("text/plain"))
diff --git a/src/backends/julia/juliaexpression.cpp b/src/backends/julia/juliaexpression.cpp
index 6d0bf915..374ebc1b 100644
--- a/src/backends/julia/juliaexpression.cpp
+++ b/src/backends/julia/juliaexpression.cpp
@@ -69,7 +69,9 @@ void JuliaExpression::finalize(const QString& output, const QString& error, bool
     } else {
         if (!m_plot_filename.isEmpty() && QFileInfo(m_plot_filename).exists()) {
             // If we have plot in result, show it
-            setResult(new Cantor::ImageResult(QUrl::fromLocalFile(m_plot_filename)));
+            auto* result = new Cantor::ImageResult(QUrl::fromLocalFile(m_plot_filename));
+            result->setRole(Cantor::Result::Role::Plot);
+            setResult(result);
         } else {
             if (!output.isEmpty())
                 setResult(new Cantor::TextResult(output));
diff --git a/src/backends/maxima/maximaexpression.cpp b/src/backends/maxima/maximaexpression.cpp
index 86e6c0d9..47053239 100644
--- a/src/backends/maxima/maximaexpression.cpp
+++ b/src/backends/maxima/maximaexpression.cpp
@@ -17,6 +17,8 @@
 #include <QApplication>
 #include <QDebug>
 #include <QDir>
+#include <QFileInfo>
+#include <QImage>
 #include <QRegularExpression>
 #include <QScreen>
 #include <QTemporaryFile>
@@ -46,6 +48,9 @@ void MaximaExpression::evaluate()
         m_isPlot = false;
         m_plotResult = nullptr;
         m_plotResultIndex = -1;
+        m_plotResultLoadScheduled = false;
+        m_plotFileLastSize = -1;
+        m_plotResultLoadAttempts = 0;
     }
 
     QString cmd = command();
@@ -434,7 +439,10 @@ void MaximaExpression::parseResult(const QString& resultContent)
             if (m_plotResult)
                 result = m_plotResult;
             else
+            {
+                schedulePlotResultLoad();
                 result = new Cantor::TextResult(i18n("Waiting for the plot result"));
+            }
         }
         else
             result = new Cantor::TextResult(textContent);
@@ -544,17 +552,63 @@ void MaximaExpression::addInformation(const QString& information)
 
 void MaximaExpression::imageChanged()
 {
-    if(m_tempFile->size()>0)
-    {
-        m_plotResult = new Cantor::ImageResult( QUrl::fromLocalFile(m_tempFile->fileName()) );
+    schedulePlotResultLoad();
+}
+
+void MaximaExpression::schedulePlotResultLoad()
+{
+    if (m_plotResult || m_plotResultLoadScheduled)
+        return;
+
+    m_plotResultLoadScheduled = true;
+    QTimer::singleShot(100, this, &MaximaExpression::loadPlotResult);
+}
+
+void MaximaExpression::loadPlotResult()
+{
+    m_plotResultLoadScheduled = false;
+
+    if (!m_tempFile || m_plotResult)
+        return;
 
-        // Check, that we already parse maxima output for this plot, and if not, keep it up to this moment
-        // If it's true, replace text info result by real plot and set status as Done
-        if (m_plotResultIndex != -1)
+    const QFileInfo plotFileInfo(m_tempFile->fileName());
+    if (!plotFileInfo.exists() || plotFileInfo.size() <= 0)
+    {
+        if (m_plotResultLoadAttempts < 10)
         {
-            replaceResult(m_plotResultIndex, m_plotResult);
-            if (status() != Cantor::Expression::Error)
-                setStatus(Cantor::Expression::Done);
+            ++m_plotResultLoadAttempts;
+            schedulePlotResultLoad();
         }
+        else if (m_plotResultIndex != -1 && status() == Cantor::Expression::Computing)
+            setStatus(Cantor::Expression::Done);
+
+        return;
+    }
+
+    // QFileSystemWatcher can notify before gnuplot finishes writing.
+    if (plotFileInfo.size() != m_plotFileLastSize)
+    {
+        m_plotFileLastSize = plotFileInfo.size();
+        schedulePlotResultLoad();
+        return;
+    }
+
+    auto* plotResult = new Cantor::ImageResult(QUrl::fromLocalFile(m_tempFile->fileName()));
+    if (plotResult->data().value<QImage>().isNull() && m_plotResultLoadAttempts < 10)
+    {
+        delete plotResult;
+        ++m_plotResultLoadAttempts;
+        schedulePlotResultLoad();
+        return;
+    }
+
+    m_plotResult = plotResult;
+    m_plotResult->setRole(Cantor::Result::Role::Plot);
+
+    if (m_plotResultIndex != -1)
+    {
+        replaceResult(m_plotResultIndex, m_plotResult);
+        if (status() == Cantor::Expression::Computing)
+            setStatus(Cantor::Expression::Done);
     }
 }
diff --git a/src/backends/maxima/maximaexpression.h b/src/backends/maxima/maximaexpression.h
index 3d3ac647..e7121442 100644
--- a/src/backends/maxima/maximaexpression.h
+++ b/src/backends/maxima/maximaexpression.h
@@ -35,9 +35,11 @@ public:
 
 private Q_SLOTS:
     void imageChanged();
+    void loadPlotResult();
 
 private:
     void parseResult(const QString&);
+    void schedulePlotResultLoad();
 
     QTemporaryFile* m_tempFile = nullptr;
     QFileSystemWatcher m_fileWatch;
@@ -47,6 +49,9 @@ private:
     bool m_isDraw = false;
     Cantor::Result* m_plotResult = nullptr;
     int m_plotResultIndex = -1;
+    bool m_plotResultLoadScheduled = false;
+    qint64 m_plotFileLastSize = -1;
+    int m_plotResultLoadAttempts = 0;
     QString m_errorBuffer;
 };
 
diff --git a/src/backends/octave/octaveexpression.cpp b/src/backends/octave/octaveexpression.cpp
index d548ab4d..121f1803 100644
--- a/src/backends/octave/octaveexpression.cpp
+++ b/src/backends/octave/octaveexpression.cpp
@@ -235,6 +235,7 @@ void OctaveExpression::imageChanged()
     const QUrl& url = QUrl::fromLocalFile(m_plotFilename);
     QByteArray pdfData = file.readAll();
     auto* newResult = new Cantor::PdfResult(url, pdfData);
+    newResult->setRole(Cantor::Result::Role::Plot);
 
     bool found = false;
     for (int i = 0; i < results().size(); i++)
diff --git a/src/backends/python/pythonexpression.cpp b/src/backends/python/pythonexpression.cpp
index 6f400578..5a5d97e3 100644
--- a/src/backends/python/pythonexpression.cpp
+++ b/src/backends/python/pythonexpression.cpp
@@ -143,6 +143,7 @@ void PythonExpression::imageChanged()
         return;
 
     auto* newResult = new Cantor::ImageResult(QUrl::fromLocalFile(m_tempFile->fileName()));
+    newResult->setRole(Cantor::Result::Role::Plot);
     if (result() == nullptr)
         setResult(newResult);
     else
@@ -153,6 +154,7 @@ void PythonExpression::imageChanged()
             {
                 replaceResult(i, newResult);
                 found = true;
+                break;
             }
         if (!found)
             addResult(newResult);
diff --git a/src/backends/qalculate/qalculateexpression.cpp b/src/backends/qalculate/qalculateexpression.cpp
index 8ceaea1e..6eefcc4a 100644
--- a/src/backends/qalculate/qalculateexpression.cpp
+++ b/src/backends/qalculate/qalculateexpression.cpp
@@ -646,7 +646,9 @@ void QalculateExpression::evaluatePlotCommand()
     deletePlotDataParameters(plotDataParameterList);
 
     if (plotInline) {
-        setResult(new Cantor::ImageResult(QUrl::fromLocalFile(QString::fromStdString(plotParameters.filename))));
+        auto* result = new Cantor::ImageResult(QUrl::fromLocalFile(QString::fromStdString(plotParameters.filename)));
+        result->setRole(Cantor::Result::Role::Plot);
+        setResult(result);
         setStatus(Cantor::Expression::Done);
     }
 }
diff --git a/src/backends/sage/sageexpression.cpp b/src/backends/sage/sageexpression.cpp
index b7addd4a..a6dedb21 100644
--- a/src/backends/sage/sageexpression.cpp
+++ b/src/backends/sage/sageexpression.cpp
@@ -220,12 +220,16 @@ void SageExpression::evalFinished()
         if(type.inherits(QLatin1String("image/gif")))
         {
             qDebug()<<"adding animation";
-            addResult( new Cantor::AnimationResult(QUrl::fromLocalFile(m_imagePath), i18n("Result of %1" , command() ) ) );
+            auto* result = new Cantor::AnimationResult(QUrl::fromLocalFile(m_imagePath), i18n("Result of %1" , command() ) );
+            result->setRole(Cantor::Result::Role::Plot);
+            addResult(result);
         }
         else
         {
             qDebug()<<"adding image";
-            addResult( new Cantor::ImageResult(QUrl::fromLocalFile(m_imagePath ), i18n("Result of %1" , command() ) ) );
+            auto* result = new Cantor::ImageResult(QUrl::fromLocalFile(m_imagePath ), i18n("Result of %1" , command() ) );
+            result->setRole(Cantor::Result::Role::Plot);
+            addResult(result);
         }
     }
     setStatus(Cantor::Expression::Done);
diff --git a/src/backends/scilab/scilabexpression.cpp b/src/backends/scilab/scilabexpression.cpp
index 5c60dcfe..4477b9f2 100644
--- a/src/backends/scilab/scilabexpression.cpp
+++ b/src/backends/scilab/scilabexpression.cpp
@@ -97,7 +97,9 @@ void ScilabExpression::parsePlotFile(QString filename)
     qDebug() << "parsePlotFile";
     qDebug() << "ScilabExpression::parsePlotFile: " << filename;
 
-    setResult(new ScilabPlotResult(QUrl::fromLocalFile(filename)));
+    auto* result = new ScilabPlotResult(QUrl::fromLocalFile(filename));
+    result->setRole(Cantor::Result::Role::Plot);
+    setResult(result);
 
     setPlotPending(false);
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.