Skip to content
Permalink
Browse files
[FEATURE] History for attribute table form view display expressions
It's possible to re-use the last 10 display expressions in the form view
of the attribute table.

The expressions will also be persisted in the project file.

Using fields will now show field aliases instead of column names where
available.
  • Loading branch information
m-kuhn committed Sep 5, 2017
1 parent 117261b commit 63d0a91
Show file tree
Hide file tree
Showing 3 changed files with 142 additions and 49 deletions.
@@ -171,13 +171,6 @@ class QgsDualView : QStackedWidget
:rtype: str
%End

protected:

void columnBoxInit();
%Docstring
Initializes widgets which depend on the attributes of this layer
%End

public slots:

void setCurrentEditSelection( const QgsFeatureIds &fids );
@@ -244,6 +237,9 @@ class QgsDualView : QStackedWidget
\param mode new mode
%End

protected:
virtual void hideEvent( QHideEvent *event );

};

class QgsAttributeTableAction : QAction
@@ -56,7 +56,6 @@ QgsDualView::QgsDualView( QWidget *parent )

mConditionalFormatWidget->hide();

mPreviewActionMapper = new QSignalMapper( this );

mPreviewColumnsMenu = new QMenu( this );
mActionPreviewColumnsMenu->setMenu( mPreviewColumnsMenu );
@@ -66,7 +65,6 @@ QgsDualView::QgsDualView( QWidget *parent )

// Connect layer list preview signals
connect( mActionExpressionPreview, &QAction::triggered, this, &QgsDualView::previewExpressionBuilder );
connect( mPreviewActionMapper, static_cast < void ( QSignalMapper::* )( QObject * ) > ( &QSignalMapper::mapped ), this, &QgsDualView::previewColumnChanged );
connect( mFeatureList, &QgsFeatureListView::displayExpressionChanged, this, &QgsDualView::previewExpressionChanged );
}

@@ -149,16 +147,15 @@ void QgsDualView::columnBoxInit()
if ( fieldIndex == -1 )
continue;

if ( QgsGui::editorWidgetRegistry()->findBest( mLayer, field.name() ).type() != QLatin1String( "Hidden" ) )
QString fieldName = field.name();
if ( QgsGui::editorWidgetRegistry()->findBest( mLayer, fieldName ).type() != QLatin1String( "Hidden" ) )
{
QIcon icon = mLayer->fields().iconForField( fieldIndex );
QString text = field.name();
QString text = mLayer->attributeDisplayName( fieldIndex );

// Generate action for the preview popup button of the feature list
QAction *previewAction = new QAction( icon, text, mFeatureListPreviewButton );
mPreviewActionMapper->setMapping( previewAction, previewAction );
connect( previewAction, &QAction::triggered, this, [previewAction, this]( bool ) { this->mPreviewActionMapper->map( previewAction ); }
);
connect( previewAction, &QAction::triggered, this, [ = ] { previewColumnChanged( previewAction, fieldName ); } );
mPreviewColumnsMenu->addAction( previewAction );

if ( text == defaultField )
@@ -168,21 +165,26 @@ void QgsDualView::columnBoxInit()
}
}

QAction *sortByPreviewExpression = new QAction( QgsApplication::getThemeIcon( QStringLiteral( "sort.svg" ) ), tr( "Sort by preview expression" ), this );
connect( sortByPreviewExpression, &QAction::triggered, this, &QgsDualView::sortByPreviewExpression );
mFeatureListPreviewButton->addAction( sortByPreviewExpression );

QAction *separator = new QAction();
separator->setSeparator( true );
mFeatureListPreviewButton->addAction( separator );
restoreRecentDisplayExpressions();

// If there is no single field found as preview
if ( !mFeatureListPreviewButton->defaultAction() )
{
mFeatureList->setDisplayExpression( displayExpression );
mFeatureListPreviewButton->setDefaultAction( mActionExpressionPreview );
mDisplayExpression = mFeatureList->displayExpression();
setDisplayExpression( mFeatureList->displayExpression() );
}
else
{
mFeatureListPreviewButton->defaultAction()->trigger();
}

