Skip to content
Permalink
Browse files

Work on layout and layout item serialization and restoration

  • Loading branch information
nyalldawson committed Nov 29, 2017
1 parent ca75e8c commit b74a0efa34d0031a89333e870e268abc18a7e7db
Showing with 396 additions and 196 deletions.
  1. +17 −0 python/core/layout/qgslayout.sip
  2. +0 −2 python/core/layout/qgslayoutframe.sip
  3. +0 −2 python/core/layout/qgslayoutitemattributetable.sip
  4. +3 −2 python/core/layout/qgslayoutitemgroup.sip
  5. +0 −2 python/core/layout/qgslayoutitemhtml.sip
  6. +0 −3 python/core/layout/qgslayoutitemlabel.sip
  7. +3 −2 python/core/layout/qgslayoutitemlegend.sip
  8. +3 −2 python/core/layout/qgslayoutitemmap.sip
  9. +1 −2 python/core/layout/qgslayoutitempage.sip
  10. +3 −2 python/core/layout/qgslayoutitempicture.sip
  11. +0 −2 python/core/layout/qgslayoutitempolygon.sip
  12. +0 −2 python/core/layout/qgslayoutitempolyline.sip
  13. +1 −2 python/core/layout/qgslayoutitemscalebar.sip
  14. +1 −1 python/core/layout/qgslayoutitemshape.sip
  15. +0 −2 python/core/layout/qgslayoutitemtexttable.sip
  16. +167 −0 src/core/layout/qgslayout.cpp
  17. +19 −0 src/core/layout/qgslayout.h
  18. +0 −5 src/core/layout/qgslayoutframe.cpp
  19. +0 −1 src/core/layout/qgslayoutframe.h
  20. +6 −2 src/core/layout/qgslayoutitem.cpp
  21. +28 −10 src/core/layout/qgslayoutitem.h
  22. +0 −5 src/core/layout/qgslayoutitemattributetable.cpp
  23. +0 −1 src/core/layout/qgslayoutitemattributetable.h
  24. +13 −21 src/core/layout/qgslayoutitemgroup.cpp
  25. +3 −2 src/core/layout/qgslayoutitemgroup.h
  26. +0 −5 src/core/layout/qgslayoutitemhtml.cpp
  27. +0 −1 src/core/layout/qgslayoutitemhtml.h
  28. +0 −5 src/core/layout/qgslayoutitemlabel.cpp
  29. +0 −2 src/core/layout/qgslayoutitemlabel.h
  30. +18 −11 src/core/layout/qgslayoutitemlegend.cpp
  31. +4 −1 src/core/layout/qgslayoutitemlegend.h
  32. +11 −5 src/core/layout/qgslayoutitemmap.cpp
  33. +2 −1 src/core/layout/qgslayoutitemmap.h
  34. +5 −0 src/core/layout/qgslayoutitempage.cpp
  35. +1 −3 src/core/layout/qgslayoutitempage.h
  36. +32 −27 src/core/layout/qgslayoutitempicture.cpp
  37. +5 −1 src/core/layout/qgslayoutitempicture.h
  38. +0 −5 src/core/layout/qgslayoutitempolygon.cpp
  39. +0 −1 src/core/layout/qgslayoutitempolygon.h
  40. +0 −5 src/core/layout/qgslayoutitempolyline.cpp
  41. +0 −1 src/core/layout/qgslayoutitempolyline.h
  42. +1 −2 src/core/layout/qgslayoutitemregistry.h
  43. +14 −12 src/core/layout/qgslayoutitemscalebar.cpp
  44. +3 −2 src/core/layout/qgslayoutitemscalebar.h
  45. +5 −0 src/core/layout/qgslayoutitemshape.cpp
  46. +1 −2 src/core/layout/qgslayoutitemshape.h
  47. +0 −5 src/core/layout/qgslayoutitemtexttable.cpp
  48. +0 −1 src/core/layout/qgslayoutitemtexttable.h
  49. +1 −0 src/core/layout/qgslayoutitemundocommand.cpp
  50. +6 −2 src/core/layout/qgslayoutmultiframe.cpp
  51. +17 −8 src/core/layout/qgslayoutmultiframe.h
  52. +1 −0 src/core/layout/qgslayoutpagecollection.cpp
  53. +1 −7 tests/src/core/testqgslayoutitem.cpp
  54. +0 −5 tests/src/core/testqgslayoutmultiframe.cpp
  55. +0 −1 tests/src/gui/testqgslayoutview.cpp
