18#include "moc_qgsprocessinghistoryprovider.cpp"
30#include <nlohmann/json.hpp>
33#include <QRegularExpression>
34#include <QRegularExpressionMatch>
46 return QStringLiteral(
"processing" );
51 const QString logPath = oldLogPath();
52 if ( !QFile::exists( logPath ) )
55 QFile logFile( logPath );
56 if ( logFile.open( QIODevice::ReadOnly ) )
58 QTextStream in( &logFile );
59 QList<QgsHistoryEntry> entries;
62 const QString line = in.readLine().trimmed();
63 QStringList parts = line.split( QStringLiteral(
"|~|" ) );
64 if ( parts.size() <= 1 )
65 parts = line.split(
'|' );
67 if ( parts.size() == 3 && parts.at( 0 ).startsWith( QLatin1String(
"ALGORITHM" ), Qt::CaseInsensitive ) )
70 details.insert( QStringLiteral(
"python_command" ), parts.at( 2 ) );
72 const thread_local QRegularExpression algIdRegEx( QStringLiteral(
"processing\\.run\\(\"(.*?)\"" ) );
73 const QRegularExpressionMatch match = algIdRegEx.match( parts.at( 2 ) );
74 if ( match.hasMatch() )
75 details.insert( QStringLiteral(
"algorithm_id" ), match.captured( 1 ) );
77 entries.append(
QgsHistoryEntry(
id(), QDateTime::fromString( parts.at( 1 ), QStringLiteral(
"yyyy-MM-d hh:mm:ss" ) ), details ) );
93 , mAlgorithmId( mEntry.entry.value(
"algorithm_id" ).toString() )
94 , mPythonCommand( mEntry.entry.value(
"python_command" ).toString() )
95 , mProcessCommand( mEntry.entry.value(
"process_command" ).toString() )
96 , mProvider( provider )
98 const QVariant parameters = mEntry.entry.value( QStringLiteral(
"parameters" ) );
99 if ( parameters.userType() == QMetaType::Type::QVariantMap )
101 const QVariantMap parametersMap = parameters.toMap();
102 mInputs = parametersMap.value( QStringLiteral(
"inputs" ) ).toMap();
108 if ( mPythonCommand.isEmpty() )
111 QString execAlgorithmDialogCommand = mPythonCommand;
112 execAlgorithmDialogCommand.replace( QLatin1String(
"processing.run(" ), QLatin1String(
"processing.execAlgorithmDialog(" ) );
115 const QStringList script = {
116 QStringLiteral(
"import processing" ),
117 QStringLiteral(
"from qgis.core import QgsProcessingOutputLayerDefinition, QgsProcessingFeatureSourceDefinition, QgsProperty, QgsCoordinateReferenceSystem, QgsFeatureRequest" ),
118 QStringLiteral(
"from qgis.PyQt.QtCore import QDate, QTime, QDateTime" ),
119 QStringLiteral(
"from qgis.PyQt.QtGui import QColor" ),
120 execAlgorithmDialogCommand
123 mProvider->emitExecute( script.join(
'\n' ) );
129 if ( !mPythonCommand.isEmpty() )
131 QAction *pythonAction =
new QAction(
132 QObject::tr(
"Copy as Python Command" ), menu
135 QObject::connect( pythonAction, &QAction::triggered, menu, [=] {
136 copyText( mPythonCommand );
138 menu->addAction( pythonAction );
140 if ( !mProcessCommand.isEmpty() )
142 QAction *processAction =
new QAction(
143 QObject::tr(
"Copy as qgis_process Command" ), menu
146 QObject::connect( processAction, &QAction::triggered, menu, [=] {
147 copyText( mProcessCommand );
149 menu->addAction( processAction );
151 if ( !mInputs.isEmpty() )
153 QAction *inputsAction =
new QAction(
154 QObject::tr(
"Copy as JSON" ), menu
157 QObject::connect( inputsAction, &QAction::triggered, menu, [=] {
160 menu->addAction( inputsAction );
163 if ( !mPythonCommand.isEmpty() )
165 if ( !menu->isEmpty() )
167 menu->addSeparator();
170 QAction *createTestAction =
new QAction(
171 QObject::tr(
"Create Test…" ), menu
173 QObject::connect( createTestAction, &QAction::triggered, menu, [=] {
174 mProvider->emitCreateTest( mPythonCommand );
176 menu->addAction( createTestAction );
180 void copyText(
const QString &text )
182 QMimeData *m =
new QMimeData();
184 QApplication::clipboard()->setMimeData( m );
188 QString mAlgorithmId;
189 QString mPythonCommand;
190 QString mProcessCommand;
196class ProcessingHistoryPythonCommandNode :
public ProcessingHistoryBaseNode
200 : ProcessingHistoryBaseNode( entry, provider )
203 QVariant data(
int role = Qt::DisplayRole )
const override
207 case Qt::DisplayRole:
209 QString display = mPythonCommand;
210 if ( display.length() > 300 )
212 display = QObject::tr(
"%1…" ).arg( display.left( 299 ) );
216 case Qt::DecorationRole:
228 codeEditor->setReadOnly(
true );
229 codeEditor->setCaretLineVisible(
false );
232 codeEditor->setEdgeMode( QsciScintilla::EdgeNone );
233 codeEditor->setWrapMode( QsciScintilla::WrapMode::WrapWord );
236 const QString introText = QStringLiteral(
"\"\"\"\n%1\n\"\"\"\n\n " ).arg( QObject::tr(
"Double-click on the history item or paste the command below to re-run the algorithm" ) );
237 codeEditor->setText( introText + mPythonCommand );
243class ProcessingHistoryProcessCommandNode :
public ProcessingHistoryBaseNode
247 : ProcessingHistoryBaseNode( entry, provider )
250 QVariant data(
int role = Qt::DisplayRole )
const override
254 case Qt::DisplayRole:
256 QString display = mProcessCommand;
257 if ( display.length() > 300 )
259 display = QObject::tr(
"%1…" ).arg( display.left( 299 ) );
263 case Qt::DecorationRole:
275 codeEditor->setReadOnly(
true );
276 codeEditor->setCaretLineVisible(
false );
279 codeEditor->setEdgeMode( QsciScintilla::EdgeNone );
280 codeEditor->setWrapMode( QsciScintilla::WrapMode::WrapWord );
282 codeEditor->setText( mProcessCommand );
289class ProcessingHistoryJsonNode :
public ProcessingHistoryBaseNode
293 : ProcessingHistoryBaseNode( entry, provider )
299 QVariant data(
int role = Qt::DisplayRole )
const override
303 case Qt::DisplayRole:
305 QString display = mJsonSingleLine;
306 if ( display.length() > 300 )
308 display = QObject::tr(
"%1…" ).arg( display.left( 299 ) );
312 case Qt::DecorationRole:
324 codeEditor->setReadOnly(
true );
325 codeEditor->setCaretLineVisible(
false );
328 codeEditor->setEdgeMode( QsciScintilla::EdgeNone );
329 codeEditor->setWrapMode( QsciScintilla::WrapMode::WrapWord );
331 codeEditor->setText( mJson );
337 QString mJsonSingleLine;
341class ProcessingHistoryRootNode :
public ProcessingHistoryBaseNode
345 : ProcessingHistoryBaseNode( entry, provider )
347 const QVariant parameters = mEntry.entry.value( QStringLiteral(
"parameters" ) );
348 if ( parameters.type() == QVariant::Map )
355 mDescription = mPythonCommand;
358 if ( mDescription.length() > 300 )
360 mDescription = QObject::tr(
"%1…" ).arg( mDescription.left( 299 ) );
363 addChild(
new ProcessingHistoryPythonCommandNode( mEntry, mProvider ) );
364 addChild(
new ProcessingHistoryProcessCommandNode( mEntry, mProvider ) );
365 addChild(
new ProcessingHistoryJsonNode( mEntry, mProvider ) );
373 QVariant data(
int role = Qt::DisplayRole )
const override
375 if ( mAlgorithmInformation.displayName.isEmpty() )
382 case Qt::DisplayRole:
384 const QString algName = mAlgorithmInformation.
displayName;
385 if ( !mDescription.isEmpty() )
386 return QStringLiteral(
"[%1] %2 - %3" ).arg( mEntry.timestamp.toString( QStringLiteral(
"yyyy-MM-dd hh:mm" ) ), algName, mDescription );
388 return QStringLiteral(
"[%1] %2" ).arg( mEntry.timestamp.toString( QStringLiteral(
"yyyy-MM-dd hh:mm" ) ), algName );
391 case Qt::DecorationRole:
393 return mAlgorithmInformation.icon;
404 return mEntry.entry.value( QStringLiteral(
"log" ) ).toString();
407 QString mDescription;
415 return new ProcessingHistoryRootNode( entry,
this );
420 if ( ProcessingHistoryRootNode *rootNode =
dynamic_cast<ProcessingHistoryRootNode *
>( node ) )
422 rootNode->setEntry( entry );
426QString QgsProcessingHistoryProvider::oldLogPath()
const
429 return userDir + QStringLiteral(
"/processing.log" );
432void QgsProcessingHistoryProvider::emitExecute(
const QString &commands )
437void 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.