Skip to content

Commit

Permalink
Work on layout and layout item serialization and restoration
Browse files Browse the repository at this point in the history
  • Loading branch information
nyalldawson committed Dec 6, 2017
1 parent ca75e8c commit b74a0ef
Show file tree
Hide file tree
Showing 55 changed files with 396 additions and 196 deletions.
17 changes: 17 additions & 0 deletions python/core/layout/qgslayout.sip
Expand Up @@ -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
Expand Down
2 changes: 0 additions & 2 deletions python/core/layout/qgslayoutframe.sip
Expand Up @@ -35,8 +35,6 @@ class QgsLayoutFrame: QgsLayoutItem

virtual int type() const;

virtual QString stringType() const;

virtual QString uuid() const;


Expand Down
2 changes: 0 additions & 2 deletions python/core/layout/qgslayoutitemattributetable.sip
Expand Up @@ -38,8 +38,6 @@ class QgsLayoutItemAttributeTable: QgsLayoutTable

virtual int type() const;

virtual QString stringType() const;

virtual QString displayName() const;


Expand Down
5 changes: 3 additions & 2 deletions python/core/layout/qgslayoutitemgroup.sip
Expand Up @@ -28,8 +28,6 @@ class QgsLayoutItemGroup: QgsLayoutItem

virtual int type() const;

virtual QString stringType() const;

virtual QString displayName() const;


Expand Down Expand Up @@ -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 );

Expand Down
2 changes: 0 additions & 2 deletions python/core/layout/qgslayoutitemhtml.sip
Expand Up @@ -38,8 +38,6 @@ class QgsLayoutItemHtml: QgsLayoutMultiFrame

virtual int type() const;

virtual QString stringType() const;


static QgsLayoutItemHtml *create( QgsLayout *layout ) /Factory/;
%Docstring
Expand Down
3 changes: 0 additions & 3 deletions python/core/layout/qgslayoutitemlabel.sip
Expand Up @@ -40,11 +40,8 @@ class QgsLayoutItemLabel: QgsLayoutItem
:rtype: QgsLayoutItemLabel
%End


virtual int type() const;

virtual QString stringType() const;

virtual QString displayName() const;

void adjustSizeToText();
Expand Down
5 changes: 3 additions & 2 deletions python/core/layout/qgslayoutitemlegend.sip
Expand Up @@ -65,8 +65,6 @@ class QgsLayoutItemLegend : QgsLayoutItem

virtual int type() const;

virtual QString stringType() const;

virtual QString displayName() const;


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


virtual void finalizeRestoreFromXml();



public slots:

Expand Down
5 changes: 3 additions & 2 deletions python/core/layout/qgslayoutitemmap.sip
Expand Up @@ -38,8 +38,6 @@ class QgsLayoutItemMap : QgsLayoutItem

virtual int type() const;

virtual QString stringType() const;


void assignFreeId();
%Docstring
Expand Down Expand Up @@ -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 );
Expand Down
3 changes: 1 addition & 2 deletions python/core/layout/qgslayoutitempage.sip
Expand Up @@ -46,9 +46,8 @@ class QgsLayoutItemPage : QgsLayoutItem
:rtype: QgsLayoutItemPage
%End


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


void setPageSize( const QgsLayoutSize &size );
%Docstring
Expand Down
5 changes: 3 additions & 2 deletions python/core/layout/qgslayoutitempicture.sip
Expand Up @@ -50,8 +50,6 @@ class QgsLayoutItemPicture: QgsLayoutItem

virtual int type() const;

virtual QString stringType() const;


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

virtual void finalizeRestoreFromXml();


public slots:

void setPictureRotation( double rotation );
Expand Down
2 changes: 0 additions & 2 deletions python/core/layout/qgslayoutitempolygon.sip
Expand Up @@ -43,8 +43,6 @@ class QgsLayoutItemPolygon: QgsLayoutNodesItem

virtual int type() const;

virtual QString stringType() const;

virtual QString displayName() const;


Expand Down
2 changes: 0 additions & 2 deletions python/core/layout/qgslayoutitempolyline.sip
Expand Up @@ -49,8 +49,6 @@ class QgsLayoutItemPolyline: QgsLayoutNodesItem

virtual int type() const;

virtual QString stringType() const;

virtual QString displayName() const;


Expand Down
3 changes: 1 addition & 2 deletions python/core/layout/qgslayoutitemscalebar.sip
Expand Up @@ -27,8 +27,6 @@ class QgsLayoutItemScaleBar: QgsLayoutItem

virtual int type() const;

virtual QString stringType() const;


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

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

virtual void finalizeRestoreFromXml();

protected:

Expand Down
2 changes: 1 addition & 1 deletion python/core/layout/qgslayoutitemshape.sip
Expand Up @@ -34,7 +34,7 @@ class QgsLayoutItemShape : QgsLayoutItem
%End

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


virtual QString displayName() const;

Expand Down
2 changes: 0 additions & 2 deletions python/core/layout/qgslayoutitemtexttable.sip
Expand Up @@ -30,8 +30,6 @@ class QgsLayoutItemTextTable : QgsLayoutTable

virtual int type() const;

virtual QString stringType() const;

virtual QString displayName() const;


Expand Down
167 changes: 167 additions & 0 deletions src/core/layout/qgslayout.cpp
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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 ) );
Expand Down

0 comments on commit b74a0ef

Please sign in to comment.