Skip to content
Permalink
Browse files

Annotations are now handled at a project level

This commit implements a new QgsAnnotationManager class, which
handles storage, writing and retrieval of annotations.

QgsProject has an annotationManager() attached to it. Map canvases
sync their visible QgsMapCanvasAnnotationItems to the annotations
contained within the project's annotation manager.

This moves all management, storage and retrieval of annotations
up to core and out of app/canvas.
  • Loading branch information
nyalldawson committed Jan 30, 2017
1 parent dd51843 commit 4058f4734bdf3852201cfb4289e3b0492e491a4a
Showing with 395 additions and 181 deletions.
  1. +18 −0 python/core/annotations/qgsannotation.sip
  2. +24 −0 python/core/annotations/qgsannotationmanager.sip
  3. +2 −0 python/core/annotations/qgshtmlannotation.sip
  4. +3 −1 python/core/annotations/qgssvgannotation.sip
  5. +2 −0 python/core/annotations/qgstextannotation.sip
  6. +2 −2 python/core/composer/qgscomposermap.sip
  7. +1 −0 python/core/core.sip
  8. +2 −0 python/core/qgsproject.sip
  9. +2 −2 src/app/composer/qgscomposermapwidget.cpp
  10. +12 −92 src/app/qgisapp.cpp
  11. +3 −4 src/app/qgisapp.h
  12. +4 −6 src/app/qgsformannotationdialog.cpp
  13. +4 −6 src/app/qgshtmlannotationdialog.cpp
  14. +4 −7 src/app/qgsmaptoolannotation.cpp
  15. +4 −6 src/app/qgssvgannotationdialog.cpp
  16. +4 −6 src/app/qgstextannotationdialog.cpp
  17. +2 −0 src/core/CMakeLists.txt
  18. +4 −1 src/core/annotations/qgsannotation.cpp
  19. +60 −14 src/core/annotations/qgsannotationmanager.cpp
  20. +12 −1 src/core/annotations/qgsannotationmanager.h
  21. +131 −0 src/core/annotations/qgsannotationregistry.h
  22. +5 −0 src/core/annotations/qgshtmlannotation.h
  23. +5 −0 src/core/annotations/qgssvgannotation.h
  24. +5 −0 src/core/annotations/qgstextannotation.h
  25. +15 −20 src/core/composer/qgscomposermap.cpp
  26. +14 −5 src/core/composer/qgscomposermap.h
  27. +8 −0 src/core/qgsapplication.cpp
  28. +9 −0 src/core/qgsapplication.h
  29. +13 −0 src/core/qgsproject.cpp
  30. +9 −0 src/core/qgsproject.h
  31. +5 −0 src/gui/qgsformannotation.h
  32. +6 −5 src/gui/qgsmapcanvasannotationitem.cpp
  33. +1 −3 src/gui/qgsmapcanvasannotationitem.h
@@ -1,8 +1,26 @@
%ModuleHeaderCode
#include <qgshtmlannotation.h>
#include <qgssvgannotation.h>
#include <qgstextannotation.h>
%End

