I have build a custom toolbar and a tool for a qgis plugin (QGIS 2.18.13) updating a working one in QGIS 1.8. I have several layers loaded, two of them being vector layers for nodes (points) and edges (lines). The tool creates a custom cursor and allows to draw nodes or edges on a map, depending if the user has selected nodes or edges layer.
When changing to a different layer (no matter what the new layer type is) the tool should be automatically deactivated. This worked in QGIS 1.8, but not now. The tool keeps active when I change from one layer to another. For example, if I am adding nodes and I change to edges layer, the tool keeps activate and does not add edges but nodes. I have tried to force action toggle, but anyway it does not deactivate.
Maybe I am not understanding the triggered signals.
In QGIS 1.8 the tool deactivate() method was called just after layer change, but I do not know who triggers this. Is it the Canvas?
Note: There is no problem with the behaviour of the tool once activated. It does what it is supossed to do.
Here is the relevant code I have:
class myToolbar:
def __init__(self, iface, toolBar):
# References to QGIS interface
self.iface = iface
self.canvas = iface.mapCanvas()
# Actions
self.addFeatureAction = QAction(QIcon( \
":/myPlugin/images/icon.png"), \
u"Add nodes or edges", \
self.iface.mainWindow())
self.addFeatureAction.setWhatsThis(u"MyPlugin: Add nodes or edges")
self.addFeatureAction.setStatusTip(u"MyPlugin: Add nodes or edges")
# Set checkable
self.addFeatureAction.setCheckable(True)
# Disable tools
self.addFeatureAction.setEnabled(False)
# Connect signals
self.addFeatureAction.triggered.connect(self.newFeatureRun)
# Add action to toolBar
toolBar.addAction(self.anadirFeatureAction)
# Create tools
self.newFeatureTool = addFeaturesTool(self.iface)
self.newFeatureTool.setAction(self.addFeatureAction)
# Store previous tool
self.previousTool = self.canvas.mapTool()
# Activate tools on layer change
QObject.connect(self.iface, SIGNAL("currentLayerChanged(QgsMapLayer*)"), self.onCurrentLayerChanged)
QObject.connect(self.iface.mapCanvas(), SIGNAL("layersChanged()"), self.onCurrentLayerChanged)
def onCurrentLayerChanged(self):
try:
layer = self.canvas.currentLayer()
if layer == None or layer.type() != QgsMapLayer.VectorLayer \
or not self.iface.legendInterface().isLayerVisible(layer):
self.addFeatureAction.setEnabled(False)
return
# Activate according geometry
if layer.geometryType() == QGis.Point:
if layer.customProperty("customEditable"):
self.addFeatureAction.setEnabled(True)
else:
self.addFeatureAction.setEnabled(False)
elif layer.geometryType() == QGis.Line:
if layer.customProperty("customEditable"):
self.addFeatureAction.setEnabled(True)
else:
self.addFeatureAction.setEnabled(False)
else:
self.addFeatureAction.setEnabled(False)
except Exception as e:
QgsMessageLog.logMessage( "ERROR onCurrentLayerChanged: {0}".format(e), "issueLayers", 2)
def newFeatureRun(self):
if self.addFeatureAction.isChecked():
# Save last tool and select new one
self.previousTool = self.canvas.mapTool()
self.canvas.setMapTool(self.newFeatureTool)
else:
tmpTool = self.canvas.mapTool()
self.canvas.setMapTool(self.previousTool)
class addFeaturesTool(QgsMapTool):
def __init__(self, iface):
QgsMapTool.__init__(self, iface.mapCanvas())
self.iface = iface
self.canvas = iface.mapCanvas()
self.layer = None
self.previousNode = QgsPoint(0,0)
self.nextNode = QgsPoint(0,0)
# Rubberband
self.rubber = QgsRubberBand(self.canvas, False)
# Lists to store initial and end nodes
self.capturedNodes = []
self.markers = []
self.cursor = QCursor(QPixmap(["16 16 3 1",
" c None",
". c #FF0000",
"+ c #FFFFFF",
" ",
" +.+ ",
" ++.++ ",
" +.....+ ",
" +. .+ ",
" +. . .+ ",
" +. . .+ ",
" ++. . .++",
" ... ...+... ...",
" ++. . .++",
" +. . .+ ",
" +. . .+ ",
" ++. .+ ",
" ++.....+ ",
" ++.++ ",
" +.+ "]))
def canvasPressEvent(self,event):
pass
def canvasMoveEvent(self,event):
ptCursor = self.toMapCoordinates(event.pos())
self.rubber.movePoint(ptCursor)
pass
def canvasReleaseEvent(self,event):
if event.button() == Qt.LeftButton:
coordinates = self.toMapCoordinates(event.pos())
if self.layer.geometryType() == QGis.Point:
self.addNode( coordinates )
elif self.layer.geometryType() == QGis.Line:
self.addEdgeExtrem( coordenadas )
else:
self.resetCaptura()
pass
def activate(self):
self.layer = self.canvas.currentLayer()
caps = self.layer.dataProvider().capabilities()
if not caps & QgsVectorDataProvider.AddFeatures:
QMessageBox.critical(None, "MyPlugin", \
u"Layer provider does not allow to add new elements")
return
QgsMapTool.activate(self)
self.canvas.setCursor(self.cursor)
def deactivate(self):
self.resetCapture()
QgsMapTool.deactivate(self)
def resetCapture(self):
self.capturedNodes = []
self.rubber.reset(False)
for mItem in self.markers:
self.canvas.scene().removeItem(mItem)
del mItem
def isZoomTool(self):
return False
def isTransient(self):
return False
def isEditTool(self):
return True
def addNode(self, coord):
# Adds node
def saveNode(self, point):
# Saves node to layer
def addEdgeExtrem(self, coord):
# Add extrems to edge
def addEdge(self, previousNode, currentNode):
# adds new edge
UPDATE 1:
Maybe I should have written the question in a simpler way: After activate a custom action (button) on a custom maptoolbar, I want, when changing to a different layer the tool to be unset (so the previous tool will be active) an the button not to be shown checked. So, the user can deactivate the tool by toggling the button on the toolbar or by changing to a different layer. I am unable to get this behaviour working.
UPDATE 2:
I found I can do every time I change a layer on the onCurrentLayerChanged function the following:
if self.addFeatureAction.isChecked():
self.addFeatureAction.toggle()
self.newFeatureRun()
but as eventually the toolbar will have several actions I do not find this useful (to check if each action is active to run the action that is attached to its trigger signal). I have also tried to trigger directly:
if self.addFeatureAction.isChecked():
self.addFeatureAction.trigger()
but I do not understand why the trigger signal changes again the checked status, so it is not working as I expected.
iface.legendInterface().currentLayerChanged.disconnect(your_call)
self.iface.mapCanvas().unsetMapTool(self.mapTool)