mirror of
				https://github.com/qgis/QGIS.git
				synced 2025-11-04 00:04:25 -05:00 
			
		
		
		
	Allow sorting attribute table by expression
This commit is contained in:
		
							parent
							
								
									11ee2fc809
								
							
						
					
					
						commit
						d07d9edda6
					
				@ -583,6 +583,7 @@
 | 
			
		||||
        <file>themes/default/multieditMixedValues.svg</file>
 | 
			
		||||
        <file>themes/default/multieditSameValues.svg</file>
 | 
			
		||||
        <file>themes/default/locked_repeating.svg</file>
 | 
			
		||||
        <file>themes/default/sort.svg</file>
 | 
			
		||||
    </qresource>
 | 
			
		||||
    <qresource prefix="/images/tips">
 | 
			
		||||
        <file alias="symbol_levels.png">qgis_tips/symbol_levels.png</file>
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										73
									
								
								images/themes/default/sort.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								images/themes/default/sort.svg
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,73 @@
 | 
			
		||||
<?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"
 | 
			
		||||
   version="1.1"
 | 
			
		||||
   id="svg5477"
 | 
			
		||||
   viewBox="0 0 4.2333333 4.2333335"
 | 
			
		||||
   height="16"
 | 
			
		||||
   width="16"
 | 
			
		||||
   inkscape:version="0.91 r13725"
 | 
			
		||||
   sodipodi:docname="sort.svg">
 | 
			
		||||
  <sodipodi:namedview
 | 
			
		||||
     pagecolor="#ffffff"
 | 
			
		||||
     bordercolor="#666666"
 | 
			
		||||
     borderopacity="1"
 | 
			
		||||
     objecttolerance="10"
 | 
			
		||||
     gridtolerance="10"
 | 
			
		||||
     guidetolerance="10"
 | 
			
		||||
     inkscape:pageopacity="0"
 | 
			
		||||
     inkscape:pageshadow="2"
 | 
			
		||||
     inkscape:window-width="1920"
 | 
			
		||||
     inkscape:window-height="1016"
 | 
			
		||||
     id="namedview13"
 | 
			
		||||
     showgrid="true"
 | 
			
		||||
     inkscape:zoom="39.333334"
 | 
			
		||||
     inkscape:cx="1.7617969"
 | 
			
		||||
     inkscape:cy="7.4154532"
 | 
			
		||||
     inkscape:window-x="0"
 | 
			
		||||
     inkscape:window-y="27"
 | 
			
		||||
     inkscape:window-maximized="1"
 | 
			
		||||
     inkscape:current-layer="svg5477">
 | 
			
		||||
    <inkscape:grid
 | 
			
		||||
       type="xygrid"
 | 
			
		||||
       id="grid3341" />
 | 
			
		||||
  </sodipodi:namedview>
 | 
			
		||||
  <defs
 | 
			
		||||
     id="defs5479" />
 | 
			
		||||
  <metadata
 | 
			
		||||
     id="metadata5482">
 | 
			
		||||
    <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 />
 | 
			
		||||
      </cc:Work>
 | 
			
		||||
    </rdf:RDF>
 | 
			
		||||
  </metadata>
 | 
			
		||||
  <path
 | 
			
		||||
     inkscape:connector-curvature="0"
 | 
			
		||||
     style="display:inline;fill:#6d97c4;fill-opacity:1;fill-rule:evenodd;stroke:#415a75;stroke-width:0.19843751;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;enable-background:new"
 | 
			
		||||
     d="m 2.9088665,3.7628126 -1.0794756,-1.2402344 0.7733851,0 0.2887393,-2.12183447 0.3139285,2.12183447 0.7828981,0 z"
 | 
			
		||||
     id="path2848-7-3-9-4"
 | 
			
		||||
     sodipodi:nodetypes="ccccccc" />
 | 
			
		||||
  <path
 | 
			
		||||
     inkscape:connector-curvature="0"
 | 
			
		||||
     id="path31772"
 | 
			
		||||
     style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:medium;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans, Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:tb-rl;text-anchor:start;fill:#f79191;fill-opacity:1;stroke:#a40000;stroke-width:0.19843751;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
 | 
			
		||||
     d="m 1.2400171,1.4925339 -0.54129826,0 -0.0854208,0.2668105 -0.3479775,0 0.49723917,-1.46451483 0.41271749,0 0.4972392,1.46451483 -0.3479775,0 -0.084522,-0.2668105 m -0.45497826,-0.271715 0.36775916,0 L 0.969368,0.63815188 0.78503884,1.2208189"
 | 
			
		||||
     sodipodi:nodetypes="ccccccccccccc" />
 | 
			
		||||
  <path
 | 
			
		||||
     inkscape:connector-curvature="0"
 | 
			
		||||
     id="path31774"
 | 
			
		||||
     style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:medium;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans, Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:tb-rl;text-anchor:start;fill:#91bbf7;fill-opacity:1;stroke:#0044a4;stroke-width:0.19843751;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
 | 
			
		||||
     d="m 0.40469133,2.2147734 1.12845407,0 0,0.2285545 -0.72023241,0.9505123 0.74091331,0 0,0.285448 -1.16981581,0 0,-0.2285546 0.72023251,-0.9505123 -0.69955167,0 0,-0.2854479"
 | 
			
		||||
     sodipodi:nodetypes="ccccccccccc" />
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 After Width: | Height: | Size: 3.5 KiB  | 
@ -104,4 +104,15 @@ class QgsAttributeTableConfig
 | 
			
		||||
     * Deserialize to XML on layer load
 | 
			
		||||
     */
 | 
			
		||||
    void readXml( const QDomNode& node );
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the expression used for sorting.
 | 
			
		||||
     */
 | 
			
		||||
    QString sortExpression() const;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Set the sort expression used for sorting.
 | 
			
		||||
     */
 | 
			
		||||
    void setSortExpression( const QString& sortExpression );
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -142,6 +142,20 @@ class QgsAttributeTableFilterModel: QSortFilterProxyModel, QgsFeatureModel
 | 
			
		||||
     */
 | 
			
		||||
    virtual void sort( int column, Qt::SortOrder order = Qt::AscendingOrder );
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Sort by the given expression using the given order.
 | 
			
		||||
     * Prefetches all the data from the layer to speed up sorting.
 | 
			
		||||
     *
 | 
			
		||||
     * @param column The expression which should be used for sorting
 | 
			
		||||
     * @param order  The order ( Qt::AscendingOrder or Qt::DescendingOrder )
 | 
			
		||||
     */
 | 
			
		||||
    void sort( QString expression, Qt::SortOrder order = Qt::AscendingOrder );
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The expression which is used to sort the attribute table.
 | 
			
		||||
     */
 | 
			
		||||
    QString sortExpression() const;
 | 
			
		||||
 | 
			
		||||
    /** Returns the map canvas*/
 | 
			
		||||
    QgsMapCanvas* mapCanvas() const;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -148,6 +148,19 @@ class QgsAttributeTableModel : QAbstractTableModel
 | 
			
		||||
     */
 | 
			
		||||
    void prefetchColumnData( int column );
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Prefetches the entire data for one expression. Based on this cached information
 | 
			
		||||
     * the sorting can later be done in a performant way.
 | 
			
		||||
     *
 | 
			
		||||
     * @param expression The expression to cache
 | 
			
		||||
     */
 | 
			
		||||
    void prefetchSortData( const QString& expression );
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The expression which was used to fill the sorting cache
 | 
			
		||||
     */
 | 
			
		||||
    QString sortCacheExpression() const;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Set a request that will be used to fill this attribute table model.
 | 
			
		||||
     * In contrast to a filter, the request will constrain the data shown without the possibility
 | 
			
		||||
