1

I wrote a qgis plugin where I can modify some features interactively. I have a class MovePointTool derived from QgsMapToolIdentify.

When a point is moved, I call a function that does some (relatively light) processing and modifies some other dependent layers (self.database.moveIP and self.database.display in the snippet below).

class MovePointTool(QgsMapToolIdentify):

    def __init__(self, mapCanvas, layer, database):
        self.ready = True
        print("Creating move instance")
        QgsMapToolIdentify.__init__(self, mapCanvas)
        self.setCursor(Qt.CrossCursor)
        self.layer    = layer
        self.dragging = False
        self.feature  = None
        self.database = database

    def canvasPressEvent(self, event):
        print("press")
        found_features = self.identify(event.x(), event.y(),
                                       [self.layer],
                                       self.TopDownAll)
        if len(found_features) > 0:
            self.dragging = True
            self.feature  = found_features[0].mFeature
        else:
            self.dragging = False
            self.feature  = None

    def canvasMoveEvent(self, event):
        if self.dragging and self.ready:
            self.ready = False
            point = self.toLayerCoordinates(self.layer,
                                            event.pos())
            geometry = QgsGeometry.fromPoint(point)
            self.layer.changeGeometry(self.feature.id(), geometry)
            self.database.moveIP(self.feature.id(), self.toLayerCoordinates(self.layer, event.pos()))
            self.database.display()
            self.canvas().refresh()
            print('c')
            self.ready = True

    def canvasReleaseEvent(self, event):
            print("release")
            self.dragging = False
            self.feature  = None

I am not sure how QgsMapToolIdentify works, and if it is asynchronous. I have no experience with asynchronous programming. I had to add the self.ready flag above to avoid segmentation faults (not sure how robust it is).

However, the canvas does not refresh smoothly (lag and miss some moves), except if the

print('c') 

line is present and IF the python console if open. Then, it refreshes very smoothly.

This is a strange issue. What is the proper way to be able to move a point, do some processing based on this move and then refresh the canvas? Is there any barrier to add somewhere?

1

I found a solution, using mapCanvasRefreshed signal, in order to wait for the canvas to be refreshed before doing processing again. It mimics a synchronous call of self.canvas.refresh(), which I think would be useful in several applications.

I suppose the print was useful because it took some time to print to the console, hence letting enough time for the canvas to be refreshed.

class MovePointTool(QgsMapToolIdentify):

    def __init__(self, mapCanvas, layer, database):
        self.ready = True
        print("Creating move instance")
        QgsMapToolIdentify.__init__(self, mapCanvas)
        self.setCursor(Qt.CrossCursor)
        self.layer    = layer
        self.dragging = False
        self.feature  = None
        self.database = database

    #CHANGE: Allow processing when the canvas has been refreshed
    def setReady(self):
        self.ready = True

    def canvasPressEvent(self, event):
        print("press")
        found_features = self.identify(event.x(), event.y(),
                                       [self.layer],
                                       self.TopDownAll)
        if len(found_features) > 0:
            self.dragging = True
            self.feature  = found_features[0].mFeature
        else:
            self.dragging = False
            self.feature  = None

    def canvasMoveEvent(self, event):
        if self.dragging and self.ready:
            self.ready = False
            point = self.toLayerCoordinates(self.layer, event.pos())
            geometry = QgsGeometry.fromPoint(point)
            self.layer.changeGeometry(self.feature.id(), geometry)
            self.database.moveIP(self.feature.id(), self.toLayerCoordinates(self.layer, event.pos()))
            self.database.display()
            self.canvas().refresh()
            #CHANGE: Call setReady when the canvas has been refreshed
            self.canvas().mapCanvasRefreshed.connect( self.setReady )


    def canvasReleaseEvent(self, event):
            print("release")
            self.dragging = False
            self.feature  = None

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.