class QgsAnnotation : QObject
{
%TypeHeaderCode
#include <qgsannotation.h>
%End

%ConvertToSubClassCode
if ( dynamic_cast< QgsTextAnnotation* > ( sipCpp ) )
sipType = sipType_QgsTextAnnotation;
else if ( dynamic_cast< QgsSvgAnnotation* > ( sipCpp ) )
sipType = sipType_QgsSvgAnnotation;
else if ( dynamic_cast< QgsHtmlAnnotation* > ( sipCpp ) )
sipType = sipType_QgsHtmlAnnotation;
else
sipType = NULL;
%End

public:

QgsAnnotation( QObject* parent /TransferThis/ = nullptr );
@@ -0,0 +1,24 @@
class QgsAnnotationManager : QObject
{
%TypeHeaderCode
#include <qgsannotationmanager.h>
%End
public:

explicit QgsAnnotationManager( QgsProject* project = nullptr );
~QgsAnnotationManager();

bool addAnnotation( QgsAnnotation* annotation /Transfer/ );
bool removeAnnotation( QgsAnnotation* annotation );
void clear();
QList< QgsAnnotation* > annotations() const;
bool readXml( const QDomElement& element, const QDomDocument& doc );
QDomElement writeXml( QDomDocument& doc ) const;

signals:

void annotationAdded( QgsAnnotation* annotation );
void annotationRemoved();
void annotationAboutToBeRemoved( QgsAnnotation* annotation );

};
@@ -19,6 +19,8 @@ class QgsHtmlAnnotation : QgsAnnotation

virtual void setAssociatedFeature( const QgsFeature& feature );

static QgsHtmlAnnotation* create() /Factory/;

protected:

void renderAnnotation( QgsRenderContext& context, QSizeF size ) const;
@@ -12,7 +12,9 @@ class QgsSvgAnnotation : QgsAnnotation

void setFilePath( const QString& file );
QString filePath() const;


static QgsSvgAnnotation* create() /Factory/;

protected:

void renderAnnotation( QgsRenderContext& context, QSizeF size ) const;
@@ -13,6 +13,8 @@ class QgsTextAnnotation : QgsAnnotation
virtual void writeXml( QDomElement& elem, QDomDocument & doc ) const;
virtual void readXml( const QDomElement& itemElem, const QDomDocument& doc );

static QgsTextAnnotation* create() /Factory/;

protected:

void renderAnnotation( QgsRenderContext& context, QSizeF size ) const;
@@ -258,8 +258,8 @@ class QgsComposerMap : QgsComposerItem
/** Sets canvas pointer (necessary to query and draw map canvas items)*/
void setMapCanvas( QGraphicsView* canvas /Transfer/ );

void setDrawCanvasItems( bool b );
bool drawCanvasItems() const;
void setDrawAnnotations( bool draw );
bool drawAnnotations() const;

/** Returns the conversion factor map units -> mm*/
double mapUnitsToMM() const;
@@ -175,6 +175,7 @@
%Include qgsxmlutils.sip

%Include annotations/qgsannotation.sip
%Include annotations/qgsannotationmanager.sip
%Include annotations/qgshtmlannotation.sip
%Include annotations/qgssvgannotation.sip
%Include annotations/qgstextannotation.sip
@@ -325,6 +325,8 @@ class QgsProject : QObject, QgsExpressionContextGenerator
*/
QgsMapThemeCollection* mapThemeCollection();

QgsAnnotationManager* annotationManager();

/**
* Set a list of layers which should not be taken into account on map identification
*/
@@ -683,7 +683,7 @@ void QgsComposerMapWidget::updateGuiElements()
mKeepLayerStylesCheckBox->setCheckState( mComposerMap->keepLayerStyles() ? Qt::Checked : Qt::Unchecked );

//draw canvas items
if ( mComposerMap->drawCanvasItems() )
if ( mComposerMap->drawAnnotations() )
{
mDrawCanvasItemsCheckBox->setCheckState( Qt::Checked );
}
@@ -951,7 +951,7 @@ void QgsComposerMapWidget::on_mDrawCanvasItemsCheckBox_stateChanged( int state )
}

mComposerMap->beginCommand( tr( "Canvas items toggled" ) );
mComposerMap->setDrawCanvasItems( state == Qt::Checked );
mComposerMap->setDrawAnnotations( state == Qt::Checked );
mUpdatePreviewButton->setEnabled( false ); //prevent crashes because of many button clicks
mComposerMap->setCacheUpdated( false );
mComposerMap->cache();
@@ -106,6 +106,8 @@
#include "qgsapplayertreeviewmenuprovider.h"
#include "qgsapplication.h"
#include "qgsactionmanager.h"
#include "qgsannotationmanager.h"
#include "qgsannotationregistry.h"
#include "qgsattributetabledialog.h"
#include "qgsattributedialog.h"
#include "qgsauthmanager.h"
@@ -777,6 +779,10 @@ QgisApp::QgisApp( QSplashScreen *splash, bool restorePlugins, bool skipVersionCh
functionProfile( &QgisApp::updateRecentProjectPaths, this, QStringLiteral( "Update recent project paths" ) );
functionProfile( &QgisApp::updateProjectFromTemplates, this, QStringLiteral( "Update project from templates" ) );
functionProfile( &QgisApp::legendLayerSelectionChanged, this, QStringLiteral( "Legend layer selection changed" ) );

QgsApplication::annotationRegistry()->addAnnotationType( QgsAnnotationMetadata( QStringLiteral( "FormAnnotationItem" ), &QgsFormAnnotation::create ) );
connect( QgsProject::instance()->annotationManager(), &QgsAnnotationManager::annotationAdded, this, &QgisApp::annotationCreated );

mSaveRollbackInProgress = false;

QFileSystemWatcher* projectsTemplateWatcher = new QFileSystemWatcher( this );
@@ -1416,6 +1422,12 @@ void QgisApp::dropEventTimeout()
mMapCanvas->refresh();
}

