Search code examples
pythonpyqtpyqt5qlistwidget

PyQt5 QListWidget with checkboxes and drag and drop


I'm trying to build a PyQt5 app, I'd like to create a list widget whose elements are drag-and-drop-able, but where each widget is also associated with a checkbox.

I can create a drag-and-drop-able QListWidget like so:

import sys

from PyQt5.QtWidgets import QApplication, QListWidget


if __name__ == '__main__':
    app = QApplication(sys.argv)

    lw = QListWidget()
    for i in range(5):
        text = f'Item {i}'
        lw.addItem(text)

    lw.setDragDropMode(lw.InternalMove)
    lw.show()

    sys.exit(app.exec_())

However, when I try to add checkboxes to each item, the drag and drop functionality seems to break. Drag and drop doesn't work with the below code:

import sys

from PyQt5.QtWidgets import QApplication, QListWidget, QListWidgetItem
from PyQt5.QtCore import Qt


if __name__ == '__main__':
    app = QApplication(sys.argv)

    lw = QListWidget()
    for i in range(5):
        text = f'Item {i}'
        item = QListWidgetItem(text)
        item.setFlags(Qt.ItemIsUserCheckable | Qt.ItemIsEnabled)
        lw.addItem(item)

    lw.setDragDropMode(lw.InternalMove)  # Drag and drop doesn't work...
    lw.show()

    sys.exit(app.exec_())

Is there a way to get drag-and-drop-able elements which also each contain a checkbox?

Thanks in advance


Solution

  • By default, a QListWidgetItem has the following flags activated:

    Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled
    

    But you are overwriting it by removing the ItemIsDragEnabled flag, preventing the items from being dragged.

    So the solution is to activate the flag using a | operator:

    item.setFlags(item.flags() | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled)
    

    Although you see it is unnecessary since these flags are activated by default, the simplest thing is to activate the initial state:

    import sys
    
    from PyQt5.QtWidgets import QApplication, QListWidget, QListWidgetItem
    from PyQt5.QtCore import Qt
    
    if __name__ == '__main__':
        app = QApplication(sys.argv)
    
        lw = QListWidget()
        for i in range(5):
            text = f'Item {i}'
            item = QListWidgetItem(text)
            item.setCheckState(Qt.Unchecked)
            lw.addItem(item)
        lw.setDragDropMode(lw.InternalMove)
        lw.show()
        sys.exit(app.exec_())