File indexing completed on 2024-12-01 09:37:32
0001 /* 0002 SPDX-License-Identifier: GPL-2.0-or-later 0003 SPDX-FileCopyrightText: 2012 Filipe Saraiva <filipe@kde.org> 0004 */ 0005 0006 #include "pythonexpression.h" 0007 0008 #include <config-cantorlib.h> 0009 0010 #include "textresult.h" 0011 #include "imageresult.h" 0012 #include "helpresult.h" 0013 #include "session.h" 0014 #include "settings.h" 0015 0016 #include <QDebug> 0017 #include <QDir> 0018 #include <QTemporaryFile> 0019 #include <QFileSystemWatcher> 0020 0021 #include "pythonsession.h" 0022 0023 PythonExpression::PythonExpression(Cantor::Session* session, bool internal) : Cantor::Expression(session, internal) 0024 { 0025 } 0026 0027 PythonExpression::~PythonExpression() { 0028 delete m_tempFile; 0029 } 0030 0031 void PythonExpression::evaluate() 0032 { 0033 if(m_tempFile) { 0034 delete m_tempFile; 0035 m_tempFile = nullptr; 0036 } 0037 0038 session()->enqueueExpression(this); 0039 } 0040 0041 QString PythonExpression::internalCommand() 0042 { 0043 QString cmd = command(); 0044 0045 // handle matplotlib's show() command 0046 if((PythonSettings::integratePlots()) && (command().contains(QLatin1String("show()")))) 0047 { 0048 QString extension; 0049 if (PythonSettings::inlinePlotFormat() == 0) 0050 extension = QLatin1String("pdf"); 0051 else if (PythonSettings::inlinePlotFormat() == 1) 0052 extension = QLatin1String("svg"); 0053 else if (PythonSettings::inlinePlotFormat() == 2) 0054 extension = QLatin1String("png"); 0055 0056 m_tempFile = new QTemporaryFile(QDir::tempPath() + QLatin1String("/cantor_python-XXXXXX.%1").arg(extension)); 0057 m_tempFile->open(); 0058 QString saveFigCommand = QLatin1String("savefig('%1')"); 0059 cmd.replace(QLatin1String("show()"), saveFigCommand.arg(m_tempFile->fileName())); 0060 0061 // set the plot size in inches 0062 // TODO: matplotlib is usually imported via "import matplotlib.pyplot as plt" and we set 0063 // plot size for plt below but it's still possible to name the module differently and we 0064 // somehow need to handle such cases, too. 0065 const double w = PythonSettings::plotWidth() / 2.54; 0066 const double h = PythonSettings::plotHeight() / 2.54; 0067 cmd += QLatin1String("\nplt.figure(figsize=(%1, %2))").arg(QString::number(w), QString::number(h)); 0068 0069 QFileSystemWatcher* watcher = fileWatcher(); 0070 watcher->removePaths(watcher->files()); 0071 watcher->addPath(m_tempFile->fileName()); 0072 connect(watcher, &QFileSystemWatcher::fileChanged, this, &PythonExpression::imageChanged, Qt::UniqueConnection); 0073 } 0074 // TODO: handle other plotting frameworks 0075 0076 QStringList commandLine = cmd.split(QLatin1String("\n")); 0077 QString commandProcessing; 0078 0079 for(const QString& command : commandLine) 0080 { 0081 const QString firstLineWord = command.trimmed().replace(QLatin1String("("), QLatin1String(" ")) 0082 .split(QLatin1String(" ")).at(0); 0083 0084 // Ignore comments 0085 if (firstLineWord.length() != 0 && firstLineWord[0] == QLatin1Char('#')){ 0086 commandProcessing += command + QLatin1String("\n"); 0087 continue; 0088 } 0089 0090 if(firstLineWord.contains(QLatin1String("execfile"))){ 0091 commandProcessing += command; 0092 continue; 0093 } 0094 0095 commandProcessing += command + QLatin1String("\n"); 0096 } 0097 0098 return commandProcessing; 0099 } 0100 0101 void PythonExpression::parseOutput(const QString& output) 0102 { 0103 qDebug() << "expression output: " << output; 0104 if(command().simplified().startsWith(QLatin1String("help("))) 0105 { 0106 QString resultStr = output; 0107 setResult(new Cantor::HelpResult(resultStr.remove(output.lastIndexOf(QLatin1String("None")), 4))); 0108 } else { 0109 if (!output.isEmpty()) 0110 addResult(new Cantor::TextResult(output)); 0111 } 0112 0113 setStatus(Cantor::Expression::Done); 0114 } 0115 0116 void PythonExpression::parseError(const QString& error) 0117 { 0118 qDebug() << "expression error: " << error; 0119 setErrorMessage(error); 0120 0121 setStatus(Cantor::Expression::Error); 0122 } 0123 0124 void PythonExpression::parseWarning(const QString& warning) 0125 { 0126 if (!warning.isEmpty()) 0127 { 0128 auto* result = new Cantor::TextResult(warning); 0129 result->setStdErr(true); 0130 addResult(result); 0131 } 0132 } 0133 0134 void PythonExpression::imageChanged() 0135 { 0136 if(m_tempFile->size() <= 0) 0137 return; 0138 0139 auto* newResult = new Cantor::ImageResult(QUrl::fromLocalFile(m_tempFile->fileName())); 0140 if (result() == nullptr) 0141 setResult(newResult); 0142 else 0143 { 0144 bool found = false; 0145 for (int i = 0; i < results().size(); i++) 0146 if (results()[i]->type() == newResult->type()) 0147 { 0148 replaceResult(i, newResult); 0149 found = true; 0150 } 0151 if (!found) 0152 addResult(newResult); 0153 } 0154 setStatus(Done); 0155 }