I have been trying to make custom shortcut to set visibility to given layer. For example, I have two layers I work on LAYER_1 however I use LAYER_2 as reference, but sometimes I need to hide LAYER_2 and I want to achieve it by using Keyboard Key rather than clicking. By the help of forum I have made some code, but it does not work. I guess it's something with my toggleLayer function. It's my first time using PyQt. What I am doing wrong?

from PyQt5.QtWidgets import QWidget, QShortcut, QApplication, QMessageBox
from PyQt5.QtGui import QKeySequence

root = QgsProject.instance().layerTreeRoot()
current = root.findLayer(iface.activeLayer().id())

def setToggleLayer():
  current = root.findLayer(iface.activeLayer().id())

def toggleLayer():
  new_state = Qt.Checked if current.isVisible() == Qt.Unchecked else Qt.Unchecked

shortcutSetToggleLayer = QShortcut(QKeySequence(Qt.AltModifier + Qt.Key_K), iface.mainWindow())

shortcutToggleLayer = QShortcut(QKeySequence(Qt.Key_K), iface.mainWindow())

I've also tried something more brutal to use toggleLayer:

def toggleLayer():
    if root.findLayer(layer.id()).isVisible() == True:
    elif root.findLayer(layer.id()).isVisible() == False:

But it returns

AttributeError: 'QgsLayerTreeLayer' object has no attribute 'id

It seems that my programmer skill are too low right now and i am having a lot of problems. I would like to have this plugin to be loaded once and not having to load it by console every now and then. So i decided to load script by plugins panel(ZIP install) and be able to trigger code by using icon to on toolbar. I have managed to do something like this:

from PyQt5.QtWidgets import QAction, QMessageBox
from PyQt5.QtCore import QObject, Qt
from qgis.utils import iface
from qgis.core import QgsProject

def classFactory(iface):
    return Hotkeys(iface)

class Hotkeys:
    def __init__(self, iface):
        self.iface = iface

        self.root = QgsProject.instance().layerTreeRoot()
        self.current = self.root.findLayer(iface.activeLayer().id())

        self.shortcut_choose = "Alt+X"
        self.shortcut_toggle = "X"
    def initGui(self):

        self.action = QAction('HOTKEY', self.iface.mainWindow())

    def run(self):

        self.choose_action = QAction("Choose layer to toggling", iface.mainWindow())
        iface.registerMainWindowAction(self.choose_action, self.shortcut_choose)

        self.toggle_action = QAction("Toggle layer", iface.mainWindow())
        iface.registerMainWindowAction(self.toggle_action, self.shortcut_toggle)

        iface.addPluginToMenu("&Additional Hotkeys", self.choose_action)
        iface.addPluginToMenu("&Additional Hotkeys", self.toggle_action)
    def choose(self):
        self.current = self.root.findLayer(iface.activeLayer().id())

    def toggle(self):
        boolean = Qt.Checked if self.current.isVisible() == Qt.Unchecked else Qt.Unchecked
    def unload(self):
        del self.action

However it loads only when there are load layers in project unless it returns that error:


AttributeError: 'NoneType' object has no attribute 'id' 
Traceback (most recent call last):
  File "C:/PROGRA~1/QGIS3~1.12/apps/qgis/./python\qgis\utils.py", line 334, in _startPlugin
    plugins[packageName] = package.classFactory(iface)
  File "C:/Users/Piotr/AppData/Roaming/QGIS/QGIS3\profiles\default/python/plugins\minmal\__init__.py", line 9, in classFactory
    return Hotkeys(iface)
  File "C:/Users/Piotr/AppData/Roaming/QGIS/QGIS3\profiles\default/python/plugins\minmal\__init__.py", line 17, in __init__
    self.current = self.root.findLayer(iface.activeLayer().id())
AttributeError: 'NoneType' object has no attribute 'id'

How to get workaround and make this script loadable even on empty project. I mean right now it's useless because you have load this script every single time on opening project. Plus when you load it once and close qgis, on the next qgis start it will turn off plugin cause it's starting on empty. I guess it's something with active layer selection or getting data from layer root but i am not sure - i am newbie in python qgis.

I just moved root and current variables to run method and it works like a charm!


From a little search and experimentation:

from qgis.utils import iface
from qgis.core import QgsProject
from PyQt5.QtCore import QObject, Qt
from PyQt5.QtWidgets import QAction

class Toggler():

    def __init__(self):
        self.root = QgsProject.instance().layerTreeRoot()
        self.current = self.root.findLayer(iface.activeLayer().id())

        self.set_toggle_action = QAction("Set Toggle Layer", iface.mainWindow())
        iface.registerMainWindowAction(self.set_toggle_action, "Alt+K")
        iface.addPluginToMenu("&Set Toggle Layer", self.set_toggle_action)

        self.toggle_action = QAction("Toggle Layer",  iface.mainWindow())
        iface.registerMainWindowAction(self.toggle_action, "K")
        iface.addPluginToMenu("&Toggle Layer", self.toggle_action)

    def setToggleLayer(self):
        self.current = self.root.findLayer(iface.activeLayer().id())

    def toggleLayer(self):
        new_state = Qt.Checked if self.current.isVisible() == Qt.Unchecked else Qt.Unchecked

toggle = Toggler()

Your current variable need to be inside a class otherwise the setToggleLayer function does nothing


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.