QAction *sortByPreviewExpression = new QAction( QgsApplication::getThemeIcon( QStringLiteral( "sort.svg" ) ), tr( "Sort by preview expression" ), this );
connect( sortByPreviewExpression, &QAction::triggered, this, &QgsDualView::sortByPreviewExpression );
mFeatureListPreviewButton->addAction( sortByPreviewExpression );
}

void QgsDualView::setView( QgsDualView::ViewMode view )
@@ -314,6 +316,95 @@ void QgsDualView::initModels( QgsMapCanvas *mapCanvas, const QgsFeatureRequest &
mFeatureListModel = new QgsFeatureListModel( mFilterModel, mFilterModel );
}

void QgsDualView::restoreRecentDisplayExpressions()
{
const QVariantList previewExpressions = mLayer->customProperty( QStringLiteral( "dualview/previewExpressions" ) ).toList();

for ( const QVariant &previewExpression : previewExpressions )
insertRecentlyUsedDisplayExpression( previewExpression.toString() );
}

void QgsDualView::saveRecentDisplayExpressions() const
{
QList<QAction *> actions = mFeatureListPreviewButton->actions();

// Remove existing same action
int index = actions.indexOf( mLastDisplayExpressionAction );
if ( index != -1 )
{
QVariantList previewExpressions;
for ( ; index < actions.length(); ++index )
{
QAction *action = actions.at( index );
previewExpressions << action->property( "previewExpression" );
}

mLayer->setCustomProperty( QStringLiteral( "dualview/previewExpressions" ), previewExpressions );
}
}

void QgsDualView::setDisplayExpression( const QString &expression )
{
mDisplayExpression = expression;
insertRecentlyUsedDisplayExpression( expression );
}

void QgsDualView::insertRecentlyUsedDisplayExpression( const QString &expression )
{
QList<QAction *> actions = mFeatureListPreviewButton->actions();

// Remove existing same action
int index = actions.indexOf( mLastDisplayExpressionAction );
if ( index != -1 )
{
for ( int i = 0; index + i < actions.length(); ++i )
{
QAction *action = actions.at( index );
if ( action->text() == expression || i >= 9 )
{
if ( action == mLastDisplayExpressionAction )
mLastDisplayExpressionAction = nullptr;
mFeatureListPreviewButton->removeAction( action );
}
else
{
if ( !mLastDisplayExpressionAction )
mLastDisplayExpressionAction = action;
}
}
}

QString name = expression;
QIcon icon = QgsApplication::getThemeIcon( QStringLiteral( "/mIconExpressionPreview.svg" ) );
if ( expression.startsWith( QLatin1String( "COALESCE( \"" ) ) && expression.endsWith( QLatin1String( ", '<NULL>' )" ) ) )
{
name = expression.mid( 11, expression.length() - 24 ); // Numbers calculated from the COALESCE / <NULL> parts

int fieldIndex = mLayer->fields().indexOf( name );
if ( fieldIndex != -1 )
{
name = mLayer->attributeDisplayName( fieldIndex );
icon = mLayer->fields().iconForField( fieldIndex );
}
else
{
name = expression;
}
}

QAction *previewAction = new QAction( icon, name, mFeatureListPreviewButton );
previewAction->setProperty( "previewExpression", expression );
connect( previewAction, &QAction::triggered, this, [expression, this]( bool )
{
setDisplayExpression( expression );
mFeatureListPreviewButton->setText( expression );
}
);

mFeatureListPreviewButton->insertAction( mLastDisplayExpressionAction, previewAction );
mLastDisplayExpressionAction = previewAction;
}

void QgsDualView::on_mFeatureList_aboutToChangeEditSelection( bool &ok )
{
if ( mLayer->isEditable() && !mAttributeForm->save() )
@@ -387,33 +478,27 @@ void QgsDualView::previewExpressionBuilder()
mFeatureListPreviewButton->setPopupMode( QToolButton::MenuButtonPopup );
}

mDisplayExpression = mFeatureList->displayExpression();
setDisplayExpression( mFeatureList->displayExpression() );
}

