Skip to content

Commit

Permalink
[layout] Import attribute table from composition
Browse files Browse the repository at this point in the history
  • Loading branch information
elpaso committed Jan 9, 2018
1 parent 2b2dae8 commit f297e86
Show file tree
Hide file tree
Showing 11 changed files with 249 additions and 9 deletions.
2 changes: 1 addition & 1 deletion python/core/layout/qgslayoutitemattributetable.sip
Expand Up @@ -63,7 +63,7 @@ Returns the source for attributes shown in the table body.
.. seealso:: :py:func:`setSource()`
%End

QgsVectorLayer *sourceLayer();
QgsVectorLayer *sourceLayer() const;
%Docstring
Returns the source layer for the table, considering the table source mode. For example,
if the table is set to atlas feature mode, then the source layer will be the
Expand Down
144 changes: 138 additions & 6 deletions src/core/layout/qgscompositionconverter.cpp
Expand Up @@ -45,6 +45,9 @@
#include "qgslayoutitemscalebar.h"
#include "qgslayoutitemlegend.h"
#include "qgslayoutitemhtml.h"
#include "qgslayouttable.h"
#include "qgslayoutitemattributetable.h"
#include "qgslayouttablecolumn.h"
#include "qgslayoutmultiframe.h"
#include "qgslayoutframe.h"

Expand Down Expand Up @@ -299,11 +302,6 @@ QList<QgsLayoutObject *> QgsCompositionConverter::addItemsFromCompositionXml( Qg
}
}

/* TODO: following item types are not yet converted
// known multi-frame types
LayoutAttributeTable, //!< Attribute table
*/

QgsStringMap mapIdUiidMap;

// Map (this needs to come first to build the uuid <-> ID map for map composer items
Expand All @@ -316,7 +314,6 @@ QList<QgsLayoutObject *> QgsCompositionConverter::addItemsFromCompositionXml( Qg
newItems << layoutItem ;
}


// Label
for ( int i = 0; i < parentElement.elementsByTagName( QStringLiteral( "ComposerLabel" ) ).size(); i++ )
{
Expand Down Expand Up @@ -412,6 +409,21 @@ QList<QgsLayoutObject *> QgsCompositionConverter::addItemsFromCompositionXml( Qg
newItems << layoutItem ;
}

// Attribute Table
for ( int i = 0; i < parentElement.elementsByTagName( QStringLiteral( "ComposerAttributeTableV2" ) ).size(); i++ )
{
QDomNode itemNode( parentElement.elementsByTagName( QStringLiteral( "ComposerAttributeTableV2" ) ).at( i ) );
QgsLayoutItemAttributeTable *layoutItem = new QgsLayoutItemAttributeTable( layout );
readTableXml( layoutItem, itemNode.toElement(), layout->project() );
// Adjust position for frames
const QList<QgsLayoutFrame *> framesList( layoutItem->frames() );
for ( const auto &frame : framesList )
{
adjustPos( layout, frame, position, pasteInPlace, zOrderOffset, pasteShiftPos, pageNumber );
}
newItems << layoutItem ;
}

return newItems;
}

Expand Down Expand Up @@ -1311,6 +1323,126 @@ bool QgsCompositionConverter::readHtmlXml( QgsLayoutItemHtml *layoutItem, const
return true;
}

