Skip to content

Commit

Permalink
Merge pull request #5137 from m-kuhn/expressionhistory
Browse files Browse the repository at this point in the history
[FEATURE] History for attribute table form view display expressions
  • Loading branch information
m-kuhn committed Sep 6, 2017
2 parents 3b839ce + 6f9be4d commit f0cf4c3
Show file tree
Hide file tree
Showing 4 changed files with 152 additions and 58 deletions.
19 changes: 10 additions & 9 deletions .ci/travis/linux/script.sh
Expand Up @@ -13,6 +13,8 @@
# #
###########################################################################

set -e

export PYTHONPATH=${HOME}/osgeo4travis/lib/python3.3/site-packages/
export PATH=${HOME}/osgeo4travis/bin:${HOME}/osgeo4travis/sbin:${HOME}/OTB-5.6.0-Linux64/bin:${PATH}
export LD_LIBRARY_PATH=${HOME}/osgeo4travis/lib
Expand All @@ -27,18 +29,17 @@ DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
export OTB_APPLICATION_PATH=${HOME}/OTB-5.6.0-Linux64/lib/otb/applications
export LD_PRELOAD=/lib/x86_64-linux-gnu/libSegFault.so

export CTEST_BUILD_COMMAND="/usr/bin/make -j3 -i -k"
export CTEST_BUILD_COMMAND="/usr/bin/make -j3 -i"

# This works around an issue where travis would timeout on master because
# This works around an issue where travis would timeout because
# when make is run inside ctest no output is generated. At the current time
# nobody know why, but at least this workaround gets travis results for master
# nobody know why, but at least this workaround gets travis results
# back. Better approaches VERY welcome.
if [[ ${TRAVIS_PULL_REQUEST} == "false" ]];
then
pushd build
$CTEST_BUILD_COMMAND
popd
fi
pushd build
echo "travis_fold:start:qgis_build"
$CTEST_BUILD_COMMAND
echo "travis_fold:end:qgis_build"
popd

python ${TRAVIS_BUILD_DIR}/.ci/travis/scripts/ctest2travis.py \
xvfb-run ctest -V -E "$(cat ${DIR}/blacklist.txt | sed -r '/^(#.*?)?$/d' | paste -sd '|' -)" -S ${DIR}/../travis.ctest --output-on-failure
10 changes: 3 additions & 7 deletions python/gui/attributetable/qgsdualview.sip
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
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( mFeatureListPreviewButton );
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
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 f0cf4c3

Please sign in to comment.