Mustafa AHCI
Mustafa AHCI

Reputation: 225

Focusing on the next QListWidget item while there are hidden items

Before I talk about my problem, I want to talk about what I want to do.

If I click on Qlistwidget with the mouse and focus there, I have no problem focusing on the next item with the keyboard (up/down arrow key), even if there are hidden items. The system does this automatically, without the need to write any code.

But I want to do this without losing focus on QLineEdit. So I decided to use QShortcut:

self.__up_key = QShortcut(Qt.Key_Up, self)
self.__dw_key = QShortcut(Qt.Key_Down, self)

self.__up_key.activated.connect(self.__ctrl)
self.__dw_key.activated.connect(self.__ctrl)

def __ctrl(self):
    curr = self._list_widget.currentRow()
    if self.sender().key().toString() == "Up": 
        self._list_widget.setCurrentRow(curr - 1)
    else:
        self._list_widget.setCurrentRow(curr + 1)

It works like this if there are no hidden items. But if there are hidden items, their indexes still exist, so even if the next item is hidden, it selects it. Of course we cannot see this.

And I have come to such a conclusion:

def __ctrl(self):
    if self.sender().key().toString() == "Up":
        self._list_widget.setCurrentRow(self.prevVisibleItem())
    else:
        self._list_widget.setCurrentRow(self.nextVisibleItem())

def nextVisibleItem(self):
    for row in range(self._list_widget.currentRow() + 1, self._list_widget.count()):
        if not self._list_widget.isRowHidden(row):
            return row
    return self._list_widget.currentRow()

def prevVisibleItem(self):
    for row in range(self._list_widget.currentRow() - 1, -1, -1):
        if not self._list_widget.isRowHidden(row):
            return row
    return self._list_widget.currentRow()

I check each item whether it is hidden or not. This works for me, but I'm looking for a more effective way. When I focus on QListWidget, I can navigate between each item without writing any code. I wonder how this happens.

Minimal Example:

import sys

from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QListWidget, QLineEdit, QShortcut


class Window(QWidget):

    def __init__(self):
        super().__init__()
        self._layout = QVBoxLayout()

        self.entry = QLineEdit()
        self.entry.setPlaceholderText("Search")

        self._list_widget = QListWidget()
        __items = [str(i) + "- item" for i in range(6)]
        self._list_widget.addItems(__items)

        __hidden_items = [self._list_widget.item(1), self._list_widget.item(3), self._list_widget.item(5)]

        for i in __hidden_items:
            i.setHidden(True)

        self.__up_key = QShortcut(Qt.Key_Up, self)
        self.__dw_key = QShortcut(Qt.Key_Down, self)

        self.__up_key.activated.connect(self.__ctrl)
        self.__dw_key.activated.connect(self.__ctrl)

        self._layout.addWidget(self.entry)
        self._layout.addWidget(self._list_widget)
        self.setLayout(self._layout)
        self.show()

    def __ctrl(self):
        if self.sender().key().toString() == "Up":
            self._list_widget.setCurrentRow(self.prevVisibleItem())
        else:
            self._list_widget.setCurrentRow(self.nextVisibleItem())

    def nextVisibleItem(self):
        for row in range(self._list_widget.currentRow() + 1, self._list_widget.count()):
            if not self._list_widget.isRowHidden(row):
                return row
        return self._list_widget.currentRow()

    def prevVisibleItem(self):
        for row in range(self._list_widget.currentRow() - 1, -1, -1):
            if not self._list_widget.isRowHidden(row):
                return row
        return self._list_widget.currentRow()


if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = Window()
    sys.exit(app.exec_())

Upvotes: 2

Views: 564

Answers (1)

eyllanesc
eyllanesc

Reputation: 244291

A more elegant solution is to use moveCursor to get the QModelIndex and set the current index to the view:

def __ctrl(self):
    shorcut = self.sender()
    index = QModelIndex()
    if shorcut.key() == QKeySequence(Qt.Key_Up):
        index = self._list_widget.moveCursor(
            QAbstractItemView.MoveUp, Qt.NoModifier
        )
    elif shorcut.key() == QKeySequence(Qt.Key_Down):
        index = self._list_widget.moveCursor(
            QAbstractItemView.MoveDown, Qt.NoModifier
        )
    if index.isValid():
        self._list_widget.setCurrentIndex(index)

Upvotes: 3

Related Questions