void QgsDualView::previewColumnChanged( QObject *action )
void QgsDualView::previewColumnChanged( QAction *previewAction, const QString &expression )
{
QAction *previewAction = qobject_cast< QAction * >( action );

if ( previewAction )
if ( !mFeatureList->setDisplayExpression( QStringLiteral( "COALESCE( \"%1\", '<NULL>' )" ).arg( expression ) ) )
{
if ( !mFeatureList->setDisplayExpression( QStringLiteral( "COALESCE( \"%1\", '<NULL>' )" ).arg( previewAction->text() ) ) )
{
QMessageBox::warning( this,
tr( "Could not set preview column" ),
tr( "Could not set column '%1' as preview column.\nParser error:\n%2" )
.arg( previewAction->text(), mFeatureList->parserErrorString() )
);
}
else
{
mFeatureListPreviewButton->setDefaultAction( previewAction );
mFeatureListPreviewButton->setPopupMode( QToolButton::InstantPopup );
}
QMessageBox::warning( this,
tr( "Could not set preview column" ),
tr( "Could not set column '%1' as preview column.\nParser error:\n%2" )
.arg( previewAction->text(), mFeatureList->parserErrorString() )
);
}
else
{
mFeatureListPreviewButton->setText( previewAction->text() );
mFeatureListPreviewButton->setIcon( previewAction->icon() );
mFeatureListPreviewButton->setPopupMode( QToolButton::InstantPopup );
}

mDisplayExpression = mFeatureList->displayExpression();

Q_ASSERT( previewAction );
setDisplayExpression( mFeatureList->displayExpression() );
}

int QgsDualView::featureCount()
@@ -438,6 +523,12 @@ void QgsDualView::copyCellContent() const
}
}

void QgsDualView::hideEvent( QHideEvent *event )
{
Q_UNUSED( event )
saveRecentDisplayExpressions();
}

void QgsDualView::viewWillShowContextMenu( QMenu *menu, const QModelIndex &atIndex )
{
if ( !menu )
@@ -197,13 +197,6 @@ class GUI_EXPORT QgsDualView : public QStackedWidget, private Ui::QgsDualViewBas
*/
QString sortExpression() const;

protected:

/**
* Initializes widgets which depend on the attributes of this layer
*/
void columnBoxInit();

public slots:

/**
@@ -265,6 +258,9 @@ class GUI_EXPORT QgsDualView : public QStackedWidget, private Ui::QgsDualViewBas
*/
void formModeChanged( QgsAttributeForm::Mode mode );

protected:
virtual void hideEvent( QHideEvent *event ) override;

private slots:

void on_mFeatureList_aboutToChangeEditSelection( bool &ok );
@@ -278,7 +274,7 @@ class GUI_EXPORT QgsDualView : public QStackedWidget, private Ui::QgsDualViewBas

void previewExpressionBuilder();

void previewColumnChanged( QObject *previewAction );
void previewColumnChanged( QAction *previewAction, const QString &expression );

void viewWillShowContextMenu( QMenu *menu, const QModelIndex &atIndex );

@@ -335,16 +331,26 @@ class GUI_EXPORT QgsDualView : public QStackedWidget, private Ui::QgsDualViewBas
void rebuildFullLayerCache();

private:

/**
* Initializes widgets which depend on the attributes of this layer
*/
void columnBoxInit();
void initLayerCache( bool cacheGeometry );
void initModels( QgsMapCanvas *mapCanvas, const QgsFeatureRequest &request, bool loadFeatures );
void restoreRecentDisplayExpressions();
void saveRecentDisplayExpressions() const;
void setDisplayExpression( const QString &expression );
void insertRecentlyUsedDisplayExpression( const QString &expression );

QgsAttributeEditorContext mEditorContext;
QgsAttributeTableModel *mMasterModel = nullptr;
QgsAttributeTableFilterModel *mFilterModel = nullptr;
QgsFeatureListModel *mFeatureListModel = nullptr;
QgsAttributeForm *mAttributeForm = nullptr;
QSignalMapper *mPreviewActionMapper = nullptr;
QMenu *mPreviewColumnsMenu = nullptr;
QMenu *mPreviewActionMenu = nullptr;
QAction *mLastDisplayExpressionAction = nullptr;
QMenu *mHorizontalHeaderMenu = nullptr;
QgsVectorLayerCache *mLayerCache = nullptr;
QgsVectorLayer *mLayer = nullptr;

0 comments on commit 63d0a91

Please sign in to comment.