[GIS] NameError: global name ‘layer’ is not defined

pyqgispythonqgisqgis-plugins

I've modified code of a plugin go2nextfeature as below

import os

from collections import OrderedDict
from geo_utils import utils as geo_utils, vector_utils as vutils
from qgis.core import *
from qgis.gui import *
from PyQt4 import QtGui, uic
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from qgis.PyQt.QtCore import QVariant

import buttons_utils, operator

FORM_CLASS, _ = uic.loadUiType(os.path.join(
os.path.dirname(__file__), 'go2nextfeature_dockwidget.ui'))


class Go2NextFeatureDockWidget(QtGui.QDockWidget, FORM_CLASS):
closingPlugin = pyqtSignal()
plugin_name = 'Go2NextFeature 1.14'

def __init__(self, iface, parent=None):
    """Constructor."""
    super(Go2NextFeatureDockWidget, self).__init__(parent)
    self.setupUi(self)
    self.setSizePolicy(QtGui.QSizePolicy.MinimumExpanding, QtGui.QSizePolicy.MinimumExpanding)

    self.iface = iface

    self.feats_od = OrderedDict()
    self.lay_id = None
    self.lay = None
    self.attribs_od = OrderedDict()
    self.attrib = None
    self.ft_pos = -1
    self.sel_ft_pos = -1
    self.sel_ft_ids = None

    self.tool = None

    self.fra_main.setLineWidth(3)
    fra_main_lay = QVBoxLayout(self.fra_main)
    fra_main_lay.setContentsMargins(0, 0, 0, 0)
    fra_main_lay.setSpacing(0)

    # frame 1
    self.fra_1 = QFrame(self.fra_main)
    self.fra_1.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)

    fra_1_lay = QFormLayout(self.fra_1)

    self.lbl_layer = QLabel('Layer:')
    self.cbo_layer = QComboBox()
    self.cbo_layer.setMinimumContentsLength(10)
    self.cbo_layer.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
    fra_1_lay.addRow(self.lbl_layer, self.cbo_layer)

    self.lbl_attribute = QLabel('Attribute:')
    self.cbo_attribute = QComboBox()
    self.cbo_attribute.setMinimumContentsLength(10)
    self.cbo_attribute.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
    fra_1_lay.addRow(self.lbl_attribute, self.cbo_attribute)

    self.lbl_curr = QLabel('Value:')
    self.txt_curr = QLineEdit()
    self.txt_curr.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
    self.txt_curr.setReadOnly(True)
    fra_1_lay.addRow(self.lbl_curr, self.txt_curr)

    self.lbl_ftCount1 = QLabel("Count:")
    self.lbl_ftCount = QLabel("- of - features")
    fra_1_lay.addRow(self.lbl_ftCount1, self.lbl_ftCount)

    self.lbl_action = QLabel('Action')
    self.fra_action = QFrame(self.fra_1)
    fra_action_lay = QHBoxLayout(self.fra_action)
    fra_action_lay.setContentsMargins(5, 0, 5, 0)
    self.rad_action_pan = QRadioButton('Pan')
    self.rad_action_pan.setChecked(True)
    self.rad_action_zoom = QRadioButton('Zoom')

    self.gra_action = QButtonGroup(self.fra_1)
    self.gra_action.addButton(self.rad_action_pan)
    self.gra_action.addButton(self.rad_action_zoom)

    fra_action_lay.addWidget(self.rad_action_pan)
    fra_action_lay.addWidget(self.rad_action_zoom)
    fra_1_lay.addRow(self.lbl_action, self.fra_action)

    # frame 2
    self.fra_2 = QFrame(self.fra_main)
    fra_2_lay = QVBoxLayout(self.fra_2)
    fra_2_lay.setContentsMargins(5, 0, 10, 0)

    self.chk_use_sel = QCheckBox('Go to selected features only')
    fra_2_lay.addWidget(self.chk_use_sel)

    self.chk_start_sel = QCheckBox('Start from selected feature')
    fra_2_lay.addWidget(self.chk_start_sel)

    self.chk_select = QCheckBox('Select')

    fra_2_lay.addWidget(self.chk_select)

    # frame 3
    self.fra_3 = QFrame(self.fra_main)
    fra_3_lay = QHBoxLayout(self.fra_3)
    fra_3_lay.setContentsMargins(5, 5, 10, 10)

    self.btn_prev = QPushButton(u'\u25C0\u25C0')
    self.btn_nuf = QPushButton('NUF')
    self.btn_next = QPushButton(u'\u25B6\u25B6')

    fra_3_lay.addWidget(self.btn_prev)
    fra_3_lay.addWidget(self.btn_nuf)
    fra_3_lay.addWidget(self.btn_next)
    self.btn_prev.resize(11, 100)
    self.btn_nuf.resize(11, 100)
    self.btn_next.resize(11, 100)

    # Add everything to main frame
    fra_main_lay.addWidget(self.fra_1)
    fra_main_lay.addWidget(self.fra_2)
    fra_main_lay.addWidget(self.fra_3)

    # Switch to activate attributes combo update
    self.attrib_update = False

    self.setup()

