How to QComboBox Items arranged in given order, or reversely given order or a to z or z to a in PyQt5?

Question:

My aim is to rearrange combo box items in

  1. Alphabetically ascending order (A to Z),
  2. Alphabetically Descending Order(Z to A).
  3. Given Order ( As per the list, no change, that is the first item is America, the Second is Russia and the Last One is India)
  4. Given Order in reverse order (Item in reverse order, that is the First item is India, the second one is France and the Last One is America)

Here is my code. Mouse right-click in combo box will show my option. Based on user selection, items in Qcombo Box will rearrange. How to get it?

import sys
from PyQt5.QtWidgets import QApplication, QWidget, QComboBox, QPushButton, QVBoxLayout,QMenu,QDesktopWidget,QMainWindow
from PyQt5.QtCore import Qt,QPoint,pyqtSignal,QSize

class CheckableComboBox(QComboBox):

    def __init__(self):
        super().__init__()

        self.setEditable(True)
        self.lineEdit().setReadOnly(True)
        self.setContextMenuPolicy(Qt.CustomContextMenu)
        self.customContextMenuRequested.connect(self.showMenu)

        self.setStyleSheet("QComboBox QAbstractItemView::item {border: none;padding-left: 5px;}")
        self.lineEdit().installEventFilter(self)
        self.closeOnLineEditClick = True
        self.double_click = None

    def showMenu(self,pos):
        menu = QMenu()
        atoz_action = menu.addAction("A to Z - Ascending Order")
        ztoa_action = menu.addAction(("Z to A - Descending Order"))
        copy_action1 = menu.addSeparator()
        copy_action2 = menu.addAction(("Given Order"))
        copy_action3 = menu.addAction(("Given Order - Reverse"))

        mainwindow_geomentry = QDesktopWidget().screenGeometry()
        widget_qpoint = (self.mapToGlobal(QPoint()))
        widget_qpoint_x = widget_qpoint.x()

        xpos = mainwindow_geomentry.width() - (self.size().width()+menu.sizeHint().width())
        xx =  xpos - widget_qpoint_x

        if xx <= 0 :
            aa = 0-(menu.sizeHint().width())
        else:
            aa = self.size().width()

        action = menu.exec_(self.mapToGlobal(QPoint(aa-2,0)))

        if action == atoz_action:
            self.model().sort(0)
            self.setCurrentIndex(0)
        elif action == ztoa_action:
            print("bbbbbbbbbb")

class MyApp(QWidget):

    def __init__(self):
        super().__init__()

        mainLayout = QVBoxLayout()
        self.setLayout(mainLayout)

        self.data = ['America', 'Russia', 'China', 'Germany', 'France', 'India']

        self.combo = CheckableComboBox()
        self.combo.setMinimumSize(600,40)

        btn = QPushButton('Print Values')
        btn.clicked.connect(self.getValue)

        mainLayout.addWidget(self.combo)
        mainLayout.addWidget(btn)

        self.combo.addItems(self.data)

    def getValue(self):
        print(self.combo.currentData())


if __name__ == '__main__':
    app = QApplication(sys.argv)
    app.setStyleSheet('''QWidget {font-size: 20px;}''')
    myApp = MyApp()
    myApp.show()
    app.exit(app.exec_())

I will try to solve it in ascending order(first case) by self. model().sort(0). Its work. But I was Struck with another three cases. I can’t able to solve it.

Asked By: Hobby_Devloper

||

Answers:

QComboBox uses a Qt model for its items (the default is a QStandardItemModel), so you can use sort() to change the sorting depending on your needs.

Since you also need to reverse the original order, a possible solution is to add a custom data field (using setItemData()) that simply stores the source index.

Then it’s just a matter of choosing the sortRole and order.

class CheckableComboBox(QComboBox):

    def __init__(self):
        super().__init__()

        self.setEditable(True)
        self.lineEdit().setReadOnly(True)

        self.setStyleSheet("""
            QComboBox QAbstractItemView::item {
                border: none;
                padding-left: 5px;
            }
        """)

    def contextMenuEvent(self, event):
        model = self.model()
        for i in range(model.rowCount()):
            if self.itemData(i) is None:
                self.setItemData(i, i)

        menu = QMenu()
        atoz_action = menu.addAction("A to Z - Ascending Order")
        ztoa_action = menu.addAction(("Z to A - Descending Order"))
        copy_action1 = menu.addSeparator()
        originalAction = menu.addAction(("Given Order"))
        reverseAction = menu.addAction(("Given Order - Reverse"))

        action = menu.exec_(event.globalPos())
        if action in (atoz_action, ztoa_action):
            model.setSortRole(Qt.DisplayRole)
        else:
            model.setSortRole(Qt.UserRole)

        if action in (atoz_action, originalAction):
            order = Qt.AscendingOrder
        else:
            order = Qt.DescendingOrder

        model.sort(self.modelColumn(), order)

Note that I replaced the showMenu function with a more appropriate override of customContextMenu, and I also removed the repositioning because it used an unreliable computation (and an obsolete class, QDesktopWidget) which placed the menu in odd and confusing places.

Answered By: musicamante
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.