mirror of
https://github.com/qgis/QGIS.git
synced 2025-12-04 00:06:46 -05:00
[processing] Show more detail in history dialog
Use a tree display for processing history entries, where the root item for each entry shows the full algorithm log when clicked, and the python/qgis_process commands are instead shown as child items This provides more useful information for users browsing the history, while still making the all the previous information available
This commit is contained in:
parent
0360367b1e
commit
e9976c2ee7
@ -35,6 +35,8 @@ This should only be called once -- calling multiple times will result in duplica
|
||||
|
||||
virtual QgsHistoryEntryNode *createNodeForEntry( const QgsHistoryEntry &entry, const QgsHistoryWidgetContext &context ) /Factory/;
|
||||
|
||||
virtual void updateNodeForEntry( QgsHistoryEntryNode *node, const QgsHistoryEntry &entry, const QgsHistoryWidgetContext &context );
|
||||
|
||||
|
||||
signals:
|
||||
|
||||
|
||||
@ -35,6 +35,8 @@ This should only be called once -- calling multiple times will result in duplica
|
||||
|
||||
virtual QgsHistoryEntryNode *createNodeForEntry( const QgsHistoryEntry &entry, const QgsHistoryWidgetContext &context ) /Factory/;
|
||||
|
||||
virtual void updateNodeForEntry( QgsHistoryEntryNode *node, const QgsHistoryEntry &entry, const QgsHistoryWidgetContext &context );
|
||||
|
||||
|
||||
signals:
|
||||
|
||||
|
||||
@ -22,6 +22,8 @@
|
||||
#include "qgshistoryentrynode.h"
|
||||
#include "qgsprocessingregistry.h"
|
||||
#include "qgscodeeditorpython.h"
|
||||
#include "qgscodeeditorshell.h"
|
||||
#include "qgscodeeditorjson.h"
|
||||
#include "qgsjsonutils.h"
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
@ -83,11 +85,12 @@ void QgsProcessingHistoryProvider::portOldLog()
|
||||
|
||||
///@cond PRIVATE
|
||||
|
||||
class ProcessingHistoryNode : public QgsHistoryEntryGroup
|
||||
|
||||
class ProcessingHistoryBaseNode : public QgsHistoryEntryGroup
|
||||
{
|
||||
public:
|
||||
|
||||
ProcessingHistoryNode( const QgsHistoryEntry &entry, QgsProcessingHistoryProvider *provider )
|
||||
ProcessingHistoryBaseNode( const QgsHistoryEntry &entry, QgsProcessingHistoryProvider *provider )
|
||||
: mEntry( entry )
|
||||
, mAlgorithmId( mEntry.entry.value( "algorithm_id" ).toString() )
|
||||
, mPythonCommand( mEntry.entry.value( "python_command" ).toString() )
|
||||
@ -100,65 +103,7 @@ class ProcessingHistoryNode : public QgsHistoryEntryGroup
|
||||
{
|
||||
const QVariantMap parametersMap = parameters.toMap();
|
||||
mInputs = parametersMap.value( QStringLiteral( "inputs" ) ).toMap();
|
||||
mDescription = QgsProcessingUtils::variantToPythonLiteral( mInputs );
|
||||
}
|
||||
else
|
||||
{
|
||||
// an older history entry which didn't record inputs
|
||||
mDescription = mPythonCommand;
|
||||
}
|
||||
|
||||
if ( mDescription.length() > 300 )
|
||||
{
|
||||
mDescription = QObject::tr( "%1…" ).arg( mDescription.left( 299 ) );
|
||||
}
|
||||
}
|
||||
|
||||
QVariant data( int role = Qt::DisplayRole ) const override
|
||||
{
|
||||
if ( mAlgorithmInformation.displayName.isEmpty() )
|
||||
{
|
||||
mAlgorithmInformation = QgsApplication::processingRegistry()->algorithmInformation( mAlgorithmId );
|
||||
}
|
||||
|
||||
switch ( role )
|
||||
{
|
||||
case Qt::DisplayRole:
|
||||
{
|
||||
const QString algName = mAlgorithmInformation.displayName;
|
||||
if ( !mDescription.isEmpty() )
|
||||
return QStringLiteral( "[%1] %2 - %3" ).arg( mEntry.timestamp.toString( QStringLiteral( "yyyy-MM-dd hh:mm" ) ),
|
||||
algName,
|
||||
mDescription );
|
||||
else
|
||||
return QStringLiteral( "[%1] %2" ).arg( mEntry.timestamp.toString( QStringLiteral( "yyyy-MM-dd hh:mm" ) ),
|
||||
algName );
|
||||
}
|
||||
|
||||
case Qt::DecorationRole:
|
||||
{
|
||||
return mAlgorithmInformation.icon;
|
||||
}
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
QWidget *createWidget( const QgsHistoryWidgetContext & ) override
|
||||
{
|
||||
QgsCodeEditorPython *codeEditor = new QgsCodeEditorPython( );
|
||||
codeEditor->setReadOnly( true );
|
||||
codeEditor->setCaretLineVisible( false );
|
||||
codeEditor->setLineNumbersVisible( false );
|
||||
codeEditor->setFoldingVisible( false );
|
||||
codeEditor->setEdgeMode( QsciScintilla::EdgeNone );
|
||||
codeEditor->setWrapMode( QsciScintilla::WrapMode::WrapWord );
|
||||
|
||||
|
||||
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" ) );
|
||||
codeEditor->setText( introText + mPythonCommand );
|
||||
|
||||
return codeEditor;
|
||||
}
|
||||
|
||||
bool doubleClicked( const QgsHistoryWidgetContext & ) override
|
||||
@ -248,18 +193,248 @@ class ProcessingHistoryNode : public QgsHistoryEntryGroup
|
||||
QString mPythonCommand;
|
||||
QString mProcessCommand;
|
||||
QVariantMap mInputs;
|
||||
QString mDescription;
|
||||
|
||||
mutable QgsProcessingAlgorithmInformation mAlgorithmInformation;
|
||||
QgsProcessingHistoryProvider *mProvider = nullptr;
|
||||
|
||||
};
|
||||
|
||||
class ProcessingHistoryPythonCommandNode : public ProcessingHistoryBaseNode
|
||||
{
|
||||
public:
|
||||
|
||||
ProcessingHistoryPythonCommandNode( const QgsHistoryEntry &entry, QgsProcessingHistoryProvider *provider )
|
||||
: ProcessingHistoryBaseNode( entry, provider )
|
||||
{}
|
||||
|
||||
QVariant data( int role = Qt::DisplayRole ) const override
|
||||
{
|
||||
switch ( role )
|
||||
{
|
||||
case Qt::DisplayRole:
|
||||
{
|
||||
QString display = mPythonCommand;
|
||||
if ( display.length() > 300 )
|
||||
{
|
||||
display = QObject::tr( "%1…" ).arg( display.left( 299 ) );
|
||||
}
|
||||
return display;
|
||||
}
|
||||
case Qt::DecorationRole:
|
||||
return QgsApplication::getThemeIcon( QStringLiteral( "mIconPythonFile.svg" ) );
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
QWidget *createWidget( const QgsHistoryWidgetContext & ) override
|
||||
{
|
||||
QgsCodeEditorPython *codeEditor = new QgsCodeEditorPython( );
|
||||
codeEditor->setReadOnly( true );
|
||||
codeEditor->setCaretLineVisible( false );
|
||||
codeEditor->setLineNumbersVisible( false );
|
||||
codeEditor->setFoldingVisible( false );
|
||||
codeEditor->setEdgeMode( QsciScintilla::EdgeNone );
|
||||
codeEditor->setWrapMode( QsciScintilla::WrapMode::WrapWord );
|
||||
|
||||
|
||||
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" ) );
|
||||
codeEditor->setText( introText + mPythonCommand );
|
||||
|
||||
return codeEditor;
|
||||
}
|
||||
};
|
||||
|
||||
class ProcessingHistoryProcessCommandNode : public ProcessingHistoryBaseNode
|
||||
{
|
||||
public:
|
||||
|
||||
ProcessingHistoryProcessCommandNode( const QgsHistoryEntry &entry, QgsProcessingHistoryProvider *provider )
|
||||
: ProcessingHistoryBaseNode( entry, provider )
|
||||
{}
|
||||
|
||||
QVariant data( int role = Qt::DisplayRole ) const override
|
||||
{
|
||||
switch ( role )
|
||||
{
|
||||
case Qt::DisplayRole:
|
||||
{
|
||||
QString display = mProcessCommand;
|
||||
if ( display.length() > 300 )
|
||||
{
|
||||
display = QObject::tr( "%1…" ).arg( display.left( 299 ) );
|
||||
}
|
||||
return display;
|
||||
}
|
||||
case Qt::DecorationRole:
|
||||
return QgsApplication::getThemeIcon( QStringLiteral( "mActionTerminal.svg" ) );
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
QWidget *createWidget( const QgsHistoryWidgetContext & ) override
|
||||
{
|
||||
QgsCodeEditorShell *codeEditor = new QgsCodeEditorShell( );
|
||||
codeEditor->setReadOnly( true );
|
||||
codeEditor->setCaretLineVisible( false );
|
||||
codeEditor->setLineNumbersVisible( false );
|
||||
codeEditor->setFoldingVisible( false );
|
||||
codeEditor->setEdgeMode( QsciScintilla::EdgeNone );
|
||||
codeEditor->setWrapMode( QsciScintilla::WrapMode::WrapWord );
|
||||
|
||||
codeEditor->setText( mProcessCommand );
|
||||
|
||||
return codeEditor;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class ProcessingHistoryJsonNode : public ProcessingHistoryBaseNode
|
||||
{
|
||||
public:
|
||||
|
||||
ProcessingHistoryJsonNode( const QgsHistoryEntry &entry, QgsProcessingHistoryProvider *provider )
|
||||
: ProcessingHistoryBaseNode( entry, provider )
|
||||
{
|
||||
mJson = QString::fromStdString( QgsJsonUtils::jsonFromVariant( mInputs ).dump( 2 ) );
|
||||
mJsonSingleLine = QString::fromStdString( QgsJsonUtils::jsonFromVariant( mInputs ).dump() );
|
||||
}
|
||||
|
||||
QVariant data( int role = Qt::DisplayRole ) const override
|
||||
{
|
||||
switch ( role )
|
||||
{
|
||||
case Qt::DisplayRole:
|
||||
{
|
||||
QString display = mJsonSingleLine;
|
||||
if ( display.length() > 300 )
|
||||
{
|
||||
display = QObject::tr( "%1…" ).arg( display.left( 299 ) );
|
||||
}
|
||||
return display;
|
||||
}
|
||||
case Qt::DecorationRole:
|
||||
return QgsApplication::getThemeIcon( QStringLiteral( "mIconFieldJson.svg" ) );
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
QWidget *createWidget( const QgsHistoryWidgetContext & ) override
|
||||
{
|
||||
QgsCodeEditorJson *codeEditor = new QgsCodeEditorJson( );
|
||||
codeEditor->setReadOnly( true );
|
||||
codeEditor->setCaretLineVisible( false );
|
||||
codeEditor->setLineNumbersVisible( false );
|
||||
codeEditor->setFoldingVisible( false );
|
||||
codeEditor->setEdgeMode( QsciScintilla::EdgeNone );
|
||||
codeEditor->setWrapMode( QsciScintilla::WrapMode::WrapWord );
|
||||
|
||||
codeEditor->setText( mJson );
|
||||
|
||||
return codeEditor;
|
||||
}
|
||||
|
||||
QString mJson;
|
||||
QString mJsonSingleLine;
|
||||
};
|
||||
|
||||
|
||||
class ProcessingHistoryRootNode : public ProcessingHistoryBaseNode
|
||||
{
|
||||
public:
|
||||
|
||||
ProcessingHistoryRootNode( const QgsHistoryEntry &entry, QgsProcessingHistoryProvider *provider )
|
||||
: ProcessingHistoryBaseNode( entry, provider )
|
||||
{
|
||||
const QVariant parameters = mEntry.entry.value( QStringLiteral( "parameters" ) );
|
||||
if ( parameters.type() == QVariant::Map )
|
||||
{
|
||||
mDescription = QgsProcessingUtils::variantToPythonLiteral( mInputs );
|
||||
}
|
||||
else
|
||||
{
|
||||
// an older history entry which didn't record inputs
|
||||
mDescription = mPythonCommand;
|
||||
}
|
||||
|
||||
if ( mDescription.length() > 300 )
|
||||
{
|
||||
mDescription = QObject::tr( "%1…" ).arg( mDescription.left( 299 ) );
|
||||
}
|
||||
|
||||
addChild( new ProcessingHistoryPythonCommandNode( mEntry, mProvider ) );
|
||||
addChild( new ProcessingHistoryProcessCommandNode( mEntry, mProvider ) );
|
||||
addChild( new ProcessingHistoryJsonNode( mEntry, mProvider ) );
|
||||
}
|
||||
|
||||
void setEntry( const QgsHistoryEntry &entry )
|
||||
{
|
||||
mEntry = entry;
|
||||
}
|
||||
|
||||
QVariant data( int role = Qt::DisplayRole ) const override
|
||||
{
|
||||
if ( mAlgorithmInformation.displayName.isEmpty() )
|
||||
{
|
||||
mAlgorithmInformation = QgsApplication::processingRegistry()->algorithmInformation( mAlgorithmId );
|
||||
}
|
||||
|
||||
switch ( role )
|
||||
{
|
||||
case Qt::DisplayRole:
|
||||
{
|
||||
const QString algName = mAlgorithmInformation.displayName;
|
||||
if ( !mDescription.isEmpty() )
|
||||
return QStringLiteral( "[%1] %2 - %3" ).arg( mEntry.timestamp.toString( QStringLiteral( "yyyy-MM-dd hh:mm" ) ),
|
||||
algName,
|
||||
mDescription );
|
||||
else
|
||||
return QStringLiteral( "[%1] %2" ).arg( mEntry.timestamp.toString( QStringLiteral( "yyyy-MM-dd hh:mm" ) ),
|
||||
algName );
|
||||
}
|
||||
|
||||
case Qt::DecorationRole:
|
||||
{
|
||||
return mAlgorithmInformation.icon;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
QString html( const QgsHistoryWidgetContext & ) const override
|
||||
{
|
||||
return mEntry.entry.value( QStringLiteral( "log" ) ).toString();
|
||||
}
|
||||
|
||||
QString mDescription;
|
||||
mutable QgsProcessingAlgorithmInformation mAlgorithmInformation;
|
||||
|
||||
};
|
||||
|
||||
///@endcond
|
||||
|
||||
QgsHistoryEntryNode *QgsProcessingHistoryProvider::createNodeForEntry( const QgsHistoryEntry &entry, const QgsHistoryWidgetContext & )
|
||||
{
|
||||
return new ProcessingHistoryNode( entry, this );
|
||||
return new ProcessingHistoryRootNode( entry, this );
|
||||
}
|
||||
|
||||
void QgsProcessingHistoryProvider::updateNodeForEntry( QgsHistoryEntryNode *node, const QgsHistoryEntry &entry, const QgsHistoryWidgetContext & )
|
||||
{
|
||||
if ( ProcessingHistoryRootNode *rootNode = dynamic_cast< ProcessingHistoryRootNode * >( node ) )
|
||||
{
|
||||
rootNode->setEntry( entry );
|
||||
}
|
||||
}
|
||||
|
||||
QString QgsProcessingHistoryProvider::oldLogPath() const
|
||||
|
||||
@ -45,6 +45,7 @@ class GUI_EXPORT QgsProcessingHistoryProvider : public QgsAbstractHistoryProvide
|
||||
void portOldLog();
|
||||
|
||||
QgsHistoryEntryNode *createNodeForEntry( const QgsHistoryEntry &entry, const QgsHistoryWidgetContext &context ) override SIP_FACTORY;
|
||||
void updateNodeForEntry( QgsHistoryEntryNode *node, const QgsHistoryEntry &entry, const QgsHistoryWidgetContext &context ) override;
|
||||
|
||||
signals:
|
||||
|
||||
@ -72,7 +73,7 @@ class GUI_EXPORT QgsProcessingHistoryProvider : public QgsAbstractHistoryProvide
|
||||
//! Returns the path to the old log file
|
||||
QString oldLogPath() const;
|
||||
|
||||
friend class ProcessingHistoryNode;
|
||||
friend class ProcessingHistoryBaseNode;
|
||||
|
||||
};
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user