def setup(self):
    self.setWindowTitle(Go2NextFeatureDockWidget.plugin_name)
    # self.chk_select.setEnabled(False)

    QgsMapLayerRegistry.instance().layerWasAdded.connect(self.update_layers_combos)
    QgsMapLayerRegistry.instance().layerRemoved.connect(self.update_layers_combos)

    self.chk_start_sel.toggled.connect(self.chk_start_sel_clicked)
    self.chk_use_sel.toggled.connect(self.chk_use_sel_clicked)

    self.cbo_layer.activated.connect(self.cbo_layer_activated)
    self.cbo_attribute.activated.connect(self.cbo_attrib_activated)

    self.btn_prev.pressed.connect(self.btn_prev_pressed)
    self.btn_nuf.pressed.connect(self.btn_nuf_pressed)
    self.btn_next.pressed.connect(self.btn_next_pressed)

    self.update_layers_combos()

    # Turn off controls
    self.cbo_attribute.setEnabled(False)
    self.chk_use_sel.setEnabled(False)
    self.chk_start_sel.setEnabled(False)
    self.chk_select.setEnabled(False)
    self.btn_prev.setEnabled(False)
    self.btn_nuf.setEnabled(False)
    self.btn_next.setEnabled(False)

    # Shortcut
    shortcut = QShortcut(QKeySequence(Qt.Key_F8), self.iface.mainWindow())
    shortcut.setContext(Qt.ApplicationShortcut)
    shortcut.activated.connect(self.btn_next_pressed)

def closeEvent(self, event):
    self.closingPlugin.emit()
    event.accept()

def update_layers_combos(self):

        prev_lay_id = None
        if self.cbo_layer.currentIndex() > -1:
            prev_lay_id = self.cbo_layer.itemData(self.cbo_layer.currentIndex())

        # Find layers IDs and names
        layer_ids = QgsMapLayerRegistry.instance().mapLayers()
        names_ids_d = {}
        for layer_id in layer_ids:
            layer = geo_utils.LayerUtils.get_lay_from_id(layer_id)
            if layer is not None and layer.type() == 0:
                names_ids_d[layer_id] = layer.name()

        # Fill combo
        self.cbo_layer.clear()
        self.cbo_layer.addItem('', None)

        sorted_ids_names = sorted(names_ids_d.items(), key=operator.itemgetter(1))

        for name_id in sorted_ids_names:
            self.cbo_layer.addItem(name_id[1], name_id[0])

        if prev_lay_id is not None:
            self.lay_id = self.set_layercombo_index(self.cbo_layer, prev_lay_id)

        # self.chk_select.setEnabled(self.lay_id is not None)
        self.btn_prev.setEnabled(self.lay_id is not None)
        self.btn_next.setEnabled(self.lay_id is not None)
        self.cbo_layer_activated()

        if not self.lay_id:
            self.txt_curr.setText('-')
            self.lbl_ftCount.setText("- of %d features" % (len(self.feats_od)))

def chk_start_sel_clicked(self):

    if self.chk_start_sel.isChecked():
        self.sel_ft_ids = self.lay.selectedFeaturesIds()
        if len(self.sel_ft_ids) != 1:
            self.iface.messageBar().pushMessage(
                Go2NextFeatureDockWidget.plugin_name,
                'Please select one feature.',
                QgsMessageBar.WARNING)  # TODO: softcode
            return

        self.ft_pos = -1
        self.sel_ft_pos = -1

        # Reset buttons
        self.btn_prev.setEnabled(False)
        self.btn_nuf.setEnabled(True)
        self.btn_next.setEnabled(True)

