PyQt: How to get most of QListWidget
Asked Answered
G

1

7

The code builds a dialog box with a single QListWidget and a single QPushButton.

Clicking the button adds a single List Item.

Right-click on a List Item brings up a right-click menu with "Remove Item" command available.

Choosing "Remove Item" command removes a List Item from List Widget.

Would be interesting to see how the following ListWidgets ops could be implemented:

  1. Ability to move the list items up and down (re-arrange).
  2. Being able to multi-select and multi-delete list items.
  3. A better more robust list-item sorting.

Example:

import sys, os
from PyQt4 import QtCore, QtGui    

class ThumbListWidget(QtGui.QListWidget):
    def __init__(self, type, parent=None):
        super(ThumbListWidget, self).__init__(parent)
        self.setAcceptDrops(True)
        self.setIconSize(QtCore.QSize(124, 124))

    def dragEnterEvent(self, event):
        if event.mimeData().hasUrls:
            event.accept()
        else:
            event.ignore()

    def dragMoveEvent(self, event):
        if event.mimeData().hasUrls:
            event.setDropAction(QtCore.Qt.CopyAction)
            event.accept()
        else:
            event.ignore()

    def dropEvent(self, event):
        if event.mimeData().hasUrls:
            event.setDropAction(QtCore.Qt.CopyAction)
            event.accept()
            links = []
            for url in event.mimeData().urls():
                links.append(str(url.toLocalFile()))
            self.emit(QtCore.SIGNAL("dropped"), links)
        else:
            event.ignore()

class Dialog_01(QtGui.QMainWindow):
    def __init__(self):
        super(QtGui.QMainWindow,self).__init__()
        self.listItems={}

        myQWidget = QtGui.QWidget()
        myBoxLayout = QtGui.QVBoxLayout()
        myQWidget.setLayout(myBoxLayout)
        self.setCentralWidget(myQWidget)

        self.myListWidget = ThumbListWidget(self)  
        self.myListWidget.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
        self.myListWidget.connect(self.myListWidget, QtCore.SIGNAL("customContextMenuRequested(QPoint)" ), self.listItemRightClicked)

        myButton = QtGui.QPushButton("Add List Item")

        myBoxLayout.addWidget(self.myListWidget)
        myBoxLayout.addWidget(myButton)
        myButton.clicked.connect(self.addListWidgetItem)                

    def addListWidgetItem(self):
        listItemName='Item '+str(len(self.listItems.keys()))        
        self.listItems[listItemName]=None
        self.rebuildListWidget() 

    def listItemRightClicked(self, QPos): 
        self.listMenu= QtGui.QMenu()
        menu_item = self.listMenu.addAction("Remove Item")
        if len(self.listItems.keys())==0: menu_item.setDisabled(True)
        self.connect(menu_item, QtCore.SIGNAL("triggered()"), self.menuItemClicked) 

        parentPosition = self.myListWidget.mapToGlobal(QtCore.QPoint(0, 0))        
        self.listMenu.move(parentPosition + QPos)

        self.listMenu.show() 

    def menuItemClicked(self):
        if len(self.listItems.keys())==0: print 'return from menuItemClicked'; return
        currentItemName=str(self.myListWidget.currentItem().text() )
        self.listItems.pop(currentItemName, None)
        self.rebuildListWidget()

    def rebuildListWidget(self):
        self.myListWidget.clear()
        items=self.listItems.keys()
        if len(items)>1: items.sort()
        for listItemName in items:
            listItem = QtGui.QListWidgetItem( listItemName, self.myListWidget )
            self.listItems[listItemName]=listItem


if __name__ == '__main__':
    app = QtGui.QApplication(sys.argv)
    dialog_1 = Dialog_01()
    dialog_1.show()
    dialog_1.resize(480,320)
    sys.exit(app.exec_())
Geminate answered 18/3, 2014 at 19:21 Comment(6)
In my experience, when you have to handle inserting, deletion, ordering, sorting and the like, you generally have a better time using QListView (view-model-based) than QListWidget (item-based). QAbstractItemModel has all the slots needed for sorting, inserting, etc.Winglet
On item 3: what does "better" and "more robust" mean?Falzetta
As it's now, sort() lists strings in fashion such as: 'Item 2', 'Item 20', 'Item 3', 'Item 4' end etc. There is no logic behind of this simple string sorting. Secondly, the listItem names and their QT Objects sorted in self.listItems dictionary which won't allow two or more List Items with the same name to be created since those are used as dictionary keys. I wonder if there other better more 'popular' ways to do it... since the way I used it here is all from my head.Geminate
Sorting is alphabetical and case-insensitive, which seems a reasonable (and logical) default. I don't understand the purpose of the listItems dict or the rebuildListWidget method in your example, both of which seem redundant.Falzetta
It would be interesting to see how it could be done differently! Post it!Geminate
@Sputnix. All this stuff is already included in the QListWidget API: you just need to switch it on (see my answer).Falzetta
F
23

List-widget items can be moved up and down via drag and drop, but it is not enabled by default. To switch it on, do this:

    self.listWidget.setDragDropMode(QtGui.QAbstractItemView.InternalMove)

Multiple-selection is one of several selection-modes available. To switch it on, do this:

    self.listWidget.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection)

Sorting is disabled by default. To switch it on, do this:

    self.listWidget.setSortingEnabled(True)

To re-sort the list, do one of these:

    self.listWidget.sortItems() # ascending by default
    self.listWidget.sortItems(QtCore.Qt.DescendingOrder)

Sorting is alphabetical and case-insensitive by default. If you want a custom sort-order, subclass QListWidgetItem and re-implement its less-than operator:

class ListWidgetItem(QtGui.QListWidgetItem):
    def __lt__(self, other):
        return self.text() < other.text() # or whatever
Falzetta answered 19/3, 2014 at 1:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.