void QgisApp::annotationCreated( QgsAnnotation* annotation )
{
// create canvas annotation item for annotation
QgsMapCanvasAnnotationItem* canvasItem = new QgsMapCanvasAnnotationItem( annotation, mMapCanvas );
Q_UNUSED( canvasItem ); //item is already added automatically to canvas scene
}

void QgisApp::registerCustomDropHandler( QgsCustomDropHandler* handler )
{
@@ -1817,31 +1829,6 @@ void QgisApp::showStyleManager()
dlg.exec();
}

void QgisApp::writeAnnotationItemsToProject( QDomDocument& doc )
{
QDomElement documentElem = doc.documentElement();
if ( documentElem.isNull() )
{
return;
}

QList<QgsMapCanvasAnnotationItem*> items = annotationItems();
QgsMapCanvasAnnotationItem* item = nullptr;
QListIterator<QgsMapCanvasAnnotationItem*> i( items );
// save lowermost annotation (at end of list) first
i.toBack();
while ( i.hasPrevious() )
{
item = i.previous();

if ( !item || !item->annotation() )
{
continue;
}
item->annotation()->writeXml( documentElem, doc );
}
}

void QgisApp::showPythonDialog()
{
if ( !mPythonUtils || !mPythonUtils->isEnabled() )
@@ -2831,11 +2818,8 @@ void QgisApp::setupConnections()
this, SLOT( readProject( const QDomDocument & ) ) );
connect( QgsProject::instance(), SIGNAL( writeProject( QDomDocument & ) ),
this, SLOT( writeProject( QDomDocument & ) ) );
connect( QgsProject::instance(), SIGNAL( writeProject( QDomDocument& ) ),
this, SLOT( writeAnnotationItemsToProject( QDomDocument& ) ) );

connect( QgsProject::instance(), SIGNAL( readProject( const QDomDocument & ) ), this, SLOT( loadComposersFromProject( const QDomDocument& ) ) );
connect( QgsProject::instance(), SIGNAL( readProject( const QDomDocument & ) ), this, SLOT( loadAnnotationItemsFromProject( const QDomDocument& ) ) );

connect( this, SIGNAL( projectRead() ),
this, SLOT( fileOpenedOKAfterLaunch() ) );
@@ -6936,70 +6920,6 @@ void QgisApp::on_mPrintComposersMenu_aboutToShow()
mPrintComposersMenu->addActions( acts );
}

bool QgisApp::loadAnnotationItemsFromProject( const QDomDocument& doc )
{
if ( !mMapCanvas )
{
return false;
}

removeAnnotationItems();

if ( doc.isNull() )
{
return false;
}

QList< QgsAnnotation* > annotations;

QDomNodeList textItemList = doc.elementsByTagName( QStringLiteral( "TextAnnotationItem" ) );
for ( int i = 0; i < textItemList.size(); ++i )
{
QgsTextAnnotation* newTextItem = new QgsTextAnnotation();
newTextItem->readXml( textItemList.at( i ).toElement(), doc );
annotations << newTextItem;
}

QDomNodeList formItemList = doc.elementsByTagName( QStringLiteral( "FormAnnotationItem" ) );
for ( int i = 0; i < formItemList.size(); ++i )
{
QgsFormAnnotation* newFormItem = new QgsFormAnnotation();
newFormItem->readXml( formItemList.at( i ).toElement(), doc );
annotations << newFormItem;
}

#ifdef WITH_QTWEBKIT
QDomNodeList htmlItemList = doc.elementsByTagName( QStringLiteral( "HtmlAnnotationItem" ) );
for ( int i = 0; i < htmlItemList.size(); ++i )
{
QgsHtmlAnnotation* newHtmlItem = new QgsHtmlAnnotation();
newHtmlItem->readXml( htmlItemList.at( i ).toElement(), doc );
annotations << newHtmlItem;
}
#endif

QDomNodeList svgItemList = doc.elementsByTagName( QStringLiteral( "SVGAnnotationItem" ) );
for ( int i = 0; i < svgItemList.size(); ++i )
{
QgsSvgAnnotation* newSvgItem = new QgsSvgAnnotation();
newSvgItem->readXml( svgItemList.at( i ).toElement(), doc );
annotations << newSvgItem;
}

Q_FOREACH ( QgsAnnotation* annotation, annotations )
{
if ( !annotation->mapPositionCrs().isValid() )
{
annotation->setMapPositionCrs( mMapCanvas->mapSettings().destinationCrs() );
}

// create canvas annotation items
QgsMapCanvasAnnotationItem* canvasItem = new QgsMapCanvasAnnotationItem( annotation, mMapCanvas );
Q_UNUSED( canvasItem ); //item is already added automatically to canvas scene
}
return true;
}