bool QgsCompositionConverter::readTableXml( QgsLayoutItemAttributeTable *layoutItem, const QDomElement &itemElem, const QgsProject *project )
{

Q_UNUSED( project );
readOldComposerObjectXml( layoutItem, itemElem );

//first create the frames
layoutItem->setResizeMode( static_cast< QgsLayoutMultiFrame::ResizeMode >( itemElem.attribute( QStringLiteral( "resizeMode" ), QStringLiteral( "0" ) ).toInt() ) );
QDomNodeList frameList = itemElem.elementsByTagName( QStringLiteral( "ComposerFrame" ) );
for ( int i = 0; i < frameList.size(); ++i )
{
QDomElement frameElem = frameList.at( i ).toElement();
QgsLayoutFrame *newFrame = new QgsLayoutFrame( layoutItem->layout(), layoutItem );
restoreGeneralComposeItemProperties( newFrame, frameElem );
// Read frame XML
double x = itemElem.attribute( QStringLiteral( "sectionX" ) ).toDouble();
double y = itemElem.attribute( QStringLiteral( "sectionY" ) ).toDouble();
double width = itemElem.attribute( QStringLiteral( "sectionWidth" ) ).toDouble();
double height = itemElem.attribute( QStringLiteral( "sectionHeight" ) ).toDouble();
newFrame->setContentSection( QRectF( x, y, width, height ) );
newFrame->setHidePageIfEmpty( itemElem.attribute( QStringLiteral( "hidePageIfEmpty" ), QStringLiteral( "0" ) ).toInt() );
newFrame->setHideBackgroundIfEmpty( itemElem.attribute( QStringLiteral( "hideBackgroundIfEmpty" ), QStringLiteral( "0" ) ).toInt() );
layoutItem->addFrame( newFrame, false );
}

layoutItem->setEmptyTableBehavior( static_cast<QgsLayoutTable::EmptyTableMode>( itemElem.attribute( QStringLiteral( "emptyTableMode" ), QStringLiteral( "0" ) ).toInt() ) );
layoutItem->setEmptyTableMessage( itemElem.attribute( QStringLiteral( "emptyTableMessage" ), QObject::tr( "No matching records" ) ) );
layoutItem->setShowEmptyRows( itemElem.attribute( QStringLiteral( "showEmptyRows" ), QStringLiteral( "0" ) ).toInt() );
if ( !QgsFontUtils::setFromXmlChildNode( layoutItem->mHeaderFont, itemElem, QStringLiteral( "headerFontProperties" ) ) )
{
layoutItem->mHeaderFont.fromString( itemElem.attribute( QStringLiteral( "headerFont" ), QLatin1String( "" ) ) );
}
layoutItem->setHeaderFontColor( QgsSymbolLayerUtils::decodeColor( itemElem.attribute( QStringLiteral( "headerFontColor" ), QStringLiteral( "0,0,0,255" ) ) ) );
layoutItem->setHeaderHAlignment( static_cast<QgsLayoutTable::HeaderHAlignment>( itemElem.attribute( QStringLiteral( "headerHAlignment" ), QStringLiteral( "0" ) ).toInt() ) ) ;
layoutItem->setHeaderMode( static_cast<QgsLayoutTable::HeaderMode>( itemElem.attribute( QStringLiteral( "headerMode" ), QStringLiteral( "0" ) ).toInt() ) );
if ( !QgsFontUtils::setFromXmlChildNode( layoutItem->mContentFont, itemElem, QStringLiteral( "contentFontProperties" ) ) )
{
layoutItem->mContentFont.fromString( itemElem.attribute( QStringLiteral( "contentFont" ), QLatin1String( "" ) ) );
}
layoutItem->setContentFontColor( QgsSymbolLayerUtils::decodeColor( itemElem.attribute( QStringLiteral( "contentFontColor" ), QStringLiteral( "0,0,0,255" ) ) ) );
layoutItem->setCellMargin( itemElem.attribute( QStringLiteral( "cellMargin" ), QStringLiteral( "1.0" ) ).toDouble() );
layoutItem->setGridStrokeWidth( itemElem.attribute( QStringLiteral( "gridStrokeWidth" ), QStringLiteral( "0.5" ) ).toDouble() );
layoutItem->setHorizontalGrid( itemElem.attribute( QStringLiteral( "horizontalGrid" ), QStringLiteral( "1" ) ).toInt() );
layoutItem->setVerticalGrid( itemElem.attribute( QStringLiteral( "verticalGrid" ), QStringLiteral( "1" ) ).toInt() );
layoutItem->setShowGrid( itemElem.attribute( QStringLiteral( "showGrid" ), QStringLiteral( "1" ) ).toInt() );
layoutItem->setGridColor( QgsSymbolLayerUtils::decodeColor( itemElem.attribute( QStringLiteral( "gridColor" ), QStringLiteral( "0,0,0,255" ) ) ) );
layoutItem->setBackgroundColor( QgsSymbolLayerUtils::decodeColor( itemElem.attribute( QStringLiteral( "backgroundColor" ), QStringLiteral( "255,255,255,0" ) ) ) );
layoutItem->setWrapBehavior( static_cast<QgsLayoutTable::WrapBehavior>( itemElem.attribute( QStringLiteral( "wrapBehavior" ), QStringLiteral( "0" ) ).toInt() ) );

//restore column specifications
layoutItem->mColumns.clear();
QDomNodeList columnsList = itemElem.elementsByTagName( QStringLiteral( "displayColumns" ) );
if ( !columnsList.isEmpty() )
{
QDomElement columnsElem = columnsList.at( 0 ).toElement();
QDomNodeList columnEntryList = columnsElem.elementsByTagName( QStringLiteral( "column" ) );
for ( int i = 0; i < columnEntryList.size(); ++i )
{
QDomElement columnElem = columnEntryList.at( i ).toElement();
QgsLayoutTableColumn *column = new QgsLayoutTableColumn;
column->mHAlignment = static_cast< Qt::AlignmentFlag >( columnElem.attribute( QStringLiteral( "hAlignment" ), QString::number( Qt::AlignLeft ) ).toInt() );
column->mVAlignment = static_cast< Qt::AlignmentFlag >( columnElem.attribute( QStringLiteral( "vAlignment" ), QString::number( Qt::AlignVCenter ) ).toInt() );
column->mHeading = columnElem.attribute( QStringLiteral( "heading" ), QLatin1String( "" ) );
column->mAttribute = columnElem.attribute( QStringLiteral( "attribute" ), QLatin1String( "" ) );
column->mSortByRank = columnElem.attribute( QStringLiteral( "sortByRank" ), QStringLiteral( "0" ) ).toInt();
column->mSortOrder = static_cast< Qt::SortOrder >( columnElem.attribute( QStringLiteral( "sortOrder" ), QString::number( Qt::AscendingOrder ) ).toInt() );
column->mWidth = columnElem.attribute( QStringLiteral( "width" ), QStringLiteral( "0.0" ) ).toDouble();

QDomNodeList bgColorList = columnElem.elementsByTagName( QStringLiteral( "backgroundColor" ) );
if ( !bgColorList.isEmpty() )
{
QDomElement bgColorElem = bgColorList.at( 0 ).toElement();
bool redOk, greenOk, blueOk, alphaOk;
int bgRed, bgGreen, bgBlue, bgAlpha;
bgRed = bgColorElem.attribute( QStringLiteral( "red" ) ).toDouble( &redOk );
bgGreen = bgColorElem.attribute( QStringLiteral( "green" ) ).toDouble( &greenOk );
bgBlue = bgColorElem.attribute( QStringLiteral( "blue" ) ).toDouble( &blueOk );
bgAlpha = bgColorElem.attribute( QStringLiteral( "alpha" ) ).toDouble( &alphaOk );
if ( redOk && greenOk && blueOk && alphaOk )
{
column->mBackgroundColor = QColor( bgRed, bgGreen, bgBlue, bgAlpha );
}
}
layoutItem->mColumns.append( column );
}
}

//restore cell styles
QDomNodeList stylesList = itemElem.elementsByTagName( QStringLiteral( "cellStyles" ) );
if ( !stylesList.isEmpty() )
{
QDomElement stylesElem = stylesList.at( 0 ).toElement();

QMap< QgsLayoutTable::CellStyleGroup, QString >::const_iterator it = layoutItem->mCellStyleNames.constBegin();
for ( ; it != layoutItem->mCellStyleNames.constEnd(); ++it )
{
QString styleName = it.value();
QDomNodeList styleList = stylesElem.elementsByTagName( styleName );
if ( !styleList.isEmpty() )
{
QDomElement styleElem = styleList.at( 0 ).toElement();
QgsLayoutTableStyle *style = layoutItem->mCellStyles.value( it.key() );
if ( style )
style->readXml( styleElem );
}
}
}

// look for stored layer name
QString layerId = itemElem.attribute( QStringLiteral( "vectorLayer" ) );
QString layerName = itemElem.attribute( QStringLiteral( "vectorLayerName" ) );
QString layerSource = itemElem.attribute( QStringLiteral( "vectorLayerSource" ) );
QString layerProvider = itemElem.attribute( QStringLiteral( "vectorLayerProvider" ) );

QgsVectorLayerRef layerRef( layerId, layerName, layerSource, layerProvider );
layoutItem->setVectorLayer( layerRef.resolveWeakly( project ) );

return true;
}