@ -160,7 +173,7 @@ class QgsAttributeTableModel : QAbstractTableModel
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the the feature request
 | 
			
		||||
     */
 | 
			
		||||
    const QgsFeatureRequest &request() const;
 | 
			
		||||
    const QgsFeatureRequest& request() const;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Sets the context in which this table is shown.
 | 
			
		||||
@ -188,7 +201,7 @@ class QgsAttributeTableModel : QAbstractTableModel
 | 
			
		||||
     * Empty extra columns to announce from this model.
 | 
			
		||||
     * Any extra columns need to be implemented by proxy models in front of this model.
 | 
			
		||||
     */
 | 
			
		||||
    void setExtraColumns(int extraColumns);
 | 
			
		||||
    void setExtraColumns( int extraColumns );
 | 
			
		||||
 | 
			
		||||
  public slots:
 | 
			
		||||
 | 
			
		||||
@ -226,7 +239,7 @@ class QgsAttributeTableModel : QAbstractTableModel
 | 
			
		||||
     * Launched when eatures have been deleted
 | 
			
		||||
     * @param fids feature ids
 | 
			
		||||
     */
 | 
			
		||||
    virtual void featuresDeleted( const QgsFeatureIds& fid );
 | 
			
		||||
    virtual void featuresDeleted( const QgsFeatureIds& fids );
 | 
			
		||||
    /**
 | 
			
		||||
     * Launched when a feature has been added
 | 
			
		||||
     * @param fid feature id
 | 
			
		||||
 | 
			
		||||
@ -115,10 +115,25 @@ class QgsDualView : QStackedWidget
 | 
			
		||||
     */
 | 
			
		||||
    QgsAttributeTableModel* masterModel() const;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Set the request
 | 
			
		||||
     *
 | 
			
		||||
     * @param request The request
 | 
			
		||||
     */
 | 
			
		||||
    void setRequest( const QgsFeatureRequest& request );
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Set the feature selection model
 | 
			
		||||
     *
 | 
			
		||||
     * @param featureSelectionManager the feature selection model
 | 
			
		||||
     */
 | 
			
		||||
    void setFeatureSelectionManager( QgsIFeatureSelectionManager* featureSelectionManager );
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns the table view
 | 
			
		||||
     *
 | 
			
		||||
     * @return The table view
 | 
			
		||||
     */
 | 
			
		||||
    QgsAttributeTableView* tableView();
 | 
			
		||||
    /**
 | 
			
		||||
     * Set the attribute table config which should be used to control
 | 
			
		||||
@ -126,6 +141,16 @@ class QgsDualView : QStackedWidget
 | 
			
		||||
     */
 | 
			
		||||
    void setAttributeTableConfig( const QgsAttributeTableConfig& config );
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Set the expression used for sorting the table and feature list.
 | 
			
		||||
     */
 | 
			
		||||
    void setSortExpression( const QString& sortExpression );
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the expression used for sorting the table and feature list.
 | 
			
		||||
     */
 | 
			
		||||
    QString sortExpression() const;
 | 
			
		||||
 | 
			
		||||
  protected:
 | 
			
		||||
    /**
 | 
			
		||||
     * Initializes widgets which depend on the attributes of this layer
 | 
			
		||||
 | 
			
		||||
@ -16,6 +16,8 @@
 | 
			
		||||
 | 
			
		||||
#include <QDockWidget>
 | 
			
		||||
#include <QMessageBox>
 | 
			
		||||
#include <QGridLayout>
 | 
			
		||||
#include <QDialogButtonBox>
 | 
			
		||||
 | 
			
		||||
#include "qgsattributetabledialog.h"
 | 
			
		||||
#include "qgsattributetablemodel.h"
 | 
			
		||||
@ -23,11 +25,11 @@
 | 
			
		||||
#include "qgsattributetableview.h"
 | 
			
		||||
#include "qgsorganizetablecolumnsdialog.h"
 | 
			
		||||
 | 
			
		||||
#include <qgsapplication.h>
 | 
			
		||||
#include <qgsvectordataprovider.h>
 | 
			
		||||
#include <qgsvectorlayer.h>
 | 
			
		||||
#include <qgsexpression.h>
 | 
			
		||||
 | 
			
		||||
#include "qgsapplication.h"
 | 
			
		||||
#include "qgsvectorlayer.h"
 | 
			
		||||
#include "qgsvectordataprovider.h"
 | 
			
		||||
#include "qgsexpression.h"
 | 
			
		||||
#include "qgsexpressionbuilderwidget.h"
 | 
			
		||||
#include "qgisapp.h"
 | 
			
		||||
#include "qgsaddattrdialog.h"
 | 
			
		||||
#include "qgsdelattrdialog.h"
 | 
			
		||||
@ -616,6 +618,52 @@ void QgsAttributeTableDialog::on_mAddFeature_clicked()
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void QgsAttributeTableDialog::on_mSortButton_clicked()
 | 
			
		||||
{
 | 
			
		||||
  QgsAttributeTableConfig config = mLayer->attributeTableConfig();
 | 
			
		||||
 | 
			
		||||
  QDialog orderByDlg;
 | 
			
		||||
  orderByDlg.setWindowTitle( tr( "Configure attribute table sort order" ) );
 | 
			
		||||
  QDialogButtonBox* dialogButtonBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
 | 
			
		||||
  QGridLayout* layout = new QGridLayout();
 | 
			
		||||
  connect( dialogButtonBox, SIGNAL( accepted() ), &orderByDlg, SLOT( accept() ) );
 | 
			
		||||
  connect( dialogButtonBox, SIGNAL( rejected() ), &orderByDlg, SLOT( reject() ) );
 | 
			
		||||
  orderByDlg.setLayout( layout );
 | 
			
		||||
 | 
			
		||||
  QGroupBox* sortingGroupBox = new QGroupBox();
 | 
			
		||||
  sortingGroupBox->setTitle( tr( "Enable sorting order in attribute table" ) );
 | 
			
		||||
  sortingGroupBox->setCheckable( true );
 | 
			
		||||
  sortingGroupBox->setChecked( !mMainView->sortExpression().isEmpty() );
 | 
			
		||||
  layout->addWidget( sortingGroupBox );
 | 
			
		||||
  sortingGroupBox->setLayout( new QGridLayout() );
 | 
			
		||||
 | 
			
		||||
  QgsExpressionBuilderWidget* expressionBuilder = new QgsExpressionBuilderWidget();
 | 
			
		||||
  expressionBuilder->setExpressionText( mMainView->sortExpression().isEmpty() ? mLayer->displayExpression() : mMainView->sortExpression() );
 | 
			
		||||
  QgsExpressionContext context;
 | 
			
		||||
  context << QgsExpressionContextUtils::globalScope()
 | 
			
		||||
  << QgsExpressionContextUtils::projectScope();
 | 
			
		||||
  expressionBuilder->setExpressionContext( context );
 | 
			
		||||
  expressionBuilder->setLayer( mLayer );
 | 
			
		||||
  sortingGroupBox->layout()->addWidget( expressionBuilder );
 | 
			
		||||
 | 
			
		||||
  layout->addWidget( dialogButtonBox );
 | 
			
		||||
  if ( orderByDlg.exec() )
 | 
			
		||||
  {
 | 
			
		||||
    if ( sortingGroupBox->isChecked() )
 | 
			
		||||
    {
 | 
			
		||||
      mMainView->setSortExpression( expressionBuilder->expressionText() );
 | 
			
		||||
      config.setSortExpression( expressionBuilder->expressionText() );
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
      mMainView->setSortExpression( QString() );
 | 
			
		||||
      config.setSortExpression( QString() );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    mLayer->setAttributeTableConfig( config );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void QgsAttributeTableDialog::on_mExpressionSelectButton_clicked()
 | 
			
		||||
{
 | 
			
		||||
  QgsExpressionSelectionDialog* dlg = new QgsExpressionSelectionDialog( mLayer );
 | 
			
		||||
 | 
			
		||||
@ -154,6 +154,8 @@ class APP_EXPORT QgsAttributeTableDialog : public QDialog, private Ui::QgsAttrib
 | 
			
		||||
 | 
			
		||||
    void on_mHelpButton_clicked() { QgsContextHelp::run( metaObject()->className() ); }
 | 
			
		||||
 | 
			
		||||
    void on_mSortButton_clicked();
 | 
			
		||||
 | 
			
		||||
    void on_mExpressionSelectButton_clicked();
 | 
			
		||||
    void filterColumnChanged( QObject* filterAction );
 | 
			
		||||
    void filterExpressionBuilder();
 | 
			
		||||
 | 
			
		||||
@ -173,6 +173,18 @@ void QgsAttributeTableConfig::readXml( const QDomNode& node )
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  mSortExpression = configNode.toElement().attribute( "sortExpression" );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QString QgsAttributeTableConfig::sortExpression() const
 | 
			
		||||
{
 | 
			
		||||
  return mSortExpression;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void QgsAttributeTableConfig::setSortExpression( const QString& sortExpression )
 | 
			
		||||
{
 | 
			
		||||
  mSortExpression = sortExpression;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void QgsAttributeTableConfig::writeXml( QDomNode& node ) const
 | 
			
		||||
@ -182,6 +194,8 @@ void QgsAttributeTableConfig::writeXml( QDomNode& node ) const
 | 
			
		||||
  QDomElement configElement  = doc.createElement( "attributetableconfig" );
 | 
			
		||||
  configElement.setAttribute( "actionWidgetStyle", mActionWidgetStyle == ButtonList ? "buttonList" : "dropDown" );
 | 
			
		||||
 | 
			
		||||
  configElement.setAttribute( "sortExpression", mSortExpression );
 | 
			
		||||
 | 
			
		||||
  QDomElement columnsElement  = doc.createElement( "columns" );
 | 
			
		||||
 | 
			
		||||
  Q_FOREACH ( const ColumnConfig& column, mColumns )
 | 
			
		||||
 | 
			
		||||
@ -110,10 +110,20 @@ class CORE_EXPORT QgsAttributeTableConfig
 | 
			
		||||
     */
 | 
			
		||||
    void readXml( const QDomNode& node );
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the expression used for sorting.
 | 
			
		||||
     */
 | 
			
		||||
    QString sortExpression() const;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Set the sort expression used for sorting.
 | 
			
		||||
     */
 | 
			
		||||
    void setSortExpression( const QString& sortExpression );
 | 
			
		||||
 | 
			
		||||
  private:
 | 
			
		||||
    QVector<ColumnConfig> mColumns;
 | 
			
		||||
    ActionWidgetStyle mActionWidgetStyle;
 | 
			
		||||
    QString mSortExpression;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Q_DECLARE_METATYPE( QgsAttributeTableConfig::ColumnConfig )
 | 
			
		||||
 | 
			
		||||
