1

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.

  • Check this solution gis.stackexchange.com/a/259647/49538 and for disconnect simply use iface.legendInterface().currentLayerChanged.disconnect(your_call) – Fran Raga Jan 23 '18 at 8:57
  • I have no problems to detect when I am changing from a layer to a different one. QObject.connect(self.iface, SIGNAL("currentLayerChanged(QgsMapLayer*)"), self.onCurrentLayerChanged) or self.iface.currentLayerChanged.connect(self.onCurrentLayerChanged) if you prefer, do the work right. But non eof them are deactivating the custom tool I have created, and thatt is what I want as it was properly working on QGIS 1.8. I do not know if there is any change on how calls to the custom tool functions (activate(), deactivate(), isTransient(), or isEditTool() for example) are performed oin QGIS 2.18 – anuf Jan 23 '18 at 9:34
  • try using self.iface.mapCanvas().unsetMapTool(self.mapTool) – Fran Raga Jan 23 '18 at 10:06
  • Yes, you are riight taht unsetMapTool() forces the tool to be deactivated (and no otehr activated), but my button (action) on the toolbar still keeps pressed. 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. – anuf Jan 24 '18 at 10:40
  • I found I can do every time I change a layer on the onCurrentLayerChanged function the following: check if self.addFeatureAction.isChecked() and if so do self.addFeatureAction.toggle() and then 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, but I don not understand why the trigger signal changes again the checked status. – anuf Jan 24 '18 at 12:20
1

Try listenning to the change of layer event using this:

self.iface.currentLayerChanged.connect(self.your_call)
  • Is it not the same as QObject.connect(self.iface, SIGNAL("currentLayerChanged(QgsMapLayer*)"), self.onCurrentLayerChanged)? Do they trigger different functions? – anuf Jan 23 '18 at 8:23
  • @anuf - This post shows a more recent syntax style which should be recommended. – Joseph Jan 23 '18 at 12:05
  • 1
    Thanks @Joseph. I new already about it. I have unchenged the signals as the old are still valid. I now they will be soon no longer valid. – anuf Jan 24 '18 at 10:31

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.