template <class T, class T2>
bool QgsCompositionConverter::readPolyXml( T *layoutItem, const QDomElement &itemElem, const QgsProject *project )
Expand Down
5 changes: 5 additions & 0 deletions src/core/layout/qgscompositionconverter.h
Expand Up @@ -43,6 +43,7 @@ class QgsLayoutItemMap;
class QgsLayoutItemScaleBar;
class QgsLayoutItemLegend;
class QgsLayoutItemHtml;
class QgsLayoutItemAttributeTable;


/**
Expand Down Expand Up @@ -195,6 +196,10 @@ class CORE_EXPORT QgsCompositionConverter
const QDomElement &itemElem,
const QgsProject *project );

static bool readTableXml( QgsLayoutItemAttributeTable *layoutItem,
const QDomElement &itemElem,
const QgsProject *project );

static bool readOldComposerObjectXml( QgsLayoutObject *layoutItem, const QDomElement &itemElem );

static void readOldDataDefinedPropertyMap( const QDomElement &itemElem,
Expand Down
2 changes: 1 addition & 1 deletion src/core/layout/qgslayoutitemattributetable.cpp
Expand Up @@ -549,7 +549,7 @@ QVariant QgsLayoutItemAttributeTable::replaceWrapChar( const QVariant &variant )
return replaced;
}

QgsVectorLayer *QgsLayoutItemAttributeTable::sourceLayer()
QgsVectorLayer *QgsLayoutItemAttributeTable::sourceLayer() const
{
switch ( mSource )
{
Expand Down
2 changes: 1 addition & 1 deletion src/core/layout/qgslayoutitemattributetable.h
Expand Up @@ -82,7 +82,7 @@ class CORE_EXPORT QgsLayoutItemAttributeTable: public QgsLayoutTable
* atlas coverage layer. If the table is set to layer attributes mode, then
* the source layer will be the user specified vector layer.
*/
QgsVectorLayer *sourceLayer();
QgsVectorLayer *sourceLayer() const;

