Home > Back-end >  PySide QlistView drag and drop
PySide QlistView drag and drop

Time:09-06

I'm trying to implement a simple drag and drop functionality from one QListView to another QListView by using PySide6.

Here ist the ui:

import sys

import qdarktheme
from PySide6.QtWidgets import QApplication, QWidget, QMainWindow, QVBoxLayout, QListView

from models.pipeline_model import PipelineListModel
from models.toolbox_model import ToolboxListModel, MyModel


class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()

        toolbox_list_model = ToolboxListModel()
        pipeline_list_model = PipelineListModel()

        for i in range(10):
            toolbox_list_model.toolbox_items.append(MyModel(x=i))

        toolbox_list_view = QListView()
        toolbox_list_view.setDragEnabled(True)
        toolbox_list_view.setModel(toolbox_list_model)

        pipeline_list_view = QListView()
        pipeline_list_view.setAcceptDrops(True)
        pipeline_list_view.setModel(pipeline_list_model)

        vbox = QVBoxLayout()
        vbox.addWidget(toolbox_list_view)
        vbox.addWidget(pipeline_list_view)

        main_widget = QWidget()
        main_widget.setLayout(vbox)
        self.setCentralWidget(main_widget)


if __name__ == '__main__':
    app = QApplication()
    app.setStyleSheet(qdarktheme.load_stylesheet())
    main_window = MainWindow()

    main_window.show()

    # Start the event loop.
    sys.exit(app.exec())

The Toolbox listmodel (where the items should be dragged from):

from typing import Union, Any

import PySide6
from PySide6 import QtCore
from PySide6.QtCore import Qt

from models.model_data import MyModel


class ToolboxListModel(QtCore.QAbstractListModel):
    def __init__(self):
        super().__init__()
        self.toolbox_items: list[MyModel] = []

    def data(self, index: Union[PySide6.QtCore.QModelIndex, PySide6.QtCore.QPersistentModelIndex],
             role: int = ...) -> Any:
        if role == Qt.DisplayRole:
            model = self.toolbox_items[index.row()]
            return model.x

    def rowCount(self, parent: Union[PySide6.QtCore.QModelIndex, PySide6.QtCore.QPersistentModelIndex] = ...) -> int:
        return len(self.toolbox_items)

The target listmodel (where the toolbox items shall be dropped):

from typing import Union, Any

import PySide6
from PySide6 import QtCore
from PySide6.QtCore import Qt

from models.model_data import MyModel


class PipelineListModel(QtCore.QAbstractListModel):
    def __init__(self):
        super().__init__()
        self.toolbox_items: list[MyModel] = []

    def data(self, index: Union[PySide6.QtCore.QModelIndex, PySide6.QtCore.QPersistentModelIndex],
             role: int = ...) -> Any:
        if role == Qt.DisplayRole:
            model = self.toolbox_items[index.row()]
            return model.x

    def rowCount(self, parent: Union[PySide6.QtCore.QModelIndex, PySide6.QtCore.QPersistentModelIndex] = ...) -> int:
        return len(self.toolbox_items)

and finally the shared datamodel:

import dataclasses


@dataclasses.dataclass
class MyModel:
    x: int

Even though, the dragEnabled is set to True and the receiver QListView is set to accept the drop, the drag operation doesn't even start.

What am I doing wrong here? How to do the d'n'd operation correctly?

CodePudding user response:

As explained in the model view documentation (which I strongly suggest to read in its entirety), you must override the flags() function, because the default behavior only makes items enabled and selectable.

Setting the Drag flag on the view only allows drag operations, but only the model can tell what items can be dragged.

If you want to allow an index to be draggable, you must also provide the ItemIsDragEnabled flag:

class ToolboxListModel(QtCore.QAbstractListModel):
    def flags(self, index):
        flags = super().flags(index)
        if index.isValid():
            flags |= QtCore.Qt.ItemFlags.ItemIsDragEnabled
        return flags

Then, depending on the behavior you want on the target model, you may need to override flags to accept drops onto existing items (to overwrite items), or implement other functions as shown in the documentation about subclassing QAbstractListModel, since it doesn't support resizable lists by default.

  • Related