Search code examples
c++qtdrag-and-dropdraggableqlistview

Why drag&drop does not called dropEvent?


I guess no one knows about it. I've been asking the same question for 2 days, and no one answers.

I find a toDoList project about drag&drop. And I wonder Can I get the item that dragging or dropped. I'm reading the documentation for 2 days. I implement the methods.

protected:
    void dragEnterEvent( QDragEnterEvent *anEvent ) override;
    void dragMoveEvent( QDragMoveEvent *anEvent ) override;
    void dragLeaveEvent( QDragLeaveEvent *anEvent ) override;
    void dropEvent( QDropEvent *anEvent ) override;

There are 2 listviews and toolbar. I add add and remove to the toolbar.
I can drag or drop but, I can't get text of the the items dragging. This is the main code. And I really wonder, we override the methods right. But we do not connect the methods to something. How does the method works ?

todolist::todolist(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::todolist)
{
    QWidget* pWidget = new QWidget(this);
        pWidget->setStyleSheet("background-color: #ECF0F1");
        setCentralWidget(pWidget);

        QVBoxLayout* pMainLayout = new QVBoxLayout();
        pWidget->setLayout(pMainLayout);

        QLabel* pwTitle = new QLabel("To Do List", this);
        pMainLayout->addWidget(pwTitle);
        pwTitle->setAlignment(Qt::AlignCenter);
        pwTitle->setStyleSheet("font-size: 30pt; margin: 10%;");

        QHBoxLayout* pHLayoutLabels = new QHBoxLayout();
        pMainLayout->addLayout(pHLayoutLabels);

        QLabel* plblPending = new QLabel("Pending", this);
        plblPending->setStyleSheet("font-size: 15pt;");
        pHLayoutLabels->addWidget(plblPending);

        QLabel* plblCompleted = new QLabel("Completed", this);
        plblCompleted->setStyleSheet("font-size: 15pt;");
        pHLayoutLabels->addWidget(plblCompleted);

        QHBoxLayout* pHLayout = new QHBoxLayout();
        pMainLayout->addLayout(pHLayout);

        m_pwPending = new QListView(this);
        m_pwPending->setDragEnabled(true);
        m_pwPending->setAcceptDrops(true);
        m_pwPending->setDropIndicatorShown(true);
        m_pwPending->setDefaultDropAction(Qt::MoveAction);
        pHLayout->addWidget(m_pwPending);

        m_pwCompleted = new QListView(this);
        m_pwCompleted->setDragEnabled(true);
        m_pwCompleted->setAcceptDrops(true);
        m_pwCompleted->setDropIndicatorShown(true);
        m_pwCompleted->setDefaultDropAction(Qt::MoveAction);
        pHLayout->addWidget(m_pwCompleted);

        m_pwPending->setModel(new QStringListModel());
        m_pwCompleted->setModel(new QStringListModel());

        m_pwPending->setStyleSheet
        ("QListView { font-size: 20pt; font-weight: bold; }"
         "QListView::item { background-color: #E74C3C; padding: 10%;"
         "border: 1px solid #C0392B; }"
         "QListView::item::hover { background-color: #C0392B }");

        m_pwCompleted->setStyleSheet
        ("QListView { font-size: 20pt; font-weight: bold; }"
         "QListView::item { background-color: #2ECC71; padding: 10%;"
         "border: 1px solid #27AE60; }"
         "QListView::item::hover { background-color: #27AE60 }");


        QToolBar* pToolBar = new QToolBar(this);
        addToolBar(pToolBar);

        m_pActAdd = new QAction(this);
            m_pActAdd->setIcon(QIcon(":/resources/add.png"));
            connect(m_pActAdd, &QAction::triggered, this, &todolist::onAdd);

            m_pActRemove = new QAction(this);
            m_pActRemove->setIcon(QIcon(":/resources/remove.png"));
            connect(m_pActRemove, &QAction::triggered, this, &todolist::onRemove);

        pToolBar->addAction(m_pActAdd);
        pToolBar->addAction(m_pActRemove);

        setAcceptDrops(true);
}

void todolist::onAdd()
{
    m_pwPending->model()->insertRow(m_pwPending->model()->rowCount());
    QModelIndex oIndex = m_pwPending->model()->index(
    m_pwPending->model()->rowCount() - 1, 0);

    m_pwPending->edit(oIndex);

}

void todolist::onRemove()
{
    QModelIndex oIndex = m_pwPending->currentIndex();
    m_pwPending->model()->removeRow(oIndex.row());
}
void todolist::dropEvent(QDropEvent* event) {
    const QMimeData* mimeData = event->mimeData();
    QString temp;
    if(mimeData->hasText()) {
        temp = mimeData->text();
    }

    QMessageBox::information(this,"x",temp);
}

void todolist::dragEnterEvent(QDragEnterEvent *anEvent)
{
    anEvent->setAccepted(true);
}

void todolist::dragMoveEvent(QDragMoveEvent *anEvent)
{

}

void todolist::dragLeaveEvent(QDragLeaveEvent *anEvent)
{

}
todolist::~todolist()
{
    delete ui;
}

QMimeData* ListModel::mimeData(const QModelIndexList& qMIndices) const {

  QMimeData* const pQMimeData = new QMimeData;
  QString qText;

  for (const QModelIndex& qMIndex : qMIndices) {
    qText += data(qMIndex, Qt::DisplayRole).toString() + "\n";
  }
  pQMimeData->setText(qText);

  std::cout << "1" << std::endl;
  draggedData = qText;
  return pQMimeData;
}