/**
* Sets the vector \a layer from which to display feature attributes.
Expand Down
1 change: 1 addition & 0 deletions src/core/layout/qgslayouttable.h
Expand Up @@ -678,6 +678,7 @@ class CORE_EXPORT QgsLayoutTable: public QgsLayoutMultiFrame
QColor backgroundColor( int row, int column ) const;

friend class TestQgsLayoutTable;
friend class QgsCompositionConverter;
};

#endif // QGSLAYOUTTABLE_H
2 changes: 2 additions & 0 deletions src/core/layout/qgslayouttablecolumn.h
Expand Up @@ -199,6 +199,8 @@ class CORE_EXPORT QgsLayoutTableColumn : public QObject
Qt::SortOrder mSortOrder = Qt::AscendingOrder;
double mWidth = 0.0;

friend class QgsCompositionConverter;

};

#endif //QGSLAYOUTTABLECOLUMN_H
30 changes: 30 additions & 0 deletions tests/src/core/testqgscompositionconverter.cpp
Expand Up @@ -41,6 +41,7 @@
#include "qgslayoutitemlegend.h"
#include "qgslayoutatlas.h"
#include "qgslayoutitemhtml.h"
#include "qgslayoutitemattributetable.h"


class TestQgsCompositionConverter: public QObject
Expand All @@ -61,6 +62,11 @@ class TestQgsCompositionConverter: public QObject
*/
void importComposerTemplateLegend();

/**
* Test import attribute table from a composer template
*/
void importComposerTemplateAttributeTable();

