Merge pull request #9992 from 3nids/itembrowser

allow browsing feature list in feature mode
This commit is contained in:
Denis Rouzaud 2019-05-15 14:43:33 -05:00 committed by GitHub
commit 053f54c304
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 663 additions and 118 deletions

View File

@ -751,6 +751,8 @@
<file>themes/default/mIconDataDefineColor.svg</file>
<file>themes/default/mIconDataDefineColorOn.svg</file>
<file>themes/default/mActionNewVirtualLayer.svg</file>
<file>themes/default/mActionDoubleArrowRight.svg</file>
<file>themes/default/mActionDoubleArrowLeft.svg</file>
</qresource>
<qresource prefix="/images/tips">
<file alias="symbol_levels.png">qgis_tips/symbol_levels.png</file>

View File

@ -0,0 +1,81 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
height="16"
viewBox="0 0 4.2333332 4.2333335"
width="16"
version="1.1"
id="svg8"
sodipodi:docname="mActionDoubleArrowLeft.svg"
inkscape:version="0.92.2 5c3e80d, 2017-08-06">
<metadata
id="metadata14">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs12" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1280"
inkscape:window-height="938"
id="namedview10"
showgrid="false"
inkscape:zoom="20.85965"
inkscape:cx="8.5803974"
inkscape:cy="4.1339743"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="0"
inkscape:current-layer="svg8" />
<g
transform="rotate(90,148.23542,148.23542)"
id="g6"
style="fill-rule:evenodd">
<path
d="M 2.1166581,296.07395 0.39686386,293.42811 H 3.8364709 Z"
id="path2"
inkscape:connector-curvature="0"
style="fill:#6d97c4;stroke:#415a75;stroke-width:0.26458332;stroke-linecap:round;stroke-linejoin:round" />
<path
d="m 2.1127601,295.83164 -1.4731694,-2.27117 0.23030714,-7e-5 1.35909406,2.0991 z"
id="path4"
inkscape:connector-curvature="0"
style="fill:#ffffff;fill-opacity:0.52906979" />
</g>
<g
style="fill-rule:evenodd"
transform="rotate(90,148.63229,148.63229)"
id="g6-7">
<path
style="fill:#6d97c4;stroke:#415a75;stroke-width:0.26458332;stroke-linecap:round;stroke-linejoin:round"
inkscape:connector-curvature="0"
d="M 2.1166581,296.07395 0.39686386,293.42811 H 3.8364709 Z"
id="path2-9" />
<path
style="fill:#ffffff;fill-opacity:0.52906979"
inkscape:connector-curvature="0"
d="m 2.1127601,295.83164 -1.4731694,-2.27117 0.23030714,-7e-5 1.35909406,2.0991 z"
id="path4-3" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -0,0 +1,81 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
height="16"
viewBox="0 0 4.2333332 4.2333335"
width="16"
version="1.1"
id="svg8"
sodipodi:docname="mActionDoubleArrowRight.svg"
inkscape:version="0.92.2 5c3e80d, 2017-08-06">
<metadata
id="metadata14">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs12" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1280"
inkscape:window-height="938"
id="namedview10"
showgrid="false"
inkscape:zoom="20.85965"
inkscape:cx="8.5803974"
inkscape:cy="4.1339743"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="0"
inkscape:current-layer="svg8" />
<g
transform="rotate(-90,-144.39894,148.63228)"
id="g6"
style="fill-rule:evenodd">
<path
d="M 2.1166581,296.07395 0.39686386,293.42811 H 3.8364709 Z"
id="path2"
inkscape:connector-curvature="0"
style="fill:#6d97c4;stroke:#415a75;stroke-width:0.26458332;stroke-linecap:round;stroke-linejoin:round" />
<path
d="m 2.1127601,295.83164 -1.4731694,-2.27117 0.23030714,-7e-5 1.35909406,2.0991 z"
id="path4"
inkscape:connector-curvature="0"
style="fill:#ffffff;fill-opacity:0.52906979" />
</g>
<g
style="fill-rule:evenodd"
transform="rotate(-90,-144.00207,148.23541)"
id="g6-7">
<path
style="fill:#6d97c4;stroke:#415a75;stroke-width:0.26458332;stroke-linecap:round;stroke-linejoin:round"
inkscape:connector-curvature="0"
d="M 2.1166581,296.07395 0.39686386,293.42811 H 3.8364709 Z"
id="path2-9" />
<path
style="fill:#ffffff;fill-opacity:0.52906979"
inkscape:connector-curvature="0"
d="m 2.1127601,295.83164 -1.4731694,-2.27117 0.23030714,-7e-5 1.35909406,2.0991 z"
id="path4-3" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -1,2 +1,3 @@
# The following has been generated automatically from src/gui/attributetable/qgsdualview.h
QgsDualView.ViewMode.baseClass = QgsDualView
QgsDualView.FeatureListBrowsingAction.baseClass = QgsDualView

