[layout] Import attribute table from composition

This commit is contained in:
Alessandro Pasotti 2018-01-08 19:06:10 +01:00
parent 2b2dae828f
commit f297e86201
11 changed files with 249 additions and 9 deletions

View File

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

View File

@ -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"
@ -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
@ -316,7 +314,6 @@ QList<QgsLayoutObject *> QgsCompositionConverter::addItemsFromCompositionXml( Qg
newItems << layoutItem ;
}
// Label
for ( int i = 0; i < parentElement.elementsByTagName( QStringLiteral( "ComposerLabel" ) ).size(); i++ )
{
@ -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;
}
@ -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 )

View File

@ -43,6 +43,7 @@ class QgsLayoutItemMap;
class QgsLayoutItemScaleBar;
class QgsLayoutItemLegend;
class QgsLayoutItemHtml;
class QgsLayoutItemAttributeTable;
/**
@ -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,

View File

@ -549,7 +549,7 @@ QVariant QgsLayoutItemAttributeTable::replaceWrapChar( const QVariant &variant )
return replaced;
}
QgsVectorLayer *QgsLayoutItemAttributeTable::sourceLayer()
QgsVectorLayer *QgsLayoutItemAttributeTable::sourceLayer() const
{
switch ( mSource )
{

View File

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

View File

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

View File

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

View File

@ -41,6 +41,7 @@
#include "qgslayoutitemlegend.h"
#include "qgslayoutatlas.h"
#include "qgslayoutitemhtml.h"
#include "qgslayoutitemattributetable.h"
class TestQgsCompositionConverter: public QObject
@ -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
*/
@ -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" ) );

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 82 KiB

View File

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