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.