QGIS API Documentation  3.12.1-BucureČ™ti (121cc00ff0)
qgslayoutattributeselectiondialog.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgslayoutattributeselectiondialog.cpp
3  -------------------------------------
4  begin : November 2017
5  copyright : (C) 2017 by Nyall Dawson
6  email : nyall dot dawson at gmail dot com
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
20 #include "qgsvectorlayer.h"
22 #include "qgsdoublespinbox.h"
23 #include "qgssettings.h"
24 #include "qgsgui.h"
25 #include "qgslayouttablecolumn.h"
26 #include "qgshelp.h"
27 
28 #include <QCheckBox>
29 #include <QDialogButtonBox>
30 #include <QGridLayout>
31 #include <QLabel>
32 #include <QLineEdit>
33 #include <QPushButton>
34 #include <QSpinBox>
35 #include <QSortFilterProxyModel>
36 
37 
38 
39 //QgsLayoutAttributeTableColumnModel
40 
42  : QAbstractTableModel( parent )
43  , mTable( table )
44 {
45 }
46 
47 QModelIndex QgsLayoutAttributeTableColumnModel::index( int row, int column, const QModelIndex &parent ) const
48 {
49  if ( hasIndex( row, column, parent ) )
50  {
51  if ( ( mTable->columns() )[row] )
52  {
53  return createIndex( row, column, ( mTable->columns() )[row] );
54  }
55  }
56  return QModelIndex();
57 }
58 
59 QModelIndex QgsLayoutAttributeTableColumnModel::parent( const QModelIndex &child ) const
60 {
61  Q_UNUSED( child )
62  return QModelIndex();
63 }
64 
66 {
67  if ( parent.isValid() )
68  return 0;
69 
70  return mTable->columns().length();
71 }
72 
74 {
75  Q_UNUSED( parent )
76  return 4;
77 }
78 
79 QVariant QgsLayoutAttributeTableColumnModel::data( const QModelIndex &index, int role ) const
80 {
81  if ( !index.isValid() ||
82  ( role != Qt::DisplayRole && role != Qt::EditRole && role != Qt::UserRole ) )
83  {
84  return QVariant();
85  }
86 
87  if ( index.row() >= mTable->columns().length() )
88  {
89  return QVariant();
90  }
91 
92  //get column for index
93  QgsLayoutTableColumn *column = columnFromIndex( index );
94  if ( !column )
95  {
96  return QVariant();
97  }
98 
99  if ( role == Qt::UserRole )
100  {
101  //user role stores reference in column object
102  return qVariantFromValue( column );
103  }
104 
105  switch ( index.column() )
106  {
107  case 0:
108  return column->attribute();
109  case 1:
110  return column->heading();
111  case 2:
112  {
113  if ( role == Qt::DisplayRole )
114  {
115  switch ( column->hAlignment() )
116  {
117  case Qt::AlignHCenter:
118  switch ( column->vAlignment() )
119  {
120  case Qt::AlignTop:
121  return tr( "Top center" );
122  case Qt::AlignBottom:
123  return tr( "Bottom center" );
124  default:
125  return tr( "Middle center" );
126  }
127  case Qt::AlignRight:
128  switch ( column->vAlignment() )
129  {
130  case Qt::AlignTop:
131  return tr( "Top right" );
132  case Qt::AlignBottom:
133  return tr( "Bottom right" );
134  default:
135  return tr( "Middle right" );
136  }
137  case Qt::AlignLeft:
138  default:
139  switch ( column->vAlignment() )
140  {
141  case Qt::AlignTop:
142  return tr( "Top left" );
143  case Qt::AlignBottom:
144  return tr( "Bottom left" );
145  default:
146  return tr( "Middle left" );
147  }
148  }
149  }
150  else
151  {
152  //edit role
153  return int( column->hAlignment() | column->vAlignment() );
154  }
155  }
156  case 3:
157  {
158  if ( role == Qt::DisplayRole )
159  {
160  return column->width() <= 0 ? tr( "Automatic" ) : QString( tr( "%1 mm" ) ).arg( column->width(), 0, 'f', 2 );
161  }
162  else
163  {
164  //edit role
165  return column->width();
166  }
167  }
168  default:
169  return QVariant();
170  }
171 
172 }
173 
174 QVariant QgsLayoutAttributeTableColumnModel::headerData( int section, Qt::Orientation orientation, int role ) const
175 {
176  if ( !mTable )
177  {
178  return QVariant();
179  }
180 
181  if ( role == Qt::DisplayRole )
182  {
183  if ( orientation == Qt::Vertical ) //row
184  {
185  return QVariant( section );
186  }
187  else
188  {
189  switch ( section )
190  {
191  case 0:
192  return QVariant( tr( "Attribute" ) );
193 
194  case 1:
195  return QVariant( tr( "Heading" ) );
196 
197  case 2:
198  return QVariant( tr( "Alignment" ) );
199 
200  case 3:
201  return QVariant( tr( "Width" ) );
202 
203  default:
204  return QVariant();
205  }
206  }
207  }
208  else
209  {
210  return QVariant();
211  }
212 }
213 
214 bool QgsLayoutAttributeTableColumnModel::setData( const QModelIndex &index, const QVariant &value, int role )
215 {
216  if ( !index.isValid() || role != Qt::EditRole || !mTable )
217  {
218  return false;
219  }
220  if ( index.row() >= mTable->columns().length() )
221  {
222  return false;
223  }
224 
225  //get column for index
226  QgsLayoutTableColumn *column = columnFromIndex( index );
227  if ( !column )
228  {
229  return false;
230  }
231 
232  switch ( index.column() )
233  {
234  case 0:
235  // also update column's heading, if it hasn't been customized
236  if ( column->heading().isEmpty() || ( column->heading() == column->attribute() ) )
237  {
238  column->setHeading( value.toString() );
239  emit dataChanged( createIndex( index.row(), 1 ), createIndex( index.row(), 1 ) );
240  }
241  column->setAttribute( value.toString() );
242  emit dataChanged( index, index );
243  return true;
244  case 1:
245  column->setHeading( value.toString() );
246  emit dataChanged( index, index );
247  return true;
248  case 2:
249  column->setHAlignment( Qt::AlignmentFlag( value.toInt() & Qt::AlignHorizontal_Mask ) );
250  column->setVAlignment( Qt::AlignmentFlag( value.toInt() & Qt::AlignVertical_Mask ) );
251  emit dataChanged( index, index );
252  return true;
253  case 3:
254  column->setWidth( value.toDouble() );
255  emit dataChanged( index, index );
256  return true;
257  default:
258  break;
259  }
260 
261  return false;
262 }
263 
264 Qt::ItemFlags QgsLayoutAttributeTableColumnModel::flags( const QModelIndex &index ) const
265 {
266  Qt::ItemFlags flags = QAbstractTableModel::flags( index );
267 
268  if ( index.isValid() )
269  {
270  return flags | Qt::ItemIsEditable;
271  }
272  else
273  {
274  return flags;
275  }
276 }
277 
278 bool QgsLayoutAttributeTableColumnModel::removeRows( int row, int count, const QModelIndex &parent )
279 {
280  Q_UNUSED( parent )
281 
282  int maxRow = std::min( row + count - 1, mTable->columns().length() - 1 );
283  beginRemoveRows( QModelIndex(), row, maxRow );
284  //move backwards through rows, removing each corresponding QgsComposerTableColumn
285  for ( int i = maxRow; i >= row; --i )
286  {
287  delete ( mTable->columns() )[i];
288  mTable->columns().removeAt( i );
289  }
290  endRemoveRows();
291  return true;
292 }
293 
294 bool QgsLayoutAttributeTableColumnModel::insertRows( int row, int count, const QModelIndex &parent )
295 {
296  Q_UNUSED( parent )
297  beginInsertRows( QModelIndex(), row, row + count - 1 );
298  //create new QgsComposerTableColumns for each inserted row
299  for ( int i = row; i < row + count; ++i )
300  {
302  mTable->columns().insert( i, col );
303  }
304  endInsertRows();
305  return true;
306 }
307 
309 {
310  if ( ( direction == ShiftUp && row <= 0 ) ||
311  ( direction == ShiftDown && row >= rowCount() - 1 ) )
312  {
313  //row is already at top/bottom
314  return false;
315  }
316 
317  //we shift a row by removing the next row up/down, then reinserting it before/after the target row
318  int swapWithRow = direction == ShiftUp ? row - 1 : row + 1;
319 
320  //remove row
321  beginRemoveRows( QModelIndex(), swapWithRow, swapWithRow );
322  QgsLayoutTableColumn *temp = mTable->columns().takeAt( swapWithRow );
323  endRemoveRows();
324 
325  //insert row
326  beginInsertRows( QModelIndex(), row, row );
327  mTable->columns().insert( row, temp );
328  endInsertRows();
329 
330  return true;
331 }
332 
334 {
335  beginResetModel();
336  mTable->resetColumns();
337  endResetModel();
338 }
339 
341 {
342  QgsLayoutTableColumn *column = static_cast<QgsLayoutTableColumn *>( index.internalPointer() );
343  return column;
344 }
345 
347 {
348  if ( !mTable )
349  {
350  return QModelIndex();
351  }
352 
353  int r = mTable->columns().indexOf( column );
354 
355  QModelIndex idx = index( r, 0, QModelIndex() );
356  if ( idx.isValid() )
357  {
358  return idx;
359  }
360 
361  return QModelIndex();
362 }
363 
365 {
366  if ( !column || !mTable )
367  {
368  return;
369  }
370 
371  //find current highest sort by rank
372  int highestRank = 0;
373  for ( auto columnIt = mTable->columns().constBegin(); columnIt != mTable->columns().constEnd(); ++columnIt )
374  {
375  highestRank = std::max( highestRank, ( *columnIt )->sortByRank() );
376  }
377 
378  column->setSortByRank( highestRank + 1 );
379  column->setSortOrder( order );
380 
381  QModelIndex idx = indexFromColumn( column );
382  emit dataChanged( idx, idx );
383 }
384 
386 {
387  if ( !mTable || !column )
388  {
389  return;
390  }
391 
392  column->setSortByRank( 0 );
393  QModelIndex idx = indexFromColumn( column );
394  emit dataChanged( idx, idx );
395 }
396 
397 static bool columnsBySortRank( QgsLayoutTableColumn *a, QgsLayoutTableColumn *b )
398 {
399  return a->sortByRank() < b->sortByRank();
400 }
401 
403 {
404  if ( !mTable || !column )
405  {
406  return false;
407  }
408  if ( ( direction == ShiftUp && column->sortByRank() <= 1 )
409  || ( direction == ShiftDown && column->sortByRank() <= 0 ) )
410  {
411  //already at start/end of list or not being used for sort
412  return false;
413  }
414 
415  //find column before this one in sort order
416  QVector<QgsLayoutTableColumn *> sortedColumns;
417  const QgsLayoutTableColumns &columns = mTable->columns();
418  for ( QgsLayoutTableColumn *currentColumn : columns )
419  {
420  if ( currentColumn->sortByRank() > 0 )
421  {
422  sortedColumns.append( currentColumn );
423  }
424  }
425  std::stable_sort( sortedColumns.begin(), sortedColumns.end(), columnsBySortRank );
426  int columnPos = sortedColumns.indexOf( column );
427 
428  if ( ( columnPos == 0 && direction == ShiftUp )
429  || ( ( columnPos == sortedColumns.length() - 1 ) && direction == ShiftDown ) )
430  {
431  //column already at start/end
432  return false;
433  }
434 
435  QgsLayoutTableColumn *swapColumn = direction == ShiftUp ?
436  sortedColumns[ columnPos - 1]
437  : sortedColumns[ columnPos + 1];
438  QModelIndex idx = indexFromColumn( column );
439  QModelIndex idxSwap = indexFromColumn( swapColumn );
440 
441  //now swap sort ranks
442  int oldSortRank = column->sortByRank();
443  column->setSortByRank( swapColumn->sortByRank() );
444  emit dataChanged( idx, idx );
445 
446  swapColumn->setSortByRank( oldSortRank );
447  emit dataChanged( idxSwap, idxSwap );
448 
449  return true;
450 }
451 
452 
453 
454 //QgsLayoutTableSortColumnsProxyModel
455 
457  : QSortFilterProxyModel( parent )
458  , mTable( table )
459  , mFilterType( filterType )
460 {
461  setDynamicSortFilter( true );
462 }
463 
464 bool QgsLayoutTableSortColumnsProxyModel::filterAcceptsRow( int source_row, const QModelIndex &source_parent ) const
465 {
466  //get QgsComposerTableColumn corresponding to row
467  QModelIndex index = sourceModel()->index( source_row, 0, source_parent );
468  QgsLayoutTableColumn *column = columnFromSourceIndex( index );
469 
470  if ( !column )
471  {
472  return false;
473  }
474 
475  if ( ( column->sortByRank() > 0 && mFilterType == ShowSortedColumns )
476  || ( column->sortByRank() <= 0 && mFilterType == ShowUnsortedColumns ) )
477  {
478  //column matches filter type
479  return true;
480  }
481  else
482  {
483  return false;
484  }
485 }
486 
488 {
489  //get column corresponding to an index from the proxy
490  QModelIndex sourceIndex = mapToSource( index );
491  return columnFromSourceIndex( sourceIndex );
492 }
493 
495 {
496  //get column corresponding to an index from the source model
497  QVariant columnAsVariant = sourceModel()->data( sourceIndex, Qt::UserRole );
498  QgsLayoutTableColumn *column = qobject_cast<QgsLayoutTableColumn *>( columnAsVariant.value<QObject *>() );
499  return column;
500 }
501 
502 bool QgsLayoutTableSortColumnsProxyModel::lessThan( const QModelIndex &left, const QModelIndex &right ) const
503 {
504  QgsLayoutTableColumn *column1 = columnFromSourceIndex( left );
505  QgsLayoutTableColumn *column2 = columnFromSourceIndex( right );
506  if ( !column1 )
507  {
508  return false;
509  }
510  if ( !column2 )
511  {
512  return true;
513  }
514  return column1->sortByRank() < column2->sortByRank();
515 }
516 
517 int QgsLayoutTableSortColumnsProxyModel::columnCount( const QModelIndex &parent ) const
518 {
519  Q_UNUSED( parent )
520  return 2;
521 }
522 
523 QVariant QgsLayoutTableSortColumnsProxyModel::data( const QModelIndex &index, int role ) const
524 {
525  if ( ( role != Qt::DisplayRole && role != Qt::EditRole ) || !index.isValid() )
526  {
527  return QVariant();
528  }
529 
530  QgsLayoutTableColumn *column = columnFromIndex( index );
531  if ( !column )
532  {
533  return QVariant();
534  }
535 
536  switch ( index.column() )
537  {
538  case 0:
539  return column->attribute();
540  case 1:
541  if ( role == Qt::DisplayRole )
542  {
543  switch ( column->sortOrder() )
544  {
545  case Qt::DescendingOrder:
546  return tr( "Descending" );
547  case Qt::AscendingOrder:
548  default:
549  return tr( "Ascending" );
550  }
551  }
552  else
553  {
554  //edit role
555  return column->sortOrder();
556  }
557 
558  default:
559  return QVariant();
560  }
561 }
562 
563 QVariant QgsLayoutTableSortColumnsProxyModel::headerData( int section, Qt::Orientation orientation, int role ) const
564 {
565  if ( !mTable )
566  {
567  return QVariant();
568  }
569 
570  if ( role == Qt::DisplayRole )
571  {
572  if ( orientation == Qt::Vertical ) //row
573  {
574  return QVariant( section );
575  }
576  else
577  {
578  switch ( section )
579  {
580  case 0:
581  return QVariant( tr( "Attribute" ) );
582 
583  case 1:
584  return QVariant( tr( "Sort Order" ) );
585 
586  default:
587  return QVariant();
588  }
589  }
590  }
591  else
592  {
593  return QVariant();
594  }
595 }
596 
597 Qt::ItemFlags QgsLayoutTableSortColumnsProxyModel::flags( const QModelIndex &index ) const
598 {
599  Qt::ItemFlags flags = QSortFilterProxyModel::flags( index );
600 
601  if ( index.column() == 1 )
602  {
603  //only sort order is editable
604  flags |= Qt::ItemIsEditable;
605  }
606 
607  return flags;
608 }
609 
610 bool QgsLayoutTableSortColumnsProxyModel::setData( const QModelIndex &index, const QVariant &value, int role )
611 {
612  if ( !index.isValid() || role != Qt::EditRole )
613  return false;
614 
615  if ( !mTable )
616  {
617  return false;
618  }
619 
620  QgsLayoutTableColumn *column = columnFromIndex( index );
621  if ( !column )
622  {
623  return false;
624  }
625 
626  if ( index.column() == 1 )
627  {
628  column->setSortOrder( static_cast< Qt::SortOrder >( value.toInt() ) );
629  emit dataChanged( index, index );
630  return true;
631  }
632 
633  return false;
634 }
635 
637 {
638  QModelIndex proxyIndex = index( row, 0 );
639  return columnFromIndex( proxyIndex );
640 }
641 
643 {
644  invalidate();
645 }
646 
647 
648 // QgsLayoutColumnAlignmentDelegate
649 
650 QgsLayoutColumnAlignmentDelegate::QgsLayoutColumnAlignmentDelegate( QObject *parent ) : QItemDelegate( parent )
651 {
652 
653 }
654 
655 QWidget *QgsLayoutColumnAlignmentDelegate::createEditor( QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index ) const
656 {
657  Q_UNUSED( option )
658  Q_UNUSED( index )
659 
660  //create a combo box showing alignment options
661  QComboBox *comboBox = new QComboBox( parent );
662 
663  comboBox->addItem( tr( "Top left" ), int( Qt::AlignTop | Qt::AlignLeft ) );
664  comboBox->addItem( tr( "Top center" ), int( Qt::AlignTop | Qt::AlignHCenter ) );
665  comboBox->addItem( tr( "Top right" ), int( Qt::AlignTop | Qt::AlignRight ) );
666  comboBox->addItem( tr( "Middle left" ), int( Qt::AlignVCenter | Qt::AlignLeft ) );
667  comboBox->addItem( tr( "Middle center" ), int( Qt::AlignVCenter | Qt::AlignHCenter ) );
668  comboBox->addItem( tr( "Middle right" ), int( Qt::AlignVCenter | Qt::AlignRight ) );
669  comboBox->addItem( tr( "Bottom left" ), int( Qt::AlignBottom | Qt::AlignLeft ) );
670  comboBox->addItem( tr( "Bottom center" ), int( Qt::AlignBottom | Qt::AlignHCenter ) );
671  comboBox->addItem( tr( "Bottom right" ), int( Qt::AlignBottom | Qt::AlignRight ) );
672 
673  Qt::AlignmentFlag alignment = ( Qt::AlignmentFlag )index.model()->data( index, Qt::EditRole ).toInt();
674  comboBox->setCurrentIndex( comboBox->findData( alignment ) );
675 
676  return comboBox;
677 }
678 
679 void QgsLayoutColumnAlignmentDelegate::setEditorData( QWidget *editor, const QModelIndex &index ) const
680 {
681  Qt::AlignmentFlag alignment = ( Qt::AlignmentFlag )index.model()->data( index, Qt::EditRole ).toInt();
682 
683  //set the value for the combobox
684  QComboBox *comboBox = static_cast<QComboBox *>( editor );
685  comboBox->setCurrentIndex( comboBox->findData( alignment ) );
686 }
687 
688 void QgsLayoutColumnAlignmentDelegate::setModelData( QWidget *editor, QAbstractItemModel *model, const QModelIndex &index ) const
689 {
690  QComboBox *comboBox = static_cast<QComboBox *>( editor );
691  Qt::AlignmentFlag alignment = ( Qt::AlignmentFlag ) comboBox->currentData().toInt();
692  model->setData( index, alignment, Qt::EditRole );
693 }
694 
695 void QgsLayoutColumnAlignmentDelegate::updateEditorGeometry( QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index ) const
696 {
697  Q_UNUSED( index )
698  editor->setGeometry( option.rect );
699 }
700 
701 
702 // QgsLayoutColumnSourceDelegate
703 
705  : QItemDelegate( parent )
706  , mVectorLayer( vlayer )
707  , mLayoutObject( layoutObject )
708 {
709 
710 }
711 
712 QgsExpressionContext QgsLayoutColumnSourceDelegate::createExpressionContext() const
713 {
714  if ( !mLayoutObject )
715  {
716  return QgsExpressionContext();
717  }
718 
719  QgsExpressionContext expContext = mLayoutObject->createExpressionContext();
720  expContext.lastScope()->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "row_number" ), 1, true ) );
721  expContext.setHighlightedVariables( QStringList() << QStringLiteral( "row_number" ) );
722  return expContext;
723 }
724 
725 QWidget *QgsLayoutColumnSourceDelegate::createEditor( QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index ) const
726 {
727  Q_UNUSED( option )
728  Q_UNUSED( index )
729 
730  QgsFieldExpressionWidget *fieldExpression = new QgsFieldExpressionWidget( parent );
731  fieldExpression->setLayer( mVectorLayer );
732  fieldExpression->registerExpressionContextGenerator( this );
733 
734  //listen out for field changes
735  connect( fieldExpression, static_cast < void ( QgsFieldExpressionWidget::* )( const QString & ) >( &QgsFieldExpressionWidget::fieldChanged ), this, [ = ] { const_cast< QgsLayoutColumnSourceDelegate * >( this )->commitAndCloseEditor(); } );
736  return fieldExpression;
737 }
738 
739 void QgsLayoutColumnSourceDelegate::setEditorData( QWidget *editor, const QModelIndex &index ) const
740 {
741  QString field = index.model()->data( index, Qt::EditRole ).toString();
742 
743  //set the value for the field combobox
744  QgsFieldExpressionWidget *fieldExpression = static_cast<QgsFieldExpressionWidget *>( editor );
745  fieldExpression->setField( field );
746 }
747 
748 void QgsLayoutColumnSourceDelegate::setModelData( QWidget *editor, QAbstractItemModel *model, const QModelIndex &index ) const
749 {
750  QgsFieldExpressionWidget *fieldExpression = static_cast<QgsFieldExpressionWidget *>( editor );
751  QString field = fieldExpression->currentField();
752 
753  model->setData( index, field, Qt::EditRole );
754 }
755 
756 void QgsLayoutColumnSourceDelegate::updateEditorGeometry( QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index ) const
757 {
758  Q_UNUSED( index )
759  editor->setGeometry( option.rect );
760 }
761 
762 void QgsLayoutColumnSourceDelegate::commitAndCloseEditor()
763 {
764  QgsFieldExpressionWidget *fieldExpression = qobject_cast<QgsFieldExpressionWidget *>( sender() );
765  emit commitData( fieldExpression );
766 }
767 
768 
769 // QgsLayoutColumnSortOrderDelegate
770 
771 QgsLayoutColumnSortOrderDelegate::QgsLayoutColumnSortOrderDelegate( QObject *parent ) : QItemDelegate( parent )
772 {
773 
774 }
775 
776 QWidget *QgsLayoutColumnSortOrderDelegate::createEditor( QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index ) const
777 {
778  Q_UNUSED( option )
779  Q_UNUSED( index )
780 
781  QComboBox *comboBox = new QComboBox( parent );
782  QStringList sortOrders;
783  sortOrders << tr( "Ascending" ) << tr( "Descending" );
784  comboBox->addItems( sortOrders );
785  return comboBox;
786 }
787 
788 void QgsLayoutColumnSortOrderDelegate::setEditorData( QWidget *editor, const QModelIndex &index ) const
789 {
790  Qt::SortOrder order = ( Qt::SortOrder )index.model()->data( index, Qt::EditRole ).toInt();
791 
792  //set the value for the combobox
793  QComboBox *comboBox = static_cast<QComboBox *>( editor );
794  switch ( order )
795  {
796  case Qt::DescendingOrder:
797  comboBox->setCurrentIndex( 1 );
798  break;
799  case Qt::AscendingOrder:
800  default:
801  comboBox->setCurrentIndex( 0 );
802  break;
803  }
804 }
805 
806 void QgsLayoutColumnSortOrderDelegate::setModelData( QWidget *editor, QAbstractItemModel *model, const QModelIndex &index ) const
807 {
808  QComboBox *comboBox = static_cast<QComboBox *>( editor );
809  int value = comboBox->currentIndex();
810  Qt::SortOrder order;
811  switch ( value )
812  {
813  case 1:
814  order = Qt::DescendingOrder;
815  break;
816  case 0:
817  default:
818  order = Qt::AscendingOrder;
819  break;
820  }
821 
822  model->setData( index, order, Qt::EditRole );
823 }
824 
825 void QgsLayoutColumnSortOrderDelegate::updateEditorGeometry( QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index ) const
826 {
827  Q_UNUSED( index )
828  editor->setGeometry( option.rect );
829 }
830 
831 
832 //
833 // QgsLayoutColumnWidthDelegate
834 //
835 
837  : QItemDelegate( parent )
838 {
839 
840 }
841 
842 QWidget *QgsLayoutColumnWidthDelegate::createEditor( QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index ) const
843 {
844  Q_UNUSED( index )
845  Q_UNUSED( option )
846  QgsDoubleSpinBox *editor = new QgsDoubleSpinBox( parent );
847  editor->setMinimum( 0 );
848  editor->setMaximum( 1000 );
849  editor->setDecimals( 2 );
850  editor->setSuffix( tr( " mm" ) );
851  editor->setSpecialValueText( tr( "Automatic" ) );
852  editor->setShowClearButton( true );
853  return editor;
854 }
855 
856 void QgsLayoutColumnWidthDelegate::setEditorData( QWidget *editor, const QModelIndex &index ) const
857 {
858  int value = index.model()->data( index, Qt::EditRole ).toInt();
859 
860  QgsDoubleSpinBox *spinBox = static_cast<QgsDoubleSpinBox *>( editor );
861  spinBox->setValue( value );
862 }
863 
864 void QgsLayoutColumnWidthDelegate::setModelData( QWidget *editor, QAbstractItemModel *model, const QModelIndex &index ) const
865 {
866  QgsDoubleSpinBox *spinBox = static_cast<QgsDoubleSpinBox *>( editor );
867  spinBox->interpretText();
868  int value = spinBox->value();
869 
870  model->setData( index, value, Qt::EditRole );
871 }
872 
873 void QgsLayoutColumnWidthDelegate::updateEditorGeometry( QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index ) const
874 {
875  Q_UNUSED( index )
876  editor->setGeometry( option.rect );
877 }
878 
879 
880 // QgsLayoutAttributeSelectionDialog
881 
883  QWidget *parent, Qt::WindowFlags f )
884  : QDialog( parent, f )
885  , mTable( table )
886  , mVectorLayer( vLayer )
887 
888 {
889  setupUi( this );
891 
892  connect( mRemoveColumnPushButton, &QPushButton::clicked, this, &QgsLayoutAttributeSelectionDialog::mRemoveColumnPushButton_clicked );
893  connect( mAddColumnPushButton, &QPushButton::clicked, this, &QgsLayoutAttributeSelectionDialog::mAddColumnPushButton_clicked );
894  connect( mColumnUpPushButton, &QPushButton::clicked, this, &QgsLayoutAttributeSelectionDialog::mColumnUpPushButton_clicked );
895  connect( mColumnDownPushButton, &QPushButton::clicked, this, &QgsLayoutAttributeSelectionDialog::mColumnDownPushButton_clicked );
896  connect( mResetColumnsPushButton, &QPushButton::clicked, this, &QgsLayoutAttributeSelectionDialog::mResetColumnsPushButton_clicked );
897  connect( mClearColumnsPushButton, &QPushButton::clicked, this, &QgsLayoutAttributeSelectionDialog::mClearColumnsPushButton_clicked );
898  connect( mAddSortColumnPushButton, &QPushButton::clicked, this, &QgsLayoutAttributeSelectionDialog::mAddSortColumnPushButton_clicked );
899  connect( mRemoveSortColumnPushButton, &QPushButton::clicked, this, &QgsLayoutAttributeSelectionDialog::mRemoveSortColumnPushButton_clicked );
900  connect( mSortColumnUpPushButton, &QPushButton::clicked, this, &QgsLayoutAttributeSelectionDialog::mSortColumnUpPushButton_clicked );
901  connect( mSortColumnDownPushButton, &QPushButton::clicked, this, &QgsLayoutAttributeSelectionDialog::mSortColumnDownPushButton_clicked );
902  connect( buttonBox, &QDialogButtonBox::helpRequested, this, &QgsLayoutAttributeSelectionDialog::showHelp );
903 
904  if ( mTable )
905  {
906  //set up models, views and delegates
907  mColumnModel = new QgsLayoutAttributeTableColumnModel( mTable, mColumnsTableView );
908  mColumnsTableView->setModel( mColumnModel );
909  mColumnsTableView->horizontalHeader()->setSectionResizeMode( QHeaderView::Stretch );
910 
911  mColumnSourceDelegate = new QgsLayoutColumnSourceDelegate( vLayer, mColumnsTableView, mTable );
912  mColumnsTableView->setItemDelegateForColumn( 0, mColumnSourceDelegate );
913  mColumnAlignmentDelegate = new QgsLayoutColumnAlignmentDelegate( mColumnsTableView );
914  mColumnsTableView->setItemDelegateForColumn( 2, mColumnAlignmentDelegate );
915  mColumnWidthDelegate = new QgsLayoutColumnWidthDelegate( mColumnsTableView );
916  mColumnsTableView->setItemDelegateForColumn( 3, mColumnWidthDelegate );
917 
918  mAvailableSortProxyModel = new QgsLayoutTableSortColumnsProxyModel( mTable, QgsLayoutTableSortColumnsProxyModel::ShowUnsortedColumns, mSortColumnComboBox );
919  mAvailableSortProxyModel->setSourceModel( mColumnModel );
920  mSortColumnComboBox->setModel( mAvailableSortProxyModel );
921  mSortColumnComboBox->setModelColumn( 0 );
922 
923  mColumnSortOrderDelegate = new QgsLayoutColumnSortOrderDelegate( mSortColumnTableView );
924  mSortColumnTableView->setItemDelegateForColumn( 1, mColumnSortOrderDelegate );
925 
926  mSortedProxyModel = new QgsLayoutTableSortColumnsProxyModel( mTable, QgsLayoutTableSortColumnsProxyModel::ShowSortedColumns, mSortColumnTableView );
927  mSortedProxyModel->setSourceModel( mColumnModel );
928  mSortedProxyModel->sort( 0, Qt::AscendingOrder );
929  mSortColumnTableView->setSortingEnabled( false );
930  mSortColumnTableView->setModel( mSortedProxyModel );
931  mSortColumnTableView->horizontalHeader()->setSectionResizeMode( QHeaderView::Stretch );
932  }
933 
934  mOrderComboBox->insertItem( 0, tr( "Ascending" ) );
935  mOrderComboBox->insertItem( 1, tr( "Descending" ) );
936 }
937 
938 void QgsLayoutAttributeSelectionDialog::mRemoveColumnPushButton_clicked()
939 {
940  //remove selected rows from model
941  QModelIndexList indexes = mColumnsTableView->selectionModel()->selectedRows();
942  int count = indexes.count();
943 
944  for ( int i = count; i > 0; --i )
945  mColumnModel->removeRow( indexes.at( i - 1 ).row(), QModelIndex() );
946 }
947 
948 void QgsLayoutAttributeSelectionDialog::mAddColumnPushButton_clicked()
949 {
950  //add a new row to the model
951  mColumnModel->insertRow( mColumnModel->rowCount() );
952 }
953 
954 void QgsLayoutAttributeSelectionDialog::mColumnUpPushButton_clicked()
955 {
956  //move selected row up
957 
958  QModelIndexList indexes = mColumnsTableView->selectionModel()->selectedRows();
959  int count = indexes.count();
960 
961  std::reverse( indexes.begin(), indexes.end() );
962  for ( int i = count; i > 0; --i )
963  mColumnModel->moveRow( indexes.at( i - 1 ).row(), QgsLayoutAttributeTableColumnModel::ShiftUp );
964 }
965 
966 void QgsLayoutAttributeSelectionDialog::mColumnDownPushButton_clicked()
967 {
968  //move selected row down
969  QModelIndexList indexes = mColumnsTableView->selectionModel()->selectedRows();
970  int count = indexes.count();
971 
972  for ( int i = count; i > 0; --i )
973  mColumnModel->moveRow( indexes.at( i - 1 ).row(), QgsLayoutAttributeTableColumnModel::ShiftDown );
974 }
975 
976 void QgsLayoutAttributeSelectionDialog::mResetColumnsPushButton_clicked()
977 {
978  //reset columns to match vector layer's fields
979  mColumnModel->resetToLayer();
980  mSortColumnComboBox->setCurrentIndex( 0 );
981 }
982 
983 void QgsLayoutAttributeSelectionDialog::mClearColumnsPushButton_clicked()
984 {
985  //remove all columns
986  mColumnModel->removeRows( 0, mColumnModel->rowCount() );
987  mSortColumnComboBox->setCurrentIndex( 0 );
988 }
989 
990 void QgsLayoutAttributeSelectionDialog::mAddSortColumnPushButton_clicked()
991 {
992  //add column to sort order widget
993  QgsLayoutTableColumn *column = mAvailableSortProxyModel->columnFromRow( mSortColumnComboBox->currentIndex() );
994  if ( ! column )
995  {
996  return;
997  }
998 
999  mColumnModel->setColumnAsSorted( column, mOrderComboBox->currentIndex() == 0 ? Qt::AscendingOrder : Qt::DescendingOrder );
1000 
1001  //required so that rows can be reordered if initially no rows were shown in the table view
1002  mSortedProxyModel->resetFilter();
1003 }
1004 
1005 void QgsLayoutAttributeSelectionDialog::mRemoveSortColumnPushButton_clicked()
1006 {
1007  //remove selected rows from sort order widget
1008  QItemSelection sortSelection( mSortColumnTableView->selectionModel()->selection() );
1009  if ( sortSelection.length() < 1 )
1010  {
1011  return;
1012  }
1013  QModelIndex selectedIndex = sortSelection.indexes().at( 0 );
1014  int rowToRemove = selectedIndex.row();
1015 
1016  //find corresponding column
1017  QgsLayoutTableColumn *column = nullptr;
1018  column = mSortedProxyModel->columnFromIndex( selectedIndex );
1019 
1020  if ( !column )
1021  {
1022  return;
1023  }
1024 
1025  //set column as unsorted
1026  mColumnModel->setColumnAsUnsorted( column );
1027  //set next row as selected
1028  mSortColumnTableView->selectRow( rowToRemove );
1029 }
1030 
1031 void QgsLayoutAttributeSelectionDialog::showHelp()
1032 {
1033  QgsHelp::openHelp( QStringLiteral( "print_composer/composer_items/composer_attribute_table.html" ) );
1034 }
1035 
1036 void QgsLayoutAttributeSelectionDialog::mSortColumnUpPushButton_clicked()
1037 {
1038  //find selected row
1039  QItemSelection sortSelection( mSortColumnTableView->selectionModel()->selection() );
1040  if ( sortSelection.length() < 1 )
1041  {
1042  return;
1043  }
1044  QModelIndex selectedIndex = sortSelection.indexes().at( 0 );
1045 
1046  QgsLayoutTableColumn *column = mSortedProxyModel->columnFromIndex( selectedIndex );
1047 
1048  if ( !column )
1049  {
1050  return;
1051  }
1053 }
1054 
1055 void QgsLayoutAttributeSelectionDialog::mSortColumnDownPushButton_clicked()
1056 {
1057  //find selected row
1058  QItemSelection sortSelection( mSortColumnTableView->selectionModel()->selection() );
1059  if ( sortSelection.length() < 1 )
1060  {
1061  return;
1062  }
1063 
1064  QModelIndex selectedIndex = sortSelection.indexes().at( 0 );
1065 
1066  QgsLayoutTableColumn *column = mSortedProxyModel->columnFromIndex( selectedIndex );
1067 
1068  if ( !column )
1069  {
1070  return;
1071  }
1073 }
1074 
The QgsSpinBox is a spin box with a clear button that will set the value to the defined clear value...
bool moveRow(int row, ShiftDirection direction)
Moves the specified row up or down in the model.
QgsLayoutAttributeTableColumnModel(QgsLayoutItemAttributeTable *table, QObject *parent=nullptr)
Constructor for QgsLayoutAttributeTableColumnModel.
The QgsFieldExpressionWidget class reates a widget to choose fields and edit expressions It contains ...
QWidget * createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override
Single variable definition for use within a QgsExpressionContextScope.
bool moveColumnInSortRank(QgsLayoutTableColumn *column, ShiftDirection direction)
Moves a column up or down in the sort rank for the QgsLayoutItemAttributeTable.
void resetToLayer()
Resets the attribute table&#39;s columns to match the source layer&#39;s fields.
bool insertRows(int row, int count, const QModelIndex &parent=QModelIndex()) override
bool setData(const QModelIndex &index, const QVariant &value, int role=Qt::EditRole) override
QgsExpressionContext createExpressionContext() const override
Creates an expression context relating to the objects&#39; current state.
void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override
Qt::ItemFlags flags(const QModelIndex &index) const override
ColumnFilterType
Controls whether the proxy model shows sorted or unsorted columns.
QgsLayoutColumnWidthDelegate(QObject *parent=nullptr)
constructor
void setLayer(QgsMapLayer *layer)
Sets the layer used to display the fields and expression.
QgsLayoutColumnSortOrderDelegate(QObject *parent=nullptr)
constructor
QgsLayoutTableColumn * columnFromIndex(const QModelIndex &index) const
Returns the QgsLayoutTableColumn corresponding to an index in the proxy model.
QModelIndex indexFromColumn(QgsLayoutTableColumn *column)
Returns a QModelIndex corresponding to a QgsLayoutTableColumn in the model.
void setShowClearButton(bool showClearButton)
Sets whether the widget will show a clear button.
void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override
QVariant data(const QModelIndex &index, int role) const override
QVector< QgsLayoutTableColumn *> QgsLayoutTableColumns
List of column definitions for a QgsLayoutTable.
QgsLayoutTableColumn * columnFromIndex(const QModelIndex &index) const
Returns the QgsLayoutTableColumn corresponding to an index in the model.
void setHeading(const QString &heading)
Sets the heading for a column, which is the value displayed in the column&#39;s header cell...
void setSpecialValueText(const QString &txt)
Set the special-value text to be txt If set, the spin box will display this text instead of a numeric...
QWidget * createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override
QVariant data(const QModelIndex &index, int role) const override
bool lessThan(const QModelIndex &left, const QModelIndex &right) const override
void addVariable(const QgsExpressionContextScope::StaticVariable &variable)
Adds a variable into the context scope.
A model for displaying columns shown in a QgsLayoutAttributeTable.
Stores properties of a column for a QgsLayoutTable.
void setAttribute(const QString &attribute)
Sets the attribute name or expression used for the column&#39;s values.
void resetFilter()
Invalidates the current filter used by the proxy model.
A delegate for showing column alignment as a combo box.
QVariant headerData(int section, Qt::Orientation orientation, int role=Qt::DisplayRole) const override
void resetColumns()
Resets the attribute table&#39;s columns to match the vector layer&#39;s fields.
Qt::AlignmentFlag hAlignment() const
Returns the horizontal alignment for a column, which controls the alignment used for drawing column v...
QgsLayoutTableColumn * columnFromRow(int row)
Returns the QgsLayoutTableColumn corresponding to a row in the proxy model.
void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const override
Qt::AlignmentFlag vAlignment() const
Returns the vertical alignment for a column, which controls the alignment used for drawing column val...
int sortByRank() const
Returns the sort rank for the column.
A delegate for showing column attribute source as a QgsFieldExpressionWidget.
QgsExpressionContextScope * lastScope()
Returns the last scope added to the context.
int columnCount(const QModelIndex &parent=QModelIndex()) const override
QWidget * createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override
ShiftDirection
Controls whether a row/column is shifted up or down.
void setEditorData(QWidget *editor, const QModelIndex &index) const override
void setHAlignment(Qt::AlignmentFlag alignment)
Sets the horizontal alignment for a column, which controls the alignment used for drawing column valu...
void setSortByRank(int rank)
Sets the sort rank for the column.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
QWidget * createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override
void registerExpressionContextGenerator(const QgsExpressionContextGenerator *generator)
Register an expression context generator class that will be used to retrieve an expression context fo...
double width() const
Returns the width for the column in mm, or 0 if column width is automatically calculated.
A layout table subclass that displays attributes from a vector layer.
void setColumnAsUnsorted(QgsLayoutTableColumn *column)
Sets a specified column as an unsorted column in the QgsLayoutItemAttributeTable. ...
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const override
Allows for filtering QgsComposerAttributeTable columns by columns which are sorted or unsorted...
Qt::ItemFlags flags(const QModelIndex &index) const override
QVariant headerData(int section, Qt::Orientation orientation, int role=Qt::DisplayRole) const override
Qt::SortOrder sortOrder() const
Returns the sort order for the column.
QModelIndex index(int row, int column, const QModelIndex &parent) const override
A delegate for showing column sort order as a combo box.
QModelIndex parent(const QModelIndex &child) const override
void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override
void fieldChanged(const QString &fieldName)
Emitted when the currently selected field changes.
A delegate for showing column width as a spin box.
bool setData(const QModelIndex &index, const QVariant &value, int role=Qt::EditRole) override
int columnCount(const QModelIndex &parent=QModelIndex()) const override
QgsLayoutColumnAlignmentDelegate(QObject *parent=nullptr)
constructor
void setEditorData(QWidget *editor, const QModelIndex &index) const override
QgsLayoutAttributeSelectionDialog(QgsLayoutItemAttributeTable *table, QgsVectorLayer *vLayer, QWidget *parent=nullptr, Qt::WindowFlags f=nullptr)
constructor
void setWidth(const double width)
Sets the width for a column in mm.
QgsLayoutTableSortColumnsProxyModel(QgsLayoutItemAttributeTable *table, ColumnFilterType filterType, QObject *parent=nullptr)
Constructor for QgsLayoutTableSortColumnsProxyModel.
void setEditorData(QWidget *editor, const QModelIndex &index) const override
void setColumnAsSorted(QgsLayoutTableColumn *column, Qt::SortOrder order)
Sets a specified column as a sorted column in the QgsLayoutItemAttributeTable.
void setField(const QString &fieldName)
sets the current field or expression in the widget
QString attribute() const
Returns the attribute name or expression used for the column&#39;s values.
static void enableAutoGeometryRestore(QWidget *widget, const QString &key=QString())
Register the widget to allow its position to be automatically saved and restored when open and closed...
Definition: qgsgui.cpp:133
static void openHelp(const QString &key)
Opens help topic for the given help key using default system web browser.
Definition: qgshelp.cpp:36
bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override
A base class for objects which belong to a layout.
void setHighlightedVariables(const QStringList &variableNames)
Sets the list of variable names within the context intended to be highlighted to the user...
int rowCount(const QModelIndex &parent=QModelIndex()) const override
void setVAlignment(Qt::AlignmentFlag alignment)
Sets the vertical alignment for a column, which controls the alignment used for drawing column values...
QgsLayoutTableColumn * columnFromSourceIndex(const QModelIndex &sourceIndex) const
Returns the QgsLayoutTableColumn corresponding to an index from the source QgsLayoutItemAttributeTabl...
QgsLayoutColumnSourceDelegate(QgsVectorLayer *vlayer, QObject *parent=nullptr, const QgsLayoutObject *layoutObject=nullptr)
constructor
QgsLayoutTableColumns & columns()
Returns a reference to the list of QgsLayoutTableColumns shown in the table.
void setSortOrder(Qt::SortOrder order)
Sets the sort order for the column.
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const override
Represents a vector layer which manages a vector based data sets.
void setEditorData(QWidget *editor, const QModelIndex &index) const override
QString heading() const
Returns the heading for a column, which is the value displayed in the column&#39;s header cell...
bool removeRows(int row, int count, const QModelIndex &parent=QModelIndex()) override
QString currentField(bool *isExpression=nullptr, bool *isValid=nullptr) const
currentField returns the currently selected field or expression if allowed
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const override