0

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

  • 3
    u got the answer : "layer is not defined" (ironic) : where is it defined ? where does it come from ? hard to diagnose without the full class definition ... – snaileater Apr 20 at 12:49
  • the layer is that of selected feature, which changes once I select another feature of different layer. how should I define this here. Sorry for being novice, I am new to QGIS. – Keshav Sharma Apr 20 at 12:53
  • Yeah but how is the layer referenced ? Is it the active layer ? Is it referenced by its name ? – snaileater Apr 20 at 13:50
  • layer isn't a parameter, so it isn't defined. If it's part of the class, you'd need to show us that code, too. Please Edit the question in response to requests for clarification – Vince Apr 20 at 13:52
  • I've added the full code. – Keshav Sharma Apr 20 at 14:10
2

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)

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.