/**
* Test import HTML from a composer template
*/
Expand Down Expand Up @@ -440,6 +446,30 @@ void TestQgsCompositionConverter::importComposerTemplateLegend()

}

void TestQgsCompositionConverter::importComposerTemplateAttributeTable()
{
QDomElement composerElem( loadComposer( "2x_template_attributetable.qpt" ) );
QgsProject project;
project.read( QStringLiteral( TEST_DATA_DIR ) + "/layouts/sample_project.qgs" );
QDomElement docElem = composerElem.elementsByTagName( QStringLiteral( "Composition" ) ).at( 0 ).toElement();
std::unique_ptr< QgsPrintLayout > layout( QgsCompositionConverter::createLayoutFromCompositionXml( docElem, &project ) );

QVERIFY( layout.get() );
QCOMPARE( layout->pageCollection()->pageCount(), 1 );

QList<QgsLayoutMultiFrame *> items = layout->multiFrames();
QVERIFY( items.size() > 0 );

// Check the HTML
const QgsLayoutItemAttributeTable *table = qobject_cast<QgsLayoutItemAttributeTable * >( items.at( 0 ) );
QVERIFY( table );
QVERIFY( table->sourceLayer() );
QVERIFY( table->sourceLayer()->isValid() );

checkRenderedImage( layout.get(), QTest::currentTestFunction(), 0 );

}

