Have you ever wanted to animate a QGIS map canvas item. No? Well soon you will.
First we need to create a custom QgsMapCanvasItem
from PyQt4.QtCore import QPointF, QRectF, QTimer, QObject, pyqtProperty, QPropertyAnimation, Qt from PyQt4.QtGui import QPainter, QBrush, QColor from qgis.gui import QgsMapCanvasItem from qgis.core import QgsPoint class PingLocationMarker(QgsMapCanvasItem): """ Position marker for the current location in the viewer. """ class AniObject(QObject): def __init__(self): super(PingLocationMarker.AniObject, self).__init_<a href="https://woostuff.files.wordpress.com/2014/10/marker.gif"><img src="https://woostuff.files.wordpress.com/2014/10/marker.gif?w=300" alt="marker" width="300" height="193" class="alignnone size-medium wp-image-61493" /></a>_() self._size = 0 self.startsize = 0 self.maxsize = 32 @pyqtProperty(int) def size(self): return self._size @size.setter def size(self, value): self._size = value def __init__(self, canvas): self.canvas = canvas self.map_pos = QgsPoint(0.0, 0.0) self.aniobject = PingLocationMarker.AniObject() QgsMapCanvasItem.__init__(self, canvas) self.anim = QPropertyAnimation(self.aniobject, "size") self.anim.setDuration(1000) self.anim.setStartValue(self.aniobject.startsize) self.anim.setEndValue(self.aniobject.maxsize) self.anim.setLoopCount(-1) self.anim.valueChanged.connect(self.value_changed) self.anim.start() @property def size(self): return self.aniobject.size @property def halfsize(self): return self.aniobject.maxsize / 2.0 @property def maxsize(self): return self.aniobject.maxsize def value_changed(self, value): self.update() def paint(self, painter, xxx, xxx2): self.setCenter(self.map_pos) rect = QRectF(0 - self.halfsize, 0 - self.halfsize, self.size, self.size) painter.setRenderHint(QPainter.Antialiasing) painter.setBrush(Qt.green) painter.setPen(Qt.green) painter.drawEllipse(QPointF(0,0), self.size, self.size) def boundingRect(self): return QRectF(-self.halfsize * 2.0, -self.halfsize * 2.0, 2.0 * self.maxsize, 2.0 * self.maxsize) def setCenter(self, map_pos): self.map_pos = map_pos self.setPos(self.toCanvasCoordinates(self.map_pos)) def updatePosition(self): self.setCenter(self.map_pos) marker = PingLocationMarker(iface.mapCanvas())
and this is the result
wweeeee animated canvas item.
Run the above code in a QGIS Python console editor window and you should get the same effect.
So what is this magic? Well it turns out to be pretty easy all thanks to the handy class QPropertyAnimation
. QPropertyAnimation
takes a QObject
and sets a property value until the end value is hit over the duration, the cool thing with this class is that it can take any QVariant type, which is pretty much anything, and it will go from start value to end value. You can also use other easing curves to change how the values change over time.
Super nifty!
The main important part of this is:
self.anim = QPropertyAnimation(self.aniobject, "size") self.anim.setDuration(1000) self.anim.setStartValue(self.aniobject.startsize) self.anim.setEndValue(self.aniobject.maxsize) self.anim.setLoopCount(-1) self.anim.valueChanged.connect(self.value_changed) self.anim.start()
which calls the value_changed
and self.update()
methods, when update is called paint
will be called and we grab the current animation value. Note: -1
loop count means run forever.
self.aniobject
is a custom QObject
to hold our current animation value. QgsMapCanvasItem
is not a QObject
so we have to make another object to hold that value for us. I tried double inheritance here and it didn’t like it so I went with a nested class which is nice anyway.
And that is all you need to make a animated QgsMapCanvasItem
, remember that QPropertyAnimation
can be used on any QObject
so you could do some pretty cool stuff with this if you have the need.
Be interested to hear any ideas on if we can use this in QGIS.
Note: A canvas item like this isn’t part of any layer. Canvas items live on on the canvas itself, above or below the map image. The markers that you see when you enable editing on a layer are canvas items, as are the lines when drawing a measure line, even the image you see in the canvas is a canvas item, etc.
Filed under: Open Source