@@ -424,6 +424,23 @@ class QgsLayout : QGraphicsScene, QgsExpressionContextGenerator, QgsLayoutUndoOb
:rtype: bool
%End

QVector< QgsLayoutItem * > addItemsFromXml( const QDomElement &parentElement, const QDomDocument &document,
const QgsReadWriteContext &context,
QPointF *position = 0, bool pasteInPlace = false );
%Docstring
Add items from an XML representation to the layout. Used for project file reading and pasting items from clipboard.

The ``position`` argument is optional, and if it is not specified the items will be restored to their
original position from the XML serialization. If specified, the items will be positioned such that the top-left
bounds of all added items is located at this ``position``.

The ``pasteInPlace`` argument determines whether the serialized position should be respected, but remapped to the
origin of the page corresponding to the page at ``position``.

A list of the newly added items is returned.
:rtype: list of QgsLayoutItem
%End

QgsLayoutUndoStack *undoStack();
%Docstring
Returns a pointer to the layout's undo stack, which manages undo/redo states for the layout
@@ -35,8 +35,6 @@ class QgsLayoutFrame: QgsLayoutItem

virtual int type() const;

virtual QString stringType() const;

virtual QString uuid() const;


@@ -38,8 +38,6 @@ class QgsLayoutItemAttributeTable: QgsLayoutTable

virtual int type() const;

virtual QString stringType() const;

virtual QString displayName() const;


@@ -28,8 +28,6 @@ class QgsLayoutItemGroup: QgsLayoutItem

virtual int type() const;

virtual QString stringType() const;

virtual QString displayName() const;


@@ -70,6 +68,9 @@ class QgsLayoutItemGroup: QgsLayoutItem
virtual void paint( QPainter *painter, const QStyleOptionGraphicsItem *itemStyle, QWidget *pWidget );


virtual void finalizeRestoreFromXml();


protected:
virtual void draw( QgsRenderContext &context, const QStyleOptionGraphicsItem *itemStyle = 0 );

@@ -38,8 +38,6 @@ class QgsLayoutItemHtml: QgsLayoutMultiFrame

virtual int type() const;

virtual QString stringType() const;


static QgsLayoutItemHtml *create( QgsLayout *layout ) /Factory/;
%Docstring
@@ -40,11 +40,8 @@ class QgsLayoutItemLabel: QgsLayoutItem
:rtype: QgsLayoutItemLabel
%End


virtual int type() const;

virtual QString stringType() const;

virtual QString displayName() const;

void adjustSizeToText();
@@ -65,8 +65,6 @@ class QgsLayoutItemLegend : QgsLayoutItem

virtual int type() const;

virtual QString stringType() const;

virtual QString displayName() const;


@@ -451,6 +449,9 @@ class QgsLayoutItemLegend : QgsLayoutItem
virtual void paint( QPainter *painter, const QStyleOptionGraphicsItem *itemStyle, QWidget *pWidget );


virtual void finalizeRestoreFromXml();



public slots:

@@ -38,8 +38,6 @@ class QgsLayoutItemMap : QgsLayoutItem

virtual int type() const;

virtual QString stringType() const;


void assignFreeId();
%Docstring
@@ -423,6 +421,9 @@ Returns true if the map contains layers with blend modes or flattened layers for
:rtype: QgsMapSettings
%End

virtual void finalizeRestoreFromXml();


protected:

virtual void draw( QgsRenderContext &context, const QStyleOptionGraphicsItem *itemStyle = 0 );
@@ -46,9 +46,8 @@ class QgsLayoutItemPage : QgsLayoutItem
:rtype: QgsLayoutItemPage
%End


virtual int type() const;
virtual QString stringType() const;


void setPageSize( const QgsLayoutSize &size );
%Docstring
@@ -50,8 +50,6 @@ class QgsLayoutItemPicture: QgsLayoutItem

virtual int type() const;

virtual QString stringType() const;


static QgsLayoutItemPicture *create( QgsLayout *layout ) /Factory/;
%Docstring
@@ -234,6 +232,9 @@ class QgsLayoutItemPicture: QgsLayoutItem
:rtype: Format
%End

virtual void finalizeRestoreFromXml();