void TestQgsCompositionConverter::importComposerTemplateHtml()
{
QDomElement composerElem( loadComposer( "2x_template_html.qpt" ) );
Expand Down
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
70 changes: 70 additions & 0 deletions tests/testdata/layouts/2x_template_attributetable.qpt
@@ -0,0 +1,70 @@
<Composer title="composer title" visible="1">
<Composition resizeToContentsMarginLeft="0" snapping="0" showPages="1" guidesVisible="1" resizeToContentsMarginTop="0" worldFileMap="" alignmentSnap="1" printResolution="305" paperWidth="297" gridVisible="0" snapGridOffsetX="0" smartGuides="1" snapGridOffsetY="0" resizeToContentsMarginRight="0" snapTolerancePixels="5" printAsRaster="0" generateWorldFile="0" paperHeight="210" numPages="1" snapGridResolution="10" resizeToContentsMarginBottom="0">
<symbol alpha="1" clip_to_extent="1" type="fill" name="">
<layer pass="0" class="SimpleFill" locked="0">
<prop k="border_width_map_unit_scale" v="0,0,0,0,0,0"/>
<prop k="color" v="241,244,199,255"/>
<prop k="joinstyle" v="bevel"/>
<prop k="offset" v="0,0"/>
<prop k="offset_map_unit_scale" v="0,0,0,0,0,0"/>
<prop k="offset_unit" v="MM"/>
<prop k="outline_color" v="175,179,138,255"/>
<prop k="outline_style" v="solid"/>
<prop k="outline_width" v="0.26"/>
<prop k="outline_width_unit" v="MM"/>
<prop k="style" v="solid"/>
</layer>
</symbol>
<ComposerAttributeTableV2 vectorLayerName="points" source="0" showGrid="1" maxFeatures="30" resizeMode="0" filterFeatures="false" featureFilter="" emptyTableMode="1" wrapString="" wrapBehaviour="0" headerMode="1" backgroundColor="240,33,33,255" showEmptyRows="0" emptyTableMessage="" showOnlyVisibleFeatures="1" vectorLayer="points20171212162310546" vectorLayerSource="../points.shp" composerMap="0" headerHAlignment="2" contentFontColor="115,115,115,255" headerFontColor="194,143,12,255" cellMargin="1.2" filterToAtlasIntersection="0" relationId="" gridStrokeWidth="0.7" gridColor="245,57,220,255" showUniqueRowsOnly="1" vectorLayerProvider="ogr">
<headerFontProperties description="MS Shell Dlg 2,8.25,-1,5,50,0,0,0,0,0" style=""/>
<contentFontProperties description="MS Shell Dlg 2,8.25,-1,5,50,0,0,0,0,0" style=""/>
<displayColumns>
<column width="0" vAlignment="128" attribute="Class" sortByRank="0" hAlignment="1" heading="Class" sortOrder="0">
<backgroundColor alpha="0" red="0" blue="0" green="0"/>
</column>
<column width="0" vAlignment="128" attribute="Heading" sortByRank="0" hAlignment="1" heading="Heading" sortOrder="0">
<backgroundColor alpha="0" red="0" blue="0" green="0"/>
</column>
<column width="0" vAlignment="128" attribute="Importance" sortByRank="0" hAlignment="1" heading="Importance" sortOrder="0">
<backgroundColor alpha="0" red="0" blue="0" green="0"/>
</column>
<column width="0" vAlignment="128" attribute="Pilots" sortByRank="0" hAlignment="1" heading="Pilots" sortOrder="0">
<backgroundColor alpha="0" red="0" blue="0" green="0"/>
</column>
<column width="0" vAlignment="128" attribute="Cabin Crew" sortByRank="0" hAlignment="1" heading="Cabin Crew" sortOrder="0">
<backgroundColor alpha="0" red="0" blue="0" green="0"/>
</column>
<column width="0" vAlignment="128" attribute="Staff" sortByRank="0" hAlignment="1" heading="Staff" sortOrder="0">
<backgroundColor alpha="0" red="0" blue="0" green="0"/>
</column>
</displayColumns>
<cellStyles>
<oddColumns enabled="0" cellBackgroundColor="255,255,255,255"/>
<evenColumns enabled="1" cellBackgroundColor="255,255,255,255"/>
<oddRows enabled="0" cellBackgroundColor="255,255,255,255"/>
<evenRows enabled="0" cellBackgroundColor="255,255,255,255"/>
<firstColumn enabled="0" cellBackgroundColor="255,255,255,255"/>
<lastColumn enabled="0" cellBackgroundColor="255,255,255,255"/>
<headerRow enabled="0" cellBackgroundColor="255,255,255,255"/>
<firstRow enabled="0" cellBackgroundColor="255,255,255,255"/>
<lastRow enabled="0" cellBackgroundColor="255,255,255,255"/>
</cellStyles>
<ComposerFrame sectionWidth="89.7078" sectionHeight="74.6091" hideBackgroundIfEmpty="0" hidePageIfEmpty="0" sectionX="0" sectionY="0">
<ComposerItem pagey="16.737" page="1" id="" lastValidViewScaleFactor="-1" positionMode="0" positionLock="false" x="21.5722" y="16.737" visibility="1" zValue="14" background="false" transparency="0" frameJoinStyle="miter" blendMode="0" width="89.7078" outlineWidth="0.3" excludeFromExports="0" uuid="{cd165488-e216-4dcb-a56e-06cddbe0c7e7}" height="74.6091" itemRotation="0" frame="false" pagex="21.5722">
<FrameColor alpha="255" red="0" blue="0" green="0"/>
<BackgroundColor alpha="255" red="255" blue="255" green="255"/>
<customproperties/>
</ComposerItem>
</ComposerFrame>
<ComposerFrame sectionWidth="89.7078" sectionHeight="46.3787" hideBackgroundIfEmpty="0" hidePageIfEmpty="0" sectionX="0" sectionY="74.6091">
<ComposerItem pagey="66.4954" page="1" id="" lastValidViewScaleFactor="-1" positionMode="0" positionLock="false" x="111.28" y="66.4954" visibility="1" zValue="15" background="false" transparency="0" frameJoinStyle="miter" blendMode="0" width="89.7078" outlineWidth="0.3" excludeFromExports="0" uuid="{72ab9ed3-6c68-49cb-9593-209a38f72d75}" height="46.3787" itemRotation="0" frame="false" pagex="111.28">
<FrameColor alpha="255" red="0" blue="0" green="0"/>
<BackgroundColor alpha="255" red="255" blue="255" green="255"/>
<customproperties/>
</ComposerItem>
</ComposerFrame>
<customproperties/>
</ComposerAttributeTableV2>
<customproperties/>
</Composition>
</Composer>

0 comments on commit f297e86

Please sign in to comment.