[GIS] How to disconnect a signal when untoggling plugin icon

pyqgisqgisqgis-plugins

Following on from this question, I've created a signal whereby when the plugin icon is toggled (or checked), the user can change the visibility of a layer which is connected to a function (print_me). This will print a simple message.

However, I've tried to disconnect this signal when the plugin icon is untoggled but I receive the following error:

TypeError: 'function' object is not connected

Even though the function is connected as the message is still printed. How could I solve this?

class Example:
    def __init__(self, iface):
        ...

def run(self, checked):
    #################
    if not self.pluginIsActive:
        self.pluginIsActive = True
        if self.dockwidget == None:
            self.dockwidget = Example_dockDockWidget()
        self.dockwidget.closingPlugin.connect(self.onClosePlugin)
    #################
    root = QgsProject.instance().layerTreeRoot()
    group = root.findGroup('Group')

    def print_me():
        print 'Message!'

    if checked:
        for layers in group.children():
            layers.visibilityChanged.connect(print_me)
    else:
        for layers in group.children():
            layers.visibilityChanged.disconnect(print_me)

Edit:

Although a solution I posted involved putting the print_me function outside the class, I need this function to be inside the class. This is because, in my plugin, the function makes references to the QGIS interface such as printing values to the plugin's dockwidget.

If I incorporate a try method such as the following:

if checked:
    for layers in group.children():
        layers.visibilityChanged.connect(print_me)
else:
    for layers in group.children():
        try:
            layers.visibilityChanged.disconnect(print_me)
        except TypeError:
            pass

The error is ignored but if I toggle the icon again, the connection is made again. So it runs the print_me() function twice! I don't understand why a connection can be made when the icon is checked but when unchecked, it cannot be disconnected…

Best Answer

The print_me() function should be a function on its own and not inside another. The following works:

class Example:
    def __init__(self, iface):
        ...

    def print_me(self):
        print 'Message!'

    def run(self, checked):
        if checked:
            root.visibilityChanged.connect(self.print_me)
        else:
            root.visibilityChanged.disconnect(self.print_me)

Annoyingly, this was the first thing I tested but for some reason kept failing. After having QGIS reinstalled recently and re-testing this approach, it works!


Another method which also worked but is probably not as efficient is to move the function outside the class:

def print_me():
    print 'Message!'

class Example:
    def __init__(self, iface):
        ...
    def run(self, checked):
        if checked:
            root.visibilityChanged.connect(print_me)
        else:
            root.visibilityChanged.disconnect(print_me)
Related Question