Allow sorting attribute table by expression

This commit is contained in:
Matthias Kuhn 2016-04-08 19:18:20 +02:00
parent 11ee2fc809
commit d07d9edda6
17 changed files with 402 additions and 59 deletions

View File

@ -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>

View 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

View File

@ -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 );
};

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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 );

View File

@ -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();

View File

@ -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 )

View File

@ -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 )

View File

@ -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 )

View File

@ -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; }

View File

@ -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;

View File

@ -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

View File

@ -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 )

View File

@ -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

View File

@ -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">