@ -194,6 +194,20 @@ void QgsAttributeTableFilterModel::setAttributeTableConfig( const QgsAttributeTa
 | 
			
		||||
      endResetModel();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  sort( config.sortExpression() );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void QgsAttributeTableFilterModel::sort( QString expression, Qt::SortOrder order )
 | 
			
		||||
{
 | 
			
		||||
  QSortFilterProxyModel::sort( -1 );
 | 
			
		||||
  masterModel()->prefetchSortData( expression );
 | 
			
		||||
  QSortFilterProxyModel::sort( 0, order ) ;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QString QgsAttributeTableFilterModel::sortExpression() const
 | 
			
		||||
{
 | 
			
		||||
  return masterModel()->sortCacheExpression();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void QgsAttributeTableFilterModel::setSelectedOnTop( bool selectedOnTop )
 | 
			
		||||
 | 
			
		||||
@ -178,6 +178,20 @@ class GUI_EXPORT QgsAttributeTableFilterModel: public QSortFilterProxyModel, pub
 | 
			
		||||
     */
 | 
			
		||||
    virtual void sort( int column, Qt::SortOrder order = Qt::AscendingOrder ) override;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Sort by the given expression using the given order.
 | 
			
		||||
     * Prefetches all the data from the layer to speed up sorting.
 | 
			
		||||
     *
 | 
			
		||||
     * @param expression The expression which should be used for sorting
 | 
			
		||||
     * @param order      The order ( Qt::AscendingOrder or Qt::DescendingOrder )
 | 
			
		||||
     */
 | 
			
		||||
    void sort( QString expression, Qt::SortOrder order = Qt::AscendingOrder );
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The expression which is used to sort the attribute table.
 | 
			
		||||
     */
 | 
			
		||||
    QString sortExpression() const;
 | 
			
		||||
 | 
			
		||||
    /** Returns the map canvas*/
 | 
			
		||||
    QgsMapCanvas* mapCanvas() const { return mCanvas; }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -39,7 +39,7 @@ QgsAttributeTableModel::QgsAttributeTableModel( QgsVectorLayerCache *layerCache,
 | 
			
		||||
    : QAbstractTableModel( parent )
 | 
			
		||||
    , mLayerCache( layerCache )
 | 
			
		||||
    , mFieldCount( 0 )
 | 
			
		||||
    , mCachedField( -1 )
 | 
			
		||||
    , mSortCacheExpression( "" )
 | 
			
		||||
    , mExtraColumns( 0 )
 | 
			
		||||
{
 | 
			
		||||
  mExpressionContext << QgsExpressionContextUtils::globalScope()
 | 
			
		||||
@ -160,7 +160,7 @@ bool QgsAttributeTableModel::removeRows( int row, int count, const QModelIndex &
 | 
			
		||||
  // clean old references
 | 
			
		||||
  for ( int i = row; i < row + count; i++ )
 | 
			
		||||
  {
 | 
			
		||||
    mFieldCache.remove( mRowIdMap[i] );
 | 
			
		||||
    mSortCache.remove( mRowIdMap[i] );
 | 
			
		||||
    mIdRowMap.remove( mRowIdMap[i] );
 | 
			
		||||
    mRowIdMap.remove( i );
 | 
			
		||||
  }
 | 
			
		||||
@ -206,7 +206,8 @@ void QgsAttributeTableModel::featureAdded( QgsFeatureId fid )
 | 
			
		||||
 | 
			
		||||
  if ( featOk && mFeatureRequest.acceptFeature( mFeat ) )
 | 
			
		||||
  {
 | 
			
		||||
    mFieldCache[fid] = mFeat.attribute( mCachedField );
 | 
			
		||||
    mExpressionContext.setFeature( mFeat );
 | 
			
		||||
    mSortCache[fid] = mSortCacheExpression.evaluate();
 | 
			
		||||
 | 
			
		||||
    int n = mRowIdMap.size();
 | 
			
		||||
    beginInsertRows( QModelIndex(), n, n );
 | 
			
		||||
@ -236,10 +237,8 @@ void QgsAttributeTableModel::editCommandEnded()
 | 
			
		||||
 | 
			
		||||
void QgsAttributeTableModel::attributeDeleted( int idx )
 | 
			
		||||
{
 | 
			
		||||
  if ( idx == mCachedField )
 | 
			
		||||
  {
 | 
			
		||||
    prefetchColumnData( -1 );
 | 
			
		||||
  }
 | 
			
		||||
  if ( mSortCacheAttributes.contains( idx ) )
 | 
			
		||||
    prefetchSortData( "" );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void QgsAttributeTableModel::layerDeleted()
 | 
			
		||||
@ -256,9 +255,13 @@ void QgsAttributeTableModel::attributeValueChanged( QgsFeatureId fid, int idx, c
 | 
			
		||||
{
 | 
			
		||||
  QgsDebugMsgLevel( QString( "(%4) fid: %1, idx: %2, value: %3" ).arg( fid ).arg( idx ).arg( value.toString() ).arg( mFeatureRequest.filterType() ), 3 );
 | 
			
		||||
 | 
			
		||||
  if ( idx == mCachedField )
 | 
			
		||||
    mFieldCache[fid] = value;
 | 
			
		||||
 | 
			
		||||
  if ( mSortCacheAttributes.contains( idx ) )
 | 
			
		||||
  {
 | 
			
		||||
    // if the expression used multiple fields, this will not work but this way we don't have
 | 
			
		||||
    // to run the expensive query in the 80% cases where we just have a simple column for sorting
 | 
			
		||||
    // or it's the first used column, this works just fine
 | 
			
		||||
    mSortCache[fid] = value;
 | 
			
		||||
  }
 | 
			
		||||
  // No filter request: skip all possibly heavy checks
 | 
			
		||||
  if ( mFeatureRequest.filterType() == QgsFeatureRequest::FilterNone )
 | 
			
		||||
  {
 | 
			
		||||
@ -577,27 +580,22 @@ QVariant QgsAttributeTableModel::data( const QModelIndex &index, int role ) cons
 | 
			
		||||
      return QVariant( Qt::AlignLeft );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  QVariant val;
 | 
			
		||||
 | 
			
		||||
  // if we don't have the row in current cache, load it from layer first
 | 
			
		||||
  if ( mCachedField == fieldId )
 | 
			
		||||
  if ( role == SortRole )
 | 
			
		||||
  {
 | 
			
		||||
    val = mFieldCache[rowId];
 | 
			
		||||
    return mSortCache[rowId];
 | 
			
		||||
  }
 | 
			
		||||
  else
 | 
			
		||||
 | 
			
		||||
  if ( mFeat.id() != rowId || !mFeat.isValid() )
 | 
			
		||||
  {
 | 
			
		||||
    if ( mFeat.id() != rowId || !mFeat.isValid() )
 | 
			
		||||
    {
 | 
			
		||||
      if ( !loadFeatureAtId( rowId ) )
 | 
			
		||||
        return QVariant( "ERROR" );
 | 
			
		||||
    if ( !loadFeatureAtId( rowId ) )
 | 
			
		||||
      return QVariant( "ERROR" );
 | 
			
		||||
 | 
			
		||||
      if ( mFeat.id() != rowId )
 | 
			
		||||
        return QVariant( "ERROR" );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    val = mFeat.attribute( fieldId );
 | 
			
		||||
    if ( mFeat.id() != rowId )
 | 
			
		||||
      return QVariant( "ERROR" );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  QVariant val = mFeat.attribute( fieldId );
 | 
			
		||||
 | 
			
		||||
  if ( role == Qt::DisplayRole )
 | 
			
		||||
  {
 | 
			
		||||
    return mWidgetFactories[index.column()]->representValue( layer(), fieldId, mWidgetConfigs[index.column()], mAttributeWidgetCaches[index.column()], val );
 | 
			
		||||
@ -730,34 +728,51 @@ QgsFeature QgsAttributeTableModel::feature( const QModelIndex &idx ) const
 | 
			
		||||
 | 
			
		||||
void QgsAttributeTableModel::prefetchColumnData( int column )
 | 
			
		||||
{
 | 
			
		||||
  mFieldCache.clear();
 | 
			
		||||
 | 
			
		||||
  if ( column == -1 )
 | 
			
		||||
  if ( column == -1 || column >= mAttributes.count() )
 | 
			
		||||
  {
 | 
			
		||||
    mCachedField = -1;
 | 
			
		||||
    prefetchSortData( "" );
 | 
			
		||||
  }
 | 
			
		||||
  else
 | 
			
		||||
  {
 | 
			
		||||
    if ( column >= mAttributes.count() )
 | 
			
		||||
      return;
 | 
			
		||||
    int fieldId = mAttributes.at( column );
 | 
			
		||||
    const QgsFields& fields = layer()->fields();
 | 
			
		||||
    QStringList fldNames;
 | 
			
		||||
    fldNames << fields[fieldId].name();
 | 
			
		||||
 | 
			
		||||
    QgsFeatureRequest r( mFeatureRequest );
 | 
			
		||||
    QgsFeatureIterator it = mLayerCache->getFeatures( r.setFlags( QgsFeatureRequest::NoGeometry ).setSubsetOfAttributes( fldNames, fields ) );
 | 
			
		||||
 | 
			
		||||
    QgsFeature f;
 | 
			
		||||
    while ( it.nextFeature( f ) )
 | 
			
		||||
    {
 | 
			
		||||
      mFieldCache.insert( f.id(), f.attribute( fieldId ) );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    mCachedField = fieldId;
 | 
			
		||||
    prefetchSortData( mLayerCache->layer()->fields().at( mAttributes.at( column ) ).name() );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void QgsAttributeTableModel::prefetchSortData( const QString& expressionString )
 | 
			
		||||
{
 | 
			
		||||
  mSortCache.clear();
 | 
			
		||||
  mSortCacheAttributes.clear();
 | 
			
		||||
 | 
			
		||||
  mSortCacheExpression = QgsExpression( expressionString );
 | 
			
		||||
 | 
			
		||||
  mSortCacheExpression.prepare( &mExpressionContext );
 | 
			
		||||
 | 
			
		||||
  Q_FOREACH ( const QString& col, mSortCacheExpression.referencedColumns() )
 | 
			
		||||
  {
 | 
			
		||||
    mSortCacheAttributes.append( mLayerCache->layer()->fieldNameIndex( col ) );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  QgsFeatureRequest request = QgsFeatureRequest( mFeatureRequest )
 | 
			
		||||
                              .setFlags( QgsFeatureRequest::NoGeometry )
 | 
			
		||||
                              .setSubsetOfAttributes( mSortCacheAttributes );
 | 
			
		||||
  QgsFeatureIterator it = mLayerCache->getFeatures( request );
 | 
			
		||||
 | 
			
		||||
  QgsFeature f;
 | 
			
		||||
  while ( it.nextFeature( f ) )
 | 
			
		||||
  {
 | 
			
		||||
    mExpressionContext.setFeature( f );
 | 
			
		||||
    mSortCache.insert( f.id(), mSortCacheExpression.evaluate( &mExpressionContext ) );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QString QgsAttributeTableModel::sortCacheExpression() const
 | 
			
		||||
{
 | 
			
		||||
  if ( mSortCacheExpression.rootNode() )
 | 
			
		||||
    return mSortCacheExpression.expression();
 | 
			
		||||
  else
 | 
			
		||||
    return QString();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void QgsAttributeTableModel::setRequest( const QgsFeatureRequest& request )
 | 
			
		||||
{
 | 
			
		||||
  mFeatureRequest = request;
 | 
			
		||||
 | 
			
		||||
@ -193,6 +193,19 @@ class GUI_EXPORT QgsAttributeTableModel: public QAbstractTableModel
 | 
			
		||||
     */
 | 
			
		||||
    void prefetchColumnData( int column );
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Prefetches the entire data for one expression. Based on this cached information
 | 
			
		||||
     * the sorting can later be done in a performant way.
 | 
			
		||||
     *
 | 
			
		||||
     * @param expression The expression to cache
 | 
			
		||||
     */
 | 
			
		||||
    void prefetchSortData( const QString& expression );
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The expression which was used to fill the sorting cache
 | 
			
		||||
     */
 | 
			
		||||
    QString sortCacheExpression() const;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Set a request that will be used to fill this attribute table model.
 | 
			
		||||
     * In contrast to a filter, the request will constrain the data shown without the possibility
 | 
			
		||||
@ -335,9 +348,10 @@ class GUI_EXPORT QgsAttributeTableModel: public QAbstractTableModel
 | 
			
		||||
    QgsFeatureRequest mFeatureRequest;
 | 
			
		||||
 | 
			
		||||
    /** The currently cached column */
 | 
			
		||||
    int mCachedField;
 | 
			
		||||
    /** Allows caching of one specific column (used for sorting) */
 | 
			
		||||
    QHash<QgsFeatureId, QVariant> mFieldCache;
 | 
			
		||||
    QgsExpression mSortCacheExpression;
 | 
			
		||||
    QgsAttributeList mSortCacheAttributes;
 | 
			
		||||
    /** Allows caching of one value per column (used for sorting) */
 | 
			
		||||
    QHash<QgsFeatureId, QVariant> mSortCache;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Holds the bounds of changed cells while an update operation is running
 | 
			
		||||
 | 
			
		||||
@ -196,6 +196,10 @@ void QgsDualView::columnBoxInit()
 | 
			
		||||
  {
 | 
			
		||||
    mFeatureListPreviewButton->defaultAction()->trigger();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  QAction* sortByPreviewExpression = new QAction( QgsApplication::getThemeIcon( "sort.svg" ), tr( "Sort by preview expression" ), this );
 | 
			
		||||
  connect( sortByPreviewExpression, SIGNAL( triggered( bool ) ), this, SLOT( sortByPreviewExpression() ) );
 | 
			
		||||
  mFeatureListPreviewButton->addAction( sortByPreviewExpression );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void QgsDualView::setView( QgsDualView::ViewMode view )
 | 
			
		||||
@ -439,6 +443,11 @@ void QgsDualView::previewExpressionChanged( const QString& expression )
 | 
			
		||||
  mLayerCache->layer()->setDisplayExpression( expression );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void QgsDualView::sortByPreviewExpression()
 | 
			
		||||
{
 | 
			
		||||
  setSortExpression( mFeatureList->displayExpression() );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void QgsDualView::featureFormAttributeChanged()
 | 
			
		||||
{
 | 
			
		||||
  mFeatureList->setCurrentFeatureEdited( true );
 | 
			
		||||
@ -470,6 +479,19 @@ void QgsDualView::setAttributeTableConfig( const QgsAttributeTableConfig& config
 | 
			
		||||
  mFilterModel->setAttributeTableConfig( config );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void QgsDualView::setSortExpression( const QString& sortExpression )
 | 
			
		||||
{
 | 
			
		||||
  if ( sortExpression.isNull() )
 | 
			
		||||
    mFilterModel->sort( -1 );
 | 
			
		||||
  else
 | 
			
		||||
    mFilterModel->sort( sortExpression );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QString QgsDualView::sortExpression() const
 | 
			
		||||
{
 | 
			
		||||
  return mFilterModel->sortExpression();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void QgsDualView::progress( int i, bool& cancel )
 | 
			
		||||
{
 | 
			
		||||
  if ( !mProgressDlg )
 | 
			
		||||
 | 
			
		||||
@ -180,6 +180,16 @@ class GUI_EXPORT QgsDualView : public QStackedWidget, private Ui::QgsDualViewBas
 | 
			
		||||
     */
 | 
			
		||||
    void setAttributeTableConfig( const QgsAttributeTableConfig& config );
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Set the expression used for sorting the table and feature list.
 | 
			
		||||
     */
 | 
			
		||||
    void setSortExpression( const QString& sortExpression );
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the expression used for sorting the table and feature list.
 | 
			
		||||
     */
 | 
			
		||||
    QString sortExpression() const;
 | 
			
		||||
 | 
			
		||||
  protected:
 | 
			
		||||
    /**
 | 
			
		||||
     * Initializes widgets which depend on the attributes of this layer
 | 
			
		||||
@ -240,6 +250,8 @@ class GUI_EXPORT QgsDualView : public QStackedWidget, private Ui::QgsDualViewBas
 | 
			
		||||
 | 
			
		||||
    void previewExpressionChanged( const QString& expression );
 | 
			
		||||
 | 
			
		||||
    void sortByPreviewExpression();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Will be called whenever the currently shown feature form changes.
 | 
			
		||||
     * Will forward this signal to the feature list to visually represent
 | 
			
		||||
 | 
			
		||||
@ -14,7 +14,16 @@
 | 
			
		||||
   <string>Attribute Table</string>
 | 
			
		||||
  </property>
 | 
			
		||||
  <layout class="QGridLayout" name="gridLayout">
 | 
			
		||||
   <property name="margin">
 | 
			
		||||
   <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>
 | 
			
		||||
   <property name="spacing">
 | 
			
		||||
@ -566,6 +575,29 @@
 | 
			
		||||
       </property>
 | 
			
		||||
      </widget>
 | 
			
		||||
     </item>
 | 
			
		||||
     <item>
 | 
			
		||||
      <widget class="QToolButton" name="mSortButton">
 | 
			
		||||
       <property name="toolTip">
 | 
			
		||||
        <string>Control the sort order</string>
 | 
			
		||||
       </property>
 | 
			
		||||
       <property name="text">
 | 
			
		||||
        <string>...</string>
 | 
			
		||||
       </property>
 | 
			
		||||
       <property name="icon">
 | 
			
		||||
        <iconset resource="../../images/images.qrc">
 | 
			
		||||
         <normaloff>:/images/themes/default/sort.svg</normaloff>:/images/themes/default/sort.svg</iconset>
 | 
			
		||||
       </property>
 | 
			
		||||
       <property name="iconSize">
 | 
			
		||||
        <size>
 | 
			
		||||
         <width>18</width>
 | 
			
		||||
         <height>18</height>
 | 
			
		||||
        </size>
 | 
			
		||||
       </property>
 | 
			
		||||
       <property name="autoRaise">
 | 
			
		||||
        <bool>true</bool>
 | 
			
		||||
       </property>
 | 
			
		||||
      </widget>
 | 
			
		||||
     </item>
 | 
			
		||||
     <item>
 | 
			
		||||
      <spacer name="horizontalSpacer">
 | 
			
		||||
       <property name="orientation">
 | 
			
		||||
@ -670,7 +702,16 @@
 | 
			
		||||
        <property name="sizeConstraint">
 | 
			
		||||
         <enum>QLayout::SetDefaultConstraint</enum>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="margin">
 | 
			
		||||
        <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 row="0" column="0">
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user