public slots:

void setPictureRotation( double rotation );
@@ -43,8 +43,6 @@ class QgsLayoutItemPolygon: QgsLayoutNodesItem

virtual int type() const;

virtual QString stringType() const;

virtual QString displayName() const;


@@ -49,8 +49,6 @@ class QgsLayoutItemPolyline: QgsLayoutNodesItem

virtual int type() const;

virtual QString stringType() const;

virtual QString displayName() const;


@@ -27,8 +27,6 @@ class QgsLayoutItemScaleBar: QgsLayoutItem

virtual int type() const;

virtual QString stringType() const;


static QgsLayoutItemScaleBar *create( QgsLayout *layout ) /Factory/;
%Docstring
@@ -436,6 +434,7 @@ class QgsLayoutItemScaleBar: QgsLayoutItem

virtual void refreshDataDefinedProperty( const QgsLayoutObject::DataDefinedProperty property = QgsLayoutObject::AllProperties );

virtual void finalizeRestoreFromXml();

protected:

@@ -34,7 +34,7 @@ class QgsLayoutItemShape : QgsLayoutItem
%End

virtual int type() const;
virtual QString stringType() const;


virtual QString displayName() const;

@@ -30,8 +30,6 @@ class QgsLayoutItemTextTable : QgsLayoutTable

virtual int type() const;

virtual QString stringType() const;

virtual QString displayName() const;


@@ -584,6 +584,26 @@ QDomElement QgsLayout::writeXml( QDomDocument &document, const QgsReadWriteConte
save( &mGridSettings );
save( mPageCollection.get() );

//save items except paper items and frame items (they are saved with the corresponding multiframe)
const QList<QGraphicsItem *> itemList = items();
for ( const QGraphicsItem *graphicsItem : itemList )
{
if ( const QgsLayoutItem *item = dynamic_cast< const QgsLayoutItem *>( graphicsItem ) )
{
if ( item->type() == QgsLayoutItemRegistry::LayoutPage
|| item->type() == QgsLayoutItemRegistry::LayoutFrame )
continue;

item->writeXml( element, document, context );
}
}

//save multiframes
for ( QgsLayoutMultiFrame *mf : mMultiFrames )
{
mf->writeXml( element, document, context );
}

writeXmlLayoutSettings( element, document, context );
return element;
}
@@ -623,6 +643,26 @@ void QgsLayout::deleteAndRemoveMultiFrames()
mMultiFrames.clear();
}

QPointF QgsLayout::minPointFromXml( const QDomElement &elem ) const
{
double minX = std::numeric_limits<double>::max();
double minY = std::numeric_limits<double>::max();
const QDomNodeList itemList = elem.elementsByTagName( QStringLiteral( "LayoutItem" ) );
bool found = false;
for ( int i = 0; i < itemList.size(); ++i )
{
const QDomElement currentItemElem = itemList.at( i ).toElement();

QgsLayoutPoint pos = QgsLayoutPoint::decodePoint( currentItemElem.attribute( QStringLiteral( "position" ) ) );
QPointF layoutPoint = convertToLayoutUnits( pos );

minX = std::min( minX, layoutPoint.x() );
minY = std::min( minY, layoutPoint.y() );
found = true;
}
return found ? QPointF( minX, minY ) : QPointF( 0, 0 );
}

