29#include <nlohmann/json.hpp>
32#include <QRegularExpression>
33#include <QRegularExpressionMatch>
45 return QStringLiteral(
"processing" );
50 const QString logPath = oldLogPath();
51 if ( !QFile::exists( logPath ) )
54 QFile logFile( logPath );
55 if ( logFile.open( QIODevice::ReadOnly ) )
57 QTextStream in( &logFile );
58 QList< QgsHistoryEntry > entries;
61 const QString line = in.readLine().trimmed();
62 QStringList parts = line.split( QStringLiteral(
"|~|" ) );
63 if ( parts.size() <= 1 )
64 parts = line.split(
'|' );
66 if ( parts.size() == 3 && parts.at( 0 ).startsWith( QLatin1String(
"ALGORITHM" ), Qt::CaseInsensitive ) )
69 details.insert( QStringLiteral(
"python_command" ), parts.at( 2 ) );
71 const thread_local QRegularExpression algIdRegEx( QStringLiteral(
"processing\\.run\\(\"(.*?)\"" ) );
72 const QRegularExpressionMatch match = algIdRegEx.match( parts.at( 2 ) );
73 if ( match.hasMatch() )
74 details.insert( QStringLiteral(
"algorithm_id" ), match.captured( 1 ) );
77 QDateTime::fromString( parts.at( 1 ), QStringLiteral(
"yyyy-MM-d hh:mm:ss" ) ),
95 , mAlgorithmId( mEntry.entry.value(
"algorithm_id" ).toString() )
96 , mPythonCommand( mEntry.entry.value(
"python_command" ).toString() )
97 , mProcessCommand( mEntry.entry.value(
"process_command" ).toString() )
98 , mProvider( provider )
101 const QVariant parameters = mEntry.entry.value( QStringLiteral(
"parameters" ) );
102 if ( parameters.userType() == QMetaType::Type::QVariantMap )
104 const QVariantMap parametersMap = parameters.toMap();
105 mInputs = parametersMap.value( QStringLiteral(
"inputs" ) ).toMap();
111 if ( mPythonCommand.isEmpty() )
114 QString execAlgorithmDialogCommand = mPythonCommand;
115 execAlgorithmDialogCommand.replace( QLatin1String(
"processing.run(" ), QLatin1String(
"processing.execAlgorithmDialog(" ) );
118 const QStringList script =
120 QStringLiteral(
"import processing" ),
121 QStringLiteral(
"from qgis.core import QgsProcessingOutputLayerDefinition, QgsProcessingFeatureSourceDefinition, QgsProperty, QgsCoordinateReferenceSystem, QgsFeatureRequest" ),
122 QStringLiteral(
"from qgis.PyQt.QtCore import QDate, QTime, QDateTime" ),
123 QStringLiteral(
"from qgis.PyQt.QtGui import QColor" ),
124 execAlgorithmDialogCommand
127 mProvider->emitExecute( script.join(
'\n' ) );
133 if ( !mPythonCommand.isEmpty() )
135 QAction *pythonAction =
new QAction(
136 QObject::tr(
"Copy as Python Command" ), menu );
138 QObject::connect( pythonAction, &QAction::triggered, menu, [ = ]
140 copyText( mPythonCommand );
142 menu->addAction( pythonAction );
144 if ( !mProcessCommand.isEmpty() )
146 QAction *processAction =
new QAction(
147 QObject::tr(
"Copy as qgis_process Command" ), menu );
149 QObject::connect( processAction, &QAction::triggered, menu, [ = ]
151 copyText( mProcessCommand );
153 menu->addAction( processAction );
155 if ( !mInputs.isEmpty() )
157 QAction *inputsAction =
new QAction(
158 QObject::tr(
"Copy as JSON" ), menu );
160 QObject::connect( inputsAction, &QAction::triggered, menu, [ = ]
164 menu->addAction( inputsAction );
167 if ( !mPythonCommand.isEmpty() )
169 if ( !menu->isEmpty() )
171 menu->addSeparator();
174 QAction *createTestAction =
new QAction(
175 QObject::tr(
"Create Test…" ), menu );
176 QObject::connect( createTestAction, &QAction::triggered, menu, [ = ]
178 mProvider->emitCreateTest( mPythonCommand );
180 menu->addAction( createTestAction );
184 void copyText(
const QString &text )
186 QMimeData *m =
new QMimeData();
188 QApplication::clipboard()->setMimeData( m );
192 QString mAlgorithmId;
193 QString mPythonCommand;
194 QString mProcessCommand;
201class ProcessingHistoryPythonCommandNode :
public ProcessingHistoryBaseNode
206 : ProcessingHistoryBaseNode( entry, provider )
209 QVariant data(
int role = Qt::DisplayRole )
const override
213 case Qt::DisplayRole:
215 QString display = mPythonCommand;
216 if ( display.length() > 300 )
218 display = QObject::tr(
"%1…" ).arg( display.left( 299 ) );
222 case Qt::DecorationRole:
234 codeEditor->setReadOnly(
true );
235 codeEditor->setCaretLineVisible(
false );
238 codeEditor->setEdgeMode( QsciScintilla::EdgeNone );
239 codeEditor->setWrapMode( QsciScintilla::WrapMode::WrapWord );
242 const QString introText = QStringLiteral(
"\"\"\"\n%1\n\"\"\"\n\n " ).arg(
243 QObject::tr(
"Double-click on the history item or paste the command below to re-run the algorithm" ) );
244 codeEditor->setText( introText + mPythonCommand );
250class ProcessingHistoryProcessCommandNode :
public ProcessingHistoryBaseNode
255 : ProcessingHistoryBaseNode( entry, provider )
258 QVariant data(
int role = Qt::DisplayRole )
const override
262 case Qt::DisplayRole:
264 QString display = mProcessCommand;
265 if ( display.length() > 300 )
267 display = QObject::tr(
"%1…" ).arg( display.left( 299 ) );
271 case Qt::DecorationRole:
283 codeEditor->setReadOnly(
true );
284 codeEditor->setCaretLineVisible(
false );
287 codeEditor->setEdgeMode( QsciScintilla::EdgeNone );
288 codeEditor->setWrapMode( QsciScintilla::WrapMode::WrapWord );
290 codeEditor->setText( mProcessCommand );
297class ProcessingHistoryJsonNode :
public ProcessingHistoryBaseNode
302 : ProcessingHistoryBaseNode( entry, provider )
308 QVariant data(
int role = Qt::DisplayRole )
const override
312 case Qt::DisplayRole:
314 QString display = mJsonSingleLine;
315 if ( display.length() > 300 )
317 display = QObject::tr(
"%1…" ).arg( display.left( 299 ) );
321 case Qt::DecorationRole:
333 codeEditor->setReadOnly(
true );
334 codeEditor->setCaretLineVisible(
false );
337 codeEditor->setEdgeMode( QsciScintilla::EdgeNone );
338 codeEditor->setWrapMode( QsciScintilla::WrapMode::WrapWord );
340 codeEditor->setText( mJson );
346 QString mJsonSingleLine;
350class ProcessingHistoryRootNode :
public ProcessingHistoryBaseNode
355 : ProcessingHistoryBaseNode( entry, provider )
357 const QVariant parameters = mEntry.entry.value( QStringLiteral(
"parameters" ) );
358 if ( parameters.type() == QVariant::Map )
365 mDescription = mPythonCommand;
368 if ( mDescription.length() > 300 )
370 mDescription = QObject::tr(
"%1…" ).arg( mDescription.left( 299 ) );
373 addChild(
new ProcessingHistoryPythonCommandNode( mEntry, mProvider ) );
374 addChild(
new ProcessingHistoryProcessCommandNode( mEntry, mProvider ) );
375 addChild(
new ProcessingHistoryJsonNode( mEntry, mProvider ) );
383 QVariant data(
int role = Qt::DisplayRole )
const override
385 if ( mAlgorithmInformation.displayName.isEmpty() )
392 case Qt::DisplayRole:
394 const QString algName = mAlgorithmInformation.
displayName;
395 if ( !mDescription.isEmpty() )
396 return QStringLiteral(
"[%1] %2 - %3" ).arg( mEntry.timestamp.toString( QStringLiteral(
"yyyy-MM-dd hh:mm" ) ),
400 return QStringLiteral(
"[%1] %2" ).arg( mEntry.timestamp.toString( QStringLiteral(
"yyyy-MM-dd hh:mm" ) ),
404 case Qt::DecorationRole:
406 return mAlgorithmInformation.icon;
417 return mEntry.entry.value( QStringLiteral(
"log" ) ).toString();
420 QString mDescription;
429 return new ProcessingHistoryRootNode( entry,
this );
434 if ( ProcessingHistoryRootNode *rootNode =
dynamic_cast< ProcessingHistoryRootNode *
>( node ) )
436 rootNode->setEntry( entry );
440QString QgsProcessingHistoryProvider::oldLogPath()
const
443 return userDir + QStringLiteral(
"/processing.log" );
446void QgsProcessingHistoryProvider::emitExecute(
const QString &commands )
451void QgsProcessingHistoryProvider::emitCreateTest(
const QString &command )
static QgsProcessingRegistry * processingRegistry()
Returns the application's processing registry, used for managing processing providers,...
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
static QString qgisSettingsDirPath()
Returns the path to the settings directory in user's home dir.
A JSON editor based on QScintilla2.
A Python editor based on QScintilla2.
A shell script code editor based on QScintilla2.
void setFoldingVisible(bool folding)
Set whether the folding controls are visible in the editor.
void setLineNumbersVisible(bool visible)
Sets whether line numbers should be visible in the editor.
static QgsHistoryProviderRegistry * historyProviderRegistry()
Returns the global history provider registry, used for tracking history providers.
Base class for history entry "group" nodes, which contain children of their own.
Base class for nodes representing a QgsHistoryEntry.
virtual void populateContextMenu(QMenu *menu, const QgsHistoryWidgetContext &context)
Allows the node to populate a context menu before display to the user.
virtual bool doubleClicked(const QgsHistoryWidgetContext &context)
Called when the node is double-clicked.
Encapsulates a history entry.
bool addEntries(const QList< QgsHistoryEntry > &entries, QgsHistoryProviderRegistry::HistoryEntryOptions options=QgsHistoryProviderRegistry::HistoryEntryOptions())
Adds a list of entries to the history logs.
Contains settings which reflect the context in which a history widget is shown, e....
static json jsonFromVariant(const QVariant &v)
Converts a QVariant v to a json object.
History provider for operations performed through the Processing framework.
void updateNodeForEntry(QgsHistoryEntryNode *node, const QgsHistoryEntry &entry, const QgsHistoryWidgetContext &context) override
Updates an existing history node for the given entry.
QString id() const override
Returns the provider's unique id, which is used to associate existing history entries with the provid...
void executePython(const QString &commands)
Emitted when the provider needs to execute python commands in the Processing context.
QgsHistoryEntryNode * createNodeForEntry(const QgsHistoryEntry &entry, const QgsHistoryWidgetContext &context) override
Creates a new history node for the given entry.
void createTest(const QString &command)
Emitted when the provider needs to create a Processing test with the given python command.
void portOldLog()
Ports the old text log to the history framework.
QgsProcessingHistoryProvider()
QgsProcessingAlgorithmInformation algorithmInformation(const QString &id) const
Returns basic algorithm information for the algorithm with matching ID.
static QString variantToPythonLiteral(const QVariant &value)
Converts a variant to a Python literal.