def chk_use_sel_clicked(self):

    self.sel_ft_ids = self.lay.selectedFeaturesIds()
    if self.chk_use_sel.isChecked() and len(self.sel_ft_ids) < 1:
        self.iface.messageBar().pushMessage(
            Go2NextFeatureDockWidget.plugin_name,
            'Please select at least one feature.',
            QgsMessageBar.WARNING)  # TODO: softcode
        return

    self.ft_pos = -1
    self.sel_ft_pos = -1
    self.chk_start_sel.setEnabled(not self.chk_use_sel.isChecked())
    self.chk_select.setEnabled(not self.chk_use_sel.isChecked())

    # Reset buttons
    self.btn_prev.setEnabled(False)
    self.btn_nuf.setEnabled(True)
    self.btn_next.setEnabled(True)

def cbo_layer_activated(self):

    self.attrib_update = True

    self.lay_id = self.cbo_layer.itemData(self.cbo_layer.currentIndex())

    self.chk_use_sel.setChecked(False)
    if self.lay_id is not None:

        self.lay = geo_utils.LayerUtils.get_lay_from_id(self.lay_id)

        # Register a listener for selection changed
        self.lay.selectionChanged.connect(self.lay_selection_changed)

        fields = self.lay.pendingFields()
        self.cbo_attribute.clear()
        for field in fields:

            self.attribs_od[field.name()] = field
            self.cbo_attribute.addItem(field.name(), field)

        self.cbo_attrib_activated()

    else:

        self.lay = None
        self.feats_od.clear()
        self.attrib = None
        self.attribs_od.clear()

def lay_selection_changed(self):
    self.chk_use_sel.setChecked(False)

def cbo_attrib_activated(self):

    if not self.attrib_update:
        return

    self.attrib = self.cbo_attribute.itemData(self.cbo_attribute.currentIndex())
    if self.attrib is None:
        return

    self.feats_od.clear()

    feats_d = {}
    for feat in self.lay.getFeatures():
        feats_d[feat] = feat.attribute(self.attrib.name())
        # TODO: order based on selected attribute

    # Order features by attribute
    self.feats_od.clear()
    pos = 0
    for f, a in sorted(feats_d.iteritems(), key=lambda (k, v): (v, k)):
        self.feats_od[pos] = f
        pos += 1

    self.ft_pos = -1

    self.chk_use_sel.setEnabled(True)
    self.chk_start_sel.setEnabled(True)
    self.cbo_attribute.setEnabled(True)
    self.chk_select.setEnabled(True)
    self.btn_prev.setEnabled(False)
    self.btn_nuf.setEnabled(True)
    self.btn_next.setEnabled(True)

    self.txt_curr.setText('-')
    self.lbl_ftCount.setText("- of %d features" % (len(self.feats_od)))

def btn_prev_pressed(self):

    # self.ft_pos -= 1
    self.next_ft(-1)

    self.lbl_ftCount.setText("%d of %d features" % ((self.ft_pos + 1), len(self.feats_od)))

    self.btn_next.setEnabled(True)
    self.btn_nuf.setEnabled(True)
    if self.ft_pos <= 0:
        self.btn_prev.setEnabled(False)

def btn_nuf_pressed(self):

   selection = layer.selectedFeatures()
   for feature in selection:
        layer.changeAttributeValue(fid, 7, '')

    #self.ft_pos += 1
    self.next_ft(1)

    self.lbl_ftCount.setText("%d of %d features" % ((self.ft_pos + 1), len(self.feats_od)))

    if self.ft_pos >= len(self.feats_od) - 1:
        self.btn_next.setEnabled(False)

    if len(self.feats_od) > 1 and self.ft_pos > 0:
        self.btn_prev.setEnabled(True)
        self.btn_nuf.setEnabled(True)

def btn_next_pressed(self):

    # self.ft_pos += 1
    self.next_ft(1)

    self.lbl_ftCount.setText("%d of %d features" % ((self.ft_pos + 1), len(self.feats_od)))

    if self.ft_pos >= len(self.feats_od) - 1:
        self.btn_next.setEnabled(False)

    if len(self.feats_od) > 1 and self.ft_pos > 0:
        self.btn_prev.setEnabled(True)
        self.btn_nuf.setEnabled(True)

