alphanumeric alphanumeric - 2 months ago 44
Python Question

PyQt: How to get most of QListWidget

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_())

Answer

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
Comments