1

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

  • List item starting with the search term.
  • List item containing anywhere in the search term and finally.
  • List item ends in the search item.

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_())
Bala
  • 648
  • 5
  • 15
  • Unrelated, but important: don't use globals, use instance attributes. Also, there should always be a space after every comma. – musicamante Jun 08 '21 at 17:18
  • why most of the expertise, hesitate to use global variables? Anything series.@musicamante – Bala Jun 08 '21 at 17:29
  • it's not hesitation, it's knowledge, experience and awareness. Using globals can result in various issues, including unexpected behavior and bugs (or even crashes) that can be very hard to track. Most of the times they are used inappropriately (like in your case, for which you should use instance attributes instead: `self.item_normal`, etc), while experienced programmers use them only when they *really* know what they're doing, they know very well how to use them and *why* they need them. I suggest you to do some research, starting from [this post](https://stackoverflow.com/q/19158339). – musicamante Jun 08 '21 at 17:43

1 Answers1

1

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())
        
Art
  • 2,836
  • 4
  • 17
  • 34