View File

@ -35,6 +35,14 @@ and the attributes for the currently selected feature are shown in a form.
};
enum FeatureListBrowsingAction
{
NoAction,
FlashFeature,
PanToFeature,
ZoomToFeature,
};
explicit QgsDualView( QWidget *parent /TransferThis/ = 0 );
%Docstring
Constructor
@ -42,7 +50,10 @@ Constructor
:param parent: The parent widget
%End
void init( QgsVectorLayer *layer, QgsMapCanvas *mapCanvas, const QgsFeatureRequest &request = QgsFeatureRequest(), const QgsAttributeEditorContext &context = QgsAttributeEditorContext(),
void init( QgsVectorLayer *layer,
QgsMapCanvas *mapCanvas,
const QgsFeatureRequest &request = QgsFeatureRequest(),
const QgsAttributeEditorContext &context = QgsAttributeEditorContext(),
bool loadFeatures = true );
%Docstring
Has to be called to initialize the dual view.

View File

@ -118,6 +118,16 @@ setFeatureSelectionManager
Emitted whenever the current edit selection has been changed.
:param feat: the feature, which will be edited.
%End
void currentEditSelectionProgressChanged( int progress, int count );
%Docstring
Emitted whenever the current edit selection has been changed.
:param progress: the position of the feature in the list
:param count: the number of features in the list
.. versionadded:: 3.8
%End
void displayExpressionChanged( const QString &expression );
@ -162,6 +172,36 @@ Select all currently visible features
void repaintRequested( const QModelIndexList &indexes );
void repaintRequested();
void editFirstFeature();
%Docstring
editFirstFeature will try to edit the first feature of the list
.. versionadded:: 3.8
%End
void editNextFeature();
%Docstring
editNextFeature will try to edit next feature of the list
.. versionadded:: 3.8
%End
void editPreviousFeature();
%Docstring
editPreviousFeature will try to edit previous feature of the list
.. versionadded:: 3.8
%End
void editLastFeature();
%Docstring
editLastFeature will try to edit the last feature of the list
.. versionadded:: 3.8
%End
};
/************************************************************************

View File

@ -232,12 +232,13 @@ Set canvas extent to the bounding box of a set of features
:param ids: the feature ids*
%End
void panToFeatureIds( QgsVectorLayer *layer, const QgsFeatureIds &ids );
void panToFeatureIds( QgsVectorLayer *layer, const QgsFeatureIds &ids, bool alwaysRecenter = true );
%Docstring
Centers canvas extent to feature ids
:param layer: the vector layer
:param ids: the feature ids*
:param ids: the feature ids
:param alwaysRecenter: if false, the canvas is recentered only if the bounding box is not contained within the current extent
%End
void panToSelected( QgsVectorLayer *layer = 0 );

View File

@ -13,6 +13,15 @@
* *
***************************************************************************/
#include <QClipboard>
#include <QDialog>
#include <QMenu>
#include <QMessageBox>
#include <QProgressDialog>
#include <QGroupBox>
#include <QInputDialog>
#include <QTimer>
#include "qgsapplication.h"
#include "qgsactionmanager.h"
#include "qgsattributetablemodel.h"
@ -32,20 +41,14 @@
#include "qgsgui.h"
#include "qgsexpressioncontextutils.h"
#include <QClipboard>
#include <QDialog>
#include <QMenu>
#include <QMessageBox>
#include <QProgressDialog>
#include <QGroupBox>
#include <QInputDialog>
QgsDualView::QgsDualView( QWidget *parent )
: QStackedWidget( parent )
{
setupUi( this );
connect( mFeatureList, &QgsFeatureListView::aboutToChangeEditSelection, this, &QgsDualView::mFeatureList_aboutToChangeEditSelection );
connect( mFeatureList, &QgsFeatureListView::currentEditSelectionChanged, this, &QgsDualView::mFeatureList_currentEditSelectionChanged );
connect( mFeatureListView, &QgsFeatureListView::aboutToChangeEditSelection, this, &QgsDualView::featureListAboutToChangeEditSelection );
connect( mFeatureListView, &QgsFeatureListView::currentEditSelectionChanged, this, &QgsDualView::featureListCurrentEditSelectionChanged );
connect( mFeatureListView, &QgsFeatureListView::currentEditSelectionProgressChanged, this, &QgsDualView::updateEditSelectionProgress );
mConditionalFormatWidget->hide();
@ -57,23 +60,39 @@ QgsDualView::QgsDualView( QWidget *parent )
// Connect layer list preview signals
connect( mActionExpressionPreview, &QAction::triggered, this, &QgsDualView::previewExpressionBuilder );
connect( mFeatureList, &QgsFeatureListView::displayExpressionChanged, this, &QgsDualView::previewExpressionChanged );
connect( mFeatureListView, &QgsFeatureListView::displayExpressionChanged, this, &QgsDualView::previewExpressionChanged );
connect( mNextFeatureButton, &QToolButton::clicked, mFeatureListView, &QgsFeatureListView::editNextFeature );
connect( mPreviousFeatureButton, &QToolButton::clicked, mFeatureListView, &QgsFeatureListView::editPreviousFeature );
connect( mFirstFeatureButton, &QToolButton::clicked, mFeatureListView, &QgsFeatureListView::editFirstFeature );
connect( mLastFeatureButton, &QToolButton::clicked, mFeatureListView, &QgsFeatureListView::editLastFeature );
QButtonGroup *buttonGroup = new QButtonGroup( this );
buttonGroup->setExclusive( false );
buttonGroup->addButton( mFlashButton, FlashFeature );
buttonGroup->addButton( mAutoPanButton, PanToFeature );
buttonGroup->addButton( mAutoZoomButton, ZoomToFeature );
FeatureListBrowsingAction action = QgsSettings().enumValue( QStringLiteral( "/qgis/attributeTable/featureListBrowsingAction" ), FlashFeature );
QAbstractButton *bt = buttonGroup->button( static_cast<int>( action ) );
if ( bt )
bt->setChecked( true );
connect( buttonGroup, qgis::overload< QAbstractButton *, bool >::of( &QButtonGroup::buttonToggled ), this, &QgsDualView::panZoomGroupButtonToggled );
}
void QgsDualView::init( QgsVectorLayer *layer, QgsMapCanvas *mapCanvas, const QgsFeatureRequest &request, const QgsAttributeEditorContext &context, bool loadFeatures )
void QgsDualView::init( QgsVectorLayer *layer, QgsMapCanvas *mapCanvas, const QgsFeatureRequest &request,
const QgsAttributeEditorContext &context, bool loadFeatures )
{
if ( !layer )
return;
mLayer = layer;
mEditorContext = context;
connect( mTableView, &QgsAttributeTableView::willShowContextMenu, this, &QgsDualView::viewWillShowContextMenu );
mTableView->horizontalHeader()->setContextMenuPolicy( Qt::CustomContextMenu );
connect( mTableView->horizontalHeader(), &QHeaderView::customContextMenuRequested, this, &QgsDualView::showViewHeaderMenu );
connect( mTableView, &QgsAttributeTableView::columnResized, this, &QgsDualView::tableColumnResized );
connect( mFeatureList, &QgsFeatureListView::willShowContextMenu, this, &QgsDualView::widgetWillShowContextMenu );
connect( mFeatureListView, &QgsFeatureListView::willShowContextMenu, this, &QgsDualView::widgetWillShowContextMenu );
initLayerCache( !( request.flags() & QgsFeatureRequest::NoGeometry ) || !request.filterRect().isNull() );
initModels( mapCanvas, request, loadFeatures );
@ -81,7 +100,7 @@ void QgsDualView::init( QgsVectorLayer *layer, QgsMapCanvas *mapCanvas, const Qg
mConditionalFormatWidget->setLayer( mLayer );
mTableView->setModel( mFilterModel );
mFeatureList->setModel( mFeatureListModel );
mFeatureListView->setModel( mFeatureListModel );
delete mAttributeForm;
mAttributeForm = new QgsAttributeForm( mLayer, mTempAttributeFormFeature, mEditorContext );
mTempAttributeFormFeature = QgsFeature();
@ -104,7 +123,7 @@ void QgsDualView::init( QgsVectorLayer *layer, QgsMapCanvas *mapCanvas, const Qg
connect( mFilterModel, &QgsAttributeTableFilterModel::sortColumnChanged, this, &QgsDualView::onSortColumnChanged );
if ( mFeatureListPreviewButton->defaultAction() )
mFeatureList->setDisplayExpression( mDisplayExpression );
mFeatureListView->setDisplayExpression( mDisplayExpression );
else
columnBoxInit();
@ -168,9 +187,9 @@ void QgsDualView::columnBoxInit()
// If there is no single field found as preview
if ( !mFeatureListPreviewButton->defaultAction() )
{
mFeatureList->setDisplayExpression( displayExpression );
mFeatureListView->setDisplayExpression( displayExpression );
mFeatureListPreviewButton->setDefaultAction( mActionExpressionPreview );
setDisplayExpression( mFeatureList->displayExpression() );
setDisplayExpression( mFeatureListView->displayExpression() );
}
else
{
@ -302,7 +321,7 @@ void QgsDualView::initModels( QgsMapCanvas *mapCanvas, const QgsFeatureRequest &
connect( mMasterModel, &QgsAttributeTableModel::rowsRemoved, mFilterModel, &QgsAttributeTableFilterModel::invalidate );
connect( mMasterModel, &QgsAttributeTableModel::rowsInserted, mFilterModel, &QgsAttributeTableFilterModel::invalidate );
connect( mFeatureList, &QgsFeatureListView::displayExpressionChanged, this, &QgsDualView::displayExpressionChanged );
connect( mFeatureListView, &QgsFeatureListView::displayExpressionChanged, this, &QgsDualView::displayExpressionChanged );
mFeatureListModel = new QgsFeatureListModel( mFilterModel, mFilterModel );
mFeatureListModel->setSortByDisplayExpression( true );
@ -401,13 +420,76 @@ void QgsDualView::insertRecentlyUsedDisplayExpression( const QString &expression
mLastDisplayExpressionAction = previewAction;
}
void QgsDualView::mFeatureList_aboutToChangeEditSelection( bool &ok )
void QgsDualView::updateEditSelectionProgress( int progress, int count )
{
mProgressCount->setText( QStringLiteral( "%1 / %2" ).arg( progress + 1 ).arg( count ) );
mPreviousFeatureButton->setEnabled( progress > 0 );
mNextFeatureButton->setEnabled( progress + 1 < count );
mFirstFeatureButton->setEnabled( progress > 0 );
mLastFeatureButton->setEnabled( progress + 1 < count );
}
void QgsDualView::panOrZoomToFeature( const QgsFeatureIds &featureset )
{
QgsMapCanvas *canvas = mFilterModel->mapCanvas();
if ( canvas )
{
if ( mAutoPanButton->isChecked() )
QTimer::singleShot( 0, this, [ = ]()
{
canvas->panToFeatureIds( mLayer, featureset, false );
canvas->flashFeatureIds( mLayer, featureset );
} );
else if ( mAutoZoomButton->isChecked() )
QTimer::singleShot( 0, this, [ = ]()
{
canvas->zoomToFeatureIds( mLayer, featureset );
canvas->flashFeatureIds( mLayer, featureset );
} );
else if ( mFlashButton->isChecked() )
QTimer::singleShot( 0, this, [ = ]()
{
canvas->flashFeatureIds( mLayer, featureset );
} );
}
}
void QgsDualView::panZoomGroupButtonToggled( QAbstractButton *button, bool checked )
{
if ( button == mAutoPanButton && checked )
{
QgsSettings().setEnumValue( QStringLiteral( "/qgis/attributeTable/featureListBrowsingAction" ), PanToFeature );
mAutoZoomButton->setChecked( false );
mFlashButton->setChecked( false );
}
else if ( button == mAutoZoomButton && checked )
{
QgsSettings().setEnumValue( QStringLiteral( "/qgis/attributeTable/featureListBrowsingAction" ), ZoomToFeature );
mAutoPanButton->setChecked( false );
mFlashButton->setChecked( false );
}
else if ( button == mFlashButton && checked )
{
QgsSettings().setEnumValue( QStringLiteral( "/qgis/attributeTable/featureListBrowsingAction" ), FlashFeature );
mAutoZoomButton->setChecked( false );
mAutoPanButton->setChecked( false );
}
else
{
QgsSettings().setEnumValue( QStringLiteral( "/qgis/attributeTable/featureListBrowsingAction" ), NoAction );
}
if ( checked )
panOrZoomToFeature( mFeatureListView->currentEditSelection() );
}
void QgsDualView::featureListAboutToChangeEditSelection( bool &ok )
{
if ( mLayer->isEditable() && !mAttributeForm->save() )
ok = false;
}
void QgsDualView::mFeatureList_currentEditSelectionChanged( const QgsFeature &feat )
void QgsDualView::featureListCurrentEditSelectionChanged( const QgsFeature &feat )
{
if ( !mAttributeForm )
{
@ -416,7 +498,11 @@ void QgsDualView::mFeatureList_currentEditSelectionChanged( const QgsFeature &fe
else if ( !mLayer->isEditable() || mAttributeForm->save() )
{
mAttributeForm->setFeature( feat );
setCurrentEditSelection( QgsFeatureIds() << feat.id() );
QgsFeatureIds featureset;
featureset << feat.id();
setCurrentEditSelection( featureset );
panOrZoomToFeature( featureset );
}
else
{
@ -426,8 +512,8 @@ void QgsDualView::mFeatureList_currentEditSelectionChanged( const QgsFeature &fe
void QgsDualView::setCurrentEditSelection( const QgsFeatureIds &fids )
{
mFeatureList->setCurrentFeatureEdited( false );
mFeatureList->setEditSelection( fids );
mFeatureListView->setCurrentFeatureEdited( false );
mFeatureListView->setEditSelection( fids );
}
bool QgsDualView::saveEditChanges()
@ -467,28 +553,28 @@ void QgsDualView::previewExpressionBuilder()
// Show expression builder
QgsExpressionContext context( QgsExpressionContextUtils::globalProjectLayerScopes( mLayer ) );
QgsExpressionBuilderDialog dlg( mLayer, mFeatureList->displayExpression(), this, QStringLiteral( "generic" ), context );
QgsExpressionBuilderDialog dlg( mLayer, mFeatureListView->displayExpression(), this, QStringLiteral( "generic" ), context );
dlg.setWindowTitle( tr( "Expression Based Preview" ) );
dlg.setExpressionText( mFeatureList->displayExpression() );
dlg.setExpressionText( mFeatureListView->displayExpression() );
if ( dlg.exec() == QDialog::Accepted )
{
mFeatureList->setDisplayExpression( dlg.expressionText() );
mFeatureListView->setDisplayExpression( dlg.expressionText() );
mFeatureListPreviewButton->setDefaultAction( mActionExpressionPreview );
mFeatureListPreviewButton->setPopupMode( QToolButton::MenuButtonPopup );
}
setDisplayExpression( mFeatureList->displayExpression() );
setDisplayExpression( mFeatureListView->displayExpression() );
}
void QgsDualView::previewColumnChanged( QAction *previewAction, const QString &expression )
{
if ( !mFeatureList->setDisplayExpression( QStringLiteral( "COALESCE( \"%1\", '<NULL>' )" ).arg( expression ) ) )
if ( !mFeatureListView->setDisplayExpression( QStringLiteral( "COALESCE( \"%1\", '<NULL>' )" ).arg( expression ) ) )
{
QMessageBox::warning( this,
tr( "Column Preview" ),
tr( "Could not set column '%1' as preview column.\nParser error:\n%2" )
.arg( previewAction->text(), mFeatureList->parserErrorString() )
.arg( previewAction->text(), mFeatureListView->parserErrorString() )
);
}
else
@ -498,7 +584,7 @@ void QgsDualView::previewColumnChanged( QAction *previewAction, const QString &e
mFeatureListPreviewButton->setPopupMode( QToolButton::InstantPopup );
}
setDisplayExpression( mFeatureList->displayExpression() );
setDisplayExpression( mFeatureListView->displayExpression() );
}
int QgsDualView::featureCount()
@ -841,11 +927,11 @@ void QgsDualView::onSortColumnChanged()
void QgsDualView::sortByPreviewExpression()
{
Qt::SortOrder sortOrder = Qt::AscendingOrder;
if ( mFeatureList->displayExpression() == sortExpression() )
if ( mFeatureListView->displayExpression() == sortExpression() )
{
sortOrder = mConfig.sortOrder() == Qt::AscendingOrder ? Qt::DescendingOrder : Qt::AscendingOrder;
}
setSortExpression( mFeatureList->displayExpression(), sortOrder );
setSortExpression( mFeatureListView->displayExpression(), sortOrder );
}
void QgsDualView::updateSelectedFeatures()
@ -878,7 +964,7 @@ void QgsDualView::featureFormAttributeChanged( const QString &attribute, const Q
Q_UNUSED( attribute )
Q_UNUSED( value )
if ( attributeChanged )
mFeatureList->setCurrentFeatureEdited( true );
mFeatureListView->setCurrentFeatureEdited( true );
}
void QgsDualView::setFilteredFeatures( const QgsFeatureIds &filteredFeatures )
@ -894,7 +980,7 @@ void QgsDualView::setRequest( const QgsFeatureRequest &request )
void QgsDualView::setFeatureSelectionManager( QgsIFeatureSelectionManager *featureSelectionManager )
{
mTableView->setFeatureSelectionManager( featureSelectionManager );
mFeatureList->setFeatureSelectionManager( featureSelectionManager );
mFeatureListView->setFeatureSelectionManager( featureSelectionManager );
if ( mFeatureSelectionManager && mFeatureSelectionManager->parent() == this )
delete mFeatureSelectionManager;

View File

@ -65,9 +65,19 @@ class GUI_EXPORT QgsDualView : public QStackedWidget, private Ui::QgsDualViewBas
*/
AttributeEditor = 1
};
Q_ENUM( ViewMode )
//! Action on the map canvas when browsing the list of features
enum FeatureListBrowsingAction
{
NoAction = 0, //!< No action is done
FlashFeature, //!< The feature is highlighted with a flash
PanToFeature, //!< The map is panned to the center of the feature bounding-box
ZoomToFeature, //!< The map is zoomed to contained the feature bounding-box
};
Q_ENUM( FeatureListBrowsingAction )
/**
* \brief Constructor
* \param parent The parent widget
@ -83,9 +93,12 @@ class GUI_EXPORT QgsDualView : public QStackedWidget, private Ui::QgsDualViewBas
* \param request Use a modified request to limit the shown features
* \param context The context in which this view is shown
* \param loadFeatures whether to initially load all features into the view. If set to
* FALSE, limited features can later be loaded using setFilterMode()
* FALSE, limited features can later be loaded using setFilterMode()
*/
void init( QgsVectorLayer *layer, QgsMapCanvas *mapCanvas, const QgsFeatureRequest &request = QgsFeatureRequest(), const QgsAttributeEditorContext &context = QgsAttributeEditorContext(),
void init( QgsVectorLayer *layer,
QgsMapCanvas *mapCanvas,
const QgsFeatureRequest &request = QgsFeatureRequest(),
const QgsAttributeEditorContext &context = QgsAttributeEditorContext(),
bool loadFeatures = true );
/**
@ -286,14 +299,14 @@ class GUI_EXPORT QgsDualView : public QStackedWidget, private Ui::QgsDualViewBas
private slots:
void mFeatureList_aboutToChangeEditSelection( bool &ok );
void featureListAboutToChangeEditSelection( bool &ok );
/**
* Changes the currently visible feature within the attribute editor
*
* \param feat The newly visible feature
*/
void mFeatureList_currentEditSelectionChanged( const QgsFeature &feat );
void featureListCurrentEditSelectionChanged( const QgsFeature &feat );
void previewExpressionBuilder();
@ -357,6 +370,8 @@ class GUI_EXPORT QgsDualView : public QStackedWidget, private Ui::QgsDualViewBas
void rebuildFullLayerCache();
void panZoomGroupButtonToggled( QAbstractButton *button, bool checked );
private:
/**
@ -369,6 +384,8 @@ class GUI_EXPORT QgsDualView : public QStackedWidget, private Ui::QgsDualViewBas
void saveRecentDisplayExpressions() const;
void setDisplayExpression( const QString &expression );
void insertRecentlyUsedDisplayExpression( const QString &expression );
void updateEditSelectionProgress( int progress, int count );
void panOrZoomToFeature( const QgsFeatureIds &featureset );
QgsAttributeEditorContext mEditorContext;
QgsAttributeTableModel *mMasterModel = nullptr;

View File

@ -49,6 +49,7 @@ void QgsFeatureListView::setModel( QgsFeatureListModel *featureListModel )
mModel = featureListModel;
delete mFeatureSelectionModel;
delete mCurrentEditSelectionModel;
mCurrentEditSelectionModel = new QItemSelectionModel( mModel->masterModel(), this );
if ( !mFeatureSelectionManager )
@ -169,6 +170,7 @@ void QgsFeatureListView::editSelectionChanged( const QItemSelection &deselected,
mModel->featureByIndex( mModel->mapFromMaster( indexList.first() ), feat );
emit currentEditSelectionChanged( feat );
emit currentEditSelectionProgressChanged( mModel->mapFromMaster( indexList.first() ).row(), mModel->rowCount() );
}
}
}
@ -259,47 +261,57 @@ void QgsFeatureListView::mouseReleaseEvent( QMouseEvent *event )
void QgsFeatureListView::keyPressEvent( QKeyEvent *event )
{
if ( Qt::Key_Up == event->key() || Qt::Key_Down == event->key() )
switch ( event->key() )
{
int currentRow = 0;
if ( 0 != mCurrentEditSelectionModel->selectedIndexes().count() )
{
QModelIndex localIndex = mModel->mapFromMaster( mCurrentEditSelectionModel->selectedIndexes().first() );
currentRow = localIndex.row();
}
case Qt::Key_Up:
editOtherFeature( Previous );
break;
QModelIndex newLocalIndex;
QModelIndex newIndex;
case Qt::Key_Down:
editOtherFeature( Next );
break;
switch ( event->key() )
{
case Qt::Key_Up:
newLocalIndex = mModel->index( currentRow - 1, 0 );
newIndex = mModel->mapToMaster( newLocalIndex );
if ( newIndex.isValid() )
{
setEditSelection( newIndex, QItemSelectionModel::ClearAndSelect );
scrollTo( newLocalIndex );
}
break;
case Qt::Key_Down:
newLocalIndex = mModel->index( currentRow + 1, 0 );
newIndex = mModel->mapToMaster( newLocalIndex );
if ( newIndex.isValid() )
{
setEditSelection( newIndex, QItemSelectionModel::ClearAndSelect );
scrollTo( newLocalIndex );
}
break;
default:
break;
}
default:
QListView::keyPressEvent( event );
}
else
}
void QgsFeatureListView::editOtherFeature( QgsFeatureListView::PositionInList positionInList )
{
int currentRow = 0;
if ( 0 != mCurrentEditSelectionModel->selectedIndexes().count() )
{
QListView::keyPressEvent( event );
QModelIndex localIndex = mModel->mapFromMaster( mCurrentEditSelectionModel->selectedIndexes().first() );
currentRow = localIndex.row();
}
QModelIndex newLocalIndex;
QModelIndex newIndex;
switch ( positionInList )
{
case First:
newLocalIndex = mModel->index( 0, 0 );
break;
case Previous:
newLocalIndex = mModel->index( currentRow - 1, 0 );
break;
case Next:
newLocalIndex = mModel->index( currentRow + 1, 0 );
break;
case Last:
newLocalIndex = mModel->index( mModel->rowCount() - 1, 0 );
break;
}
newIndex = mModel->mapToMaster( newLocalIndex );
if ( newIndex.isValid() )
{
setEditSelection( newIndex, QItemSelectionModel::ClearAndSelect );
scrollTo( newLocalIndex );
}
}

View File

@ -132,11 +132,18 @@ class GUI_EXPORT QgsFeatureListView : public QListView
/**
* Emitted whenever the current edit selection has been changed.
*
* \param feat the feature, which will be edited.
*/
void currentEditSelectionChanged( QgsFeature &feat );
/**
* Emitted whenever the current edit selection has been changed.
* \param progress the position of the feature in the list
* \param count the number of features in the list
* \since QGIS 3.8
*/
void currentEditSelectionProgressChanged( int progress, int count );
/**
* Emitted whenever the display expression is successfully changed
* \param expression The expression that was applied
@ -178,6 +185,32 @@ class GUI_EXPORT QgsFeatureListView : public QListView
void repaintRequested( const QModelIndexList &indexes );
void repaintRequested();
/**
* editFirstFeature will try to edit the first feature of the list
* \since QGIS 3.8
*/
void editFirstFeature() {editOtherFeature( First );}
/**
* editNextFeature will try to edit next feature of the list
* \since QGIS 3.8
*/
void editNextFeature() {editOtherFeature( Next );}
/**
* editPreviousFeature will try to edit previous feature of the list
* \since QGIS 3.8
*/
void editPreviousFeature() {editOtherFeature( Previous );}
/**
* editLastFeature will try to edit the last feature of the list
* \since QGIS 3.8
*/
void editLastFeature() {editOtherFeature( Last );}
private slots:
void editSelectionChanged( const QItemSelection &deselected, const QItemSelection &selected );
@ -192,6 +225,16 @@ class GUI_EXPORT QgsFeatureListView : public QListView
private:
void selectRow( const QModelIndex &index, bool anchor );
enum PositionInList
{
First,
Next,
Previous,
Last
};
void editOtherFeature( PositionInList positionInList );
QgsFeatureListModel *mModel = nullptr;
QItemSelectionModel *mCurrentEditSelectionModel = nullptr;

View File

@ -1074,7 +1074,7 @@ void QgsMapCanvas::zoomToFeatureIds( QgsVectorLayer *layer, const QgsFeatureIds
}
void QgsMapCanvas::panToFeatureIds( QgsVectorLayer *layer, const QgsFeatureIds &ids )
void QgsMapCanvas::panToFeatureIds( QgsVectorLayer *layer, const QgsFeatureIds &ids, bool alwaysRecenter )
{
if ( !layer )
{
@ -1085,7 +1085,8 @@ void QgsMapCanvas::panToFeatureIds( QgsVectorLayer *layer, const QgsFeatureIds &
QString errorMsg;
if ( boundingBoxOfFeatureIds( ids, layer, bbox, errorMsg ) )
{
setCenter( bbox.center() );
if ( alwaysRecenter || !mapSettings().extent().contains( bbox ) )
setCenter( bbox.center() );
refresh();
}
else

View File

@ -253,9 +253,11 @@ class GUI_EXPORT QgsMapCanvas : public QGraphicsView
/**
* Centers canvas extent to feature ids
\param layer the vector layer
\param ids the feature ids*/
void panToFeatureIds( QgsVectorLayer *layer, const QgsFeatureIds &ids );
* \param layer the vector layer
* \param ids the feature ids
* \param alwaysRecenter if false, the canvas is recentered only if the bounding box is not contained within the current extent
*/
void panToFeatureIds( QgsVectorLayer *layer, const QgsFeatureIds &ids, bool alwaysRecenter = true );
//! Pan to the selected features of current (vector) layer keeping same extent.
void panToSelected( QgsVectorLayer *layer = nullptr );

View File

@ -214,10 +214,10 @@
<property name="checkable">
<bool>true</bool>
</property>
<property name="autoRaise">
<property name="checked">
<bool>true</bool>
</property>
<property name="checked">
<property name="autoRaise">
<bool>true</bool>
</property>
<attribute name="buttonGroup">
@ -693,35 +693,6 @@
</tabstops>
<resources>
<include location="../../images/images.qrc"/>
<include location="../../images/images.qrc"/>
<include location="../../images/images.qrc"/>
<include location="../../images/images.qrc"/>
<include location="../../images/images.qrc"/>
<include location="../../images/images.qrc"/>
<include location="../../images/images.qrc"/>
<include location="../../images/images.qrc"/>
<include location="../../images/images.qrc"/>
<include location="../../images/images.qrc"/>
<include location="../../images/images.qrc"/>
<include location="../../images/images.qrc"/>
<include location="../../images/images.qrc"/>
<include location="../../images/images.qrc"/>
<include location="../../images/images.qrc"/>
<include location="../../images/images.qrc"/>
<include location="../../images/images.qrc"/>
<include location="../../images/images.qrc"/>
<include location="../../images/images.qrc"/>
<include location="../../images/images.qrc"/>
<include location="../../images/images.qrc"/>
<include location="../../images/images.qrc"/>
<include location="../../images/images.qrc"/>
<include location="../../images/images.qrc"/>
<include location="../../images/images.qrc"/>
<include location="../../images/images.qrc"/>
<include location="../../images/images.qrc"/>
<include location="../../images/images.qrc"/>
<include location="../../images/images.qrc"/>
<include location="../../images/images.qrc"/>
</resources>
<connections>
<connection>

View File

@ -73,6 +73,9 @@
</property>
<widget class="QWidget" name="listViewWdg" native="true">
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="spacing">
<number>2</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
@ -111,7 +114,7 @@
</widget>
</item>
<item>
<widget class="QgsFeatureListView" name="mFeatureList">
<widget class="QgsFeatureListView" name="mFeatureListView">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
@ -120,6 +123,199 @@
</property>
</widget>
</item>
<item>
<widget class="QWidget" name="widget" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QPushButton" name="mFirstFeatureButton">
<property name="maximumSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../images/images.qrc">
<normaloff>:/images/themes/default/mActionDoubleArrowLeft.svg</normaloff>:/images/themes/default/mActionDoubleArrowLeft.svg</iconset>
</property>
<property name="flat">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="mPreviousFeatureButton">
<property name="maximumSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../images/images.qrc">
<normaloff>:/images/themes/default/mActionArrowLeft.svg</normaloff>:/images/themes/default/mActionArrowLeft.svg</iconset>
</property>
<property name="flat">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="mNextFeatureButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../images/images.qrc">
<normaloff>:/images/themes/default/mActionArrowRight.svg</normaloff>:/images/themes/default/mActionArrowRight.svg</iconset>
</property>
<property name="flat">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="mLastFeatureButton">
<property name="maximumSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../images/images.qrc">
<normaloff>:/images/themes/default/mActionDoubleArrowRight.svg</normaloff>:/images/themes/default/mActionDoubleArrowRight.svg</iconset>
</property>
<property name="flat">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="mProgressCount">
<property name="font">
<font>
<pointsize>11</pointsize>
</font>
</property>
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="spacing">
<number>0</number>
</property>
<item>
<widget class="QToolButton" name="mFlashButton">
<property name="toolTip">
<string>Highlight currently edited feature on map</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../images/images.qrc">
<normaloff>:/images/themes/default/mActionHighlightFeature.svg</normaloff>:/images/themes/default/mActionHighlightFeature.svg</iconset>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="mAutoZoomButton">
<property name="toolTip">
<string>automatically zoom to currently edited feature</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../images/images.qrc">
<normaloff>:/images/themes/default/mActionPanToSelected.svg</normaloff>:/images/themes/default/mActionPanToSelected.svg</iconset>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="mAutoPanButton">
<property name="toolTip">
<string>automatically pan to currently edited feature</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../images/images.qrc">
<normaloff>:/images/themes/default/mActionZoomToSelected.svg</normaloff>:/images/themes/default/mActionZoomToSelected.svg</iconset>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<widget class="QFrame" name="mAttributeEditor">

View File

@ -260,7 +260,7 @@ void TestQgsAttributeTable::testSortByDisplayExpression()
std::unique_ptr< QgsAttributeTableDialog > dlg( new QgsAttributeTableDialog( tempLayer.get() ) );
dlg->mMainView->mFeatureList->setDisplayExpression( "pk" );
dlg->mMainView->mFeatureListView->setDisplayExpression( "pk" );
QgsFeatureListModel *listModel = dlg->mMainView->mFeatureListModel;
QCOMPARE( listModel->rowCount(), 3 );
@ -268,7 +268,7 @@ void TestQgsAttributeTable::testSortByDisplayExpression()
QCOMPARE( listModel->index( 1, 0 ).data( Qt::DisplayRole ), QVariant( 2 ) );
QCOMPARE( listModel->index( 2, 0 ).data( Qt::DisplayRole ), QVariant( 3 ) );
dlg->mMainView->mFeatureList->setDisplayExpression( "col1" );
dlg->mMainView->mFeatureListView->setDisplayExpression( "col1" );
QCOMPARE( listModel->index( 0, 0 ).data( Qt::DisplayRole ), QVariant( 1.8 ) );
QCOMPARE( listModel->index( 1, 0 ).data( Qt::DisplayRole ), QVariant( 3.2 ) );
QCOMPARE( listModel->index( 2, 0 ).data( Qt::DisplayRole ), QVariant( 5.0 ) );