def next_ft(self, increment):

    self.ft_pos += increment

    self.ft_pos = max(self.ft_pos, 0)
    self.sel_ft_pos = max(self.ft_pos, 0)
    self.ft_pos = min(self.ft_pos, len(self.feats_od) - 1)
    self.sel_ft_pos = min(self.ft_pos, len(self.feats_od) - 1)

    if self.chk_use_sel.isChecked():

        while not self.feats_od[self.ft_pos].id() in self.sel_ft_ids:
            self.ft_pos += increment
            if self.ft_pos >= len(self.feats_od) - 1 or self.ft_pos <= 0:
                self.ft_pos -= increment
                return

        self.sel_ft_pos += increment
        if self.sel_ft_pos == len(self.sel_ft_ids):
            self.btn_next.setEnabled(False)
        if self.sel_ft_pos == 0:
            self.btn_prev.setEnabled(False)

    if self.chk_start_sel.isChecked():

        sel_ft_ids = self.lay.selectedFeaturesIds()
        if len(sel_ft_ids) != 1:
            self.iface.messageBar().pushMessage(
                Go2NextFeatureDockWidget.plugin_name,
                'Please select only one feature.',
                QgsMessageBar.WARNING)  # TODO: softcode
            return

        sel_ft_id = sel_ft_ids[0]
        sel_ft_index = -1
        for index, feat in self.feats_od.iteritems():
            if feat.id() == sel_ft_id:
                sel_ft_index = index
                break

        if sel_ft_index != -1:
            self.ft_pos = sel_ft_index
            self.chk_start_sel.setChecked(False)

    if 0 <= self.ft_pos < len(self.feats_od):

        renderer = self.iface.mapCanvas().mapRenderer()

        if self.rad_action_pan.isChecked():
            self.iface.mapCanvas().setCenter(renderer.layerToMapCoordinates(
                self.lay,
                self.feats_od[self.ft_pos].geometry().centroid().asPoint()))

        elif self.rad_action_zoom.isChecked():

            # print renderer.layerToMapCoordinates(
            #     self.lay,
            #     self.feats_od[self.ft_pos].geometry().boundingBox())

            self.iface.mapCanvas().setExtent(renderer.layerToMapCoordinates(
                self.lay,
                self.feats_od[self.ft_pos].geometry().boundingBox()))

        if self.chk_select.isChecked():

            if QGis.QGIS_VERSION_INT >= 21600:
                self.lay.selectByIds([self.feats_od[self.ft_pos].id()])
            else:
                self.lay.setSelectedFeatures([self.feats_od[self.ft_pos].id()])

        self.iface.mapCanvas().refresh()

        attrib_value = self.feats_od[self.ft_pos].attribute(self.attrib.name())

        self.txt_curr.setText(unicode(attrib_value))
        # self.txt_curr.setText(str(self.feats_od[self.ft_pos].id()))

def set_layercombo_index(self, combo, layer_id):

    index = combo.findData(layer_id)
    if index >= 0:
        combo.setCurrentIndex(index)
        return geo_utils.LayerUtils.get_lay_from_id(self.get_combo_current_data(combo))
    else:
        if combo.count() > 0:
            combo.setCurrentIndex(0)
            return combo.itemData(0)
        else:
            return None

def get_combo_current_data(self, combo):

    index = combo.currentIndex()
    return combo.itemData(index)

def set_cursor(self, cursor_shape):
    cursor = QCursor()
    cursor.setShape(cursor_shape)
    self.iface.mapCanvas().setCursor(cursor)

but it is returning an error which says:

Traceback (most recent call last):

File
"C:/Users/kkseng/.qgis2/python/plugins\go2nextfeature\go2nextfeature_dockwidget.py",
line 349, in btn_nuf_pressed

selection = layer.selectedFeatures()

NameError: global name 'layer' is not defined

QGIS version 2.18.12, a6c461b

Best Answer

Problem has been solved. I've changed the code a little bit. here is the final code.

def btn_nuf_pressed(self):

    layer = geo_utils.LayerUtils.get_lay_from_id(self.cbo_layer.itemData(self.cbo_layer.currentIndex()))
    selection = layer.selectedFeatures()
    layer.startEditing()
    for feature in selection:
        layer.changeAttributeValue(feature.id(), 5, '')

    #self.ft_pos += 1
    self.next_ft(1)

    self.lbl_ftCount.setText("%d of %d features" % ((self.ft_pos + 1), len(self.feats_od)))

    if self.ft_pos >= len(self.feats_od) - 1:
        self.btn_next.setEnabled(False)

    if len(self.feats_od) > 1 and self.ft_pos > 0:
        self.btn_prev.setEnabled(True)
        self.btn_nuf.setEnabled(True)