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
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