[composer] Initial groundwork for multiframe table item (Sponsored

by City of Uster, Switzerland)
This commit is contained in:
Nyall Dawson 2014-07-31 07:25:10 +10:00
parent 39700e7d97
commit fefc243b71
22 changed files with 4076 additions and 83 deletions

View File

@ -114,6 +114,7 @@ SET(QGIS_APP_SRCS
composer/qgsattributeselectiondialog.cpp
composer/qgscomposer.cpp
composer/qgscomposerarrowwidget.cpp
composer/qgscomposerattributetablewidget.cpp
composer/qgscomposerhtmlwidget.cpp
composer/qgscomposeritemwidget.cpp
composer/qgscomposerlabelwidget.cpp
@ -253,6 +254,7 @@ SET (QGIS_APP_MOC_HDRS
composer/qgsattributeselectiondialog.h
composer/qgscomposer.h
composer/qgscomposerarrowwidget.h
composer/qgscomposerattributetablewidget.h
composer/qgscomposerhtmlwidget.h
composer/qgscomposeritemwidget.h
composer/qgscomposerlabelwidget.h

View File

@ -16,8 +16,9 @@
***************************************************************************/
#include "qgsattributeselectiondialog.h"
#include "qgscomposerattributetable.h"
#include "qgscomposerattributetablev2.h"
#include "qgscomposerattributetablemodel.h"
#include "qgscomposerattributetablemodelv2.h"
#include "qgsvectorlayer.h"
#include "qgsfieldexpressionwidget.h"
#include <QCheckBox>
@ -219,13 +220,17 @@ void QgsComposerColumnSortOrderDelegate::updateEditorGeometry( QWidget* editor,
// QgsAttributeSelectionDialog
QgsAttributeSelectionDialog::QgsAttributeSelectionDialog( QgsComposerAttributeTable* table, QgsVectorLayer* vLayer,
QgsAttributeSelectionDialog::QgsAttributeSelectionDialog( QgsComposerAttributeTableV2* table, QgsVectorLayer* vLayer,
QWidget* parent, Qt::WindowFlags f ): QDialog( parent, f ),
mComposerTable( table ),
mComposerTableV1( 0 ),
mVectorLayer( vLayer ),
mColumnModel( 0 ),
mColumnModelV1( 0 ),
mSortedProxyModel( 0 ),
mSortedProxyModelV1( 0 ),
mAvailableSortProxyModel( 0 ),
mAvailableSortProxyModelV1( 0 ),
mColumnAlignmentDelegate( 0 ),
mColumnSortOrderDelegate( 0 )
{
@ -237,7 +242,7 @@ QgsAttributeSelectionDialog::QgsAttributeSelectionDialog( QgsComposerAttributeTa
if ( mComposerTable )
{
//set up models, views and delegates
mColumnModel = new QgsComposerAttributeTableColumnModel( mComposerTable , mColumnsTableView );
mColumnModel = new QgsComposerAttributeTableColumnModelV2( mComposerTable , mColumnsTableView );
mColumnsTableView->setModel( mColumnModel );
mColumnsTableView->horizontalHeader()->setResizeMode( QHeaderView::Stretch );
@ -246,7 +251,7 @@ QgsAttributeSelectionDialog::QgsAttributeSelectionDialog( QgsComposerAttributeTa
mColumnAlignmentDelegate = new QgsComposerColumnAlignmentDelegate( mColumnsTableView );
mColumnsTableView->setItemDelegateForColumn( 2, mColumnAlignmentDelegate );
mAvailableSortProxyModel = new QgsComposerTableSortColumnsProxyModel( mComposerTable, QgsComposerTableSortColumnsProxyModel::ShowUnsortedColumns, mSortColumnComboBox );
mAvailableSortProxyModel = new QgsComposerTableSortColumnsProxyModelV2( mComposerTable, QgsComposerTableSortColumnsProxyModelV2::ShowUnsortedColumns, mSortColumnComboBox );
mAvailableSortProxyModel->setSourceModel( mColumnModel );
mSortColumnComboBox->setModel( mAvailableSortProxyModel );
mSortColumnComboBox->setModelColumn( 0 );
@ -254,7 +259,7 @@ QgsAttributeSelectionDialog::QgsAttributeSelectionDialog( QgsComposerAttributeTa
mColumnSortOrderDelegate = new QgsComposerColumnSortOrderDelegate( mSortColumnTableView );
mSortColumnTableView->setItemDelegateForColumn( 1, mColumnSortOrderDelegate );
mSortedProxyModel = new QgsComposerTableSortColumnsProxyModel( mComposerTable, QgsComposerTableSortColumnsProxyModel::ShowSortedColumns, mSortColumnTableView );
mSortedProxyModel = new QgsComposerTableSortColumnsProxyModelV2( mComposerTable, QgsComposerTableSortColumnsProxyModelV2::ShowSortedColumns, mSortColumnTableView );
mSortedProxyModel->setSourceModel( mColumnModel );
mSortedProxyModel->sort( 0, Qt::AscendingOrder );
mSortColumnTableView->setSortingEnabled( false );
@ -266,6 +271,57 @@ QgsAttributeSelectionDialog::QgsAttributeSelectionDialog( QgsComposerAttributeTa
mOrderComboBox->insertItem( 1, tr( "Descending" ) );
}
QgsAttributeSelectionDialog::QgsAttributeSelectionDialog( QgsComposerAttributeTable *table, QgsVectorLayer *vLayer, QWidget *parent, Qt::WindowFlags f )
: QDialog( parent, f ),
mComposerTable( 0 ),
mComposerTableV1( table ),
mVectorLayer( vLayer ),
mColumnModel( 0 ),
mColumnModelV1( 0 ),
mSortedProxyModel( 0 ),
mSortedProxyModelV1( 0 ),
mAvailableSortProxyModel( 0 ),
mAvailableSortProxyModelV1( 0 ),
mColumnAlignmentDelegate( 0 ),
mColumnSortOrderDelegate( 0 )
{
setupUi( this );
QSettings settings;
restoreGeometry( settings.value( "/Windows/AttributeSelectionDialog/geometry" ).toByteArray() );
if ( mComposerTableV1 )
{
//set up models, views and delegates
mColumnModelV1 = new QgsComposerAttributeTableColumnModel( mComposerTableV1 , mColumnsTableView );
mColumnsTableView->setModel( mColumnModelV1 );
mColumnsTableView->horizontalHeader()->setResizeMode( QHeaderView::Stretch );
mColumnSourceDelegate = new QgsComposerColumnSourceDelegate( vLayer, mColumnsTableView );
mColumnsTableView->setItemDelegateForColumn( 0, mColumnSourceDelegate );
mColumnAlignmentDelegate = new QgsComposerColumnAlignmentDelegate( mColumnsTableView );
mColumnsTableView->setItemDelegateForColumn( 2, mColumnAlignmentDelegate );
mAvailableSortProxyModelV1 = new QgsComposerTableSortColumnsProxyModel( mComposerTableV1, QgsComposerTableSortColumnsProxyModel::ShowUnsortedColumns, mSortColumnComboBox );
mAvailableSortProxyModelV1->setSourceModel( mColumnModelV1 );
mSortColumnComboBox->setModel( mAvailableSortProxyModelV1 );
mSortColumnComboBox->setModelColumn( 0 );
mColumnSortOrderDelegate = new QgsComposerColumnSortOrderDelegate( mSortColumnTableView );
mSortColumnTableView->setItemDelegateForColumn( 1, mColumnSortOrderDelegate );
mSortedProxyModelV1 = new QgsComposerTableSortColumnsProxyModel( mComposerTableV1, QgsComposerTableSortColumnsProxyModel::ShowSortedColumns, mSortColumnTableView );
mSortedProxyModelV1->setSourceModel( mColumnModelV1 );
mSortedProxyModelV1->sort( 0, Qt::AscendingOrder );
mSortColumnTableView->setSortingEnabled( false );
mSortColumnTableView->setModel( mSortedProxyModelV1 );
mSortColumnTableView->horizontalHeader()->setResizeMode( QHeaderView::Stretch );
}
mOrderComboBox->insertItem( 0, tr( "Ascending" ) );
mOrderComboBox->insertItem( 1, tr( "Descending" ) );
}
QgsAttributeSelectionDialog::~QgsAttributeSelectionDialog()
{
QSettings settings;
@ -274,63 +330,86 @@ QgsAttributeSelectionDialog::~QgsAttributeSelectionDialog()
void QgsAttributeSelectionDialog::on_mRemoveColumnPushButton_clicked()
{
if ( !mComposerTable )
if ( mComposerTable )
{
return;
//remove selected row from model
QItemSelection viewSelection( mColumnsTableView->selectionModel()->selection() );
int selectedRow = viewSelection.indexes().at( 0 ).row();
mColumnModel->removeRow( selectedRow );
}
if ( mComposerTableV1 )
{
//remove selected row from model
QItemSelection viewSelection( mColumnsTableView->selectionModel()->selection() );
int selectedRow = viewSelection.indexes().at( 0 ).row();
mColumnModelV1->removeRow( selectedRow );
}
//remove selected row from model
QItemSelection viewSelection( mColumnsTableView->selectionModel()->selection() );
int selectedRow = viewSelection.indexes().at( 0 ).row();
mColumnModel->removeRow( selectedRow );
}
void QgsAttributeSelectionDialog::on_mAddColumnPushButton_clicked()
{
if ( !mComposerTable )
if ( mComposerTable )
{
return;
//add a new row to the model
mColumnModel->insertRow( mColumnModel->rowCount() );
}
else if ( mComposerTableV1 )
{
//add a new row to the model
mColumnModelV1->insertRow( mColumnModelV1->rowCount() );
}
//add a new row to the model
mColumnModel->insertRow( mColumnModel->rowCount() );
}
void QgsAttributeSelectionDialog::on_mColumnUpPushButton_clicked()
{
if ( !mComposerTable )
if ( mComposerTable )
{
return;
//move selected row up
QItemSelection viewSelection( mColumnsTableView->selectionModel()->selection() );
int selectedRow = viewSelection.indexes().at( 0 ).row();
mColumnModel->moveRow( selectedRow, QgsComposerAttributeTableColumnModelV2::ShiftUp );
}
else if ( mComposerTableV1 )
{
//move selected row up
QItemSelection viewSelection( mColumnsTableView->selectionModel()->selection() );
int selectedRow = viewSelection.indexes().at( 0 ).row();
mColumnModelV1->moveRow( selectedRow, QgsComposerAttributeTableColumnModel::ShiftUp );
}
//move selected row up
QItemSelection viewSelection( mColumnsTableView->selectionModel()->selection() );
int selectedRow = viewSelection.indexes().at( 0 ).row();
mColumnModel->moveRow( selectedRow, QgsComposerAttributeTableColumnModel::ShiftUp );
}
void QgsAttributeSelectionDialog::on_mColumnDownPushButton_clicked()
{
if ( !mComposerTable )
if ( mComposerTable )
{
return;
//move selected row down
QItemSelection viewSelection( mColumnsTableView->selectionModel()->selection() );
int selectedRow = viewSelection.indexes().at( 0 ).row();
mColumnModel->moveRow( selectedRow, QgsComposerAttributeTableColumnModelV2::ShiftDown );
}
else if ( mComposerTableV1 )
{
//move selected row down
QItemSelection viewSelection( mColumnsTableView->selectionModel()->selection() );
int selectedRow = viewSelection.indexes().at( 0 ).row();
mColumnModelV1->moveRow( selectedRow, QgsComposerAttributeTableColumnModel::ShiftDown );
}
//move selected row down
QItemSelection viewSelection( mColumnsTableView->selectionModel()->selection() );
int selectedRow = viewSelection.indexes().at( 0 ).row();
mColumnModel->moveRow( selectedRow, QgsComposerAttributeTableColumnModel::ShiftDown );
}
void QgsAttributeSelectionDialog::on_mResetColumnsPushButton_clicked()
{
if ( !mComposerTable )
if ( mComposerTable )
{
return;
//reset columns to match vector layer's fields
mColumnModel->resetToLayer();
}
else if ( mComposerTableV1 )
{
//reset columns to match vector layer's fields
mColumnModelV1->resetToLayer();
}
//reset columns to match vector layer's fields
mColumnModel->resetToLayer();
mSortColumnComboBox->setCurrentIndex( 0 );
}
@ -338,83 +417,126 @@ void QgsAttributeSelectionDialog::on_mResetColumnsPushButton_clicked()
void QgsAttributeSelectionDialog::on_mAddSortColumnPushButton_clicked()
{
//add column to sort order widget
if ( !mComposerTable )
if ( mComposerTable )
{
return;
QgsComposerTableColumn* column = mAvailableSortProxyModel->columnFromRow( mSortColumnComboBox->currentIndex() );
if ( ! column )
{
return;
}
mColumnModel->setColumnAsSorted( column, mOrderComboBox->currentIndex() == 0 ? Qt::AscendingOrder : Qt::DescendingOrder );
//required so that rows can be reordered if initially no rows were shown in the table view
mSortedProxyModel->resetFilter();
}
else if ( mComposerTableV1 )
{
QgsComposerTableColumn* column = mAvailableSortProxyModelV1->columnFromRow( mSortColumnComboBox->currentIndex() );
if ( ! column )
{
return;
}
mColumnModelV1->setColumnAsSorted( column, mOrderComboBox->currentIndex() == 0 ? Qt::AscendingOrder : Qt::DescendingOrder );
//required so that rows can be reordered if initially no rows were shown in the table view
mSortedProxyModelV1->resetFilter();
}
QgsComposerTableColumn* column = mAvailableSortProxyModel->columnFromRow( mSortColumnComboBox->currentIndex() );
if ( ! column )
{
return;
}
mColumnModel->setColumnAsSorted( column, mOrderComboBox->currentIndex() == 0 ? Qt::AscendingOrder : Qt::DescendingOrder );
//required so that rows can be reordered if initially no rows were shown in the table view
mSortedProxyModel->resetFilter();
}
void QgsAttributeSelectionDialog::on_mRemoveSortColumnPushButton_clicked()
{
//remove selected rows from sort order widget
if ( !mComposerTable )
{
return;
}
QItemSelection sortSelection( mSortColumnTableView->selectionModel()->selection() );
QModelIndex selectedIndex = sortSelection.indexes().at( 0 );
int rowToRemove = selectedIndex.row();
//find corresponding column
QgsComposerTableColumn * column = mSortedProxyModel->columnFromIndex( selectedIndex );
QgsComposerTableColumn * column = 0;
if ( mComposerTable )
{
column = mSortedProxyModel->columnFromIndex( selectedIndex );
}
else if ( mComposerTableV1 )
{
column = mSortedProxyModelV1->columnFromIndex( selectedIndex );
}
if ( !column )
{
return;
}
//set column as unsorted
mColumnModel->setColumnAsUnsorted( column );
if ( mComposerTable )
{
mColumnModel->setColumnAsUnsorted( column );
}
else if ( mComposerTableV1 )
{
mColumnModelV1->setColumnAsUnsorted( column );
}
//set next row as selected
mSortColumnTableView->selectRow( rowToRemove );
}
void QgsAttributeSelectionDialog::on_mSortColumnUpPushButton_clicked()
{
if ( !mComposerTable )
{
return;
}
//find selected row
QItemSelection sortSelection( mSortColumnTableView->selectionModel()->selection() );
QModelIndex selectedIndex = sortSelection.indexes().at( 0 );
QgsComposerTableColumn * column = mSortedProxyModel->columnFromIndex( selectedIndex );
if ( !column )
if ( mComposerTable )
{
return;
QgsComposerTableColumn * column = mSortedProxyModel->columnFromIndex( selectedIndex );
if ( !column )
{
return;
}
mColumnModel->moveColumnInSortRank( column, QgsComposerAttributeTableColumnModelV2::ShiftUp );
}
else if ( mComposerTableV1 )
{
QgsComposerTableColumn * column = mSortedProxyModelV1->columnFromIndex( selectedIndex );
if ( !column )
{
return;
}
mColumnModelV1->moveColumnInSortRank( column, QgsComposerAttributeTableColumnModel::ShiftUp );
}
mColumnModel->moveColumnInSortRank( column, QgsComposerAttributeTableColumnModel::ShiftUp );
}
void QgsAttributeSelectionDialog::on_mSortColumnDownPushButton_clicked()
{
if ( !mComposerTable )
{
return;
}
//find selected row
QItemSelection sortSelection( mSortColumnTableView->selectionModel()->selection() );
QModelIndex selectedIndex = sortSelection.indexes().at( 0 );
QgsComposerTableColumn * column = mSortedProxyModel->columnFromIndex( selectedIndex );
if ( !column )
if ( mComposerTable )
{
return;
QgsComposerTableColumn * column = mSortedProxyModel->columnFromIndex( selectedIndex );
if ( !column )
{
return;
}
mColumnModel->moveColumnInSortRank( column, QgsComposerAttributeTableColumnModelV2::ShiftDown );
}
else if ( mComposerTableV1 )
{
QgsComposerTableColumn * column = mSortedProxyModelV1->columnFromIndex( selectedIndex );
if ( !column )
{
return;
}
mColumnModelV1->moveColumnInSortRank( column, QgsComposerAttributeTableColumnModel::ShiftDown );
}
mColumnModel->moveColumnInSortRank( column, QgsComposerAttributeTableColumnModel::ShiftDown );
}

View File

@ -28,9 +28,12 @@ class QGridLayout;
class QgsVectorLayer;
class QPushButton;
class QgsComposerAttributeTable;
class QgsComposerAttributeTableV2;
class QgsComposerAttributeTableColumnModel;
class QgsComposerAttributeTableColumnModelV2;
class QgsComposerTableSortColumnsProxyModel;
class QgsComposerTableAvailableSortProxyModel;
class QgsComposerTableSortColumnsProxyModelV2;
class QgsComposerTableAvailableSortProxyModelV2;
// QgsComposerColumnAlignmentDelegate
@ -93,7 +96,12 @@ class QgsAttributeSelectionDialog: public QDialog, private Ui::QgsAttributeSelec
{
Q_OBJECT
public:
QgsAttributeSelectionDialog( QgsComposerAttributeTableV2* table, QgsVectorLayer* vLayer, QWidget * parent = 0, Qt::WindowFlags f = 0 );
//todo - remove for QGIS 3.0
QgsAttributeSelectionDialog( QgsComposerAttributeTable* table, QgsVectorLayer* vLayer, QWidget * parent = 0, Qt::WindowFlags f = 0 );
~QgsAttributeSelectionDialog();
private slots:
@ -108,12 +116,20 @@ class QgsAttributeSelectionDialog: public QDialog, private Ui::QgsAttributeSelec
void on_mSortColumnDownPushButton_clicked();
private:
QgsComposerAttributeTable* mComposerTable;
QgsComposerAttributeTableV2* mComposerTable;
QgsComposerAttributeTable* mComposerTableV1;
const QgsVectorLayer* mVectorLayer;
QgsComposerAttributeTableColumnModel* mColumnModel;
QgsComposerTableSortColumnsProxyModel* mSortedProxyModel;
QgsComposerTableSortColumnsProxyModel* mAvailableSortProxyModel;
QgsComposerAttributeTableColumnModelV2* mColumnModel;
QgsComposerAttributeTableColumnModel* mColumnModelV1;
QgsComposerTableSortColumnsProxyModelV2* mSortedProxyModel;
QgsComposerTableSortColumnsProxyModel* mSortedProxyModelV1;
QgsComposerTableSortColumnsProxyModelV2* mAvailableSortProxyModel;
QgsComposerTableSortColumnsProxyModel* mAvailableSortProxyModelV1;
QgsComposerColumnAlignmentDelegate *mColumnAlignmentDelegate;
QgsComposerColumnSourceDelegate *mColumnSourceDelegate;
QgsComposerColumnSortOrderDelegate *mColumnSortOrderDelegate;

View File

@ -29,6 +29,7 @@
#include "qgsatlascompositionwidget.h"
#include "qgscomposerarrow.h"
#include "qgscomposerarrowwidget.h"
#include "qgscomposerattributetablewidget.h"
#include "qgscomposerframe.h"
#include "qgscomposerhtml.h"
#include "qgscomposerhtmlwidget.h"
@ -46,6 +47,7 @@
#include "qgscomposershape.h"
#include "qgscomposershapewidget.h"
#include "qgscomposerattributetable.h"
#include "qgscomposerattributetablev2.h"
#include "qgscomposertablewidget.h"
#include "qgsexception.h"
#include "qgslogger.h"
@ -166,6 +168,7 @@ QgsComposer::QgsComposer( QgisApp *qgis, const QString& title )
toggleActionGroup->addAction( mActionAddEllipse );
toggleActionGroup->addAction( mActionAddArrow );
toggleActionGroup->addAction( mActionAddTable );
toggleActionGroup->addAction( mActionAddAttributeTable );
toggleActionGroup->addAction( mActionAddHtml );
toggleActionGroup->setExclusive( true );
@ -353,6 +356,7 @@ QgsComposer::QgsComposer( QgisApp *qgis, const QString& title )
layoutMenu->addAction( mActionAddImage );
layoutMenu->addAction( mActionAddArrow );
layoutMenu->addAction( mActionAddTable );
layoutMenu->addAction( mActionAddAttributeTable );
layoutMenu->addSeparator();
layoutMenu->addAction( mActionSelectMoveItem );
layoutMenu->addAction( mActionMoveItemContent );
@ -667,6 +671,7 @@ void QgsComposer::setupTheme()
mActionAddEllipse->setIcon( QgsApplication::getThemeIcon( "/mActionAddBasicShape.png" ) );
mActionAddArrow->setIcon( QgsApplication::getThemeIcon( "/mActionAddArrow.png" ) );
mActionAddTable->setIcon( QgsApplication::getThemeIcon( "/mActionOpenTable.png" ) );
mActionAddAttributeTable->setIcon( QgsApplication::getThemeIcon( "/mActionOpenTable.png" ) );
mActionAddHtml->setIcon( QgsApplication::getThemeIcon( "/mActionAddHtml.png" ) );
mActionSelectMoveItem->setIcon( QgsApplication::getThemeIcon( "/mActionSelect.svg" ) );
mActionMoveItemContent->setIcon( QgsApplication::getThemeIcon( "/mActionMoveItemContent.png" ) );
@ -730,6 +735,7 @@ void QgsComposer::connectCompositionSlots()
connect( mComposition, SIGNAL( composerPictureAdded( QgsComposerPicture* ) ), this, SLOT( addComposerPicture( QgsComposerPicture* ) ) );
connect( mComposition, SIGNAL( composerShapeAdded( QgsComposerShape* ) ), this, SLOT( addComposerShape( QgsComposerShape* ) ) );
connect( mComposition, SIGNAL( composerTableAdded( QgsComposerAttributeTable* ) ), this, SLOT( addComposerTable( QgsComposerAttributeTable* ) ) );
connect( mComposition, SIGNAL( composerTableFrameAdded( QgsComposerAttributeTableV2*, QgsComposerFrame* ) ), this, SLOT( addComposerTableV2( QgsComposerAttributeTableV2*, QgsComposerFrame* ) ) );
connect( mComposition, SIGNAL( itemRemoved( QgsComposerItem* ) ), this, SLOT( deleteItem( QgsComposerItem* ) ) );
connect( mComposition, SIGNAL( paperSizeChanged() ), mHorizontalRuler, SLOT( update() ) );
connect( mComposition, SIGNAL( paperSizeChanged() ), mVerticalRuler, SLOT( update() ) );
@ -2427,6 +2433,14 @@ void QgsComposer::on_mActionAddTable_triggered()
}
}
void QgsComposer::on_mActionAddAttributeTable_triggered()
{
if ( mView )
{
mView->setCurrentTool( QgsComposerView::AddAttributeTable );
}
}
void QgsComposer::on_mActionAddHtml_triggered()
{
if ( mView )
@ -3211,10 +3225,21 @@ void QgsComposer::addComposerTable( QgsComposerAttributeTable* table )
{
return;
}
QgsComposerTableWidget* tWidget = new QgsComposerTableWidget( table );
mItemWidgetMap.insert( table, tWidget );
}
void QgsComposer::addComposerTableV2( QgsComposerAttributeTableV2 *table, QgsComposerFrame* frame )
{
if ( !table )
{
return;
}
QgsComposerAttributeTableWidget* tWidget = new QgsComposerAttributeTableWidget( table, frame );
mItemWidgetMap.insert( frame, tWidget );
}
void QgsComposer::addComposerHtmlFrame( QgsComposerHtml* html, QgsComposerFrame* frame )
{
if ( !html )

View File

@ -34,6 +34,7 @@ class QgsComposerRuler;
class QgsComposerScaleBar;
class QgsComposerShape;
class QgsComposerAttributeTable;
class QgsComposerAttributeTableV2;
class QgsComposerView;
class QgsComposition;
class QgsMapCanvas;
@ -192,6 +193,9 @@ class QgsComposer: public QMainWindow, private Ui::QgsComposerBase
//! Add attribute table
void on_mActionAddTable_triggered();
//! Add attribute table
void on_mActionAddAttributeTable_triggered();
void on_mActionAddHtml_triggered();
//! Save parent project
@ -387,6 +391,9 @@ class QgsComposer: public QMainWindow, private Ui::QgsComposerBase
/**Adds a composer table to the item/widget map and creates a configuration widget*/
void addComposerTable( QgsComposerAttributeTable* table );
/**Adds a composer table v2 to the item/widget map and creates a configuration widget*/
void addComposerTableV2( QgsComposerAttributeTableV2* table, QgsComposerFrame* frame );
/**Adds composer html and creates a configuration widget*/
void addComposerHtmlFrame( QgsComposerHtml* html, QgsComposerFrame* frame );

View File

@ -0,0 +1,681 @@
/***************************************************************************
qgscomposerattributetablewidget.cpp
-----------------------------------
begin : September 2014
copyright : (C) 2014 by Nyall Dawson
email : nyall dot dawson at gmail dot com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "qgscomposerattributetablewidget.h"
#include "qgscomposerframe.h"
#include "qgsattributeselectiondialog.h"
#include "qgscomposeritemwidget.h"
#include "qgscomposerattributetablev2.h"
#include "qgscomposermultiframecommand.h"
#include "qgscomposertablecolumn.h"
#include "qgscomposermap.h"
#include "qgsmaplayerregistry.h"
#include "qgsvectorlayer.h"
#include "qgsexpressionbuilderdialog.h"
#include <QColorDialog>
#include <QFontDialog>
QgsComposerAttributeTableWidget::QgsComposerAttributeTableWidget( QgsComposerAttributeTableV2* table, QgsComposerFrame* frame )
: QgsComposerItemBaseWidget( 0, table )
, mComposerTable( table )
, mFrame( frame )
{
setupUi( this );
//add widget for general composer item properties
QgsComposerItemWidget* itemPropertiesWidget = new QgsComposerItemWidget( this, mFrame );
mainLayout->addWidget( itemPropertiesWidget );
blockAllSignals( true );
mLayerComboBox->setFilters( QgsMapLayerProxyModel::VectorLayer );
connect( mLayerComboBox, SIGNAL( layerChanged( QgsMapLayer* ) ), this, SLOT( changeLayer( QgsMapLayer* ) ) );
refreshMapComboBox();
mHeaderFontColorButton->setColorDialogTitle( tr( "Select header font color" ) );
mHeaderFontColorButton->setColorDialogOptions( QColorDialog::ShowAlphaChannel );
mHeaderFontColorButton->setContext( "composer" );
mContentFontColorButton->setColorDialogTitle( tr( "Select content font color" ) );
mContentFontColorButton->setColorDialogOptions( QColorDialog::ShowAlphaChannel );
mContentFontColorButton->setContext( "composer" );
mGridColorButton->setColorDialogTitle( tr( "Select grid color" ) );
mGridColorButton->setColorDialogOptions( QColorDialog::ShowAlphaChannel );
mGridColorButton->setContext( "composer" );
updateGuiElements();
on_mComposerMapComboBox_activated( mComposerMapComboBox->currentIndex() );
if ( mComposerTable )
{
QObject::connect( mComposerTable, SIGNAL( maximumNumberOfFeaturesChanged( int ) ), this, SLOT( setMaximumNumberOfFeatures( int ) ) );
QObject::connect( mComposerTable, SIGNAL( itemChanged() ), this, SLOT( updateGuiElements() ) );
}
}
QgsComposerAttributeTableWidget::~QgsComposerAttributeTableWidget()
{
}
void QgsComposerAttributeTableWidget::showEvent( QShowEvent* /* event */ )
{
refreshMapComboBox();
}
void QgsComposerAttributeTableWidget::refreshMapComboBox()
{
//save the current entry in case it is still present after refresh
QString saveCurrentComboText = mComposerMapComboBox->currentText();
mComposerMapComboBox->blockSignals( true );
mComposerMapComboBox->clear();
if ( mComposerTable )
{
const QgsComposition* tableComposition = mComposerTable->composition();
if ( tableComposition )
{
QList<const QgsComposerMap*> mapList = tableComposition->composerMapItems();
QList<const QgsComposerMap*>::const_iterator mapIt = mapList.constBegin();
for ( ; mapIt != mapList.constEnd(); ++mapIt )
{
int mapId = ( *mapIt )->id();
mComposerMapComboBox->addItem( tr( "Map %1" ).arg( mapId ), mapId );
}
}
}
mComposerMapComboBox->blockSignals( false );
if ( mComposerMapComboBox->findText( saveCurrentComboText ) == -1 )
{
//the former entry is no longer present. Inform the scalebar about the changed composer map
on_mComposerMapComboBox_activated( mComposerMapComboBox->currentIndex() );
}
else
{
//the former entry is still present. Make it the current entry again
mComposerMapComboBox->setCurrentIndex( mComposerMapComboBox->findText( saveCurrentComboText ) );
}
}
void QgsComposerAttributeTableWidget::on_mRefreshPushButton_clicked()
{
if ( !mComposerTable )
{
return;
}
mComposerTable->refreshAttributes();
}
void QgsComposerAttributeTableWidget::on_mAttributesPushButton_clicked()
{
if ( !mComposerTable )
{
return;
}
//make deep copy of current columns, so we can restore them in case of cancellation
QList<QgsComposerTableColumn*> currentColumns;
QList<QgsComposerTableColumn*>::const_iterator it = mComposerTable->columns()->constBegin();
for ( ; it != mComposerTable->columns()->constEnd() ; ++it )
{
QgsComposerTableColumn* copy = ( *it )->clone();
currentColumns.append( copy );
}
QgsComposition* composition = mComposerTable->composition();
if ( composition )
{
composition->beginMultiFrameCommand( mComposerTable, tr( "Table attribute settings" ) );
}
QgsAttributeSelectionDialog d( mComposerTable, mComposerTable->vectorLayer(), 0 );
if ( d.exec() == QDialog::Accepted )
{
mComposerTable->refreshAttributes();
mComposerTable->update();
if ( composition )
{
composition->endMultiFrameCommand();
}
//clear currentColumns to free memory
qDeleteAll( currentColumns );
currentColumns.clear();
}
else
{
//undo changes
mComposerTable->setColumns( currentColumns );
if ( composition )
{
composition->cancelMultiFrameCommand();
}
}
}
void QgsComposerAttributeTableWidget::on_mComposerMapComboBox_activated( int index )
{
if ( !mComposerTable )
{
return;
}
QVariant itemData = mComposerMapComboBox->itemData( index );
if ( itemData.type() == QVariant::Invalid )
{
return;
}
int mapId = itemData.toInt();
const QgsComposition* tableComposition = mComposerTable->composition();
if ( tableComposition )
{
QgsComposition* composition = mComposerTable->composition();
if ( sender() && composition ) //only create command if called from GUI
{
composition->beginMultiFrameCommand( mComposerTable, tr( "Table map changed" ) );
}
mComposerTable->setComposerMap( tableComposition->getComposerMapById( mapId ) );
mComposerTable->update();
if ( sender() && composition )
{
composition->endMultiFrameCommand();
}
}
}
void QgsComposerAttributeTableWidget::on_mMaximumColumnsSpinBox_valueChanged( int i )
{
if ( !mComposerTable )
{
return;
}
QgsComposition* composition = mComposerTable->composition();
if ( composition )
{
composition->beginMultiFrameCommand( mComposerTable, tr( "Table maximum columns" ), QgsComposerMultiFrameMergeCommand::TableMaximumFeatures );
}
mComposerTable->setMaximumNumberOfFeatures( i );
mComposerTable->update();
if ( composition )
{
composition->endMultiFrameCommand();
}
}
void QgsComposerAttributeTableWidget::on_mMarginSpinBox_valueChanged( double d )
{
if ( !mComposerTable )
{
return;
}
QgsComposition* composition = mComposerTable->composition();
if ( composition )
{
composition->beginMultiFrameCommand( mComposerTable, tr( "Table margin changed" ), QgsComposerMultiFrameMergeCommand::TableMargin );
}
mComposerTable->setCellMargin( d );
mComposerTable->update();
if ( composition )
{
composition->endMultiFrameCommand();
}
}
void QgsComposerAttributeTableWidget::on_mHeaderFontPushButton_clicked()
{
if ( !mComposerTable )
{
return;
}
bool ok;
#if defined(Q_WS_MAC) && defined(QT_MAC_USE_COCOA)
// Native Mac dialog works only for Qt Carbon
QFont newFont = QFontDialog::getFont( &ok, mComposerTable->headerFont(), 0, tr( "Select Font" ), QFontDialog::DontUseNativeDialog );
#else
QFont newFont = QFontDialog::getFont( &ok, mComposerTable->headerFont(), 0, tr( "Select Font" ) );
#endif
if ( ok )
{
QgsComposition* composition = mComposerTable->composition();
if ( composition )
{
composition->beginMultiFrameCommand( mComposerTable, tr( "Table header font" ) );
}
mComposerTable->setHeaderFont( newFont );
if ( composition )
{
composition->endMultiFrameCommand();
}
}
}
void QgsComposerAttributeTableWidget::on_mHeaderFontColorButton_colorChanged( const QColor &newColor )
{
if ( !mComposerTable )
{
return;
}
QgsComposition* composition = mComposerTable->composition();
if ( composition )
{
composition->beginMultiFrameCommand( mComposerTable, tr( "Table header font color" ) );
}
mComposerTable->setHeaderFontColor( newColor );
mComposerTable->update();
if ( composition )
{
composition->endMultiFrameCommand();
}
}
void QgsComposerAttributeTableWidget::on_mContentFontPushButton_clicked()
{
if ( !mComposerTable )
{
return;
}
bool ok;
#if defined(Q_WS_MAC) && defined(QT_MAC_USE_COCOA)
// Native Mac dialog works only for Qt Carbon
QFont newFont = QFontDialog::getFont( &ok, mComposerTable->contentFont(), 0, tr( "Select Font" ), QFontDialog::DontUseNativeDialog );
#else
QFont newFont = QFontDialog::getFont( &ok, mComposerTable->contentFont(), 0, tr( "Select Font" ) );
#endif
if ( ok )
{
QgsComposition* composition = mComposerTable->composition();
if ( composition )
{
composition->beginMultiFrameCommand( mComposerTable, tr( "Table content font" ) );
}
mComposerTable->setContentFont( newFont );
if ( composition )
{
composition->endMultiFrameCommand();
}
}
}
void QgsComposerAttributeTableWidget::on_mContentFontColorButton_colorChanged( const QColor &newColor )
{
if ( !mComposerTable )
{
return;
}
QgsComposition* composition = mComposerTable->composition();
if ( composition )
{
composition->beginMultiFrameCommand( mComposerTable, tr( "Table content font color" ) );
}
mComposerTable->setContentFontColor( newColor );
mComposerTable->update();
if ( composition )
{
composition->endMultiFrameCommand();
}
}
void QgsComposerAttributeTableWidget::on_mGridStrokeWidthSpinBox_valueChanged( double d )
{
if ( !mComposerTable )
{
return;
}
QgsComposition* composition = mComposerTable->composition();
if ( composition )
{
composition->beginMultiFrameCommand( mComposerTable, tr( "Table grid stroke" ), QgsComposerMultiFrameMergeCommand::TableGridStrokeWidth );
}
mComposerTable->setGridStrokeWidth( d );
mComposerTable->update();
if ( composition )
{
composition->endMultiFrameCommand();
}
}
void QgsComposerAttributeTableWidget::on_mGridColorButton_colorChanged( const QColor& newColor )
{
if ( !mComposerTable )
{
return;
}
QgsComposition* composition = mComposerTable->composition();
if ( composition )
{
composition->beginMultiFrameCommand( mComposerTable, tr( "Table grid color" ) );
}
mComposerTable->setGridColor( newColor );
mComposerTable->update();
if ( composition )
{
composition->endMultiFrameCommand();
}
}
void QgsComposerAttributeTableWidget::on_mShowGridGroupCheckBox_toggled( bool state )
{
if ( !mComposerTable )
{
return;
}
QgsComposition* composition = mComposerTable->composition();
if ( composition )
{
composition->beginMultiFrameCommand( mComposerTable, tr( "Table grid toggled" ) );
}
mComposerTable->setShowGrid( state );
mComposerTable->update();
if ( composition )
{
composition->endMultiFrameCommand();
}
}
void QgsComposerAttributeTableWidget::updateGuiElements()
{
if ( !mComposerTable )
{
return;
}
blockAllSignals( true );
//layer combo box
if ( mComposerTable->vectorLayer() )
{
mLayerComboBox->setLayer( mComposerTable->vectorLayer() );
if ( mComposerTable->vectorLayer()->geometryType() == QGis::NoGeometry )
{
//layer has no geometry, so uncheck & disable controls which require geometry
mShowOnlyVisibleFeaturesCheckBox->setChecked( false );
mShowOnlyVisibleFeaturesCheckBox->setEnabled( false );
}
else
{
mShowOnlyVisibleFeaturesCheckBox->setEnabled( true );
}
}
//map combo box
const QgsComposerMap* cm = mComposerTable->composerMap();
if ( cm )
{
int mapIndex = mComposerMapComboBox->findText( tr( "Map %1" ).arg( cm->id() ) );
if ( mapIndex != -1 )
{
mComposerMapComboBox->setCurrentIndex( mapIndex );
}
}
mMaximumColumnsSpinBox->setValue( mComposerTable->maximumNumberOfFeatures() );
mMarginSpinBox->setValue( mComposerTable->cellMargin() );
mGridStrokeWidthSpinBox->setValue( mComposerTable->gridStrokeWidth() );
mGridColorButton->setColor( mComposerTable->gridColor() );
if ( mComposerTable->showGrid() )
{
mShowGridGroupCheckBox->setChecked( true );
}
else
{
mShowGridGroupCheckBox->setChecked( false );
}
mHeaderFontColorButton->setColor( mComposerTable->headerFontColor() );
mContentFontColorButton->setColor( mComposerTable->contentFontColor() );
if ( mComposerTable->displayOnlyVisibleFeatures() && mShowOnlyVisibleFeaturesCheckBox->isEnabled() )
{
mShowOnlyVisibleFeaturesCheckBox->setCheckState( Qt::Checked );
mComposerMapComboBox->setEnabled( true );
mComposerMapLabel->setEnabled( true );
}
else
{
mShowOnlyVisibleFeaturesCheckBox->setCheckState( Qt::Unchecked );
mComposerMapComboBox->setEnabled( false );
mComposerMapLabel->setEnabled( false );
}
mFeatureFilterEdit->setText( mComposerTable->featureFilter() );
mFeatureFilterCheckBox->setCheckState( mComposerTable->filterFeatures() ? Qt::Checked : Qt::Unchecked );
mFeatureFilterEdit->setEnabled( mComposerTable->filterFeatures() );
mFeatureFilterButton->setEnabled( mComposerTable->filterFeatures() );
mHeaderHAlignmentComboBox->setCurrentIndex(( int )mComposerTable->headerHAlignment() );
blockAllSignals( false );
}
void QgsComposerAttributeTableWidget::blockAllSignals( bool b )
{
mLayerComboBox->blockSignals( b );
mComposerMapComboBox->blockSignals( b );
mMaximumColumnsSpinBox->blockSignals( b );
mMarginSpinBox->blockSignals( b );
mGridColorButton->blockSignals( b );
mGridStrokeWidthSpinBox->blockSignals( b );
mShowGridGroupCheckBox->blockSignals( b );
mShowOnlyVisibleFeaturesCheckBox->blockSignals( b );
mFeatureFilterEdit->blockSignals( b );
mFeatureFilterCheckBox->blockSignals( b );
mHeaderHAlignmentComboBox->blockSignals( b );
mHeaderFontColorButton->blockSignals( b );
mContentFontColorButton->blockSignals( b );
}
void QgsComposerAttributeTableWidget::setMaximumNumberOfFeatures( int n )
{
mMaximumColumnsSpinBox->blockSignals( true );
mMaximumColumnsSpinBox->setValue( n );
mMaximumColumnsSpinBox->blockSignals( false );
}
void QgsComposerAttributeTableWidget::on_mShowOnlyVisibleFeaturesCheckBox_stateChanged( int state )
{
if ( !mComposerTable )
{
return;
}
QgsComposition* composition = mComposerTable->composition();
if ( composition )
{
composition->beginMultiFrameCommand( mComposerTable, tr( "Table visible only toggled" ) );
}
bool showOnlyVisibleFeatures = ( state == Qt::Checked );
mComposerTable->setDisplayOnlyVisibleFeatures( showOnlyVisibleFeatures );
mComposerTable->update();
if ( composition )
{
composition->endMultiFrameCommand();
}
//enable/disable map combobox based on state of checkbox
mComposerMapComboBox->setEnabled( state == Qt::Checked );
mComposerMapLabel->setEnabled( state == Qt::Checked );
}
void QgsComposerAttributeTableWidget::on_mFeatureFilterCheckBox_stateChanged( int state )
{
if ( !mComposerTable )
{
return;
}
if ( state == Qt::Checked )
{
mFeatureFilterEdit->setEnabled( true );
mFeatureFilterButton->setEnabled( true );
}
else
{
mFeatureFilterEdit->setEnabled( false );
mFeatureFilterButton->setEnabled( false );
}
QgsComposition* composition = mComposerTable->composition();
if ( composition )
{
composition->beginMultiFrameCommand( mComposerTable, tr( "Table feature filter toggled" ) );
}
mComposerTable->setFilterFeatures( state == Qt::Checked );
mComposerTable->update();
if ( composition )
{
composition->endMultiFrameCommand();
}
}
void QgsComposerAttributeTableWidget::on_mFeatureFilterEdit_editingFinished()
{
if ( !mComposerTable )
{
return;
}
QgsComposition* composition = mComposerTable->composition();
if ( composition )
{
composition->beginMultiFrameCommand( mComposerTable, tr( "Table feature filter modified" ) );
}
mComposerTable->setFeatureFilter( mFeatureFilterEdit->text() );
mComposerTable->update();
if ( composition )
{
composition->endMultiFrameCommand();
}
}
void QgsComposerAttributeTableWidget::on_mFeatureFilterButton_clicked()
{
if ( !mComposerTable )
{
return;
}
QgsExpressionBuilderDialog exprDlg( mComposerTable->vectorLayer(), mFeatureFilterEdit->text(), this );
exprDlg.setWindowTitle( tr( "Expression based filter" ) );
if ( exprDlg.exec() == QDialog::Accepted )
{
QString expression = exprDlg.expressionText();
if ( !expression.isEmpty() )
{
mFeatureFilterEdit->setText( expression );
QgsComposition* composition = mComposerTable->composition();
if ( composition )
{
composition->beginMultiFrameCommand( mComposerTable, tr( "Table feature filter modified" ) );
}
mComposerTable->setFeatureFilter( mFeatureFilterEdit->text() );
mComposerTable->update();
if ( composition )
{
composition->endMultiFrameCommand();
}
}
}
}
void QgsComposerAttributeTableWidget::on_mHeaderHAlignmentComboBox_currentIndexChanged( int index )
{
if ( !mComposerTable )
{
return;
}
QgsComposition* composition = mComposerTable->composition();
if ( composition )
{
composition->beginMultiFrameCommand( mComposerTable, tr( "Table header alignment changed" ) );
}
mComposerTable->setHeaderHAlignment(( QgsComposerTableV2::HeaderHAlignment )index );
if ( composition )
{
composition->endMultiFrameCommand();
}
}
void QgsComposerAttributeTableWidget::changeLayer( QgsMapLayer *layer )
{
if ( !mComposerTable )
{
return;
}
QgsVectorLayer* vl = dynamic_cast<QgsVectorLayer*>( layer );
if ( !vl )
{
return;
}
QgsComposition* composition = mComposerTable->composition();
if ( composition )
{
composition->beginMultiFrameCommand( mComposerTable, tr( "Table layer changed" ) );
}
mComposerTable->setVectorLayer( vl );
mComposerTable->update();
if ( composition )
{
composition->endMultiFrameCommand();
}
if ( vl->geometryType() == QGis::NoGeometry )
{
//layer has no geometry, so uncheck & disable controls which require geometry
mShowOnlyVisibleFeaturesCheckBox->setChecked( false );
mShowOnlyVisibleFeaturesCheckBox->setEnabled( false );
}
else
{
mShowOnlyVisibleFeaturesCheckBox->setEnabled( true );
}
}
void QgsComposerAttributeTableWidget::on_mAddFramePushButton_clicked()
{
if ( !mComposerTable || !mFrame )
{
return;
}
//create a new frame based on the current frame
QPointF pos = mFrame->pos();
//shift new frame so that it sits 10 units below current frame
pos.ry() += mFrame->rect().height() + 10;
QgsComposerFrame * newFrame = mComposerTable->createNewFrame( mFrame, pos, mFrame->rect().size() );
mComposerTable->recalculateFrameSizes();
//set new frame as selection
QgsComposition* composition = mComposerTable->composition();
if ( composition )
{
composition->setSelectedItem( newFrame );
}
}

View File

@ -0,0 +1,74 @@
/***************************************************************************
qgscomposerattributetablewidget.h
---------------------------------
begin : September 2014
copyright : (C) 2014 by Nyall Dawson
email : nyall dot dawson at gmail dot com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#ifndef QGSCOMPOSERATTRIBUTETABLEWIDGET_H
#define QGSCOMPOSERATTRIBUTETABLEWIDGET_H
#include "ui_qgscomposerattributetablewidgetbase.h"
#include "qgscomposeritemwidget.h"
class QgsComposerAttributeTableV2;
class QgsComposerFrame;
class QgsComposerAttributeTableWidget: public QgsComposerItemBaseWidget, private Ui::QgsComposerAttributeTableWidgetBase
{
Q_OBJECT
public:
QgsComposerAttributeTableWidget( QgsComposerAttributeTableV2* table, QgsComposerFrame* frame );
~QgsComposerAttributeTableWidget();
protected:
void showEvent( QShowEvent * event );
private:
QgsComposerAttributeTableV2* mComposerTable;
QgsComposerFrame* mFrame;
/**Blocks / unblocks the signals of all GUI elements*/
void blockAllSignals( bool b );
void refreshMapComboBox();
private slots:
void on_mRefreshPushButton_clicked();
void on_mAttributesPushButton_clicked();
void on_mComposerMapComboBox_activated( int index );
void on_mMaximumColumnsSpinBox_valueChanged( int i );
void on_mMarginSpinBox_valueChanged( double d );
void on_mGridStrokeWidthSpinBox_valueChanged( double d );
void on_mGridColorButton_colorChanged( const QColor& newColor );
void on_mHeaderFontPushButton_clicked();
void on_mHeaderFontColorButton_colorChanged( const QColor& newColor );
void on_mContentFontPushButton_clicked();
void on_mContentFontColorButton_colorChanged( const QColor& newColor );
void on_mShowGridGroupCheckBox_toggled( bool state );
void on_mShowOnlyVisibleFeaturesCheckBox_stateChanged( int state );
void on_mFeatureFilterCheckBox_stateChanged( int state );
void on_mFeatureFilterEdit_editingFinished();
void on_mFeatureFilterButton_clicked();
void on_mHeaderHAlignmentComboBox_currentIndexChanged( int index );
void changeLayer( QgsMapLayer* layer );
void on_mAddFramePushButton_clicked();
/**Inserts a new maximum number of features into the spin box (without the spinbox emitting a signal)*/
void setMaximumNumberOfFeatures( int n );
/**Sets the GUI elements to the values of mComposerTable*/
void updateGuiElements();
};
#endif // QGSCOMPOSERATTRIBUTETABLEWIDGET_H

View File

@ -25,7 +25,10 @@
#include <QSettings>
QgsComposerHtmlWidget::QgsComposerHtmlWidget( QgsComposerHtml* html, QgsComposerFrame* frame ): QgsComposerItemBaseWidget( 0, html ), mHtml( html ), mFrame( frame )
QgsComposerHtmlWidget::QgsComposerHtmlWidget( QgsComposerHtml* html, QgsComposerFrame* frame )
: QgsComposerItemBaseWidget( 0, html )
, mHtml( html )
, mFrame( frame )
{
setupUi( this );

View File

@ -181,9 +181,12 @@ SET(QGIS_CORE_SRCS
composer/qgscomposermapgrid.cpp
composer/qgscomposermapoverview.cpp
composer/qgscomposertable.cpp
composer/qgscomposertablev2.cpp
composer/qgscomposertablecolumn.cpp
composer/qgscomposerattributetable.cpp
composer/qgscomposerattributetablev2.cpp
composer/qgscomposerattributetablemodel.cpp
composer/qgscomposerattributetablemodelv2.cpp
composer/qgscomposertexttable.cpp
composer/qgscomposerscalebar.cpp
composer/qgscomposershape.cpp
@ -385,8 +388,11 @@ SET(QGIS_CORE_MOC_HDRS
composer/qgscomposerlabel.h
composer/qgscomposershape.h
composer/qgscomposerattributetable.h
composer/qgscomposerattributetablemodel.h
composer/qgscomposerattributetablev2.h
composer/qgscomposerattributetablemodel.h
composer/qgscomposerattributetablemodelv2.h
composer/qgscomposertable.h
composer/qgscomposertablev2.h
composer/qgscomposertablecolumn.h
composer/qgscomposerhtml.h
composer/qgscomposermultiframe.h
@ -561,6 +567,7 @@ SET(QGIS_CORE_HDRS
composer/qgsaddremovemultiframecommand.h
composer/qgsdoubleboxscalebarstyle.h
composer/qgscomposertable.h
composer/qgscomposertablev2.h
composer/qgscomposertablecolumn.h
composer/qgspaperitem.h
composer/qgsticksscalebarstyle.h

View File

@ -0,0 +1,598 @@
/***************************************************************************
qgscomposerattributetablemodelv2.cpp
--------------------
begin : September 2014
copyright : (C) 2014 by Nyall Dawson
email : nyall dot dawson at gmail dot com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "qgscomposerattributetablev2.h"
#include "qgscomposerattributetablemodelv2.h"
#include "qgscomposertablev2.h"
#include "qgscomposertablecolumn.h"
//QgsComposerAttributeTableColumnModelV2V2
QgsComposerAttributeTableColumnModelV2::QgsComposerAttributeTableColumnModelV2( QgsComposerAttributeTableV2 *composerTable, QObject *parent ) : QAbstractTableModel( parent )
, mComposerTable( composerTable )
{
}
QgsComposerAttributeTableColumnModelV2::~QgsComposerAttributeTableColumnModelV2()
{
}
QModelIndex QgsComposerAttributeTableColumnModelV2::index( int row, int column, const QModelIndex &parent ) const
{
if ( hasIndex( row, column, parent ) )
{
if (( *mComposerTable->columns() )[row] )
{
return createIndex( row, column, ( *mComposerTable->columns() )[row] );
}
}
return QModelIndex();
}
QModelIndex QgsComposerAttributeTableColumnModelV2::parent( const QModelIndex &child ) const
{
Q_UNUSED( child );
return QModelIndex();
}
int QgsComposerAttributeTableColumnModelV2::rowCount( const QModelIndex &parent ) const
{
if ( parent.isValid() )
return 0;
return mComposerTable->columns()->length();
}
int QgsComposerAttributeTableColumnModelV2::columnCount( const QModelIndex &parent ) const
{
Q_UNUSED( parent );
return 3;
}
QVariant QgsComposerAttributeTableColumnModelV2::data( const QModelIndex &index, int role ) const
{
if ( !index.isValid() ||
( role != Qt::DisplayRole && role != Qt::EditRole && role != Qt::UserRole ) )
{
return QVariant();
}
if ( index.row() >= mComposerTable->columns()->length() )
{
return QVariant();
}
//get column for index
QgsComposerTableColumn* column = columnFromIndex( index );
if ( !column )
{
return QVariant();
}
if ( role == Qt::UserRole )
{
//user role stores reference in column object
return qVariantFromValue( qobject_cast<QObject *>( column ) );
}
switch ( index.column() )
{
case 0:
return column->attribute();
case 1:
return column->heading();
case 2:
{
if ( role == Qt::DisplayRole )
{
switch ( column->hAlignment() )
{
case Qt::AlignHCenter:
return tr( "Center" );
case Qt::AlignRight:
return tr( "Right" );
case Qt::AlignLeft:
default:
return tr( "Left" );
}
}
else
{
//edit role
return column->hAlignment();
}
}
default:
return QVariant();
}
}
QVariant QgsComposerAttributeTableColumnModelV2::headerData( int section, Qt::Orientation orientation, int role ) const
{
if ( !mComposerTable )
{
return QVariant();
}
if ( role == Qt::DisplayRole )
{
if ( orientation == Qt::Vertical ) //row
{
return QVariant( section );
}
else
{
switch ( section )
{
case 0:
return QVariant( tr( "Attribute" ) );
case 1:
return QVariant( tr( "Heading" ) );
case 2:
return QVariant( tr( "Alignment" ) );
default:
return QVariant();
}
}
}
else
{
return QVariant();
}
}
bool QgsComposerAttributeTableColumnModelV2::setData( const QModelIndex& index, const QVariant& value, int role )
{
if ( !index.isValid() || role != Qt::EditRole || !mComposerTable )
{
return false;
}
if ( index.row() >= mComposerTable->columns()->length() )
{
return false;
}
//get column for index
QgsComposerTableColumn* column = columnFromIndex( index );
if ( !column )
{
return false;
}
switch ( index.column() )
{
case 0:
// also update column's heading, if it hasn't been customised
if ( column->heading().isEmpty() || ( column->heading() == column->attribute() ) )
{
column->setHeading( value.toString() );
emit dataChanged( createIndex( index.row(), 1, 0 ), createIndex( index.row(), 1, 0 ) );
}
column->setAttribute( value.toString() );
emit dataChanged( index, index );
return true;
case 1:
column->setHeading( value.toString() );
emit dataChanged( index, index );
return true;
case 2:
column->setHAlignment(( Qt::AlignmentFlag )value.toInt() );
emit dataChanged( index, index );
return true;
default:
break;
}
return false;
}
Qt::ItemFlags QgsComposerAttributeTableColumnModelV2::flags( const QModelIndex& index ) const
{
Qt::ItemFlags flags = QAbstractItemModel::flags( index );
if ( index.isValid() )
{
return flags | Qt::ItemIsEditable;
}
else
{
return flags;
}
}
bool QgsComposerAttributeTableColumnModelV2::removeRows( int row, int count, const QModelIndex& parent )
{
Q_UNUSED( parent );
int maxRow = qMin( row + count - 1, mComposerTable->columns()->length() - 1 );
beginRemoveRows( QModelIndex(), row, maxRow );
//move backwards through rows, removing each corresponding QgsComposerTableColumn
for ( int i = maxRow; i >= row; --i )
{
delete( *mComposerTable->columns() )[i];
mComposerTable->columns()->removeAt( i );
}
endRemoveRows();
return true;
}
bool QgsComposerAttributeTableColumnModelV2::insertRows( int row, int count, const QModelIndex& parent )
{
Q_UNUSED( parent );
beginInsertRows( QModelIndex(), row, row + count - 1 );
//create new QgsComposerTableColumns for each inserted row
for ( int i = row; i < row + count; ++i )
{
QgsComposerTableColumn* col = new QgsComposerTableColumn;
mComposerTable->columns()->insert( i, col );
}
endInsertRows();
return true;
}
bool QgsComposerAttributeTableColumnModelV2::moveRow( int row, ShiftDirection direction )
{
if (( direction == ShiftUp && row <= 0 ) ||
( direction == ShiftDown && row >= rowCount() - 1 ) )
{
//row is already at top/bottom
return false;
}
//we shift a row by removing the next row up/down, then reinserting it before/after the target row
int swapWithRow = direction == ShiftUp ? row - 1 : row + 1;
//remove row
beginRemoveRows( QModelIndex(), swapWithRow, swapWithRow );
QgsComposerTableColumn* temp = mComposerTable->columns()->takeAt( swapWithRow );
endRemoveRows();
//insert row
beginInsertRows( QModelIndex(), row, row );
mComposerTable->columns()->insert( row, temp );
endInsertRows();
return true;
}
void QgsComposerAttributeTableColumnModelV2::resetToLayer()
{
beginResetModel();
mComposerTable->resetColumns();
endResetModel();
}
QgsComposerTableColumn* QgsComposerAttributeTableColumnModelV2::columnFromIndex( const QModelIndex &index ) const
{
QgsComposerTableColumn* column = static_cast<QgsComposerTableColumn*>( index.internalPointer() );
return column;
}
QModelIndex QgsComposerAttributeTableColumnModelV2::indexFromColumn( QgsComposerTableColumn* column )
{
if ( !mComposerTable )
{
return QModelIndex();
}
int r = mComposerTable->columns()->indexOf( column );
QModelIndex idx = index( r, 0, QModelIndex() );
if ( idx.isValid() )
{
return idx;
}
return QModelIndex();
}
void QgsComposerAttributeTableColumnModelV2::setColumnAsSorted( QgsComposerTableColumn* column, Qt::SortOrder order )
{
if ( !column || !mComposerTable )
{
return;
}
//find current highest sort by rank
int highestRank = 0;
QList<QgsComposerTableColumn*>::const_iterator columnIt = mComposerTable->columns()->constBegin();
for ( ; columnIt != mComposerTable->columns()->constEnd(); ++columnIt )
{
highestRank = qMax( highestRank, ( *columnIt )->sortByRank() );
}
column->setSortByRank( highestRank + 1 );
column->setSortOrder( order );
QModelIndex idx = indexFromColumn( column );
emit dataChanged( idx, idx );
}
void QgsComposerAttributeTableColumnModelV2::setColumnAsUnsorted( QgsComposerTableColumn * column )
{
if ( !mComposerTable || !column )
{
return;
}
column->setSortByRank( 0 );
QModelIndex idx = indexFromColumn( column );
emit dataChanged( idx, idx );
}
static bool columnsBySortRank( QgsComposerTableColumn * a, QgsComposerTableColumn * b )
{
return a->sortByRank() < b->sortByRank();
}
bool QgsComposerAttributeTableColumnModelV2::moveColumnInSortRank( QgsComposerTableColumn * column, ShiftDirection direction )
{
if ( !mComposerTable || !column )
{
return false;
}
if (( direction == ShiftUp && column->sortByRank() <= 1 )
|| ( direction == ShiftDown && column->sortByRank() <= 0 ) )
{
//already at start/end of list or not being used for sort
return false;
}
//find column before this one in sort order
QList<QgsComposerTableColumn*> sortedColumns;
QList<QgsComposerTableColumn*>::iterator columnIt = mComposerTable->columns()->begin();
for ( ; columnIt != mComposerTable->columns()->end(); ++columnIt )
{
if (( *columnIt )->sortByRank() > 0 )
{
sortedColumns.append( *columnIt );
}
}
qStableSort( sortedColumns.begin(), sortedColumns.end(), columnsBySortRank );
int columnPos = sortedColumns.indexOf( column );
if (( columnPos == 0 && direction == ShiftUp )
|| (( columnPos == sortedColumns.length() - 1 ) && direction == ShiftDown ) )
{
//column already at start/end
return false;
}
QgsComposerTableColumn* swapColumn = direction == ShiftUp ?
sortedColumns[ columnPos - 1]
: sortedColumns[ columnPos + 1];
QModelIndex idx = indexFromColumn( column );
QModelIndex idxSwap = indexFromColumn( swapColumn );
//now swap sort ranks
int oldSortRank = column->sortByRank();
column->setSortByRank( swapColumn->sortByRank() );
emit dataChanged( idx, idx );
swapColumn->setSortByRank( oldSortRank );
emit dataChanged( idxSwap, idxSwap );
return true;
}
//QgsComposerTableSortColumnsProxyModelV2V2
QgsComposerTableSortColumnsProxyModelV2::QgsComposerTableSortColumnsProxyModelV2( QgsComposerAttributeTableV2 *composerTable, ColumnFilterType filterType, QObject *parent )
: QSortFilterProxyModel( parent )
, mComposerTable( composerTable )
, mFilterType( filterType )
{
setDynamicSortFilter( true );
}
QgsComposerTableSortColumnsProxyModelV2::~QgsComposerTableSortColumnsProxyModelV2()
{
}
bool QgsComposerTableSortColumnsProxyModelV2::filterAcceptsRow( int source_row, const QModelIndex &source_parent ) const
{
//get QgsComposerTableColumn corresponding to row
QModelIndex index = sourceModel()->index( source_row, 0, source_parent );
QgsComposerTableColumn* column = columnFromSourceIndex( index );
if ( !column )
{
return false;
}
if (( column->sortByRank() > 0 && mFilterType == ShowSortedColumns )
|| ( column->sortByRank() <= 0 && mFilterType == ShowUnsortedColumns ) )
{
//column matches filter type
return true;
}
else
{
return false;
}
}
QgsComposerTableColumn *QgsComposerTableSortColumnsProxyModelV2::columnFromIndex( const QModelIndex &index ) const
{
//get column corresponding to an index from the proxy
QModelIndex sourceIndex = mapToSource( index );
return columnFromSourceIndex( sourceIndex );
}
QgsComposerTableColumn* QgsComposerTableSortColumnsProxyModelV2::columnFromSourceIndex( const QModelIndex &sourceIndex ) const
{
//get column corresponding to an index from the source model
QVariant columnAsVariant = sourceModel()->data( sourceIndex, Qt::UserRole );
QgsComposerTableColumn* column = qobject_cast<QgsComposerTableColumn *>( columnAsVariant.value<QObject *>() );
return column;
}
bool QgsComposerTableSortColumnsProxyModelV2::lessThan( const QModelIndex &left, const QModelIndex &right ) const
{
QgsComposerTableColumn* column1 = columnFromSourceIndex( left );
QgsComposerTableColumn* column2 = columnFromSourceIndex( right );
if ( !column1 )
{
return false;
}
if ( !column2 )
{
return true;
}
return column1->sortByRank() < column2->sortByRank();
}
int QgsComposerTableSortColumnsProxyModelV2::columnCount( const QModelIndex &parent ) const
{
Q_UNUSED( parent );
return 2;
}
QVariant QgsComposerTableSortColumnsProxyModelV2::data( const QModelIndex &index, int role ) const
{
if (( role != Qt::DisplayRole && role != Qt::EditRole ) || !index.isValid() )
{
return QVariant();
}
QgsComposerTableColumn* column = columnFromIndex( index );
if ( !column )
{
return QVariant();
}
switch ( index.column() )
{
case 0:
return column->attribute();
case 1:
if ( role == Qt::DisplayRole )
{
switch ( column->sortOrder() )
{
case Qt::DescendingOrder:
return tr( "Descending" );
case Qt::AscendingOrder:
default:
return tr( "Ascending" );
}
}
else
{
//edit role
return column->sortOrder();
}
default:
return QVariant();
}
}
QVariant QgsComposerTableSortColumnsProxyModelV2::headerData( int section, Qt::Orientation orientation, int role ) const
{
if ( !mComposerTable )
{
return QVariant();
}
if ( role == Qt::DisplayRole )
{
if ( orientation == Qt::Vertical ) //row
{
return QVariant( section );
}
else
{
switch ( section )
{
case 0:
return QVariant( tr( "Attribute" ) );
case 1:
return QVariant( tr( "Sort Order" ) );
default:
return QVariant();
}
}
}
else
{
return QVariant();
}
}
Qt::ItemFlags QgsComposerTableSortColumnsProxyModelV2::flags( const QModelIndex& index ) const
{
Qt::ItemFlags flags = QAbstractItemModel::flags( index );
if ( index.column() == 1 )
{
//only sort order is editable
flags |= Qt::ItemIsEditable;
}
return flags;
}
bool QgsComposerTableSortColumnsProxyModelV2::setData( const QModelIndex& index, const QVariant& value, int role )
{
if ( !index.isValid() || role != Qt::EditRole )
return false;
if ( !mComposerTable )
{
return false;
}
QgsComposerTableColumn* column = columnFromIndex( index );
if ( !column )
{
return false;
}
if ( index.column() == 1 )
{
column->setSortOrder(( Qt::SortOrder )value.toInt() );
emit dataChanged( index, index );
return true;
}
return false;
}
QgsComposerTableColumn *QgsComposerTableSortColumnsProxyModelV2::columnFromRow( int row )
{
QModelIndex proxyIndex = index( row, 0 );
return columnFromIndex( proxyIndex );
}
void QgsComposerTableSortColumnsProxyModelV2::resetFilter()
{
invalidate();
}

View File

@ -0,0 +1,205 @@
/***************************************************************************
qgscomposerattributetablemodelv2.h
--------------------
begin : September 2014
copyright : (C) 2014 by Nyall Dawson
email : nyall dot dawson at gmail dot com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#ifndef QGSCOMPOSERATTRIBUTETABLEMODELV2_H
#define QGSCOMPOSERATTRIBUTETABLEMODELV2_H
#include <QAbstractTableModel>
#include <QSortFilterProxyModel>
class QgsComposerAttributeTableV2;
class QgsComposerTableColumn;
//QgsComposerAttributeTableColumnModelV2
/**A model for displaying columns shown in a QgsComposerAttributeTableV2*/
class CORE_EXPORT QgsComposerAttributeTableColumnModelV2: public QAbstractTableModel
{
Q_OBJECT
public:
/*! Controls whether a row/column is shifted up or down
*/
enum ShiftDirection
{
ShiftUp, /*!< shift the row/column up */
ShiftDown /*!< shift the row/column down */
};
/**Constructor for QgsComposerAttributeTableColumnModel.
* @param composerTable QgsComposerAttributeTable the model is attached to
* @param parent optional parent
*/
QgsComposerAttributeTableColumnModelV2( QgsComposerAttributeTableV2 *composerTable, QObject *parent = 0 );
virtual ~QgsComposerAttributeTableColumnModelV2();
virtual int rowCount( const QModelIndex &parent = QModelIndex() ) const;
int columnCount( const QModelIndex &parent = QModelIndex() ) const;
virtual QVariant data( const QModelIndex &index, int role ) const;
QVariant headerData( int section, Qt::Orientation orientation, int role = Qt::DisplayRole ) const;
virtual bool setData( const QModelIndex &index, const QVariant &value, int role = Qt::EditRole );
Qt::ItemFlags flags( const QModelIndex &index ) const;
bool removeRows( int row, int count, const QModelIndex &parent = QModelIndex() );
bool insertRows( int row, int count, const QModelIndex &parent = QModelIndex() );
QModelIndex index( int row, int column, const QModelIndex &parent ) const;
QModelIndex parent( const QModelIndex &child ) const;
/**Moves the specified row up or down in the model. Used for rearranging the attribute tables
* columns.
* @returns true if the move is allowed
* @param row row in model representing attribute table column to move
* @param direction direction to move the attribute table column
* @note added in 2.3
*/
bool moveRow( int row , ShiftDirection direction );
/**Resets the attribute table's columns to match the source layer's fields. Remove all existing
* attribute table columns and column customisations.
* @note added in 2.3
*/
void resetToLayer();
/**Returns the QgsComposerTableColumn corresponding to an index in the model.
* @returns QgsComposerTableColumn for specified index
* @param index a QModelIndex
* @note added in 2.3
* @see indexFromColumn
*/
QgsComposerTableColumn* columnFromIndex( const QModelIndex & index ) const;
/**Returns a QModelIndex corresponding to a QgsComposerTableColumn in the model.
* @returns QModelIndex for specified QgsComposerTableColumn
* @param column a QgsComposerTableColumn
* @note added in 2.3
* @see columnFromIndex
*/
QModelIndex indexFromColumn( QgsComposerTableColumn *column );
/**Sets a specified column as a sorted column in the QgsComposerAttributeTable. The column will be
* added to the end of the sort rank list, ie it will take the next largest available sort rank.
* @param column a QgsComposerTableColumn
* @param order sort order for column
* @note added in 2.3
* @see removeColumnFromSort
* @see moveColumnInSortRank
*/
void setColumnAsSorted( QgsComposerTableColumn *column, Qt::SortOrder order );
/**Sets a specified column as an unsorted column in the QgsComposerAttributeTable. The column will be
* removed from the sort rank list.
* @param column a QgsComposerTableColumn
* @note added in 2.3
* @see setColumnAsSorted
*/
void setColumnAsUnsorted( QgsComposerTableColumn * column );
/**Moves a column up or down in the sort rank for the QgsComposerAttributeTable.
* @param column a QgsComposerTableColumn
* @param direction direction to move the column in the sort rank list
* @note added in 2.3
* @see setColumnAsSorted
*/
bool moveColumnInSortRank( QgsComposerTableColumn * column, ShiftDirection direction );
private:
QgsComposerAttributeTableV2 * mComposerTable;
};
//QgsComposerTableSortColumnsProxyModelV2
/**Allows for filtering QgsComposerAttributeTable columns by columns which are sorted or unsorted*/
class CORE_EXPORT QgsComposerTableSortColumnsProxyModelV2: public QSortFilterProxyModel
{
Q_OBJECT
public:
/*! Controls whether the proxy model shows sorted or unsorted columns
*/
enum ColumnFilterType
{
ShowSortedColumns, /*!< show only sorted columns */
ShowUnsortedColumns/*!< show only unsorted columns */
};
/**Constructor for QgsComposerTableSortColumnsProxyModel.
* @param composerTable QgsComposerAttributeTable the model is attached to
* @param filterType filter for columns, controls whether sorted or unsorted columns are shown
* @param parent optional parent
*/
QgsComposerTableSortColumnsProxyModelV2( QgsComposerAttributeTableV2 *composerTable, ColumnFilterType filterType, QObject *parent = 0 );
virtual ~QgsComposerTableSortColumnsProxyModelV2();
bool lessThan( const QModelIndex &left, const QModelIndex &right ) const;
int columnCount( const QModelIndex &parent = QModelIndex() ) const;
virtual QVariant data( const QModelIndex &index, int role ) const;
QVariant headerData( int section, Qt::Orientation orientation, int role = Qt::DisplayRole ) const;
Qt::ItemFlags flags( const QModelIndex &index ) const;
virtual bool setData( const QModelIndex &index, const QVariant &value, int role = Qt::EditRole );
/**Returns the QgsComposerTableColumn corresponding to a row in the proxy model.
* @returns QgsComposerTableColumn for specified row
* @param row a row number
* @note added in 2.3
* @see columnFromIndex
*/
QgsComposerTableColumn* columnFromRow( int row );
/**Returns the QgsComposerTableColumn corresponding to an index in the proxy model.
* @returns QgsComposerTableColumn for specified index
* @param index a QModelIndex
* @note added in 2.3
* @see columnFromRow
* @see columnFromSourceIndex
*/
QgsComposerTableColumn* columnFromIndex( const QModelIndex & index ) const;
/**Returns the QgsComposerTableColumn corresponding to an index from the source
* QgsComposerAttributeTableColumnModel model.
* @returns QgsComposerTableColumn for specified index from QgsComposerAttributeTableColumnModel
* @param sourceIndex a QModelIndex
* @note added in 2.3
* @see columnFromRow
* @see columnFromIndex
*/
QgsComposerTableColumn* columnFromSourceIndex( const QModelIndex& sourceIndex ) const;
/**Invalidates the current filter used by the proxy model
* @note added in 2.3
*/
void resetFilter();
protected:
bool filterAcceptsRow( int source_row, const QModelIndex & source_parent ) const;
private:
QgsComposerAttributeTableV2 * mComposerTable;
ColumnFilterType mFilterType;
/**Returns a list of QgsComposerTableColumns without a set sort rank
* @returns QgsComposerTableColumns in attribute table without a sort rank
* @note added in 2.3
*/
QList<QgsComposerTableColumn*> columnsWithoutSortRank() const;
};
#endif // QGSCOMPOSERATTRIBUTETABLEMODELV2_H

View File

@ -0,0 +1,644 @@
/***************************************************************************
QgsComposerAttributeTableV2.cpp
-----------------------------
begin : September 2014
copyright : (C) 2014 by Marco Hugentobler
email : marco at hugis dot net
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "qgscomposerattributetablev2.h"
#include "qgscomposertablecolumn.h"
#include "qgscomposermap.h"
#include "qgscomposerutils.h"
#include "qgsmaplayerregistry.h"
#include "qgsvectorlayer.h"
#include "qgscomposerframe.h"
//QgsComposerAttributeTableCompareV2
QgsComposerAttributeTableCompareV2::QgsComposerAttributeTableCompareV2()
: mCurrentSortColumn( 0 ), mAscending( true )
{
}
bool QgsComposerAttributeTableCompareV2::operator()( const QgsComposerTableRow& m1, const QgsComposerTableRow& m2 )
{
QVariant v1 = m1[mCurrentSortColumn];
QVariant v2 = m2[mCurrentSortColumn];
bool less = false;
//sort null values first
if ( v1.isNull() && v2.isNull() )
{
less = false;
}
else if ( v1.isNull() )
{
less = true;
}
else if ( v2.isNull() )
{
less = false;
}
else
{
//otherwise sort by converting to corresponding type and comparing
switch ( v1.type() )
{
case QVariant::Int:
case QVariant::UInt:
case QVariant::LongLong:
case QVariant::ULongLong:
less = v1.toLongLong() < v2.toLongLong();
break;
case QVariant::Double:
less = v1.toDouble() < v2.toDouble();
break;
case QVariant::Date:
less = v1.toDate() < v2.toDate();
break;
case QVariant::DateTime:
less = v1.toDateTime() < v2.toDateTime();
break;
case QVariant::Time:
less = v1.toTime() < v2.toTime();
break;
default:
//use locale aware compare for strings
less = v1.toString().localeAwareCompare( v2.toString() ) < 0;
break;
}
}
return ( mAscending ? less : !less );
}
//
// QgsComposerAttributeTableV2
//
QgsComposerAttributeTableV2::QgsComposerAttributeTableV2( QgsComposition* composition, bool createUndoCommands )
: QgsComposerTableV2( composition, createUndoCommands )
, mVectorLayer( 0 )
, mComposerMap( 0 )
, mMaximumNumberOfFeatures( 5 )
, mShowOnlyVisibleFeatures( false )
, mFilterFeatures( false )
, mFeatureFilter( "" )
{
//set first vector layer from layer registry as default one
QMap<QString, QgsMapLayer*> layerMap = QgsMapLayerRegistry::instance()->mapLayers();
QMap<QString, QgsMapLayer*>::const_iterator mapIt = layerMap.constBegin();
for ( ; mapIt != layerMap.constEnd(); ++mapIt )
{
QgsVectorLayer* vl = dynamic_cast<QgsVectorLayer*>( mapIt.value() );
if ( vl )
{
mVectorLayer = vl;
break;
}
}
if ( mVectorLayer )
{
resetColumns();
}
connect( QgsMapLayerRegistry::instance(), SIGNAL( layerWillBeRemoved( QString ) ), this, SLOT( removeLayer( const QString& ) ) );
if ( mComposition )
{
//refresh table attributes when composition is refreshed
connect( mComposition, SIGNAL( refreshItemsTriggered() ), this, SLOT( refreshAttributes() ) );
//connect to atlas feature changes to update table rows
connect( &mComposition->atlasComposition(), SIGNAL( featureChanged( QgsFeature* ) ), this, SLOT( refreshAttributes() ) );
}
}
QgsComposerAttributeTableV2::~QgsComposerAttributeTableV2()
{
}
void QgsComposerAttributeTableV2::setVectorLayer( QgsVectorLayer* layer )
{
if ( layer == mVectorLayer )
{
//no change
return;
}
if ( mVectorLayer )
{
//disconnect from previous layer
QObject::disconnect( mVectorLayer, SIGNAL( layerModified() ), this, SLOT( refreshAttributes() ) );
}
mVectorLayer = layer;
//rebuild column list to match all columns from layer
resetColumns();
refreshAttributes();
//listen for modifications to layer and refresh table when they occur
QObject::connect( mVectorLayer, SIGNAL( layerModified() ), this, SLOT( refreshAttributes() ) );
}
void QgsComposerAttributeTableV2::resetColumns()
{
if ( !mVectorLayer )
{
return;
}
//remove existing columns
qDeleteAll( mColumns );
mColumns.clear();
//rebuild columns list from vector layer fields
const QgsFields& fields = mVectorLayer->pendingFields();
for ( int idx = 0; idx < fields.count(); ++idx )
{
QString currentAlias = mVectorLayer->attributeDisplayName( idx );
QgsComposerTableColumn* col = new QgsComposerTableColumn;
col->setAttribute( fields[idx].name() );
col->setHeading( currentAlias );
mColumns.append( col );
}
}
void QgsComposerAttributeTableV2::setComposerMap( const QgsComposerMap* map )
{
if ( map == mComposerMap )
{
//no change
return;
}
if ( mComposerMap )
{
//disconnect from previous map
QObject::disconnect( mComposerMap, SIGNAL( extentChanged() ), this, SLOT( refreshAttributes() ) );
}
mComposerMap = map;
if ( mComposerMap )
{
//listen out for extent changes in linked map
QObject::connect( mComposerMap, SIGNAL( extentChanged() ), this, SLOT( refreshAttributes() ) );
}
refreshAttributes();
}
void QgsComposerAttributeTableV2::setMaximumNumberOfFeatures( int features )
{
if ( features == mMaximumNumberOfFeatures )
{
return;
}
mMaximumNumberOfFeatures = features;
refreshAttributes();
}
void QgsComposerAttributeTableV2::setDisplayOnlyVisibleFeatures( bool visibleOnly )
{
if ( visibleOnly == mShowOnlyVisibleFeatures )
{
return;
}
mShowOnlyVisibleFeatures = visibleOnly;
refreshAttributes();
}
void QgsComposerAttributeTableV2::setFilterFeatures( bool filter )
{
if ( filter == mFilterFeatures )
{
return;
}
mFilterFeatures = filter;
refreshAttributes();
}
void QgsComposerAttributeTableV2::setFeatureFilter( const QString& expression )
{
if ( expression == mFeatureFilter )
{
return;
}
mFeatureFilter = expression;
refreshAttributes();
}
void QgsComposerAttributeTableV2::setDisplayAttributes( const QSet<int>& attr, bool refresh )
{
if ( !mVectorLayer )
{
return;
}
//rebuild columns list, taking only attributes with index in supplied QSet
qDeleteAll( mColumns );
mColumns.clear();
const QgsFields& fields = mVectorLayer->pendingFields();
if ( !attr.empty() )
{
QSet<int>::const_iterator attIt = attr.constBegin();
for ( ; attIt != attr.constEnd(); ++attIt )
{
int attrIdx = ( *attIt );
if ( !fields.exists( attrIdx ) )
{
continue;
}
QString currentAlias = mVectorLayer->attributeDisplayName( attrIdx );
QgsComposerTableColumn* col = new QgsComposerTableColumn;
col->setAttribute( fields[attrIdx].name() );
col->setHeading( currentAlias );
mColumns.append( col );
}
}
else
{
//resetting, so add all attributes to columns
for ( int idx = 0; idx < fields.count(); ++idx )
{
QString currentAlias = mVectorLayer->attributeDisplayName( idx );
QgsComposerTableColumn* col = new QgsComposerTableColumn;
col->setAttribute( fields[idx].name() );
col->setHeading( currentAlias );
mColumns.append( col );
}
}
if ( refresh )
{
refreshAttributes();
}
}
void QgsComposerAttributeTableV2::restoreFieldAliasMap( const QMap<int, QString>& map )
{
if ( !mVectorLayer )
{
return;
}
QList<QgsComposerTableColumn*>::const_iterator columnIt = mColumns.constBegin();
for ( ; columnIt != mColumns.constEnd(); ++columnIt )
{
int attrIdx = mVectorLayer->fieldNameIndex(( *columnIt )->attribute() );
if ( map.contains( attrIdx ) )
{
( *columnIt )->setHeading( map.value( attrIdx ) );
}
else
{
( *columnIt )->setHeading( mVectorLayer->attributeDisplayName( attrIdx ) );
}
}
}
bool QgsComposerAttributeTableV2::getTableContents( QgsComposerTableContents &contents )
{
if ( !mVectorLayer )
{
return false;
}
contents.clear();
//prepare filter expression
std::auto_ptr<QgsExpression> filterExpression;
bool activeFilter = false;
if ( mFilterFeatures && !mFeatureFilter.isEmpty() )
{
filterExpression = std::auto_ptr<QgsExpression>( new QgsExpression( mFeatureFilter ) );
if ( !filterExpression->hasParserError() )
{
activeFilter = true;
}
}
QgsRectangle selectionRect;
if ( mComposerMap && mShowOnlyVisibleFeatures )
{
selectionRect = *mComposerMap->currentMapExtent();
if ( mVectorLayer && mComposition->mapSettings().hasCrsTransformEnabled() )
{
//transform back to layer CRS
QgsCoordinateTransform coordTransform( mVectorLayer->crs(), mComposition->mapSettings().destinationCrs() );
try
{
selectionRect = coordTransform.transformBoundingBox( selectionRect, QgsCoordinateTransform::ReverseTransform );
}
catch ( QgsCsException &cse )
{
Q_UNUSED( cse );
return false;
}
}
}
QgsFeatureRequest req;
if ( !selectionRect.isEmpty() )
req.setFilterRect( selectionRect );
req.setFlags( mShowOnlyVisibleFeatures ? QgsFeatureRequest::ExactIntersect : QgsFeatureRequest::NoFlags );
QgsFeature f;
int counter = 0;
QgsFeatureIterator fit = mVectorLayer->getFeatures( req );
while ( fit.nextFeature( f ) && counter < mMaximumNumberOfFeatures )
{
//check feature against filter
if ( activeFilter )
{
QVariant result = filterExpression->evaluate( &f, mVectorLayer->pendingFields() );
// skip this feature if the filter evaluation is false
if ( !result.toBool() )
{
continue;
}
}
QgsComposerTableRow currentRow;
QList<QgsComposerTableColumn*>::const_iterator columnIt = mColumns.constBegin();
for ( ; columnIt != mColumns.constEnd(); ++columnIt )
{
int idx = mVectorLayer->fieldNameIndex(( *columnIt )->attribute() );
if ( idx != -1 )
{
currentRow << f.attributes()[idx];
}
else
{
// Lets assume it's an expression
QgsExpression* expression = new QgsExpression(( *columnIt )->attribute() );
expression->setCurrentRowNumber( counter + 1 );
expression->prepare( mVectorLayer->pendingFields() );
QVariant value = expression->evaluate( f ) ;
currentRow << value;
}
}
contents << currentRow;
++counter;
}
//sort the list, starting with the last attribute
QgsComposerAttributeTableCompareV2 c;
QList< QPair<int, bool> > sortColumns = sortAttributes();
for ( int i = sortColumns.size() - 1; i >= 0; --i )
{
c.setSortColumn( sortColumns.at( i ).first );
c.setAscending( sortColumns.at( i ).second );
qStableSort( contents.begin(), contents.end(), c );
}
adjustFrameToSize();
return true;
}
void QgsComposerAttributeTableV2::removeLayer( QString layerId )
{
if ( mVectorLayer )
{
if ( layerId == mVectorLayer->id() )
{
mVectorLayer = 0;
//remove existing columns
qDeleteAll( mColumns );
mColumns.clear();
}
}
}
static bool columnsBySortRank( QPair<int, QgsComposerTableColumn* > a, QPair<int, QgsComposerTableColumn* > b )
{
return a.second->sortByRank() < b.second->sortByRank();
}
QList<QPair<int, bool> > QgsComposerAttributeTableV2::sortAttributes() const
{
//generate list of all sorted columns
QList< QPair<int, QgsComposerTableColumn* > > sortedColumns;
QList<QgsComposerTableColumn*>::const_iterator columnIt = mColumns.constBegin();
int idx = 0;
for ( ; columnIt != mColumns.constEnd(); ++columnIt )
{
if (( *columnIt )->sortByRank() > 0 )
{
sortedColumns.append( qMakePair( idx, *columnIt ) );
}
idx++;
}
//sort columns by rank
qSort( sortedColumns.begin(), sortedColumns.end(), columnsBySortRank );
//generate list of column index, bool for sort direction (to match 2.0 api)
QList<QPair<int, bool> > attributesBySortRank;
QList< QPair<int, QgsComposerTableColumn* > >::const_iterator sortedColumnIt = sortedColumns.constBegin();
for ( ; sortedColumnIt != sortedColumns.constEnd(); ++sortedColumnIt )
{
attributesBySortRank.append( qMakePair(( *sortedColumnIt ).first,
( *sortedColumnIt ).second->sortOrder() == Qt::AscendingOrder ) );
}
return attributesBySortRank;
}
bool QgsComposerAttributeTableV2::writeXML( QDomElement& elem, QDomDocument & doc ) const
{
QDomElement composerTableElem = doc.createElement( "ComposerAttributeTable" );
composerTableElem.setAttribute( "showOnlyVisibleFeatures", mShowOnlyVisibleFeatures );
composerTableElem.setAttribute( "maxFeatures", mMaximumNumberOfFeatures );
composerTableElem.setAttribute( "filterFeatures", mFilterFeatures ? "true" : "false" );
composerTableElem.setAttribute( "featureFilter", mFeatureFilter );
if ( mComposerMap )
{
composerTableElem.setAttribute( "composerMap", mComposerMap->id() );
}
else
{
composerTableElem.setAttribute( "composerMap", -1 );
}
if ( mVectorLayer )
{
composerTableElem.setAttribute( "vectorLayer", mVectorLayer->id() );
}
elem.appendChild( composerTableElem );
//todo
//bool ok = tableWriteXML( composerTableElem, doc );
bool ok = true;
return ok;
}
bool QgsComposerAttributeTableV2::readXML( const QDomElement& itemElem, const QDomDocument& doc )
{
if ( itemElem.isNull() )
{
return false;
}
//TODO
//read general table properties
//if ( !tableReadXML( itemElem, doc ) )
//{
// return false;
// }
mShowOnlyVisibleFeatures = itemElem.attribute( "showOnlyVisibleFeatures", "1" ).toInt();
mFilterFeatures = itemElem.attribute( "filterFeatures", "false" ) == "true" ? true : false;
mFeatureFilter = itemElem.attribute( "featureFilter", "" );
//composer map
int composerMapId = itemElem.attribute( "composerMap", "-1" ).toInt();
if ( composerMapId == -1 )
{
mComposerMap = 0;
}
if ( composition() )
{
mComposerMap = composition()->getComposerMapById( composerMapId );
}
else
{
mComposerMap = 0;
}
if ( mComposerMap )
{
//if we have found a valid map item, listen out to extent changes on it and refresh the table
QObject::connect( mComposerMap, SIGNAL( extentChanged() ), this, SLOT( refreshAttributes() ) );
}
//vector layer
QString layerId = itemElem.attribute( "vectorLayer", "not_existing" );
if ( layerId == "not_existing" )
{
mVectorLayer = 0;
}
else
{
QgsMapLayer* ml = QgsMapLayerRegistry::instance()->mapLayer( layerId );
if ( ml )
{
mVectorLayer = dynamic_cast<QgsVectorLayer*>( ml );
if ( mVectorLayer )
{
//if we have found a valid vector layer, listen for modifications on it and refresh the table
QObject::connect( mVectorLayer, SIGNAL( layerModified() ), this, SLOT( refreshAttributes() ) );
}
}
}
//restore display attribute map. This is required to upgrade pre 2.4 projects.
QSet<int> displayAttributes;
QDomNodeList displayAttributeList = itemElem.elementsByTagName( "displayAttributes" );
if ( displayAttributeList.size() > 0 )
{
QDomElement displayAttributesElem = displayAttributeList.at( 0 ).toElement();
QDomNodeList attributeEntryList = displayAttributesElem.elementsByTagName( "attributeEntry" );
for ( int i = 0; i < attributeEntryList.size(); ++i )
{
QDomElement attributeEntryElem = attributeEntryList.at( i ).toElement();
int index = attributeEntryElem.attribute( "index", "-1" ).toInt();
if ( index != -1 )
{
displayAttributes.insert( index );
}
}
setDisplayAttributes( displayAttributes, false );
}
//restore alias map. This is required to upgrade pre 2.4 projects.
QMap<int, QString> fieldAliasMap;
QDomNodeList aliasMapNodeList = itemElem.elementsByTagName( "attributeAliasMap" );
if ( aliasMapNodeList.size() > 0 )
{
QDomElement attributeAliasMapElem = aliasMapNodeList.at( 0 ).toElement();
QDomNodeList aliasMepEntryList = attributeAliasMapElem.elementsByTagName( "aliasEntry" );
for ( int i = 0; i < aliasMepEntryList.size(); ++i )
{
QDomElement aliasEntryElem = aliasMepEntryList.at( i ).toElement();
int key = aliasEntryElem.attribute( "key", "-1" ).toInt();
QString value = aliasEntryElem.attribute( "value", "" );
fieldAliasMap.insert( key, value );
}
restoreFieldAliasMap( fieldAliasMap );
}
//restore sort columns. This is required to upgrade pre 2.4 projects.
QDomElement sortColumnsElem = itemElem.firstChildElement( "sortColumns" );
if ( !sortColumnsElem.isNull() && mVectorLayer )
{
QDomNodeList columns = sortColumnsElem.elementsByTagName( "column" );
const QgsFields& fields = mVectorLayer->pendingFields();
for ( int i = 0; i < columns.size(); ++i )
{
QDomElement columnElem = columns.at( i ).toElement();
int attribute = columnElem.attribute( "index" ).toInt();
Qt::SortOrder order = columnElem.attribute( "ascending" ) == "true" ? Qt::AscendingOrder : Qt::DescendingOrder;
//find corresponding column
QList<QgsComposerTableColumn*>::iterator columnIt = mColumns.begin();
for ( ; columnIt != mColumns.end(); ++columnIt )
{
if (( *columnIt )->attribute() == fields[attribute].name() )
{
( *columnIt )->setSortByRank( i + 1 );
( *columnIt )->setSortOrder( order );
break;
}
}
}
}
//must be done here because tableReadXML->setSceneRect changes mMaximumNumberOfFeatures
mMaximumNumberOfFeatures = itemElem.attribute( "maxFeatures", "5" ).toInt();
refreshAttributes();
emit itemChanged();
return true;
}
void QgsComposerAttributeTableV2::addFrame( QgsComposerFrame *frame, bool recalcFrameSizes )
{
mFrameItems.push_back( frame );
connect( frame, SIGNAL( sizeChanged() ), this, SLOT( recalculateFrameSizes() ) );
if ( mComposition )
{
mComposition->addComposerTableFrame( this, frame );
}
if ( recalcFrameSizes )
{
recalculateFrameSizes();
}
}

View File

@ -0,0 +1,236 @@
/***************************************************************************
qgscomposerattributetablev2.h
---------------------------
begin : September 2014
copyright : (C) 2014 by Marco Hugentobler
email : marco at hugis dot net
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#ifndef QGSCOMPOSERATTRIBUTETABLEV2_H
#define QGSCOMPOSERATTRIBUTETABLEV2_H
#include "qgscomposertablev2.h"
#include "qgscomposerattributetable.h"
class QgsComposerMap;
class QgsVectorLayer;
/**Helper class for sorting tables, takes into account sorting column and ascending / descending*/
class CORE_EXPORT QgsComposerAttributeTableCompareV2
{
public:
QgsComposerAttributeTableCompareV2();
bool operator()( const QgsComposerTableRow& m1, const QgsComposerTableRow& m2 );
/**Sets column number to sort by
* @param col column number for sorting
*/
void setSortColumn( int col ) { mCurrentSortColumn = col; }
/**Sets sort order for column sorting
* @param asc set to true to sort in ascending order, false to sort in descending order
*/
void setAscending( bool asc ) { mAscending = asc; }
private:
int mCurrentSortColumn;
bool mAscending;
};
/**A table class that displays a vector attribute table*/
class CORE_EXPORT QgsComposerAttributeTableV2: public QgsComposerTableV2
{
Q_OBJECT
public:
QgsComposerAttributeTableV2( QgsComposition* composition, bool createUndoCommands );
~QgsComposerAttributeTableV2();
/**Writes properties specific to attribute tables
* @param elem an existing QDomElement in which to store the attribute table's properties.
* @param doc QDomDocument for the destination xml.
* @see readXML
*/
bool writeXML( QDomElement& elem, QDomDocument & doc ) const;
/**Reads the properties specific to an attribute table from xml.
* @param itemElem a QDomElement holding the attribute table's desired properties.
* @param doc QDomDocument for the source xml.
* @see writeXML
*/
bool readXML( const QDomElement& itemElem, const QDomDocument& doc );
virtual void addFrame( QgsComposerFrame* frame, bool recalcFrameSizes = true );
/**Sets the vector layer from which to display feature attributes
* @param layer Vector layer for attribute table
* @see vectorLayer
*/
void setVectorLayer( QgsVectorLayer* layer );
/**Returns the vector layer the attribute table is currently using
* @returns attribute table's current vector layer
* @see setVectorLayer
*/
QgsVectorLayer* vectorLayer() const { return mVectorLayer; }
/**Resets the attribute table's columns to match the vector layer's fields
* @see setVectorLayer
*/
void resetColumns();
/**Sets the composer map to use to limit the extent of features shown in the
* attribute table. This setting only has an effect if setDisplayOnlyVisibleFeatures is
* set to true. Changing the composer map forces the table to refetch features from its
* vector layer, and may result in the table changing size to accommodate the new displayed
* feature attributes.
* @param map QgsComposerMap which drives the extents of the table's features
* @see composerMap
* @see setDisplayOnlyVisibleFeatures
*/
void setComposerMap( const QgsComposerMap* map );
/**Returns the composer map whose extents are controlling the features shown in the
* table. The extents of the map are only used if displayOnlyVisibleFeatures() is true.
* @returns composer map controlling the attribute table
* @see setComposerMap
* @see displayOnlyVisibleFeatures
*/
const QgsComposerMap* composerMap() const { return mComposerMap; }
/**Sets the maximum number of features shown by the table. Changing this setting may result
* in the attribute table changing its size to accommodate the new number of rows, and requires
* the table to refetch features from its vector layer.
* @param features maximum number of features to show in the table
* @see maximumNumberOfFeatures
*/
void setMaximumNumberOfFeatures( int features );
/**Returns the maximum number of features to be shown by the table.
* @returns maximum number of features
* @see setMaximumNumberOfFeatures
*/
int maximumNumberOfFeatures() const { return mMaximumNumberOfFeatures; }
/**Sets attribute table to only show features which are visible in a composer map item. Changing
* this setting forces the table to refetch features from its vector layer, and may result in
* the table changing size to accommodate the new displayed feature attributes.
* @param visibleOnly set to true to show only visible features
* @see displayOnlyVisibleFeatures
* @see setComposerMap
*/
void setDisplayOnlyVisibleFeatures( bool visibleOnly );
/**Returns true if the table is set to show only features visible on a corresponding
* composer map item.
* @returns true if table only shows visible features
* @see composerMap
* @see setDisplayOnlyVisibleFeatures
*/
bool displayOnlyVisibleFeatures() const { return mShowOnlyVisibleFeatures; }
/**Returns true if a feature filter is active on the attribute table
* @returns bool state of the feature filter
* @see setFilterFeatures
* @see featureFilter
*/
bool filterFeatures() const { return mFilterFeatures; }
/**Sets whether the feature filter is active for the attribute table. Changing
* this setting forces the table to refetch features from its vector layer, and may result in
* the table changing size to accommodate the new displayed feature attributes.
* @param filter Set to true to enable the feature filter
* @see filterFeatures
* @see setFeatureFilter
*/
void setFilterFeatures( bool filter );
/**Returns the current expression used to filter features for the table. The filter is only
* active if filterFeatures() is true.
* @returns feature filter expression
* @see setFeatureFilter
* @see filterFeatures
*/
QString featureFilter() const { return mFeatureFilter; }
/**Sets the expression used for filtering features in the table. The filter is only
* active if filterFeatures() is set to true. Changing this setting forces the table
* to refetch features from its vector layer, and may result in
* the table changing size to accommodate the new displayed feature attributes.
* @param expression filter to use for selecting which features to display in the table
* @see featureFilter
* @see setFilterFeatures
*/
void setFeatureFilter( const QString& expression );
/**Sets the attributes to display in the table.
* @param attr QSet of integer values refering to the attributes from the vector layer to show.
* Set to an empty QSet to show all feature attributes.
* @param refresh set to true to force the table to refetch features from its vector layer
* and immediately update the display of the table. This may result in the table changing size
* to accommodate the new displayed feature attributes.
* @see displayAttributes
*/
void setDisplayAttributes( const QSet<int>& attr, bool refresh = true );
/**Returns the attributes used to sort the table's features.
* @returns a QList of integer/bool pairs, where the integer refers to the attribute index and
* the bool to the sort order for the attribute. If true the attribute is sorted ascending,
* if false, the attribute is sorted in descending order.
* @note not available in python bindings
*/
QList<QPair<int, bool> > sortAttributes() const;
/**Queries the attribute table's vector layer for attributes to show in the table.
* @param attributeMaps list of QgsAttributeMaps where the fetched feature attributes will be stored
* @returns true if attributes were successfully fetched
* @note not available in python bindings
*/
bool getTableContents( QgsComposerTableContents &contents );
private:
/**Associated vector layer*/
QgsVectorLayer* mVectorLayer;
/**Associated composer map (used to display the visible features)*/
const QgsComposerMap* mComposerMap;
/**Maximum number of features that is displayed*/
int mMaximumNumberOfFeatures;
/**Shows only the features that are visible in the associated composer map (true by default)*/
bool mShowOnlyVisibleFeatures;
/**True if feature filtering enabled*/
bool mFilterFeatures;
/**Feature filter expression*/
QString mFeatureFilter;
/**Returns a list of attribute indices corresponding to displayed fields in the table.
* @note kept for compatibility with 2.0 api only
*/
QList<int> fieldsToDisplay() const;
/**Restores a field alias map from a pre 2.4 format project file format
* @param map QMap of integers to strings, where the string is the alias to use for the
* corresponding field, and the integer is the field index from the vector layer
*/
void restoreFieldAliasMap( const QMap<int, QString>& map );
private slots:
/**Checks if this vector layer will be removed (and sets mVectorLayer to 0 if yes) */
void removeLayer( QString layerId );
signals:
/**This signal is emitted if the maximum number of feature changes (interactively)*/
void maximumNumberOfFeaturesChanged( int n );
};
#endif // QGSCOMPOSERATTRIBUTETABLEV2_H

View File

@ -69,7 +69,11 @@ class CORE_EXPORT QgsComposerMultiFrameMergeCommand: public QgsComposerMultiFram
//composer html
HtmlSource,
HtmlStylesheet,
HtmlBreakDistance
HtmlBreakDistance,
//attribute table
TableMaximumFeatures,
TableMargin,
TableGridStrokeWidth
};
QgsComposerMultiFrameMergeCommand( Context c, QgsComposerMultiFrame* multiFrame, const QString& text );

View File

@ -0,0 +1,472 @@
/***************************************************************************
qgscomposertablev2.cpp
------------------
begin : July 2014
copyright : (C) 2014 by Nyall Dawson, Marco Hugentobler
email : nyall dot dawson at gmail dot com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "qgscomposertablev2.h"
#include "qgscomposerutils.h"
#include "qgscomposertablecolumn.h"
#include "qgssymbollayerv2utils.h"
#include "qgscomposerframe.h"
QgsComposerTableV2::QgsComposerTableV2( QgsComposition *composition, bool createUndoCommands )
: QgsComposerMultiFrame( composition, createUndoCommands )
, mCellMargin( 1.0 )
, mHeaderFontColor( Qt::black )
, mHeaderHAlignment( FollowColumn )
, mContentFontColor( Qt::black )
, mShowGrid( true )
, mGridStrokeWidth( 0.5 )
, mGridColor( Qt::black )
{
}
QgsComposerTableV2::QgsComposerTableV2()
: QgsComposerMultiFrame( 0, false )
{
}
QgsComposerTableV2::~QgsComposerTableV2()
{
}
bool QgsComposerTableV2::writeXML( QDomElement& elem, QDomDocument & doc, bool ignoreFrames ) const
{
QDomElement tableElem = doc.createElement( "ComposerTableV2" );
elem.setAttribute( "cellMargin", QString::number( mCellMargin ) );
elem.setAttribute( "headerFont", mHeaderFont.toString() );
elem.setAttribute( "headerFontColor", QgsSymbolLayerV2Utils::encodeColor( mHeaderFontColor ) );
elem.setAttribute( "headerHAlignment", QString::number(( int )mHeaderHAlignment ) );
elem.setAttribute( "contentFont", mContentFont.toString() );
elem.setAttribute( "contentFontColor", QgsSymbolLayerV2Utils::encodeColor( mContentFontColor ) );
elem.setAttribute( "gridStrokeWidth", QString::number( mGridStrokeWidth ) );
elem.setAttribute( "gridColor", QgsSymbolLayerV2Utils::encodeColor( mGridColor ) );
elem.setAttribute( "showGrid", mShowGrid );
//columns
QDomElement displayColumnsElem = doc.createElement( "displayColumns" );
QList<QgsComposerTableColumn*>::const_iterator columnIt = mColumns.constBegin();
for ( ; columnIt != mColumns.constEnd(); ++columnIt )
{
QDomElement columnElem = doc.createElement( "column" );
( *columnIt )->writeXML( columnElem, doc );
displayColumnsElem.appendChild( columnElem );
}
tableElem.appendChild( displayColumnsElem );
bool state = _writeXML( tableElem, doc, ignoreFrames );
elem.appendChild( tableElem );
return state;
}
bool QgsComposerTableV2::readXML( const QDomElement &itemElem, const QDomDocument &doc, bool ignoreFrames )
{
deleteFrames();
//first create the frames
if ( !_readXML( itemElem, doc, ignoreFrames ) )
{
return false;
}
if ( itemElem.isNull() )
{
return false;
}
mHeaderFont.fromString( itemElem.attribute( "headerFont", "" ) );
mHeaderFontColor = QgsSymbolLayerV2Utils::decodeColor( itemElem.attribute( "headerFontColor", "0,0,0,255" ) );
mHeaderHAlignment = QgsComposerTableV2::HeaderHAlignment( itemElem.attribute( "headerHAlignment", "0" ).toInt() );
mContentFont.fromString( itemElem.attribute( "contentFont", "" ) );
mContentFontColor = QgsSymbolLayerV2Utils::decodeColor( itemElem.attribute( "contentFontColor", "0,0,0,255" ) );
mCellMargin = itemElem.attribute( "cellMargin", "1.0" ).toDouble();
mGridStrokeWidth = itemElem.attribute( "gridStrokeWidth", "0.5" ).toDouble();
mShowGrid = itemElem.attribute( "showGrid", "1" ).toInt();
mGridColor = QgsSymbolLayerV2Utils::decodeColor( itemElem.attribute( "gridColor", "0,0,0,255" ) );
//restore column specifications
qDeleteAll( mColumns );
mColumns.clear();
QDomNodeList columnsList = itemElem.elementsByTagName( "displayColumns" );
if ( columnsList.size() > 0 )
{
QDomElement columnsElem = columnsList.at( 0 ).toElement();
QDomNodeList columnEntryList = columnsElem.elementsByTagName( "column" );
for ( int i = 0; i < columnEntryList.size(); ++i )
{
QDomElement columnElem = columnEntryList.at( i ).toElement();
QgsComposerTableColumn* column = new QgsComposerTableColumn;
column->readXML( columnElem );
mColumns.append( column );
}
}
return true;
}
QSizeF QgsComposerTableV2::totalSize() const
{
//TODO - handle multiple cell headers
return mTableSize;
}
void QgsComposerTableV2::render( QPainter *p, const QRectF &renderExtent )
{
//do this via rows
//eg, calculate how rows to->from
//and render them
if ( !p )
{
return;
}
if ( mComposition->plotStyle() == QgsComposition::Print ||
mComposition->plotStyle() == QgsComposition::Postscript )
{
//exporting composition, so force an attribute refresh
//we do this in case vector layer has changed via an external source (eg, another database user)
refreshAttributes();
}
p->save();
//antialiasing on
p->setRenderHint( QPainter::Antialiasing, true );
p->setPen( Qt::SolidLine );
//now draw the text
double currentX = mGridStrokeWidth;
double currentY;
QList<QgsComposerTableColumn*>::const_iterator columnIt = mColumns.constBegin();
int col = 0;
double cellHeaderHeight = QgsComposerUtils::fontAscentMM( mHeaderFont ) + 2 * mCellMargin;
double cellBodyHeight = QgsComposerUtils::fontAscentMM( mContentFont ) + 2 * mCellMargin;
QRectF cell;
for ( ; columnIt != mColumns.constEnd(); ++columnIt )
{
currentY = mGridStrokeWidth;
currentX += mCellMargin;
cell = QRectF( currentX, currentY, mMaxColumnWidthMap[col], cellHeaderHeight );
//calculate alignment of header
Qt::AlignmentFlag headerAlign = Qt::AlignLeft;
switch ( mHeaderHAlignment )
{
case FollowColumn:
headerAlign = ( *columnIt )->hAlignment();
break;
case HeaderLeft:
headerAlign = Qt::AlignLeft;
break;
case HeaderCenter:
headerAlign = Qt::AlignHCenter;
break;
case HeaderRight:
headerAlign = Qt::AlignRight;
break;
}
QgsComposerUtils::drawText( p, cell, ( *columnIt )->heading(), mHeaderFont, mHeaderFontColor, headerAlign, Qt::AlignVCenter, Qt::TextDontClip );
currentY += cellHeaderHeight;
currentY += mGridStrokeWidth;
//draw the attribute values
QgsComposerTableContents::const_iterator attIt = mTableContents.begin();
for ( ; attIt != mTableContents.end(); ++attIt )
{
cell = QRectF( currentX, currentY, mMaxColumnWidthMap[col], cellBodyHeight );
QVariant cellContents = ( *attIt ).at( col );
QString str = cellContents.toString();
QgsComposerUtils::drawText( p, cell, str, mContentFont, mContentFontColor, ( *columnIt )->hAlignment(), Qt::AlignVCenter, Qt::TextDontClip );
currentY += cellBodyHeight;
currentY += mGridStrokeWidth;
}
currentX += mMaxColumnWidthMap[ col ];
currentX += mCellMargin;
currentX += mGridStrokeWidth;
col++;
}
//and the borders
if ( mShowGrid )
{
QPen gridPen;
gridPen.setWidthF( mGridStrokeWidth );
gridPen.setColor( mGridColor );
gridPen.setJoinStyle( Qt::MiterJoin );
p->setPen( gridPen );
drawHorizontalGridLines( p, mTableContents.size() );
drawVerticalGridLines( p, mMaxColumnWidthMap );
}
p->restore();
}
void QgsComposerTableV2::setCellMargin( const double margin )
{
if ( margin == mCellMargin )
{
return;
}
mCellMargin = margin;
//since spacing has changed, we need to recalculate the table size
adjustFrameToSize();
}
void QgsComposerTableV2::setHeaderFont( const QFont &font )
{
if ( font == mHeaderFont )
{
return;
}
mHeaderFont = font;
//since font attributes have changed, we need to recalculate the table size
adjustFrameToSize();
}
void QgsComposerTableV2::setHeaderFontColor( const QColor &color )
{
if ( color == mHeaderFontColor )
{
return;
}
mHeaderFontColor = color;
repaint();
}
void QgsComposerTableV2::setHeaderHAlignment( const QgsComposerTableV2::HeaderHAlignment alignment )
{
if ( alignment == mHeaderHAlignment )
{
return;
}
mHeaderHAlignment = alignment;
repaint();
}
void QgsComposerTableV2::setContentFont( const QFont &font )
{
if ( font == mContentFont )
{
return;
}
mContentFont = font;
//since font attributes have changed, we need to recalculate the table size
adjustFrameToSize();
}
void QgsComposerTableV2::setContentFontColor( const QColor &color )
{
if ( color == mContentFontColor )
{
return;
}
mContentFontColor = color;
repaint();
}
void QgsComposerTableV2::setShowGrid( const bool showGrid )
{
if ( showGrid == mShowGrid )
{
return;
}
mShowGrid = showGrid;
//since grid spacing has changed, we need to recalculate the table size
adjustFrameToSize();
}
void QgsComposerTableV2::setGridStrokeWidth( const double width )
{
if ( width == mGridStrokeWidth )
{
return;
}
mGridStrokeWidth = width;
//since grid spacing has changed, we need to recalculate the table size
adjustFrameToSize();
}
void QgsComposerTableV2::setGridColor( const QColor &color )
{
if ( color == mGridColor )
{
return;
}
mGridColor = color;
repaint();
}
void QgsComposerTableV2::setColumns( QgsComposerTableColumns columns )
{
//remove existing columns
qDeleteAll( mColumns );
mColumns.clear();
mColumns.append( columns );
}
QMap<int, QString> QgsComposerTableV2::headerLabels() const
{
QMap<int, QString> headers;
QgsComposerTableColumns::const_iterator columnIt = mColumns.constBegin();
int col = 0;
for ( ; columnIt != mColumns.constEnd(); ++columnIt )
{
headers.insert( col, ( *columnIt )->heading() );
col++;
}
return headers;
}
void QgsComposerTableV2::refreshAttributes()
{
mMaxColumnWidthMap.clear();
mTableContents.clear();
//get new contents
if ( !getTableContents( mTableContents ) )
{
return;
}
//since contents have changed, we also need to recalculate the column widths
//and size of table
adjustFrameToSize();
}
bool QgsComposerTableV2::calculateMaxColumnWidths()
{
mMaxColumnWidthMap.clear();
//first, go through all the column headers and calculate the max width values
QgsComposerTableColumns::const_iterator columnIt = mColumns.constBegin();
int col = 0;
for ( ; columnIt != mColumns.constEnd(); ++columnIt )
{
mMaxColumnWidthMap.insert( col, QgsComposerUtils::textWidthMM( mHeaderFont, ( *columnIt )->heading() ) );
col++;
}
//next, go through all the table contents and calculate the max width values
QgsComposerTableContents::const_iterator rowIt = mTableContents.constBegin();
double currentCellTextWidth;
for ( ; rowIt != mTableContents.constEnd(); ++rowIt )
{
QgsComposerTableRow::const_iterator colIt = rowIt->constBegin();
int columnNumber = 0;
for ( ; colIt != rowIt->constEnd(); ++colIt )
{
currentCellTextWidth = QgsComposerUtils::textWidthMM( mContentFont, ( *colIt ).toString() );
mMaxColumnWidthMap[ columnNumber ] = qMax( currentCellTextWidth, mMaxColumnWidthMap[ columnNumber ] );
columnNumber++;
}
}
return true;
}
double QgsComposerTableV2::totalWidth()
{
//check how much space each column needs
if ( !calculateMaxColumnWidths() )
{
return 0;
}
//adapt frame to total width
double totalWidth = 0;
QMap<int, double>::const_iterator maxColWidthIt = mMaxColumnWidthMap.constBegin();
for ( ; maxColWidthIt != mMaxColumnWidthMap.constEnd(); ++maxColWidthIt )
{
totalWidth += maxColWidthIt.value();
}
totalWidth += ( 2 * mMaxColumnWidthMap.size() * mCellMargin );
totalWidth += ( mMaxColumnWidthMap.size() + 1 ) * mGridStrokeWidth;
return totalWidth;
}
double QgsComposerTableV2::totalHeight() const
{
//calculate height
int n = mTableContents.size();
double totalHeight = QgsComposerUtils::fontAscentMM( mHeaderFont )
+ n * QgsComposerUtils::fontAscentMM( mContentFont )
+ ( n + 1 ) * mCellMargin * 2
+ ( n + 2 ) * mGridStrokeWidth;
return totalHeight;
}
void QgsComposerTableV2::drawHorizontalGridLines( QPainter *painter, const int rows ) const
{
//horizontal lines
double halfGridStrokeWidth = mGridStrokeWidth / 2.0;
double currentY = halfGridStrokeWidth;
painter->drawLine( QPointF( halfGridStrokeWidth, currentY ), QPointF( mTableSize.width() - halfGridStrokeWidth, currentY ) );
currentY += mGridStrokeWidth;
currentY += ( QgsComposerUtils::fontAscentMM( mHeaderFont ) + 2 * mCellMargin );
for ( int row = 0; row < rows; ++row )
{
painter->drawLine( QPointF( halfGridStrokeWidth, currentY ), QPointF( mTableSize.width() - halfGridStrokeWidth, currentY ) );
currentY += mGridStrokeWidth;
currentY += ( QgsComposerUtils::fontAscentMM( mContentFont ) + 2 * mCellMargin );
}
painter->drawLine( QPointF( halfGridStrokeWidth, currentY ), QPointF( mTableSize.width() - halfGridStrokeWidth, currentY ) );
}
void QgsComposerTableV2::drawVerticalGridLines( QPainter *painter, const QMap<int, double> &maxWidthMap ) const
{
//vertical lines
double halfGridStrokeWidth = mGridStrokeWidth / 2.0;
double currentX = halfGridStrokeWidth;
painter->drawLine( QPointF( currentX, halfGridStrokeWidth ), QPointF( currentX, mTableSize.height() - halfGridStrokeWidth ) );
currentX += mGridStrokeWidth;
QMap<int, double>::const_iterator maxColWidthIt = maxWidthMap.constBegin();
for ( ; maxColWidthIt != maxWidthMap.constEnd(); ++maxColWidthIt )
{
currentX += ( maxColWidthIt.value() + 2 * mCellMargin );
painter->drawLine( QPointF( currentX, halfGridStrokeWidth ), QPointF( currentX, mTableSize.height() - halfGridStrokeWidth ) );
currentX += mGridStrokeWidth;
}
}
void QgsComposerTableV2::adjustFrameToSize()
{
mTableSize = QSizeF( totalWidth(), totalHeight() );
}

View File

@ -0,0 +1,311 @@
/***************************************************************************
qgscomposertablev2.h
------------------
begin : July 2014
copyright : (C) 2014 by Nyall Dawson, Marco Hugentobler
email : nyall dot dawson at gmail dot com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#ifndef QGSCOMPOSERTABLEV2_H
#define QGSCOMPOSERTABLEV2_H
#include "qgscomposermultiframe.h"
#include <QFont>
#include <QColor>
class QgsComposerTableColumn;
/**List of QVariants, representing a the contents of a single row in
* a QgsComposerTable
* \note Added in version 2.5
*/
typedef QList< QVariant > QgsComposerTableRow;
/**List of QgsComposerTableRows, representing rows and column cell contents
* for a QgsComposerTable
* \note Added in version 2.5
*/
typedef QList< QgsComposerTableRow > QgsComposerTableContents;
/**List of column definitions for a QgsComposerTable
* \note Added in version 2.5
*/
typedef QList<QgsComposerTableColumn*> QgsComposerTableColumns;
/**A class to display feature attributes in the print composer, and allow
* the table to span over multiple frames
* @note added in QGIS 2.5
*/
class CORE_EXPORT QgsComposerTableV2: public QgsComposerMultiFrame
{
Q_OBJECT
public:
/*! Controls how headers are horizontally aligned in a table
*/
enum HeaderHAlignment
{
FollowColumn, /*!< header uses the same alignment as the column */
HeaderLeft, /*!< align headers left */
HeaderCenter, /*!< align headers to center */
HeaderRight /*!< align headers right */
};
QgsComposerTableV2( QgsComposition* composition, bool createUndoCommands );
QgsComposerTableV2();
virtual ~QgsComposerTableV2();
virtual bool writeXML( QDomElement& elem, QDomDocument & doc, bool ignoreFrames = false ) const;
virtual bool readXML( const QDomElement& itemElem, const QDomDocument& doc, bool ignoreFrames = false );
virtual QSizeF totalSize() const;
virtual void render( QPainter* p, const QRectF& renderExtent );
/**Sets the margin distance between cell borders and their contents.
* @param margin margin for cell contents
* @see cellMargin
*/
void setCellMargin( const double margin );
/**Returns the margin distance between cell borders and their contents.
* @returns margin for cell contents
* @see setCellMargin
*/
double cellMargin() const { return mCellMargin; }
/**Sets the font used to draw header text in the table.
* @param font font for header cells
* @see headerFont
* @see setContentFont
*/
void setHeaderFont( const QFont& font );
/**Returns the font used to draw header text in the table.
* @returns font for header cells
* @see setHeaderFont
* @see contentFont
*/
QFont headerFont() const { return mHeaderFont; }
/**Sets the color used to draw header text in the table.
* @param color header text color
* @see headerFontColor
* @see setHeaderFont
* @see setContentFontColor
*/
void setHeaderFontColor( const QColor& color );
/**Returns the color used to draw header text in the table.
* @returns color for header text
* @see setHeaderFontColor
* @see headerFont
* @see contentFontColor
*/
QColor headerFontColor() const { return mHeaderFontColor; }
/**Sets the horizontal alignment for table headers
* @param alignment Horizontal alignment for table header cells
* @see headerHAlignment
*/
void setHeaderHAlignment( const HeaderHAlignment alignment );
/**Returns the horizontal alignment for table headers
* @returns Horizontal alignment for table header cells
* @see setHeaderHAlignment
*/
HeaderHAlignment headerHAlignment() const { return mHeaderHAlignment; }
/**Sets the font used to draw text in table body cells.
* @param font font for table cells
* @see contentFont
* @see setHeaderFont
*/
void setContentFont( const QFont& font );
/**Returns the font used to draw text in table body cells.
* @returns font for table cells
* @see setContentFont
* @see headerFont
*/
QFont contentFont() const { return mContentFont; }
/**Sets the color used to draw text in table body cells.
* @param color table cell text color
* @see contentFontColor
* @see setContentFont
* @see setHeaderFontColor
*/
void setContentFontColor( const QColor& color );
/**Returns the color used to draw text in table body cells.
* @returns text color for table cells
* @see setContentFontColor
* @see contentFont
* @see headerFontColor
*/
QColor contentFontColor() const { return mContentFontColor; }
/**Sets whether grid lines should be drawn in the table
* @param showGrid set to true to show grid lines
* @see showGrid
* @see setGridStrokeWidth
* @see setGridColor
*/
void setShowGrid( const bool showGrid );
/**Returns whether grid lines are drawn in the table
* @returns true if grid lines are shown
* @see setShowGrid
* @see gridStrokeWidth
* @see gridColor
*/
bool showGrid() const { return mShowGrid; }
/**Sets the width for grid lines in the table.
* @param width grid line width
* @see gridStrokeWidth
* @see setShowGrid
* @see setGridColor
*/
void setGridStrokeWidth( const double width );
/**Returns the width of grid lines in the table.
* @returns grid line width
* @see setGridStrokeWidth
* @see showGrid
* @see gridColor
*/
double gridStrokeWidth() const { return mGridStrokeWidth; }
/**Sets color used for grid lines in the table.
* @param color grid line color
* @see gridColor
* @see setShowGrid
* @see setGridStrokeWidth
*/
void setGridColor( const QColor& color );
/**Returns the color used for grid lines in the table.
* @returns grid line color
* @see setGridColor
* @see showGrid
* @see gridStrokeWidth
*/
QColor gridColor() const { return mGridColor; }
/**Returns a pointer to the list of QgsComposerTableColumns shown in the table
* @returns pointer to list of columns in table
* @see setColumns
*/
QgsComposerTableColumns* columns() { return &mColumns; }
/**Replaces the columns in the table with a specified list of QgsComposerTableColumns.
* @param columns list of QgsComposerTableColumns to show in table
* @see columns
*/
void setColumns( QgsComposerTableColumns columns );
/**Returns the text used in the column headers for the table.
* @returns QMap of int to QString, where the int is the column index (starting at 0),
* and the string is the text to use for the column's header
* @note not available in python bindings
*/
virtual QMap<int, QString> headerLabels() const;
/**Fetches the contents used for the cells in the table.
* @returns true if table contents were successfully retrieved.
* @param contents QgsComposerTableContents to store retrieved row data in
* @note not available in python bindings
*/
virtual bool getTableContents( QgsComposerTableContents &contents ) = 0;
public slots:
/**Refreshes the contents shown in the table by querying for new data.
* This also causes the column widths and size of the table to change to accommodate the
* new data.
* @see adjustFrameToSize
*/
virtual void refreshAttributes();
protected:
/**Margin between cell borders and cell text*/
double mCellMargin;
/**Header font*/
QFont mHeaderFont;
/**Header font color*/
QColor mHeaderFontColor;
/**Alignment for table headers*/
HeaderHAlignment mHeaderHAlignment;
/**Table contents font*/
QFont mContentFont;
/**Table contents font color*/
QColor mContentFontColor;
/**True if grid should be shown*/
bool mShowGrid;
/**Width of grid lines*/
double mGridStrokeWidth;
/**Color for grid lines*/
QColor mGridColor;
/**Columns to show in table*/
QgsComposerTableColumns mColumns;
/**Contents to show in table*/
QgsComposerTableContents mTableContents;
/**Map of maximum width for each column*/
QMap<int, double> mMaxColumnWidthMap;
QSizeF mTableSize;
/**Calculates the maximum width of text shown in columns.
*/
virtual bool calculateMaxColumnWidths();
//not const
double totalWidth();
double totalHeight() const;
/**Draws the horizontal grid lines for the table.
* @param painter destination painter for grid lines
* @param rows number of rows shown in table
* @see drawVerticalGridLines
*/
void drawHorizontalGridLines( QPainter* painter, const int rows ) const;
/**Draws the vertical grid lines for the table.
* @param painter destination painter for grid lines
* @param maxWidthMap QMap of int to double, where the int contains the column number and the double is the
* maximum width of text present in the column.
* @note not available in python bindings
* @see drawVerticalGridLines
* @see calculateMaxColumnWidths
*/
void drawVerticalGridLines( QPainter* painter, const QMap<int, double>& maxWidthMap ) const;
void adjustFrameToSize();
};
#endif // QGSCOMPOSERTABLEV2_H

View File

@ -31,6 +31,7 @@
#include "qgscomposerlabel.h"
#include "qgscomposermodel.h"
#include "qgscomposerattributetable.h"
#include "qgscomposerattributetablev2.h"
#include "qgsaddremovemultiframecommand.h"
#include "qgscomposermultiframecommand.h"
#include "qgspaintenginehack.h"
@ -2141,6 +2142,12 @@ void QgsComposition::endMultiFrameCommand()
}
}
void QgsComposition::cancelMultiFrameCommand()
{
delete mActiveMultiFrameCommand;
mActiveMultiFrameCommand = 0;
}
void QgsComposition::addMultiFrame( QgsComposerMultiFrame* multiFrame )
{
mMultiFrames.insert( multiFrame );
@ -2255,6 +2262,16 @@ void QgsComposition::addComposerHtmlFrame( QgsComposerHtml* html, QgsComposerFra
emit composerHtmlFrameAdded( html, frame );
}
void QgsComposition::addComposerTableFrame( QgsComposerAttributeTableV2 *table, QgsComposerFrame *frame )
{
addItem( frame );
updateBounds();
connect( frame, SIGNAL( sizeChanged() ), this, SLOT( updateBounds() ) );
emit composerTableFrameAdded( table, frame );
}
void QgsComposition::removeComposerItem( QgsComposerItem* item, const bool createCommand, const bool removeGroupItems )
{
QgsComposerMap* map = dynamic_cast<QgsComposerMap *>( item );
@ -2420,6 +2437,11 @@ void QgsComposition::sendItemAddedSignal( QgsComposerItem* item )
{
emit composerHtmlFrameAdded( html, frame );
}
QgsComposerAttributeTableV2* table = dynamic_cast<QgsComposerAttributeTableV2*>( mf );
if ( table )
{
emit composerTableFrameAdded( table, frame );
}
emit selectedItemChanged( frame );
return;
}

View File

@ -45,6 +45,7 @@ class QDomElement;
class QgsComposerArrow;
class QgsComposerMouseHandles;
class QgsComposerHtml;
class QgsComposerTableV2;
class QgsComposerItem;
class QgsComposerLabel;
class QgsComposerLegend;
@ -53,6 +54,7 @@ class QgsComposerPicture;
class QgsComposerScaleBar;
class QgsComposerShape;
class QgsComposerAttributeTable;
class QgsComposerAttributeTableV2;
class QgsComposerMultiFrame;
class QgsComposerMultiFrameCommand;
class QgsVectorLayer;
@ -459,6 +461,8 @@ class CORE_EXPORT QgsComposition : public QGraphicsScene
void beginMultiFrameCommand( QgsComposerMultiFrame* multiFrame, const QString& text, const QgsComposerMultiFrameMergeCommand::Context c = QgsComposerMultiFrameMergeCommand::Unknown );
void endMultiFrameCommand();
/**Deletes current multi frame command*/
void cancelMultiFrameCommand();
/**Adds multiframe. The object is owned by QgsComposition until removeMultiFrame is called*/
void addMultiFrame( QgsComposerMultiFrame* multiFrame );
@ -481,8 +485,10 @@ class CORE_EXPORT QgsComposition : public QGraphicsScene
void addComposerShape( QgsComposerShape* shape );
/**Adds a composer table to the graphics scene and advices composer to create a widget for it (through signal)*/
void addComposerTable( QgsComposerAttributeTable* table );
/**Adds composer html frame and advices composer to create a widget for it (through signal)*/
/**Adds composer html frame and advises composer to create a widget for it (through signal)*/
void addComposerHtmlFrame( QgsComposerHtml* html, QgsComposerFrame* frame );
/**Adds composer tablev2 frame and advises composer to create a widget for it (through signal)*/
void addComposerTableFrame( QgsComposerAttributeTableV2* table, QgsComposerFrame* frame );
/**Remove item from the graphics scene. Additionally to QGraphicsScene::removeItem, this function considers undo/redo command*/
void removeComposerItem( QgsComposerItem* item, const bool createCommand = true, const bool removeGroupItems = true );
@ -787,6 +793,8 @@ class CORE_EXPORT QgsComposition : public QGraphicsScene
void composerShapeAdded( QgsComposerShape* shape );
/**Is emitted when a new composer table has been added*/
void composerTableAdded( QgsComposerAttributeTable* table );
/**Is emitted when a new composer table frame has been added to the view*/
void composerTableFrameAdded( QgsComposerAttributeTableV2* table, QgsComposerFrame* frame );
/**Is emitted when a composer item has been removed from the scene*/
void itemRemoved( QgsComposerItem* );

View File

@ -39,7 +39,7 @@
#include "qgscomposerruler.h"
#include "qgscomposerscalebar.h"
#include "qgscomposershape.h"
#include "qgscomposerattributetable.h"
#include "qgscomposerattributetablev2.h"
#include "qgslogger.h"
#include "qgsaddremovemultiframecommand.h"
#include "qgspaperitem.h"
@ -113,6 +113,7 @@ void QgsComposerView::setCurrentTool( QgsComposerView::Tool t )
case QgsComposerView::AddEllipse:
case QgsComposerView::AddTriangle:
case QgsComposerView::AddTable:
case QgsComposerView::AddAttributeTable:
{
//using a drawing tool
//lock cursor to prevent composer items changing it
@ -345,6 +346,7 @@ void QgsComposerView::mousePressEvent( QMouseEvent* e )
case AddLabel:
case AddLegend:
case AddTable:
case AddAttributeTable:
{
QTransform t;
mRubberBandItem = new QGraphicsRectItem( 0, 0, 0, 0 );
@ -416,6 +418,7 @@ QCursor QgsComposerView::defaultCursorForTool( Tool currentTool )
case AddLegend:
case AddPicture:
case AddTable:
case AddAttributeTable:
{
QPixmap myCrosshairQPixmap = QPixmap(( const char ** )( cross_hair_cursor ) );
return QCursor( myCrosshairQPixmap, 8, 8 );
@ -865,12 +868,13 @@ void QgsComposerView::mouseReleaseEvent( QMouseEvent* e )
else
{
QgsComposerAttributeTable* newTable = new QgsComposerAttributeTable( composition() );
newTable->setSceneRect( QRectF( mRubberBandItem->transform().dx(), mRubberBandItem->transform().dy(), mRubberBandItem->rect().width(), qMax( mRubberBandItem->rect().height(), (qreal)15.0 ) ) );
QList<const QgsComposerMap*> mapItemList = composition()->composerMapItems();
if ( mapItemList.size() > 0 )
{
newTable->setComposerMap( mapItemList.at( 0 ) );
}
newTable->setSceneRect( QRectF( mRubberBandItem->transform().dx(), mRubberBandItem->transform().dy(), mRubberBandItem->rect().width(), mRubberBandItem->rect().height() ) );
composition()->addComposerTable( newTable );
composition()->setAllUnselected();
@ -883,6 +887,39 @@ void QgsComposerView::mouseReleaseEvent( QMouseEvent* e )
}
break;
case AddAttributeTable:
if ( !composition() || !mRubberBandItem )
{
removeRubberBand();
return;
}
else
{
QgsComposerAttributeTableV2* newTable = new QgsComposerAttributeTableV2( composition(), true );
QList<const QgsComposerMap*> mapItemList = composition()->composerMapItems();
if ( mapItemList.size() > 0 )
{
newTable->setComposerMap( mapItemList.at( 0 ) );
}
QgsAddRemoveMultiFrameCommand* command = new QgsAddRemoveMultiFrameCommand( QgsAddRemoveMultiFrameCommand::Added,
newTable, composition(), tr( "Attribute table added" ) );
composition()->undoStack()->push( command );
QgsComposerFrame* frame = new QgsComposerFrame( composition(), newTable, mRubberBandItem->transform().dx(),
mRubberBandItem->transform().dy(), mRubberBandItem->rect().width(),
mRubberBandItem->rect().height() );
composition()->beginMultiFrameCommand( newTable, tr( "Attribute table frame added" ) );
newTable->addFrame( frame );
composition()->endMultiFrameCommand();
composition()->setAllUnselected();
frame->setSelected( true );
emit selectedItemChanged( frame );
removeRubberBand();
emit actionFinished();
}
break;
case AddHtml:
if ( !composition() || !mRubberBandItem || ( mRubberBandItem->rect().width() < 0.1 && mRubberBandItem->rect().height() < 0.1 ) )
{
@ -994,6 +1031,7 @@ void QgsComposerView::mouseMoveEvent( QMouseEvent* e )
case AddLabel:
case AddLegend:
case AddTable:
case AddAttributeTable:
//adjust rubber band item
{
updateRubberBandRect( scenePoint, shiftModifier, altModifier );

View File

@ -36,7 +36,7 @@ class QgsComposerPicture;
class QgsComposerRuler;
class QgsComposerScaleBar;
class QgsComposerShape;
class QgsComposerAttributeTable;
class QgsComposerAttributeTableV2;
/** \ingroup MapComposer
* Widget to display the composer items. Manages the composer tools and the
@ -64,7 +64,8 @@ class GUI_EXPORT QgsComposerView: public QGraphicsView
AddRectangle,
AddEllipse,
AddTriangle,
AddTable, //add attribute table
AddTable, //add table
AddAttributeTable,
MoveItemContent, //move content of item (e.g. content of map)
Pan,
Zoom

View File

@ -0,0 +1,505 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>QgsComposerAttributeTableWidgetBase</class>
<widget class="QWidget" name="QgsComposerAttributeTableWidgetBase">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>410</width>
<height>815</height>
</rect>
</property>
<property name="windowTitle">
<string>Attribute Table</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="margin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="styleSheet">
<string notr="true">padding: 2px; font-weight: bold; background-color: rgb(200, 200, 200);</string>
</property>
<property name="text">
<string>Attribute table</string>
</property>
</widget>
</item>
<item>
<widget class="QScrollArea" name="scrollArea">
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>392</width>
<height>813</height>
</rect>
</property>
<layout class="QVBoxLayout" name="mainLayout">
<item>
<widget class="QgsCollapsibleGroupBoxBasic" name="groupBox">
<property name="title">
<string>Main properties</string>
</property>
<property name="syncGroup" stdset="0">
<string notr="true">composeritem</string>
</property>
<property name="collapsed" stdset="0">
<bool>false</bool>
</property>
<layout class="QFormLayout" name="formLayout">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
</property>
<property name="labelAlignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
<item row="0" column="0">
<widget class="QLabel" name="mLayerLabel">
<property name="text">
<string>Layer</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QgsMapLayerComboBox" name="mLayerComboBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QPushButton" name="mRefreshPushButton">
<property name="text">
<string>Refresh table data</string>
</property>
</widget>
</item>
<item row="2" column="0" colspan="2">
<widget class="QPushButton" name="mAttributesPushButton">
<property name="text">
<string>Attributes...</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="mMarginLabel">
<property name="text">
<string>Margin</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="buddy">
<cstring>mMarginSpinBox</cstring>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QDoubleSpinBox" name="mMarginSpinBox">
<property name="suffix">
<string> mm</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QgsCollapsibleGroupBoxBasic" name="groupBox_5">
<property name="title">
<string>Feature filtering</string>
</property>
<property name="syncGroup" stdset="0">
<string notr="true">composeritem</string>
</property>
<property name="collapsed" stdset="0">
<bool>false</bool>
</property>
<layout class="QFormLayout" name="formLayout_3">
<item row="0" column="0">
<widget class="QLabel" name="mMaxNumFeaturesLabel">
<property name="text">
<string>Maximum rows</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="buddy">
<cstring>mMaximumColumnsSpinBox</cstring>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QSpinBox" name="mMaximumColumnsSpinBox">
<property name="maximum">
<number>999</number>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QCheckBox" name="mShowOnlyVisibleFeaturesCheckBox">
<property name="text">
<string>Show only visible features</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="mComposerMapLabel">
<property name="text">
<string>Composer map</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="buddy">
<cstring>mComposerMapComboBox</cstring>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="mComposerMapComboBox"/>
</item>
<item row="3" column="0">
<widget class="QCheckBox" name="mFeatureFilterCheckBox">
<property name="text">
<string>Filter with</string>
</property>
</widget>
</item>
<item row="3" column="1">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLineEdit" name="mFeatureFilterEdit"/>
</item>
<item>
<widget class="QToolButton" name="mFeatureFilterButton">
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../images/images.qrc">
<normaloff>:/images/themes/default/mIconExpression.svg</normaloff>:/images/themes/default/mIconExpression.svg</iconset>
</property>
</widget>
</item>
</layout>
</item>
</layout>
<zorder>mShowOnlyVisibleFeaturesCheckBox</zorder>
<zorder>mComposerMapLabel</zorder>
<zorder>mComposerMapComboBox</zorder>
<zorder>mFeatureFilterCheckBox</zorder>
<zorder>mMaximumColumnsSpinBox</zorder>
<zorder>mMaxNumFeaturesLabel</zorder>
</widget>
</item>
<item>
<widget class="QgsCollapsibleGroupBoxBasic" name="mShowGridGroupCheckBox">
<property name="title">
<string>Show grid</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="syncGroup" stdset="0">
<string notr="true">composeritem</string>
</property>
<property name="collapsed" stdset="0">
<bool>false</bool>
</property>
<layout class="QFormLayout" name="formLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="mGridStrokeWidthLabel">
<property name="text">
<string>Stroke width</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="buddy">
<cstring>mGridStrokeWidthSpinBox</cstring>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QDoubleSpinBox" name="mGridStrokeWidthSpinBox">
<property name="suffix">
<string> mm</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Color</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QgsColorButtonV2" name="mGridColorButton">
<property name="minimumSize">
<size>
<width>120</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>120</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QgsCollapsibleGroupBoxBasic" name="groupBox_3">
<property name="title">
<string>Fonts and text styling</string>
</property>
<property name="syncGroup" stdset="0">
<string notr="true">composeritem</string>
</property>
<property name="collapsed" stdset="0">
<bool>false</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Table heading</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="3" column="1" colspan="2">
<widget class="QComboBox" name="mHeaderHAlignmentComboBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<item>
<property name="text">
<string>Follow column alignment</string>
</property>
</item>
<item>
<property name="text">
<string>Left</string>
</property>
</item>
<item>
<property name="text">
<string>Center</string>
</property>
</item>
<item>
<property name="text">
<string>Right</string>
</property>
</item>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Alignment</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Color</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Font</string>
</property>
</widget>
</item>
<item row="0" column="1" colspan="2">
<widget class="QPushButton" name="mHeaderFontPushButton">
<property name="text">
<string>Choose font...</string>
</property>
</widget>
</item>
<item row="2" column="1" colspan="2">
<widget class="QgsColorButtonV2" name="mHeaderFontColorButton">
<property name="minimumSize">
<size>
<width>120</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>120</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_4">
<property name="title">
<string>Table contents</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="1" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Color</string>
</property>
</widget>
</item>
<item row="1" column="1" colspan="2">
<widget class="QgsColorButtonV2" name="mContentFontColorButton">
<property name="minimumSize">
<size>
<width>120</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>120</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="0" column="1" colspan="2">
<widget class="QPushButton" name="mContentFontPushButton">
<property name="text">
<string>Choose font...</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="lblContentsFont">
<property name="text">
<string>Font</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QgsCollapsibleGroupBoxBasic" name="frameGroupBox">
<property name="title">
<string>Frames</string>
</property>
<property name="checkable">
<bool>false</bool>
</property>
<property name="checked">
<bool>false</bool>
</property>
<property name="collapsed" stdset="0">
<bool>false</bool>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0">
<widget class="QLabel" name="mResizeModeLabel">
<property name="text">
<string>Resize mode</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="mResizeModeComboBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QPushButton" name="mAddFramePushButton">
<property name="text">
<string>Add Frame</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>QgsCollapsibleGroupBoxBasic</class>
<extends>QGroupBox</extends>
<header location="global">qgscollapsiblegroupbox.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>QgsColorButtonV2</class>
<extends>QToolButton</extends>
<header>qgscolorbuttonv2.h</header>
</customwidget>
<customwidget>
<class>QgsMapLayerComboBox</class>
<extends>QComboBox</extends>
<header location="global">qgsmaplayercombobox.h</header>
</customwidget>
</customwidgets>
<resources>
<include location="../../images/images.qrc"/>
</resources>
<connections/>
</ui>

View File

@ -118,6 +118,7 @@
<addaction name="mActionAddNewScalebar"/>
<addaction name="mActionAddArrow"/>
<addaction name="mActionAddTable"/>
<addaction name="mActionAddAttributeTable"/>
<addaction name="mActionAddHtml"/>
</widget>
<widget class="QToolBar" name="mAtlasToolbar">
@ -573,6 +574,17 @@
<property name="text">
<string>Add Table</string>
</property>
<property name="toolTip">
<string>Add table</string>
</property>
</action>
<action name="mActionAddAttributeTable">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Add Attribute Table</string>
</property>
<property name="toolTip">
<string>Add attribute table</string>
</property>