void QgsLayout::updateZValues( const bool addUndoCommands )
{
int counter = mItemsModel->zOrderListSize();
@@ -671,10 +711,137 @@ bool QgsLayout::readXml( const QDomElement &layoutElement, const QDomDocument &d
restore( mPageCollection.get() );
restore( &mSnapper );
restore( &mGridSettings );
addItemsFromXml( layoutElement, document, context );

return true;
}

QVector< QgsLayoutItem * > QgsLayout::addItemsFromXml( const QDomElement &parentElement, const QDomDocument &document, const QgsReadWriteContext &context, QPointF *position, bool pasteInPlace )
{
std::unique_ptr< QPointF > pasteInPlacePt;
QVector< QgsLayoutItem * > newItems;
QVector< QgsLayoutMultiFrame * > newMultiFrames;

//if we are adding items to a layout which already contains items, we need to make sure
//these items are placed at the top of the layout and that zValues are not duplicated
//so, calculate an offset which needs to be added to the zValue of created items
int zOrderOffset = mItemsModel->zOrderListSize();

QPointF pasteShiftPos;
if ( position )
{
//If we are placing items relative to a certain point, then calculate how much we need
//to shift the items by so that they are placed at this point
//First, calculate the minimum position from the xml
QPointF minItemPos = minPointFromXml( parentElement );
//next, calculate how much each item needs to be shifted from its original position
//so that it's placed at the correct relative position
pasteShiftPos = *position - minItemPos;

#if 0 // TODO - move to gui
//since we are pasting items, clear the existing selection
setAllDeselected();
#endif
if ( pasteInPlace )
{
int pageNumber = mPageCollection->pageNumberForPoint( *position );
pasteInPlacePt = qgis::make_unique< QPointF >( 0, mPageCollection->page( pageNumber )->pos().y() );
}
}

const QDomNodeList layoutItemList = parentElement.elementsByTagName( QStringLiteral( "LayoutItem" ) );
for ( int i = 0; i < layoutItemList.size(); ++i )
{
const QDomElement currentItemElem = layoutItemList.at( i ).toElement();
const int itemType = currentItemElem.attribute( QStringLiteral( "type" ) ).toInt();
std::unique_ptr< QgsLayoutItem > item( QgsApplication::layoutItemRegistry()->createItem( itemType, this ) );
if ( !item )
{
// e.g. plugin based item which is no longer available
continue;
}

item->readXml( currentItemElem, document, context );
if ( position )
{
#if 0 //TODO
if ( pasteInPlacePt )
{
item->setItemPosition( newLabel->pos().x(), std::fmod( newLabel->pos().y(), ( paperHeight() + spaceBetweenPages() ) ) );
item->move( pasteInPlacePt->x(), pasteInPlacePt->y() );
}
else
{
item->move( pasteShiftPos.x(), pasteShiftPos.y() );
}
#endif
}

#if 0 //TODO - move to gui
newLabel->setSelected( true );
#endif

QgsLayoutItem *layoutItem = item.get();
addLayoutItem( item.release() );
layoutItem->setZValue( layoutItem->zValue() + zOrderOffset );
newItems << layoutItem;
}

// multiframes

//TODO - fix this. pasting multiframe frame items has no effect
const QDomNodeList multiFrameList = parentElement.elementsByTagName( QStringLiteral( "LayoutMultiFrame" ) );
for ( int i = 0; i < multiFrameList.size(); ++i )
{
const QDomElement multiFrameElem = multiFrameList.at( i ).toElement();
const int itemType = multiFrameElem.attribute( QStringLiteral( "type" ) ).toInt();
std::unique_ptr< QgsLayoutMultiFrame > mf( QgsApplication::layoutItemRegistry()->createMultiFrame( itemType, this ) );
if ( !mf )
{
// e.g. plugin based item which is no longer available
continue;
}
mf->readXml( multiFrameElem, document, context );

#if 0 //TODO?
mf->setCreateUndoCommands( true );
#endif

QgsLayoutMultiFrame *m = mf.get();
this->addMultiFrame( mf.release() );

//offset z values for frames
//TODO - fix this after fixing multiframe item paste
/*for ( int frameIdx = 0; frameIdx < mf->frameCount(); ++frameIdx )
{
QgsLayoutItemFrame * frame = mf->frame( frameIdx );
frame->setZValue( frame->zValue() + zOrderOffset );
}*/
newMultiFrames << m;
}


// we now allow items to "post-process", e.g. if they need to setup connections
// to other items in the layout, which may not have existed at the time the
// item's state was restored. E.g. a scalebar may have been restored before the map
// it is linked to
for ( QgsLayoutItem *item : qgis::as_const( newItems ) )
{
item->finalizeRestoreFromXml();
}
for ( QgsLayoutMultiFrame *mf : qgis::as_const( newMultiFrames ) )
{
mf->finalizeRestoreFromXml();
}

//Since this function adds items in an order which isn't the z-order, and each item is added to end of
//z order list in turn, it will now be inconsistent with the actual order of items in the scene.
//Make sure z order list matches the actual order of items in the scene.
mItemsModel->rebuildZList();

return newItems;
}

void QgsLayout::updateBounds()
{
setSceneRect( layoutBounds( false, 0.05 ) );

0 comments on commit b74a0ef

Please sign in to comment.
You can’t perform that action at this time.