Skip to content

Commit

Permalink
[FEATURE] History for attribute table form view display expressions
Browse files Browse the repository at this point in the history
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.
10 changes: 3 additions & 7 deletions python/gui/attributetable/qgsdualview.sip
Original file line number Diff line number Diff line change
Expand Up @@ -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 );
Expand Down Expand Up @@ -244,6 +237,9 @@ class QgsDualView : QStackedWidget
\param mode new mode
%End

protected:
virtual void hideEvent( QHideEvent *event );

};

class QgsAttributeTableAction : QAction
Expand Down
157 changes: 124 additions & 33 deletions src/gui/attributetable/qgsdualview.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ QgsDualView::QgsDualView( QWidget *parent )

mConditionalFormatWidget->hide();

mPreviewActionMapper = new QSignalMapper( this );

mPreviewColumnsMenu = new QMenu( this );
mActionPreviewColumnsMenu->setMenu( mPreviewColumnsMenu );
Expand All @@ -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 );
}

Expand Down Expand Up @@ -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 )
Expand All @@ -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 )
Expand Down Expand Up @@ -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() )
Expand Down Expand Up @@ -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()
Expand All @@ -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 )
Expand Down
24 changes: 15 additions & 9 deletions src/gui/attributetable/qgsdualview.h
Original file line number Diff line number Diff line change
Expand Up @@ -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:

/**
Expand Down Expand Up @@ -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 );
Expand All @@ -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 );

Expand Down Expand Up @@ -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;
Expand Down

0 comments on commit 63d0a91

Please sign in to comment.