4

I have a polygon layer with several instances (like a grid). I want to be able to change the selection with the up and down arrows. I made this code, and it worked for a while on a test polygon, but it didn't work on my original polygon and now it doesn't work on the test one either. Can someone see what I am doing wrong here ?

# Get the selected features from the active layer
 layer = iface.activeLayer()
 selected_features = layer.selectedFeatures()

# Get the IDs of the selected features
selected_ids = [f.id() for f in selected_features]

# Check if there are any selected features
if len(selected_ids) > 0:
   # Get the first selected ID
   current_id = selected_ids[0]
   print("Selected ID:", current_id)

# Get the next or previous ID based on the arrow key pressed
def get_next_id(current_id, increment):
    all_ids = layer.allFeatureIds()
    current_index = all_ids.index(current_id)
    next_index = (current_index + increment) % len(all_ids)
    return all_ids[next_index]

# Define the up and down arrow key functions
def up_arrow():
    global current_id
    print("Starting up_arrow()")
    next_id = get_next_id(current_id, 1)
    print("Next ID:", next_id)
    layer.removeSelection()
    layer.select(next_id)
    iface.mapCanvas().zoomToSelected()
    current_id = next_id
    print("Selected ID:", current_id)

def down_arrow():
    global current_id
    print("Starting down_arrow()")
    next_id = get_next_id(current_id, -1)
    print("Next ID:", next_id)
    layer.removeSelection()
    layer.select(next_id)
    iface.mapCanvas().zoomToSelected()
    current_id = next_id
    print("Selected ID:", current_id)

# Connect the up and down arrow keys to their respective functions
from PyQt5.QtCore import Qt
from qgis.PyQt.QtWidgets import QShortcut
up_shortcut = QShortcut(Qt.Key_Up, iface.mapCanvas().viewport())
up_shortcut.activated.connect(up_arrow)
down_shortcut = QShortcut(Qt.Key_Down, iface.mapCanvas().viewport())
down_shortcut.activated.connect(down_arrow)
1

1 Answer 1

4

You might find it more reliable to connect to the keyPressed signal of the iface.mapCanvas():

selected = None

def select_next_feature(event):
    layer = iface.activeLayer()
    selection = layer.selectedFeatures()
    
    # check that there is at least one selected feature
    if len(selection) > 0:
        global selected
        
        # if one or more features are selected, use the first one
        selected = selection[0].id()
    
        if event.key() == Qt.Key_Down:
            # increment the value of selected
            selected -= 1
            # clamp the min value of `selected` to the lowest id
            if selected <= 1:
                selected = 1
            
        elif event.key() == Qt.Key_Up:
            # increment the value of selected
            selected += 1
            # clamp the max value of `selected` to the number of available features
            # be aware this will likely cause issues if there are missing id numbers, in which case a different approach will be needed.
            if selected >= layer.featureCount():
                selected = layer.featureCount()
        
        # select the feature and zoom to it
        layer.selectByIds([selected])            
        iface.mapCanvas().zoomToSelected()
        
        # the next line I added to better show the next feature being selected in the gif
        [iface.mapCanvas().zoomOut() for x in range(4)]
        
        # update the display
        iface.mapCanvas().refresh()
        
    else:
        # inform that no features are selected
        print('select a feature first')
        
# get reference to the map canvas and connect the function to `keyPressed`
c = iface.mapCanvas()
c.keyPressed.connect(select_next_feature)

# to disconnect
#c.keyPressed.disconnect(select_next_feature)

enter image description here

2
  • Thank you! this works perfectly
    – Parisa
    Mar 1, 2023 at 14:47
  • A great solution, I like how you utilized the keyPressed event
    – Taras
    Dec 18, 2023 at 15:36

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.