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, [
this] {
 
  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, [
this] {
 
  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, [
this] {
 
  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, [
this] {
 
  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 );
 
  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 setText(const QString &text) override
 
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.