draggedData = qText; this line throws an error.


Solution

  • I used the recommended site Qt Doc. - Model/View Programming - Using Drag and Drop with Item Views to knit a small sample application for what OP asked.

    My MCVE testQListViewDragNDrop.cc:

    #include <QtWidgets>
    
    class ListModel: public QStringListModel {
    
      public:
        using QStringListModel::QStringListModel;
    
      protected:
        // for drag site
      
        // encodes dragged items (different from default).
        virtual QMimeData* mimeData(const QModelIndexList& qMIndices) const override;
        
        // for drop site
    
        // returns which kind of drop actions are supported.
        virtual Qt::DropActions supportedDropActions() const override
        {
          return Qt::MoveAction;
        }
    
        // checks whether certain dragged MIME data is droppable.
        virtual bool canDropMimeData(
          const QMimeData* pQMimeData, // dragged data
          Qt::DropAction action, // not evaluated
          int row, // not evaluated
          int column, // uninteresting for lists (but relevant for tables and trees)
          const QModelIndex& qMIndex) // uninteresting for lists (but relevant for trees)
          const override
        {
          return pQMimeData->hasText();
        }
    
        // drops dragged MIME data into model.
        virtual bool dropMimeData(
          const QMimeData* pQMimeData, // dropped data
          Qt::DropAction action, // not evaluated
          int row, // where to insert
          int column, // uninteresting for lists (but relevant for tables and trees)
          const QModelIndex& qMIndex) // uninteresting for lists (but relevant for trees)
          override;
    };
    
    QMimeData* ListModel::mimeData(const QModelIndexList& qMIndices) const
    {
      QMimeData* const pQMimeData = new QMimeData;
      QString qText;
      for (const QModelIndex& qMIndex : qMIndices) {
        qText += data(qMIndex, Qt::DisplayRole).toString() + "\n";
      }
      pQMimeData->setText(qText);
      return pQMimeData;
    }
    
    bool ListModel::dropMimeData(
      const QMimeData* pQMimeData, Qt::DropAction action,
      int row, int, const QModelIndex& qMIndex)
    {
      // get text from mime data
      const QString qText = pQMimeData->text().trimmed();
      if (qText.isEmpty()) return true;
      // split text into lines
      const QStringList qLines = qText.split(QChar('\n'));
      const int n = qLines.size();
      // fix row
      if (qMIndex.isValid()) row = qMIndex.row(); // dropped on row
      if (row < 0 || row > rowCount()) row = rowCount();
      // insert list into list model
      if (insertRows(row, n)) {
        for (const QString& qLine : qLines) {
          setData(index(row, 0), qLine);
        }
      }
      // done
      return true;
    }
    
    QStringList makeSampleData()
    {
      QStringList qList;
      qList
        << "Read the Qt doc."
        << "Write CMakeLists.txt"
        << "Write testQListViewDragNDrop.cc"
        << "Test and Debug"
        << "Fix bugs"
        << "Test and Debug"
        << "Fix bugs"
        << "Test and Debug"
        << "Fix bugs"
        << "Test and Debug"
        << "Fix bugs"
        << "Test and Debug"
        << "Fix bugs"
        << "Write SO answer";
      return qList;
    }
    
    int main(int argc, char** argv)
    {
      QApplication app(argc, argv);
      // setup GUI
      QWidget qWinMain;
      qWinMain.setWindowTitle("Test QListView Drag & Drop");
      qWinMain.resize(640, 480);
      QGridLayout qGrid;
      qGrid.addWidget(new QLabel("<b>To Do</b>"), 0, 0);
      qGrid.addWidget(new QLabel("<b>Done</b>"), 0, 1);
      qGrid.setRowStretch(1, 1);
      QListView qListToDo; // drag site
      ListModel qListModelToDo;
      qListModelToDo.setStringList(makeSampleData());
      qListToDo.setModel(&qListModelToDo);
      qListToDo.setDragEnabled(true); // allow drag in ToDo list
      qGrid.addWidget(&qListToDo, 1, 0);
      QListView qListDone; // drop site
      ListModel qListModelDone;
      qListDone.setModel(&qListModelDone);
      qListDone.viewport()->setAcceptDrops(true); // allow drops
      qListDone.setDropIndicatorShown(true); // adjust drop indicator
      qGrid.addWidget(&qListDone, 1, 1);
      qWinMain.setLayout(&qGrid);
      qWinMain.show();
      // run-time loop
      return app.exec();
    }
    

    Demo Session:

    GIF Animation of Demo Session

    Notes:

    • There is no custom list view necessary. &emdash; The QListView can be used but Drag as well as Drop have to be configured. Thereby, a QListView can be configured for drag and drop in contrast to my sample code.
    • Contrary to what I recommended in comments, there is admittedly no need to override dragEnterEvent() (or company). These events are already overridden in QListView and consult the corresponding model for drag & drop support.
    • There is even no custom string list model necessary. After configuration of list views for drag & drop, it started to work with QStringListModel as well. I derived QStringListModel to demonstrate how to customize drag & drop.
    • The overridden functions mimeData(), canDropMimeData(), and dropMimeData() are not necessary as well. Drag & drop will work even without them. (I tested this before I added them.) I don't know what MIME data is used to communicate the dragged contents. As drag site and drop site are the same widget class in my sample, it's not surprising that the dragged data can be dropped (however it's stored internally).