QGIS API Documentation 3.34.0-Prizren (ffbdd678812)
Loading...
Searching...
No Matches
qgstableeditorwidget.cpp
Go to the documentation of this file.
1// This file is part of CppSheets.
2//
3// Copyright 2018 Patrick Flynn <[email protected]>
4//
5// CppSheets is free software: you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9//
10// CppSheets is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU General Public License for more details.
14//
15// You should have received a copy of the GNU General Public License
16// along with CppSheets. If not, see <https://www.gnu.org/licenses/>.
17
19#include "qgsnumericformat.h"
20#include <QStringList>
21#include <QKeyEvent>
22#include <QHeaderView>
23#include <QMenu>
24#include <QPlainTextEdit>
25
27 : QTableWidget( parent )
28{
29 mHeaderMenu = new QMenu( this );
30 setColumnCount( 0 );
31 setRowCount( 0 );
32 connect( this, &QgsTableEditorWidget::cellChanged, this, [ = ]
33 {
34 if ( !mBlockSignals )
35 emit tableChanged();
36 } );
37
38 horizontalHeader()->setContextMenuPolicy( Qt::CustomContextMenu );
39 connect( horizontalHeader(), &QWidget::customContextMenuRequested, this, [ = ]( const QPoint & point )
40 {
41 const int column = horizontalHeader()->logicalIndexAt( point.x() );
42
43 QSet< int > selectedColumns;
44 for ( const QModelIndex &index : selectedIndexes() )
45 {
46 selectedColumns.insert( index.column() );
47 }
48 int minCol = 0;
49 int maxCol = 0;
50 bool isConsecutive = collectConsecutiveColumnRange( selectedIndexes(), minCol, maxCol );
51
52 // this is modeled off Libreoffice calc!
53 if ( selectedIndexes().count() == 1 )
54 {
55 // select whole column
56 selectColumn( column );
57 isConsecutive = true;
58 }
59 else if ( !selectedColumns.contains( column ) )
60 {
61 // select whole column
62 selectColumn( column );
63 isConsecutive = true;
64 }
65
66 mHeaderMenu->clear();
67 if ( isConsecutive )
68 {
69 QAction *insertBefore = mHeaderMenu->addAction( selectedColumns.size() > 1 ? tr( "Insert %n Column(s) Before", nullptr, selectedColumns.size() ) : tr( "Insert Column Before" ) );
70 connect( insertBefore, &QAction::triggered, this, &QgsTableEditorWidget::insertColumnsBefore );
71 QAction *insertAfter = mHeaderMenu->addAction( selectedColumns.size() > 1 ? tr( "Insert %n Column(s) After", nullptr, selectedColumns.size() ) : tr( "Insert Column After" ) );
72 connect( insertAfter, &QAction::triggered, this, &QgsTableEditorWidget::insertColumnsAfter );
73 }
74 QAction *deleteSelected = mHeaderMenu->addAction( selectedColumns.size() > 1 ? tr( "Delete %n Column(s)", nullptr, selectedColumns.size() ) : tr( "Delete Column" ) );
75 connect( deleteSelected, &QAction::triggered, this, &QgsTableEditorWidget::deleteColumns );
76
77 mHeaderMenu->popup( horizontalHeader()->mapToGlobal( point ) );
78 } );
79
80 verticalHeader()->setContextMenuPolicy( Qt::CustomContextMenu );
81 connect( verticalHeader(), &QWidget::customContextMenuRequested, this, [ = ]( const QPoint & point )
82 {
83 const int row = verticalHeader()->logicalIndexAt( point.y() );
84
85 QSet< int > selectedRows;
86 for ( const QModelIndex &index : selectedIndexes() )
87 {
88 selectedRows.insert( index.row() );
89 }
90 int minRow = 0;
91 int maxRow = 0;
92 bool isConsecutive = collectConsecutiveRowRange( selectedIndexes(), minRow, maxRow );
93
94 // this is modeled off Libreoffice calc!
95 if ( selectedIndexes().count() == 1 )
96 {
97 // select whole row
98 selectRow( row );
99 isConsecutive = true;
100 }
101 else if ( !selectedRows.contains( row ) )
102 {
103 // select whole row
104 selectRow( row );
105 isConsecutive = true;
106 }
107
108 mHeaderMenu->clear();
109 if ( isConsecutive )
110 {
111 QAction *insertBefore = mHeaderMenu->addAction( selectedRows.size() > 1 ? tr( "Insert %n Row(s) Above", nullptr, selectedRows.size() ) : tr( "Insert Row Above" ) );
112 connect( insertBefore, &QAction::triggered, this, &QgsTableEditorWidget::insertRowsAbove );
113 QAction *insertAfter = mHeaderMenu->addAction( selectedRows.size() > 1 ? tr( "Insert %n Row(s) Below", nullptr, selectedRows.size() ) : tr( "Insert Row Below" ) );
114 connect( insertAfter, &QAction::triggered, this, &QgsTableEditorWidget::insertRowsBelow );
115 }
116 QAction *deleteSelected = mHeaderMenu->addAction( selectedRows.size() > 1 ? tr( "Delete %n Row(s)", nullptr, selectedRows.size() ) : tr( "Delete Row" ) );
117 connect( deleteSelected, &QAction::triggered, this, &QgsTableEditorWidget::deleteRows );
118
119 mHeaderMenu->popup( verticalHeader()->mapToGlobal( point ) );
120 } );
121
122
123 QgsTableEditorDelegate *delegate = new QgsTableEditorDelegate( this );
124 connect( delegate, &QgsTableEditorDelegate::updateNumericFormatForIndex, this, &QgsTableEditorWidget::updateNumericFormatForIndex );
125 setItemDelegate( delegate );
126
127
128 connect( this, &QTableWidget::cellDoubleClicked, this, [ = ]
129 {
130 if ( QgsTableEditorDelegate *d = qobject_cast< QgsTableEditorDelegate *>( itemDelegate( ) ) )
131 {
132 d->setWeakEditorMode( false );
133 }
134 } );
135
136 connect( selectionModel(), &QItemSelectionModel::selectionChanged, this, &QgsTableEditorWidget::activeCellChanged );
137}
138
140{
141 qDeleteAll( mNumericFormats );
142}
143
144void QgsTableEditorWidget::updateNumericFormatForIndex( const QModelIndex &index )
145{
146 if ( QTableWidgetItem *i = item( index.row(), index.column() ) )
147 {
148 if ( QgsNumericFormat *format = mNumericFormats.value( i ) )
149 {
150 i->setData( Qt::DisplayRole, format->formatDouble( index.data( CellContent ).toDouble(), QgsNumericFormatContext() ) );
151 }
152 }
153}
154
155void QgsTableEditorWidget::updateHeaders()
156{
157 QStringList headers;
158 QStringList letters;
159
160 QString first;
161 QString current;
162
163 for ( char c = 'A'; c <= 'Z'; c++ )
164 {
165 letters.push_back( QString( c ) );
166 }
167
168 int len = letters.length();
169 int index = 0;
170 int fIndex = 0;
171
172 for ( int i = 0; i < 1000; i++ )
173 {
174 if ( index == len )
175 {
176 index = 0;
177
178 first = letters.at( fIndex );
179 fIndex++;
180
181 if ( fIndex == len )
182 {
183 fIndex = 0;
184 }
185 }
186
187 current = first;
188 current += letters.at( index );
189 headers.push_back( current );
190 current.clear();
191
192 index++;
193 }
194
195 setHorizontalHeaderLabels( headers );
196
197 headers.clear();
198 if ( mIncludeHeader )
199 headers << tr( "Header" );
200 for ( int i = 1; i <= 1000; i++ )
201 {
202 headers << QString::number( i );
203 }
204
205 setVerticalHeaderLabels( headers );
206}
207
208bool QgsTableEditorWidget::collectConsecutiveRowRange( const QModelIndexList &list, int &minRow, int &maxRow ) const
209{
210 QSet< int > includedRows;
211 minRow = std::numeric_limits< int >::max();
212 maxRow = -1;
213 for ( const QModelIndex &index : list )
214 {
215 includedRows.insert( index.row() );
216 minRow = std::min( minRow, index.row() );
217 maxRow = std::max( maxRow, index.row() );
218 }
219
220 // test that selection is consecutive rows
221 for ( int r = minRow + 1; r < maxRow; r++ )
222 {
223 if ( !includedRows.contains( r ) )
224 return false;
225 }
226 return true;
227}
228
229bool QgsTableEditorWidget::collectConsecutiveColumnRange( const QModelIndexList &list, int &minColumn, int &maxColumn ) const
230{
231 QSet< int > includedColumns;
232 minColumn = std::numeric_limits< int >::max();
233 maxColumn = -1;
234 for ( const QModelIndex &index : list )
235 {
236 includedColumns.insert( index.column() );
237 minColumn = std::min( minColumn, index.column() );
238 maxColumn = std::max( maxColumn, index.column() );
239 }
240
241 // test that selection is consecutive columns
242 for ( int r = minColumn + 1; r < maxColumn; r++ )
243 {
244 if ( !includedColumns.contains( r ) )
245 return false;
246 }
247 return true;
248}
249
250QList<int> QgsTableEditorWidget::collectUniqueRows( const QModelIndexList &list ) const
251{
252 QList<int > res;
253 for ( const QModelIndex &index : list )
254 {
255 if ( !res.contains( index.row() ) )
256 res << index.row();
257 }
258 std::sort( res.begin(), res.end() );
259 return res;
260}
261
262QList<int> QgsTableEditorWidget::collectUniqueColumns( const QModelIndexList &list ) const
263{
264 QList<int > res;
265 for ( const QModelIndex &index : list )
266 {
267 if ( !res.contains( index.column() ) )
268 res << index.column();
269 }
270 std::sort( res.begin(), res.end() );
271 return res;
272}
273
275{
276 switch ( event->key() )
277 {
278 case Qt::Key_Enter:
279 case Qt::Key_Return:
280 {
281 //Enter or return keys moves to next row
282 QTableWidget::keyPressEvent( event );
283 setCurrentCell( currentRow() + 1, currentColumn() );
284 break;
285 }
286
287 case Qt::Key_Delete:
288 {
290 break;
291 }
292
293 default:
294 QTableWidget::keyPressEvent( event );
295 }
296 if ( QgsTableEditorDelegate *d = qobject_cast< QgsTableEditorDelegate *>( itemDelegate( ) ) )
297 {
298 d->setWeakEditorMode( true );
299 }
300}
301
303{
304 mBlockSignals++;
305 qDeleteAll( mNumericFormats );
306 mNumericFormats.clear();
307
308 QgsNumericFormatContext numericContext;
309 int rowNumber = mIncludeHeader ? 1 : 0;
310 bool first = true;
311 setRowCount( contents.size() + rowNumber );
312 for ( const QgsTableRow &row : contents )
313 {
314 if ( first )
315 {
316 setColumnCount( row.size() );
317 first = false;
318 }
319
320 int colNumber = 0;
321 for ( const QgsTableCell &col : row )
322 {
323 QTableWidgetItem *item = new QTableWidgetItem( col.content().value< QgsProperty >().isActive() ? col.content().value< QgsProperty >().asExpression() : col.content().toString() );
324 item->setData( CellContent, col.content() ); // can't use EditRole, because Qt. (https://bugreports.qt.io/browse/QTBUG-11549)
325 item->setData( Qt::BackgroundRole, col.backgroundColor().isValid() ? col.backgroundColor() : QColor( 255, 255, 255 ) );
326 item->setData( PresetBackgroundColorRole, col.backgroundColor().isValid() ? col.backgroundColor() : QVariant() );
327 item->setData( Qt::ForegroundRole, col.textFormat().isValid() ? col.textFormat().color() : QVariant() );
328 item->setData( TextFormat, QVariant::fromValue( col.textFormat() ) );
329 item->setData( HorizontalAlignment, static_cast< int >( col.horizontalAlignment() ) );
330 item->setData( VerticalAlignment, static_cast< int >( col.verticalAlignment() ) );
331 item->setData( CellProperty, QVariant::fromValue( col.content().value< QgsProperty >() ) );
332
333 if ( col.content().value< QgsProperty >().isActive() )
334 item->setFlags( item->flags() & ( ~Qt::ItemIsEditable ) );
335
336 if ( auto *lNumericFormat = col.numericFormat() )
337 {
338 mNumericFormats.insert( item, lNumericFormat->clone() );
339 item->setData( Qt::DisplayRole, mNumericFormats.value( item )->formatDouble( col.content().toDouble(), numericContext ) );
340 }
341 setItem( rowNumber, colNumber, item );
342 colNumber++;
343 }
344 rowNumber++;
345 }
346
347 mBlockSignals--;
348 updateHeaders();
349
350 if ( mFirstSet )
351 {
352 resizeColumnsToContents();
353 resizeRowsToContents();
354 mFirstSet = false;
355 }
356 emit tableChanged();
357}
358
360{
361 QgsTableContents items;
362 items.reserve( rowCount() );
363
364 for ( int r = mIncludeHeader ? 1 : 0; r < rowCount(); r++ )
365 {
366 QgsTableRow row;
367 row.reserve( columnCount() );
368 for ( int c = 0; c < columnCount(); c++ )
369 {
370 QgsTableCell cell;
371 if ( QTableWidgetItem *i = item( r, c ) )
372 {
373 cell.setContent( i->data( CellProperty ).value< QgsProperty >().isActive() ? i->data( CellProperty ) : i->data( CellContent ) );
374 cell.setBackgroundColor( i->data( PresetBackgroundColorRole ).value< QColor >() );
375 cell.setTextFormat( i->data( TextFormat ).value< QgsTextFormat >() );
376 cell.setHorizontalAlignment( static_cast< Qt::Alignment >( i->data( HorizontalAlignment ).toInt() ) );
377 cell.setVerticalAlignment( static_cast< Qt::Alignment >( i->data( VerticalAlignment ).toInt() ) );
378
379 if ( mNumericFormats.value( i ) )
380 {
381 cell.setNumericFormat( mNumericFormats.value( i )->clone() );
382 }
383 }
384 row.push_back( cell );
385 }
386 items.push_back( row );
387 }
388
389 return items;
390}
391
393{
394 bool changed = false;
395 mBlockSignals++;
396 std::unique_ptr< QgsNumericFormat > newFormat( format );
397 const QModelIndexList selection = selectedIndexes();
398 QgsNumericFormatContext numericContext;
399 for ( const QModelIndex &index : selection )
400 {
401 if ( index.row() == 0 && mIncludeHeader )
402 continue;
403
404 QTableWidgetItem *i = item( index.row(), index.column() );
405 if ( !i )
406 {
407 i = new QTableWidgetItem();
408 setItem( index.row(), index.column(), i );
409 }
410 if ( !mNumericFormats.value( i ) && newFormat )
411 {
412 changed = true;
413 mNumericFormats.insert( i, newFormat->clone() );
414 }
415 else if ( mNumericFormats.value( i ) && !newFormat )
416 {
417 changed = true;
418 delete mNumericFormats.value( i );
419 mNumericFormats.remove( i );
420 }
421 else if ( newFormat && *newFormat != *mNumericFormats.value( i ) )
422 {
423 changed = true;
424 delete mNumericFormats.value( i );
425 mNumericFormats.insert( i, newFormat->clone() );
426 }
427 i->setData( Qt::DisplayRole, newFormat ? mNumericFormats.value( i )->formatDouble( i->data( CellContent ).toDouble(), numericContext ) : i->data( CellContent ) );
428 }
429 mBlockSignals--;
430 if ( changed && !mBlockSignals )
431 emit tableChanged();
432}
433
435{
436 QgsNumericFormat *f = nullptr;
437 bool first = true;
438 const QModelIndexList selection = selectedIndexes();
439 for ( const QModelIndex &index : selection )
440 {
441 if ( QTableWidgetItem *i = item( index.row(), index.column() ) )
442 {
443 if ( first )
444 {
445 f = mNumericFormats.value( i );
446 first = false;
447 }
448 else if ( ( !f && !mNumericFormats.value( i ) )
449 || ( f && mNumericFormats.value( i ) && *f == *mNumericFormats.value( i ) ) )
450 continue;
451 else
452 {
453 return nullptr;
454 }
455 }
456 else
457 {
458 return nullptr;
459 }
460 }
461 return f;
462}
463
465{
466 QgsNumericFormat *f = nullptr;
467 bool first = true;
468 const QModelIndexList selection = selectedIndexes();
469 for ( const QModelIndex &index : selection )
470 {
471 if ( QTableWidgetItem *i = item( index.row(), index.column() ) )
472 {
473 if ( first )
474 {
475 f = mNumericFormats.value( i );
476 first = false;
477 }
478 else if ( ( !f && !mNumericFormats.value( i ) )
479 || ( f && mNumericFormats.value( i ) && *f == *mNumericFormats.value( i ) ) )
480 continue;
481 else
482 {
483 return true;
484 }
485 }
486 else if ( f )
487 {
488 return true;
489 }
490 }
491 return false;
492}
493
495{
497 return f.isValid() ? f.color() : QColor();
498}
499
501{
502 QColor c;
503 bool first = true;
504 const QModelIndexList selection = selectedIndexes();
505 for ( const QModelIndex &index : selection )
506 {
507 QColor indexColor = model()->data( index, PresetBackgroundColorRole ).isValid() ? model()->data( index, PresetBackgroundColorRole ).value< QColor >() : QColor();
508 if ( first )
509 {
510 c = indexColor;
511 first = false;
512 }
513 else if ( indexColor == c )
514 continue;
515 else
516 {
517 return QColor();
518 }
519 }
520 return c;
521}
522
524{
525 Qt::Alignment alignment = Qt::AlignLeft;
526 bool first = true;
527 const QModelIndexList selection = selectedIndexes();
528 for ( const QModelIndex &index : selection )
529 {
530 Qt::Alignment cellAlign = static_cast< Qt::Alignment >( model()->data( index, HorizontalAlignment ).toInt() );
531 if ( first )
532 {
533 alignment = cellAlign;
534 first = false;
535 }
536 else if ( cellAlign == alignment )
537 continue;
538 else
539 {
540 return Qt::AlignLeft | Qt::AlignTop;
541 }
542 }
543 return alignment;
544}
545
547{
548 Qt::Alignment alignment = Qt::AlignVCenter;
549 bool first = true;
550 const QModelIndexList selection = selectedIndexes();
551 for ( const QModelIndex &index : selection )
552 {
553 Qt::Alignment cellAlign = static_cast< Qt::Alignment >( model()->data( index, VerticalAlignment ).toInt() );
554 if ( first )
555 {
556 alignment = cellAlign;
557 first = false;
558 }
559 else if ( cellAlign == alignment )
560 continue;
561 else
562 {
563 return Qt::AlignLeft | Qt::AlignTop;
564 }
565 }
566 return alignment;
567}
568
570{
571 QgsProperty property;
572 bool first = true;
573 const QModelIndexList selection = selectedIndexes();
574 for ( const QModelIndex &index : selection )
575 {
576 const QgsProperty cellProperty = model()->data( index, CellProperty ).value< QgsProperty >();
577 if ( first )
578 {
579 property = cellProperty;
580 first = false;
581 }
582 else if ( cellProperty == property )
583 continue;
584 else
585 {
586 return QgsProperty();
587 }
588 }
589 return property;
590}
591
593{
594 QgsTextFormat format;
595 bool first = true;
596 const QModelIndexList selection = selectedIndexes();
597 for ( const QModelIndex &index : selection )
598 {
599 if ( !model()->data( index, TextFormat ).isValid() )
600 return QgsTextFormat();
601
602 QgsTextFormat cellFormat = model()->data( index, TextFormat ).value< QgsTextFormat >();
603 if ( first )
604 {
605 format = cellFormat;
606 first = false;
607 }
608 else if ( cellFormat == format )
609 continue;
610 else
611 return QgsTextFormat();
612 }
613 return format;
614}
615
617{
618 double height = 0;
619 bool first = true;
620 const QModelIndexList selection = selectedIndexes();
621 for ( const QModelIndex &index : selection )
622 {
623 double thisHeight = tableRowHeight( index.row() );
624 if ( first )
625 height = thisHeight;
626 else if ( thisHeight != height )
627 {
628 return -1;
629 }
630 first = false;
631 }
632 return height;
633}
634
636{
637 double width = 0;
638 bool first = true;
639 const QModelIndexList selection = selectedIndexes();
640 for ( const QModelIndex &index : selection )
641 {
642 double thisWidth = tableColumnWidth( index.column() );
643 if ( first )
644 width = thisWidth;
645 else if ( thisWidth != width )
646 {
647 return -1;
648 }
649 first = false;
650 }
651 return width;
652}
653
655{
656 double height = 0;
657 for ( int col = 0; col < columnCount(); ++col )
658 {
659 double thisHeight = model()->data( model()->index( row + ( mIncludeHeader ? 1 : 0 ), col ), RowHeight ).toDouble();
660 height = std::max( thisHeight, height );
661 }
662 return height;
663}
664
666{
667 double width = 0;
668 for ( int row = 0; row < rowCount(); ++row )
669 {
670 double thisWidth = model()->data( model()->index( row, column ), ColumnWidth ).toDouble();
671 width = std::max( thisWidth, width );
672 }
673 return width;
674}
675
676void QgsTableEditorWidget::setTableRowHeight( int row, double height )
677{
678 if ( row == 0 && mIncludeHeader )
679 return;
680
681 bool changed = false;
682 mBlockSignals++;
683
684 for ( int col = 0; col < columnCount(); ++col )
685 {
686 if ( QTableWidgetItem *i = item( row + ( mIncludeHeader ? 1 : 0 ), col ) )
687 {
688 if ( i->data( RowHeight ).toDouble() != height )
689 {
690 i->setData( RowHeight, height );
691 changed = true;
692 }
693 }
694 else
695 {
696 QTableWidgetItem *newItem = new QTableWidgetItem();
697 newItem->setData( RowHeight, height );
698 setItem( row + ( mIncludeHeader ? 1 : 0 ), col, newItem );
699 changed = true;
700 }
701 }
702
703 mBlockSignals--;
704 if ( changed && !mBlockSignals )
705 emit tableChanged();
706}
707
708void QgsTableEditorWidget::setTableColumnWidth( int col, double width )
709{
710 bool changed = false;
711 mBlockSignals++;
712 for ( int row = 0; row < rowCount(); ++row )
713 {
714 if ( QTableWidgetItem *i = item( row, col ) )
715 {
716 if ( i->data( ColumnWidth ).toDouble() != width )
717 {
718 i->setData( ColumnWidth, width );
719 changed = true;
720 }
721 }
722 else
723 {
724 QTableWidgetItem *newItem = new QTableWidgetItem();
725 newItem->setData( ColumnWidth, width );
726 setItem( row, col, newItem );
727 changed = true;
728 }
729 }
730 mBlockSignals--;
731 if ( changed && !mBlockSignals )
732 emit tableChanged();
733}
734
736{
737 return collectUniqueRows( selectedIndexes() );
738}
739
741{
742 return collectUniqueColumns( selectedIndexes() );
743}
744
746{
747 if ( !mIncludeHeader )
748 return QVariantList();
749
750 QVariantList res;
751 res.reserve( columnCount() );
752 for ( int col = 0; col < columnCount(); ++col )
753 {
754 if ( QTableWidgetItem *i = item( 0, col ) )
755 {
756 res << i->data( CellContent );
757 }
758 else
759 {
760 res << QVariant();
761 }
762 }
763 return res;
764}
765
767{
768 if ( !mIncludeHeader )
769 return false;
770
771 return collectUniqueRows( selectedIndexes() ).contains( 0 );
772}
773
775{
776 if ( rowCount() == 0 )
777 {
778 insertRow( 0 );
779 return;
780 }
781
782 int minRow = 0;
783 int maxRow = 0;
784 if ( !collectConsecutiveRowRange( selectedIndexes(), minRow, maxRow ) )
785 return;
786
787 const int rowsToInsert = maxRow - minRow + 1;
788 for ( int i = 0; i < rowsToInsert; ++i )
789 insertRow( maxRow + 1 );
790
791 updateHeaders();
792 if ( !mBlockSignals )
793 emit tableChanged();
794}
795
797{
798 if ( rowCount() == 0 )
799 {
800 insertRow( 0 );
801 return;
802 }
803
804 int minRow = 0;
805 int maxRow = 0;
806 if ( !collectConsecutiveRowRange( selectedIndexes(), minRow, maxRow ) )
807 return;
808
809 const int rowsToInsert = maxRow - minRow + 1;
810 for ( int i = 0; i < rowsToInsert; ++i )
811 insertRow( minRow );
812
813 updateHeaders();
814 if ( !mBlockSignals )
815 emit tableChanged();
816}
817
819{
820 if ( columnCount() == 0 )
821 {
822 insertColumn( 0 );
823 return;
824 }
825
826 int minColumn = 0;
827 int maxColumn = 0;
828 if ( !collectConsecutiveColumnRange( selectedIndexes(), minColumn, maxColumn ) )
829 return;
830
831 const int columnsToInsert = maxColumn - minColumn + 1;
832 for ( int i = 0; i < columnsToInsert; ++i )
833 insertColumn( minColumn );
834
835 updateHeaders();
836 if ( !mBlockSignals )
837 emit tableChanged();
838}
839
841{
842 if ( columnCount() == 0 )
843 {
844 insertColumn( 0 );
845 return;
846 }
847
848 int minColumn = 0;
849 int maxColumn = 0;
850 if ( !collectConsecutiveColumnRange( selectedIndexes(), minColumn, maxColumn ) )
851 return;
852
853 const int columnsToInsert = maxColumn - minColumn + 1;
854 for ( int i = 0; i < columnsToInsert; ++i )
855 insertColumn( maxColumn + 1 );
856
857 updateHeaders();
858 if ( !mBlockSignals )
859 emit tableChanged();
860}
861
863{
864 const QList< int > rows = rowsAssociatedWithSelection();
865 if ( rows.empty() )
866 return;
867
868 bool changed = false;
869 for ( int i = rows.size() - 1; i >= 0 && rowCount() > 1; i-- )
870 {
871 removeRow( rows.at( i ) );
872 changed = true;
873 }
874 updateHeaders();
875 if ( changed && !mBlockSignals )
876 emit tableChanged();
877}
878
880{
881 const QList< int > columns = columnsAssociatedWithSelection();
882 if ( columns.empty() )
883 return;
884
885 bool changed = false;
886 for ( int i = columns.size() - 1; i >= 0 && columnCount() > 1; i-- )
887 {
888 removeColumn( columns.at( i ) );
889 changed = true;
890 }
891 updateHeaders();
892 if ( !mBlockSignals && changed )
893 emit tableChanged();
894}
895
897{
898 const QModelIndexList s = selectedIndexes();
899 for ( const QModelIndex &index : s )
900 {
901 selectionModel()->select( index, QItemSelectionModel::Rows | QItemSelectionModel::Select );
902 }
903}
904
906{
907 const QModelIndexList s = selectedIndexes();
908 for ( const QModelIndex &index : s )
909 {
910 selectionModel()->select( index, QItemSelectionModel::Columns | QItemSelectionModel::Select );
911 }
912}
913
915{
916 const QModelIndexList selection = selectedIndexes();
917 bool changed = false;
918 mBlockSignals++;
919 for ( const QModelIndex &index : selection )
920 {
921 if ( QTableWidgetItem *i = item( index.row(), index.column() ) )
922 {
923 i->setText( QString() );
924 i->setData( CellContent, QVariant() );
925 changed = true;
926 }
927 }
928 mBlockSignals--;
929 if ( changed && !mBlockSignals )
930 emit tableChanged();
931}
932
934{
935 const QModelIndexList selection = selectedIndexes();
936 bool changed = false;
937 mBlockSignals++;
938 for ( const QModelIndex &index : selection )
939 {
940 if ( index.row() == 0 && mIncludeHeader )
941 continue;
942
943 if ( QTableWidgetItem *i = item( index.row(), index.column() ) )
944 {
945 if ( i->data( Qt::ForegroundRole ).value< QColor >() != color )
946 {
947 i->setData( Qt::ForegroundRole, color.isValid() ? color : QVariant() );
948 QgsTextFormat f = i->data( TextFormat ).value< QgsTextFormat >();
949 f.setColor( color );
950 i->setData( TextFormat, QVariant::fromValue( f ) );
951 changed = true;
952 }
953 }
954 else
955 {
956 QTableWidgetItem *newItem = new QTableWidgetItem();
957 newItem->setData( Qt::ForegroundRole, color.isValid() ? color : QVariant() );
959 f.setColor( color );
960 newItem->setData( TextFormat, QVariant::fromValue( f ) );
961 setItem( index.row(), index.column(), newItem );
962 changed = true;
963 }
964 }
965 mBlockSignals--;
966 if ( changed && !mBlockSignals )
967 emit tableChanged();
968}
969
971{
972 const QModelIndexList selection = selectedIndexes();
973 bool changed = false;
974 mBlockSignals++;
975 for ( const QModelIndex &index : selection )
976 {
977 if ( index.row() == 0 && mIncludeHeader )
978 continue;
979
980 if ( QTableWidgetItem *i = item( index.row(), index.column() ) )
981 {
982 if ( i->data( PresetBackgroundColorRole ).value< QColor >() != color )
983 {
984 i->setData( Qt::BackgroundRole, color.isValid() ? color : QVariant() );
985 i->setData( PresetBackgroundColorRole, color.isValid() ? color : QVariant() );
986 changed = true;
987 }
988 }
989 else
990 {
991 QTableWidgetItem *newItem = new QTableWidgetItem();
992 newItem->setData( Qt::BackgroundRole, color.isValid() ? color : QVariant() );
993 newItem->setData( PresetBackgroundColorRole, color.isValid() ? color : QVariant() );
994 setItem( index.row(), index.column(), newItem );
995 changed = true;
996 }
997 }
998 mBlockSignals--;
999 if ( changed && !mBlockSignals )
1000 emit tableChanged();
1001}
1002
1004{
1005 const QModelIndexList selection = selectedIndexes();
1006 bool changed = false;
1007 mBlockSignals++;
1008 for ( const QModelIndex &index : selection )
1009 {
1010 if ( index.row() == 0 && mIncludeHeader )
1011 continue;
1012
1013 if ( QTableWidgetItem *i = item( index.row(), index.column() ) )
1014 {
1015 if ( static_cast< Qt::Alignment >( i->data( HorizontalAlignment ).toInt() ) != alignment )
1016 {
1017 i->setData( HorizontalAlignment, static_cast< int >( alignment ) );
1018 changed = true;
1019 }
1020 }
1021 else
1022 {
1023 QTableWidgetItem *newItem = new QTableWidgetItem();
1024 newItem->setData( HorizontalAlignment, static_cast< int >( alignment ) );
1025 setItem( index.row(), index.column(), newItem );
1026 changed = true;
1027 }
1028 }
1029 mBlockSignals--;
1030 if ( changed && !mBlockSignals )
1031 emit tableChanged();
1032}
1033
1035{
1036 const QModelIndexList selection = selectedIndexes();
1037 bool changed = false;
1038 mBlockSignals++;
1039 for ( const QModelIndex &index : selection )
1040 {
1041 if ( index.row() == 0 && mIncludeHeader )
1042 continue;
1043
1044 if ( QTableWidgetItem *i = item( index.row(), index.column() ) )
1045 {
1046 if ( static_cast< Qt::Alignment >( i->data( HorizontalAlignment ).toInt() ) != alignment )
1047 {
1048 i->setData( VerticalAlignment, static_cast< int >( alignment ) );
1049 changed = true;
1050 }
1051 }
1052 else
1053 {
1054 QTableWidgetItem *newItem = new QTableWidgetItem();
1055 newItem->setData( VerticalAlignment, static_cast< int >( alignment ) );
1056 setItem( index.row(), index.column(), newItem );
1057 changed = true;
1058 }
1059 }
1060 mBlockSignals--;
1061 if ( changed && !mBlockSignals )
1062 emit tableChanged();
1063}
1064
1066{
1067 const QModelIndexList selection = selectedIndexes();
1068 bool changed = false;
1069 mBlockSignals++;
1070 for ( const QModelIndex &index : selection )
1071 {
1072 if ( index.row() == 0 && mIncludeHeader )
1073 continue;
1074
1075 if ( QTableWidgetItem *i = item( index.row(), index.column() ) )
1076 {
1077 if ( i->data( CellProperty ).value< QgsProperty >() != property )
1078 {
1079 if ( property.isActive() )
1080 {
1081 i->setData( CellProperty, QVariant::fromValue( property ) );
1082 i->setText( property.asExpression() );
1083 i->setFlags( i->flags() & ( ~Qt::ItemIsEditable ) );
1084 }
1085 else
1086 {
1087 i->setData( CellProperty, QVariant() );
1088 i->setText( QString() );
1089 i->setFlags( i->flags() | Qt::ItemIsEditable );
1090 }
1091 changed = true;
1092 }
1093 }
1094 else
1095 {
1096 QTableWidgetItem *newItem = new QTableWidgetItem( property.asExpression() );
1097 if ( property.isActive() )
1098 {
1099 newItem->setData( CellProperty, QVariant::fromValue( property ) );
1100 newItem->setFlags( newItem->flags() & ( ~Qt::ItemIsEditable ) );
1101 }
1102 else
1103 {
1104 newItem->setData( CellProperty, QVariant() );
1105 newItem->setFlags( newItem->flags() | Qt::ItemIsEditable );
1106 }
1107 setItem( index.row(), index.column(), newItem );
1108 changed = true;
1109 }
1110 }
1111 mBlockSignals--;
1112 if ( changed && !mBlockSignals )
1113 emit tableChanged();
1114}
1115
1117{
1118 const QModelIndexList selection = selectedIndexes();
1119 bool changed = false;
1120 mBlockSignals++;
1121 for ( const QModelIndex &index : selection )
1122 {
1123 if ( index.row() == 0 && mIncludeHeader )
1124 continue;
1125
1126 if ( QTableWidgetItem *i = item( index.row(), index.column() ) )
1127 {
1128 i->setData( TextFormat, QVariant::fromValue( format ) );
1129 i->setData( Qt::ForegroundRole, format.color() );
1130 changed = true;
1131 }
1132 else
1133 {
1134 QTableWidgetItem *newItem = new QTableWidgetItem();
1135 newItem->setData( TextFormat, QVariant::fromValue( format ) );
1136 newItem->setData( Qt::ForegroundRole, format.color() );
1137 setItem( index.row(), index.column(), newItem );
1138 changed = true;
1139 }
1140 }
1141 mBlockSignals--;
1142 if ( changed && !mBlockSignals )
1143 emit tableChanged();
1144}
1145
1147{
1148 bool changed = false;
1149 mBlockSignals++;
1150 const QList< int > rows = rowsAssociatedWithSelection();
1151 for ( int row : rows )
1152 {
1153 if ( row == 0 && mIncludeHeader )
1154 continue;
1155
1156 for ( int col = 0; col < columnCount(); ++col )
1157 {
1158 if ( QTableWidgetItem *i = item( row, col ) )
1159 {
1160 if ( i->data( RowHeight ).toDouble() != height )
1161 {
1162 i->setData( RowHeight, height );
1163 changed = true;
1164 }
1165 }
1166 else
1167 {
1168 QTableWidgetItem *newItem = new QTableWidgetItem();
1169 newItem->setData( RowHeight, height );
1170 setItem( row, col, newItem );
1171 changed = true;
1172 }
1173 }
1174 }
1175 mBlockSignals--;
1176 if ( changed && !mBlockSignals )
1177 emit tableChanged();
1178}
1179
1181{
1182 bool changed = false;
1183 mBlockSignals++;
1184 const QList< int > cols = columnsAssociatedWithSelection();
1185 for ( int col : cols )
1186 {
1187 for ( int row = 0; row < rowCount(); ++row )
1188 {
1189 if ( QTableWidgetItem *i = item( row, col ) )
1190 {
1191 if ( i->data( ColumnWidth ).toDouble() != width )
1192 {
1193 i->setData( ColumnWidth, width );
1194 changed = true;
1195 }
1196 }
1197 else
1198 {
1199 QTableWidgetItem *newItem = new QTableWidgetItem();
1200 newItem->setData( ColumnWidth, width );
1201 setItem( row, col, newItem );
1202 changed = true;
1203 }
1204 }
1205 }
1206 mBlockSignals--;
1207 if ( changed && !mBlockSignals )
1208 emit tableChanged();
1209}
1210
1212{
1213 if ( included == mIncludeHeader )
1214 return;
1215
1216 mIncludeHeader = included;
1217
1218 if ( mIncludeHeader )
1219 insertRow( 0 );
1220 else
1221 removeRow( 0 );
1222 updateHeaders();
1223}
1224
1225void QgsTableEditorWidget::setTableHeaders( const QVariantList &headers )
1226{
1227 if ( !mIncludeHeader )
1228 return;
1229
1230 mBlockSignals++;
1231
1232 for ( int col = 0; col < columnCount(); ++col )
1233 {
1234 if ( QTableWidgetItem *i = item( 0, col ) )
1235 {
1236 i->setText( headers.value( col ).toString() );
1237 i->setData( CellContent, headers.value( col ) ); // can't use EditRole, because Qt. (https://bugreports.qt.io/browse/QTBUG-11549)
1238 }
1239 else
1240 {
1241 QTableWidgetItem *item = new QTableWidgetItem( headers.value( col ).toString() );
1242 item->setData( CellContent, headers.value( col ) ); // can't use EditRole, because Qt. (https://bugreports.qt.io/browse/QTBUG-11549)
1243 setItem( 0, col, item );
1244 }
1245 }
1246 mBlockSignals--;
1247}
1248
1250
1251QgsTableEditorTextEdit::QgsTableEditorTextEdit( QWidget *parent )
1252 : QPlainTextEdit( parent )
1253{
1254 // narrower default margins
1255 document()->setDocumentMargin( document()->documentMargin() / 2 );
1256
1257 connect( this, &QPlainTextEdit::textChanged, this, &QgsTableEditorTextEdit::resizeToContents );
1258 updateMinimumSize();
1259}
1260
1261void QgsTableEditorTextEdit::keyPressEvent( QKeyEvent *event )
1262{
1263 switch ( event->key() )
1264 {
1265 case Qt::Key_Enter:
1266 case Qt::Key_Return:
1267 {
1268 if ( event->modifiers() & Qt::ControlModifier )
1269 {
1270 // ctrl+enter inserts a line break
1271 insertPlainText( QString( '\n' ) );
1272 resizeToContents();
1273 }
1274 else
1275 {
1276 // closes editor
1277 event->ignore();
1278 }
1279 break;
1280 }
1281
1282 case Qt::Key_Right:
1283 case Qt::Key_Left:
1284 case Qt::Key_Up:
1285 case Qt::Key_Down:
1286 {
1287 if ( mWeakEditorMode )
1288 {
1289 // close editor and defer to table
1290 event->ignore();
1291 }
1292 else
1293 {
1294 QPlainTextEdit::keyPressEvent( event );
1295 }
1296 break;
1297 }
1298
1299 case Qt::Key_Tab:
1300 {
1301 if ( event->modifiers() & Qt::ControlModifier )
1302 {
1303 // if tab is pressed then defer to table, unless ctrl modifier is also held
1304 // (emulate spreadsheet behavior)
1305 insertPlainText( QString( '\t' ) );
1306 resizeToContents();
1307 }
1308 else
1309 {
1310 event->ignore();
1311 }
1312 break;
1313 }
1314
1315 default:
1316 QPlainTextEdit::keyPressEvent( event );
1317 }
1318}
1319
1320void QgsTableEditorTextEdit::updateMinimumSize()
1321{
1322 const double tm = document()->documentMargin();
1323 const QMargins cm = contentsMargins();
1324 const int width = tm * 2 + cm.left() + cm.right() + 30;
1325 const int height = tm * 2 + cm.top() + cm.bottom() + 4;
1326 QStyleOptionFrame opt;
1327 initStyleOption( &opt );
1328 const QSize sizeFromContent = style()->sizeFromContents( QStyle::CT_LineEdit, &opt, QSize( width, height ), this );
1329 setMinimumWidth( sizeFromContent.width() );
1330 setMinimumHeight( sizeFromContent.height() );
1331}
1332
1333void QgsTableEditorTextEdit::setWeakEditorMode( bool weakEditorMode )
1334{
1335 mWeakEditorMode = weakEditorMode;
1336}
1337
1338void QgsTableEditorTextEdit::resizeToContents()
1339{
1340 int oldWidth = width();
1341 int oldHeight = height();
1342 if ( mOriginalWidth == -1 )
1343 mOriginalWidth = oldWidth;
1344 if ( mOriginalHeight == -1 )
1345 mOriginalHeight = oldHeight;
1346
1347 if ( QWidget *parent = parentWidget() )
1348 {
1349 QPoint position = pos();
1350 QFontMetrics fm( font() );
1351
1352 const QStringList lines = toPlainText().split( '\n' );
1353 int maxTextLineWidth = 0;
1354 int totalTextHeight = 0;
1355 for ( const QString &line : lines )
1356 {
1357 const QRect bounds = fontMetrics().boundingRect( line );
1358 maxTextLineWidth = std::max( maxTextLineWidth, bounds.width() );
1359 totalTextHeight += fm.height();
1360 }
1361
1362 int hintWidth = minimumWidth() + maxTextLineWidth;
1363 int hintHeight = minimumHeight() + totalTextHeight;
1364 int parentWidth = parent->width();
1365 int maxWidth = isRightToLeft() ? position.x() + oldWidth : parentWidth - position.x();
1366 int maxHeight = parent->height() - position.y();
1367 int newWidth = std::clamp( hintWidth, mOriginalWidth, maxWidth );
1368 int newHeight = std::clamp( hintHeight, mOriginalHeight, maxHeight );
1369
1370 if ( mWidgetOwnsGeometry )
1371 {
1372 setMaximumWidth( newWidth );
1373 setMaximumHeight( newHeight );
1374 }
1375 if ( isRightToLeft() )
1376 move( position.x() - newWidth + oldWidth, position.y() );
1377 resize( newWidth, newHeight );
1378 }
1379}
1380
1381void QgsTableEditorTextEdit::changeEvent( QEvent *e )
1382{
1383 switch ( e->type() )
1384 {
1385 case QEvent::FontChange:
1386 case QEvent::StyleChange:
1387 case QEvent::ContentsRectChange:
1388 updateMinimumSize();
1389 break;
1390 default:
1391 break;
1392 }
1393 QPlainTextEdit::changeEvent( e );
1394}
1395
1396QgsTableEditorDelegate::QgsTableEditorDelegate( QObject *parent )
1397 : QStyledItemDelegate( parent )
1398{
1399
1400}
1401
1402void QgsTableEditorDelegate::setWeakEditorMode( bool weakEditorMode )
1403{
1404 mWeakEditorMode = weakEditorMode;
1405}
1406
1407QWidget *QgsTableEditorDelegate::createEditor( QWidget *parent, const QStyleOptionViewItem &, const QModelIndex & ) const
1408{
1409 QgsTableEditorTextEdit *w = new QgsTableEditorTextEdit( parent );
1410 w->setWeakEditorMode( mWeakEditorMode );
1411
1412 if ( !w->style()->styleHint( QStyle::SH_ItemView_DrawDelegateFrame, 0, w ) )
1413 w->setFrameShape( QFrame::NoFrame );
1414 if ( !w->style()->styleHint( QStyle::SH_ItemView_ShowDecorationSelected, 0, w ) )
1415 w->setWidgetOwnsGeometry( true );
1416
1417 return w;
1418}
1419
1420void QgsTableEditorDelegate::setEditorData( QWidget *editor, const QModelIndex &index ) const
1421{
1422 QVariant value = index.model()->data( index, QgsTableEditorWidget::CellContent );
1423 if ( QgsTableEditorTextEdit *lineEdit = qobject_cast<QgsTableEditorTextEdit * >( editor ) )
1424 {
1425 if ( index != mLastIndex || lineEdit->toPlainText() != value.toString() )
1426 {
1427 lineEdit->setPlainText( value.toString() );
1428 lineEdit->selectAll();
1429 }
1430 }
1431 mLastIndex = index;
1432}
1433
1434void QgsTableEditorDelegate::setModelData( QWidget *editor, QAbstractItemModel *model, const QModelIndex &index ) const
1435{
1436 if ( QgsTableEditorTextEdit *lineEdit = qobject_cast<QgsTableEditorTextEdit * >( editor ) )
1437 {
1438 const QString text = lineEdit->toPlainText();
1439 if ( text != model->data( index, QgsTableEditorWidget::CellContent ).toString() && !model->data( index, QgsTableEditorWidget::CellProperty ).value< QgsProperty >().isActive() )
1440 {
1441 model->setData( index, text, QgsTableEditorWidget::CellContent );
1442 model->setData( index, text, Qt::DisplayRole );
1443 emit updateNumericFormatForIndex( index );
1444 }
1445 }
1446}
1447
1448
1450
A context for numeric formats.
A numeric formatter allows for formatting a numeric value for display, using a variety of different f...
A store for object properties.
QString asExpression() const
Returns an expression string representing the state of the property, or an empty string if the proper...
QVariant value(const QgsExpressionContext &context, const QVariant &defaultValue=QVariant(), bool *ok=nullptr) const
Calculates the current value of the property, including any transforms which are set for the property...
bool isActive() const
Returns whether the property is currently active.
Encapsulates the contents and formatting of a single table cell.
void setHorizontalAlignment(Qt::Alignment alignment)
Sets the horizontal alignment for text in the cell.
void setVerticalAlignment(Qt::Alignment alignment)
Sets the vertical alignment for text in the cell.
void setBackgroundColor(const QColor &color)
Sets the cell's background color.
void setTextFormat(const QgsTextFormat &format)
Sets the cell's text format.
void setContent(const QVariant &content)
Sets the cell's content.
void setNumericFormat(QgsNumericFormat *format)
Sets the numeric format used for numbers in the cell, or nullptr if no specific format is set.
void setSelectionVerticalAlignment(Qt::Alignment alignment)
Sets the vertical alignment for the currently selected cells.
Q_DECL_DEPRECATED void setSelectionForegroundColor(const QColor &color)
Sets the foreground color for the currently selected cells.
QgsProperty selectionCellProperty()
Returns the QgsProperty used for the contents of the currently selected cells.
QVariantList tableHeaders() const
Returns the table header values.
void setSelectionTextFormat(const QgsTextFormat &format)
Sets the text format for the selected cells.
void setTableHeaders(const QVariantList &headers)
Sets the table headers.
bool isHeaderCellSelected()
Returns true if any header cells are selected.
void setSelectionBackgroundColor(const QColor &color)
Sets the background color for the currently selected cells.
void activeCellChanged()
Emitted whenever the active (or selected) cell changes in the widget.
Qt::Alignment selectionHorizontalAlignment()
Returns the horizontal alignment for the currently selected cells.
QgsTextFormat selectionTextFormat()
Returns the text format for the currently selected cells.
void insertColumnsAfter()
Inserts new columns after the current selection.
double selectionRowHeight()
Returns the height (in millimeters) of the rows associated with the current selection,...
void clearSelectedCells()
Clears the contents of the currently selected cells.
bool hasMixedSelectionNumericFormat()
Returns true if the current selection has a mix of numeric formats.
QList< int > rowsAssociatedWithSelection()
Returns a list of the rows associated with the current table selected cells.
QList< int > columnsAssociatedWithSelection()
Returns a list of the columns associated with the current table selected cells.
void deleteRows()
Deletes all rows associated with the current selected cells.
void setSelectionHorizontalAlignment(Qt::Alignment alignment)
Sets the horizontal alignment for the currently selected cells.
void setSelectionRowHeight(double height)
Sets the row height (in millimeters) for the currently selected rows, or 0 for automatic row height.
void insertRowsBelow()
Inserts new rows below the current selection.
double tableRowHeight(int row)
Returns the configured row height for the specified row, or 0 if an automatic height should be used f...
QColor selectionBackgroundColor()
Returns the background color for the currently selected cells.
QgsTableEditorWidget(QWidget *parent=nullptr)
Constructor for QgsTableEditorWidget with the specified parent widget.
Qt::Alignment selectionVerticalAlignment()
Returns the horizontal alignment for the currently selected cells.
void insertRowsAbove()
Inserts new rows above the current selection.
void setTableColumnWidth(int column, double width)
Sets the configured column width for the specified column.
void setSelectionCellProperty(const QgsProperty &property)
Sets the cell contents QgsProperty for the currently selected cells.
void setSelectionColumnWidth(double height)
Sets the column width (in millimeters) for the currently selected columns, or 0 for automatic column ...
double selectionColumnWidth()
Returns the width (in millimeters) of the columns associated with the current selection,...
void expandRowSelection()
Expands out the selection to include whole rows associated with the current selected cells.
double tableColumnWidth(int column)
Returns the configured column width for the specified column, or 0 if an automatic width should be us...
void setIncludeTableHeader(bool included)
Sets whether the table includes a header row.
QgsNumericFormat * selectionNumericFormat()
Returns the numeric format used for the currently selected cells, or nullptr if the selection has no ...
void setTableRowHeight(int row, double height)
Sets the configured row height for the specified row.
void keyPressEvent(QKeyEvent *event) override
QgsTableContents tableContents() const
Returns the current contents of the editor widget table.
void deleteColumns()
Deletes all columns associated with the current selected cells.
void setTableContents(const QgsTableContents &contents)
Sets the contents to show in the editor widget.
void tableChanged()
Emitted whenever the table contents are changed.
void insertColumnsBefore()
Inserts new columns before the current selection.
void setSelectionNumericFormat(QgsNumericFormat *format)
Sets the numeric format to use for the currently selected cells.
void expandColumnSelection()
Expands out the selection to include whole columns associated with the current selected cells.
Q_DECL_DEPRECATED QColor selectionForegroundColor()
Returns the foreground color for the currently selected cells.
Container for all settings relating to text rendering.
void setColor(const QColor &color)
Sets the color that text will be rendered in.
bool isValid() const
Returns true if the format is valid.
QColor color() const
Returns the color that text will be rendered in.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
QVector< QgsTableRow > QgsTableContents
A set of table rows.
QVector< QgsTableCell > QgsTableRow
A row of table cells.