void QgisApp::showPinnedLabels( bool show )
{
qobject_cast<QgsMapToolPinLabels*>( mMapTools.mPinLabels )->showPinnedLabels( show );
@@ -39,6 +39,7 @@ class QValidator;

class QgisAppInterface;
class QgisAppStyleSheet;
class QgsAnnotation;
class QgsMapCanvasAnnotationItem;
class QgsAuthManager;
class QgsBookmarks;
@@ -1308,16 +1309,12 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow

void showStyleManager();

void writeAnnotationItemsToProject( QDomDocument& doc );

//! Creates the composer instances in a project file and adds them to the menu
bool loadComposersFromProject( const QDomDocument& doc );

//! Slot to handle display of composers menu, e.g. sorting
void on_mPrintComposersMenu_aboutToShow();

bool loadAnnotationItemsFromProject( const QDomDocument& doc );

//! Toggles whether to show pinned labels
void showPinnedLabels( bool show );
//! Activates pin labels tool
@@ -1390,6 +1387,8 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow
//! Handles processing of dropped mimedata
void dropEventTimeout();

void annotationCreated( QgsAnnotation* annotation );

signals:

/** Emitted when a key is pressed and we want non widget sublasses to be able
@@ -17,6 +17,8 @@
#include "qgsformannotation.h"
#include "qgsmapcanvasannotationitem.h"
#include "qgsvectorlayer.h"
#include "qgsproject.h"
#include "qgsannotationmanager.h"
#include <QFileDialog>
#include <QFileInfo>
#include <QGraphicsScene>
@@ -84,12 +86,8 @@ void QgsFormAnnotationDialog::on_mBrowseToolButton_clicked()

void QgsFormAnnotationDialog::deleteItem()
{
QGraphicsScene* scene = mItem->scene();
if ( scene )
{
scene->removeItem( mItem );
}
delete mItem;
if ( mItem && mItem->annotation() )
QgsProject::instance()->annotationManager()->removeAnnotation( mItem->annotation() );
mItem = nullptr;
}

@@ -17,6 +17,8 @@
#include "qgsannotationwidget.h"
#include "qgsmapcanvasannotationitem.h"
#include "qgsvectorlayer.h"
#include "qgsproject.h"
#include "qgsannotationmanager.h"
#include <QFileDialog>
#include <QFileInfo>
#include <QGraphicsScene>
@@ -83,12 +85,8 @@ void QgsHtmlAnnotationDialog::on_mBrowseToolButton_clicked()

void QgsHtmlAnnotationDialog::deleteItem()
{
QGraphicsScene* scene = mItem->scene();
if ( scene )
{
scene->removeItem( mItem );
}
delete mItem;
if ( mItem && mItem->annotation() )
QgsProject::instance()->annotationManager()->removeAnnotation( mItem->annotation() );
mItem = nullptr;
}

@@ -29,6 +29,7 @@
#include "qgssvgannotation.h"
#include "qgsproject.h"
#include "qgscsexception.h"
#include "qgsannotationmanager.h"
#include <QDialog>
#include <QMouseEvent>

@@ -146,15 +147,11 @@ void QgsMapToolAnnotation::keyPressEvent( QKeyEvent* e )
{
if ( e->key() == Qt::Key_Backspace || e->key() == Qt::Key_Delete )
{
if ( mCanvas && mCanvas->scene() )
QCursor neutralCursor( item->cursorShapeForAction( QgsMapCanvasAnnotationItem::NoAction ) );
QgsProject::instance()->annotationManager()->removeAnnotation( item->annotation() );
if ( mCanvas )
{
QCursor neutralCursor( item->cursorShapeForAction( QgsMapCanvasAnnotationItem::NoAction ) );
mCanvas->scene()->removeItem( item );
delete item;
mCanvas->setCursor( neutralCursor );
QgsProject::instance()->setDirty( true ); // TODO QGIS3: Rework the whole annotation code to be MVC compliant, see PR #2506

// Override default shortcut management in MapCanvas
e->ignore();
}
}

0 comments on commit 4058f47

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