Bala
Bala

Reputation: 686

Arrange Items in PyQt5 QListWidget?

How to arrange the filtered QListwidget items in the following order,

For example, In My Program, my search term is "I", I want to arrange item starts with "I" ("India", "Iceland", "Iran"), then item containing anywhere ("America", "China", "Fiji","Russia"), finally items end with search term "I" ("Brunei", "Mali")

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

items = ["America","canada","Mali","India","Russia", "Fiji","Nepal","Iran","China","Japan","IceLand","Brunei"]

class Listwidget(QWidget):
    def __init__(self):
        super(). __init__()
        self.setWindowTitle("List Box Samples")

        self.le = QLineEdit()
        self.le.textChanged.connect(self.func_textchanged)
        self.lb_master = QListWidget()
        self.lb_search = QListWidget()
        vbox = QVBoxLayout(self)
        vbox.addWidget(self.le)
        vbox.addWidget(self.lb_search)

        self.lb_master.addItems(items)
        self.lb_search.addItems(items)
        self.le.setFocus()

        self.le.installEventFilter(self)

    def eventFilter(self, source,event):
        if event.type() == QEvent.KeyPress and source is self.le:
            if event.key() == Qt.Key_Backspace:
                if len(self.le.text()) == 0:
                    self.fun_normal()
        return super(Listwidget,self).eventFilter(source,event)


    def fun_normal(self):
        self.lb_search.clear()
        if normal_count > 0:
            for item in item_normal:
                self.lb_search.addItem(item.text())

    def func_search(self):
        self.lb_search.clear()
        if search_count > 0:
            for item in item_anywhere:
                self.lb_search.addItem(item.text())

    def func_textchanged(self):
        global item_normal,item_anywhere,search_count,normal_count

        item_normal = self.lb_master.findItems("*",Qt.MatchWildcard)
        item_anywhere = self.lb_master.findItems(self.le.text(), Qt.MatchContains)

        normal_count = len(item_normal)
        search_count = len(item_anywhere)

        self.func_search()

if __name__ == "__main__":
    app = QApplication(sys.argv)
    win = Listwidget()
    win.show()
    sys.exit(app.exec_())

Upvotes: 1

Views: 623

Answers (1)

Art
Art

Reputation: 3079

There are few ways to do go about what you are trying to acheive. One is to sort the list based on a key.

In your case, it could be

item_anywhere.sort(key=lambda item: item.text().lower().find(self.le.text()))

Output:

['India', 'Iran', 'IceLand', 'Fiji', 'China', 'Mali', 'America', 'Russia', 'Brunei']

As you can see there is a small problem with this approach, that is, "Mali" is in the middle of the list and not towards the end.

The only approach I could think of right now is to use filter function.

Below are the steps I followed:

  1. use the find method to find the strings (you have already done this part).
  2. Now use that list and filter it based on words starting with I or whatever the search text is.
  3. filter the list based on words that contain the search letter between them, then use sort as shown above on this list.
  4. filter the list based on words ending with I.
  5. concatenate the list
    def func_textchanged(self):

        search_text = self.le.text().lower()
        item_anywhere = self.lb_master.findItems(search_text, Qt.MatchContains)

        start_list = list(filter(lambda item: item.text().lower().startswith(search_text), item_anywhere))
        start_text = [x.text().lower() for x in start_list]

        def bw_filter(item):
            # checks if the letter is between the start and end letter, then checks if it already exists in start_list
            item_text = item.text().lower()
            return search_text in item_text[1:-1] and item_text not in start_text

        bw_lst = list(filter(bw_filter, item_anywhere))
        bw_lst.sort(key=lambda item: item.text().lower().find(search_text))  # sort it based on index
        bw_text = [x.text().lower() for x in bw_lst]

        def end_filter(item):
            # checks if the string ends with search string, then checks if it already exists in
            # start_list and between list
            item_text = item.text().lower()
            return item_text.endswith(search_text) and item_text.lower() not in start_text and item_text not in bw_text

        end_lst = list(filter(end_filter, item_anywhere))

        self.lb_search.clear()
        for item in start_list + bw_lst + end_lst:  # concatenate list
            self.lb_search.addItem(item.text())
        

Upvotes: 1

Related Questions