1

I have adopted the code available on Drag'N'Drop custom widget items between QListWidgets to work with PySide6, after few modifications I have managed to run the code and it works as it should.

But there is one bug or flaw with this code. If an item inside a QListWidget drags and drops over another item in the same QListWidget object the items starts to disappear behind each other or get stacked over each other or sometimes being shown as a blank item. Like below

stacked or missing QListWdiget image

I am not sure what is wrong or missing with the implemented code that causes the issue.

In addition I would like to disable Copying while Drag and Dropby all means, if the user holds the CTRL button while dragging+dropping internally the flash symbol changes to changes to + symbol and items are added to the list.

This feature should get disabled and I do not know how to do it, if it is not possible to disable the copy feature while dropping I would like to know if there is any workaround available, since no more than one of each item should exist in either of QWidgetList objects [listWidgetA & listWidgetB] no duplicates of any items is allowed in both lists.

To summarise I would like following issues to get solved.

  1. The flaw or issue with disappearing or stacking items while dragging and dropping the items in the same list.

  2. The possibility of disabling copying the items while holding CTRL key while dragging and dropping or suggestion for a workaround that prevents the items to get copied in the same list or other list.

Below I have enclosed the faulty code.

from PySide6 import QtGui, QtCore, QtWidgets
import sys, os, pathlib


class ThumbListWidget(QtWidgets.QListWidget):
    def __init__(self, type, parent=None):
        super(ThumbListWidget, self).__init__(parent)
        self.setIconSize(QtCore.QSize(124, 124))
        self.setDragDropMode(QtWidgets.QAbstractItemView.DragDrop)
        self.setDefaultDropAction(QtCore.Qt.MoveAction)
        self.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
        self.setAcceptDrops(True)
        self.model().rowsInserted.connect(
            self.handleRowsInserted, QtCore.Qt.QueuedConnection)

    def handleRowsInserted(self, parent, first, last):
        print(f"first:{first} last:{last} parent:{parent}")
        for index in range(first, last + 1):
            item = self.item(index)
            if item is not None and self.itemWidget(item) is None:
                index, name, icon = item.data(QtCore.Qt.UserRole)
                widget = QCustomQWidget()
                widget.setTextUp(index)
                widget.setTextDown(name)
                widget.setIcon(icon)
                item.setSizeHint(widget.sizeHint())
                self.setItemWidget(item, widget)


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

        myQWidget = QtWidgets.QWidget()
        myBoxLayout = QtWidgets.QHBoxLayout()
        myQWidget.setLayout(myBoxLayout)
        self.setCentralWidget(myQWidget)

        self.myQListWidget = ThumbListWidget(self)

        myBoxLayout.addWidget(self.myQListWidget)

        images_files_dir = pathlib.Path(__file__).parent.absolute().joinpath("custom_qlistwidget")
        for data in [
            ('No.1', 'Meyoko', pathlib.Path(images_files_dir).joinpath('among-us-small-green.png')),
            ('No.2', 'Nyaruko', pathlib.Path(images_files_dir).joinpath('among-us-small-yellow.png')),
            ('No.3', 'Louise', pathlib.Path(images_files_dir).joinpath('among-us-small-red.png'))]:
            myQListWidgetItem = QtWidgets.QListWidgetItem(self.myQListWidget)
            # store the data needed to create/re-create the custom widget
            myQListWidgetItem.setData(QtCore.Qt.UserRole, data)
            self.myQListWidget.addItem(myQListWidgetItem)

        self.listWidgetB = ThumbListWidget(self)
        myBoxLayout.addWidget(self.listWidgetB)


class QCustomQWidget(QtWidgets.QWidget):
    def __init__(self, parent=None):
        # super(QCustomQWidget, self).__init__(parent)
        super().__init__(parent)
        self.textQVBoxLayout = QtWidgets.QVBoxLayout()
        self.textUpQLabel = QtWidgets.QLabel()
        self.textDownQLabel = QtWidgets.QLabel()
        self.textQVBoxLayout.addWidget(self.textUpQLabel)
        self.textQVBoxLayout.addWidget(self.textDownQLabel)
        self.allQHBoxLayout = QtWidgets.QHBoxLayout()
        self.iconQLabel = QtWidgets.QLabel()
        self.allQHBoxLayout.addWidget(self.iconQLabel, 0)
        self.allQHBoxLayout.addLayout(self.textQVBoxLayout, 1)
        self.setLayout(self.allQHBoxLayout)
        # setStyleSheet
        self.textUpQLabel.setStyleSheet('''
            color: rgb(0, 0, 255);
        ''')
        self.textDownQLabel.setStyleSheet('''
            color: rgb(255, 0, 0);
        ''')

    def setTextUp(self, text):
        self.textUpQLabel.setText(text)

    def setTextDown(self, text):
        self.textDownQLabel.setText(text)

    def setIcon(self, imagePath):
        self.iconQLabel.setPixmap(QtGui.QPixmap(imagePath))


if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    dialog_1 = Dialog_01()
    dialog_1.show()
    dialog_1.resize(480, 320)
    app.exec() 
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
Sina S.
  • 31
  • 3

1 Answers1

0

Using the DragDrop flag automatically allows the possibility of duplicating internal items, since that is enabled by the Ctrl modifier, you just need to check for it in both dragMoveEvent and dropEvent, and eventually ignore it:

class ThumbListWidget(QtWidgets.QListWidget):
    # ...
    def dragMoveEvent(self, event):
        if event.keyboardModifiers():
            event.ignore()
        else:
            super().dragMoveEvent(event)

    def dropEvent(self, event):
        if event.keyboardModifiers():
            event.ignore()
        else:
            super().dropEvent(event)

About the other point, I was able to reproduce the "empty" cell issue, but only randomly. It might be a bug caused by the interaction between the geometries of the list widget and the item widgets (especially considering that there's a QLabel with a pixmap which might cause some layout and polishing issues), but it's really difficult to find the actual cause, and for that reason it's almost impossible to find a proper solution or workaround.
If you are able to find a way to reproduce the issue consistently, I suggest you to update your question accordingly (but be aware that wouldn't mean that we could reproduce it too).

Consider that while using item widgets seems reasonable in many simple situations, a better solution is to use a custom delegate and override its paint function to draw the contents.

musicamante
  • 41,230
  • 6
  • 33
  • 58
  • thanks, would you mind elaborate how to override the paint method in a delegate for the custom widget for which of the items textUP, textDown or Icon should be implemented!? – Sina S. Nov 30 '21 at 13:18
  • @SinaS. sorry but that would be off topic for this question. I suggest you to do some research on the topic as there are dozens of posts on the subject. – musicamante Nov 30 '21 at 18:42