Custom map pan tool freezes map canvas extent, cuts off map tiles (QGIS 3.18)

cmapcanvaspanqgis

I've written my own QGIS map tool class to access the protected member functions (canvasPressEvent, canvasReleaseEvent, etc) as well as to be able to limit the distance panned. Specifically I've subclassed this with the QgsMapToolPan class. It's pretty straight forward (this is C++, but the PyQGIS will look similar):

pan.h:

#ifndef PAN_H
#define PAN_H

#include <qgsmapcanvas.h>
#include <qgsmapmouseevent.h>
#include <qgsmaptoolpan.h>

#include <QObject>

class Pan : public QgsMapToolPan
{
    Q_OBJECT
public:
    Pan(QgsMapCanvas *qmc);
    ~Pan();
protected:
    void canvasPressEvent(QgsMapMouseEvent *e);
    void canvasReleaseEvent(QgsMapMouseEvent *e);
};

#endif // PAN_H

pan.cpp:

#include "include/pan.h"

Pan::Pan(QgsMapCanvas *qmc) : QgsMapToolPan(qmc)
{
    activate();
    setCursor(Qt::OpenHandCursor);
}

void Pan::canvasPressEvent(QgsMapMouseEvent *e)
{
    setCursor(Qt::ClosedHandCursor);
}

void Pan::canvasReleaseEvent(QgsMapMouseEvent *e)
{
    setCursor(Qt::OpenHandCursor);
}

Pan::~Pan()
{
    deactivate();
}

However when the tool is activated and the user starts panning, it reverts back to the previous canvas extent and any change in zoom scale level keeps that extent constant. Here's an example of what I mean using OpenStreetMaps:

OpenStreetMap upon app launch
OpenStreetMap upon app launch
App after panning right.  A subsequent pan action reverts back to image 1 first
App after panning right. A subsequent pan action reverts back to image 1 first
Any subsequent zooming causes the white space to move in line with the cut off, hinting at a frozen canvas extent
Any subsequent zooming causes the white space to move in line with the cut off, hinting at a frozen canvas extent

I don't see anywhere in my code where I would be setting the map canvas' extent as its previous one so this action is puzzling. I've added canvas refresh commands but the tiles are still cut off.

Best Answer

First of all, why do you expect that the canvas extent should change using your code? I don't see a new assignment to map canvas extent or map canvas center anywhere in your code. Therefore the canvas basically stays the same and appears frozen.

QgsMapToolPan updates the position and extent in the canvasReleaseEvent function, which you are hiding with your custom behaviour in Pan::canvasReleaseEvent (doing nothing but setting the cursor!).

So your options would be to either call the base implementation using something like

void Pan::canvasReleaseEvent(QgsMapMouseEvent *e)
{
    QgsMapToolPan::canvasReleaseEvent(e)
    setCursor(Qt::OpenHandCursor);
}

or if you don't want to call the default implementation an option would be to use panActionEnd, which ends the pan action and redraws the canvas.

void Pan::canvasReleaseEvent(QgsMapMouseEvent *e)
{
    setCursor(Qt::OpenHandCursor);
    mCanvas->panActionEnd(e->pos());
}

Or exactly the same using python:

class Pan(QgsMapToolPan):
    def __init__(self, canvas):
        super().__init__(canvas)
        self.canvas = canvas
        
    def canvasPressEvent(self, e):
        self.setCursor(Qt.ClosedHandCursor)
        
    def canvasReleaseEvent(self, e):
        self.setCursor(Qt.OpenHandCursor)
        self.canvas.panActionEnd(e.pos())

tool = Pan(iface.mapCanvas())
iface.mapCanvas().setMapTool(tool)
Related Question