Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

QTreeView drop event & re-selecting dropped items



  • Hey !

    I hit a bit of a weird wall in my tree subclass system. I have subclassed all tree/abastractItemModel and I'm using custom class for my nodes - lets call them myNodes.
    In any case when I try to process my drag/drop events. I have subclassed my drop event to be as follow :

    void myTree::dropEvent(QDropEvent *event) {
        if (event->mimeData()->hasFormat(myFormat)) {
            processLocalDropEvent(event);
        }
        dropIndicatorRect = QRect();
        dragActive = false;
    }
    

    the processLocalDropEvent checks if drop area is valid and then sends drop event to qabstractitemmodel with my mimedata function, that function returns a list of my myNodes.

    Once I get the myNodes list from mime function I then pass it to re-selecting function but to my surprise nothing gets reselected.

    void icTree::setSelectionOnDroppedItems(std::vector<myNodes*> _droppedNodes) {
        qDebug() << "selection re-run on drop?";
        clearSelection();
        int dataSize = _droppedNodes.size();
        for (int x = 0; x < dataSize; ++x) {
            QModelIndex itemIndex = _droppedNodes[x]->index();
            qDebug() << itemIndex << itemIndex.parent().data(Qt::DisplayRole);
            //selectionModel()->select(itemIndex, QItemSelectionModel::Select | QItemSelectionModel::Rows);
            selectionModel()->select(QItemSelection(itemIndex, itemIndex), QItemSelectionModel::Select | QItemSelectionModel::Rows);
        }
    //// PS I just added this line as I was typing the post, yeah if I do this the selection is accurate, so QT must be doing some selections after the dropEvent function! Halp where/who is calling that function ?!
        QCoreApplication::processEvents();
        system("pause");
    }
    

    The debug appear to be printing correct node index as well as the parent appear to be also accurate. But for some reason, I end up with 1-2 items selected out of 15 dragged. Even if I disable this re-selection function, the same selection patterns are present. I'm lost, does QT run any other selection function after drop event?


  • Moderators

    @Dariusz
    are you just moving indexes within your model?
    If so you are supposed to use QPersistentModelIndexes instead of storing QModelIndex, which are not valid anymore after the model has changed. Where QPersitentModelIndexes get updated upon model changes.



  • Hey @raven-worx

    I don't use modelIndexes at all during my move. I just pass vector with pointers to myNodes directly. As you can see after I do the drop and items are in new location, I run void icTree::setSelectionOnDroppedItems(std::vector<myNodes*> _droppedNodes) {} which re-creartes QModelIndexes, As I said in the "//// PS" comment. the function selects everything correctly. But then QT runs something after dropEvent that messes up my selection. I cant track down what QT runs after event drop so I can stop that.

    There must be a function that gets executed after void myTree::dropEvent(QDropEvent *event) {} that does something with the selection.


  • Moderators

    @Dariusz said in QTreeView drop event & re-selecting dropped items:

    I don't use modelIndexes at all during my move. I just pass vector with pointers to myNodes directly

    Then what does QModelIndex itemIndex = _droppedNodes[x]->index(); exactly do?



  • It creates new QModelIndexes for items after they were dropped. Sorry, I probably should not say that I don't use QModelIndexes at all. I use them but I create them after the items were dropped so they are all valid and correct. When I run this command

      QCoreApplication::processEvents();
        system("pause");
    

    I can see all my dragged items being selected = great, but after that QT somehow breaks selection. No idea why and where. :- (!

    PS I have recorded a gif with the function above(processEvents and pause to show the issue)
    https://pasteboard.co/HaOlUyz.gif

    PS2 it feels to me that when I EnterDropEvent, a selection state is somewhere stored, and after I finish drop, that selection state is being restored. But I cant figure out where.


  • Moderators

    @Dariusz
    is setSelectionOnDroppedItems() still called from within dropEvent handler?

    I am personally not a big fan of QCoreApplication::processEvents(); since it can break up so much stuff inside Qt. And i never encountered a case where it was absolutely necessary.

    You can try to restore the selection after you let Qt getting finished with the drop event.

    QMetaObject::invokeMethod(this, &setSelectionOnDroppedItems, Qt::QueuedConnection ); // will be executed in the next event loop iteration
    


  • @raven-worx
    "is setSelectionOnDroppedItems() still called from within dropEvent handler?"

    Its the last function being called so :

    void myTree::dropEvent(QDropEvent *event) {
        if (event->mimeData()->hasFormat(myFormat)) {
            processLocalDropEvent(event);
        }
        dropIndicatorRect = QRect();
        dragActive = false;
    }
    
    void myTree:: processLocalDropEvent(QDropEvent *event){
    //// There is a lot more here, like deciding on what to drop and so on, but basic version is as follow
        _droppedNodes = model()->dropMyMimeItems(event->mime,row,index,etc)
        stopAutoScroll();
        setState(NoState);
        viewport()->update(); // can remove it from here/ use later in selection function.
        setSelectionOnDroppedItems(_droppedNodes );
    }
    
    void myTree::setSelectionOnDroppedItems(std::vector<myNodes*> _droppedNodes) {
        clearSelection();
        int dataSize = _droppedNodes.size();
        for (int x = 0; x < dataSize; ++x) {
            QModelIndex itemIndex = _droppedNodes[x]->index();
            selectionModel()->select(QItemSelection(itemIndex, itemIndex), QItemSelectionModel::Select | QItemSelectionModel::Rows);
        }
    }
    

    This is full routine as far as I can tell from dropEvent till end. Will try the QMetaObject::invokeMethod sounds promising! never used it before :O thanks!

    Edit. IT WORKS! Amazing ! But Omg I feel so dirty using that method lol...


  • Lifetime Qt Champion

    Hi,

    Shouldn't you rather do that check in dragMoveEvent like described here ?



  • Hey @SGaist just got back to the issue again. So the re-selection is working using that

    QMetaObject::invokeMethod(this, "setSelectionOnDroppedItems", Qt::QueuedConnection);
    

    But I noticed that it created another "issue" mainly when I then try to select some items after drop, QT uses old selected item for starting "selection" and selects all until the item I clicked on... I'm bit lost now with it. As I made sure(I probably failed here) I have emitted drop events/mouseRelease events and so on to keep as much as I can of native QT signals but I'm missing something. Any idea what could be wrong here?

    Attached video : http://www.dariuszmakowski.com/2018_05_15_09_52_33.mp4

    Thanks for help & your time :- )


  • Moderators

    @Dariusz
    it seems like you are not accepting all events?!
    So some of them get passed to the parent widget and this confuses the widget implementation.



  • @raven-worx Hmm So I need to adjust:

    dragEnterEvent, dragMoveEvent, dragLeaveEvent, dropEvent ? Give them either

    event->acceptProposedAction() or event->ignore() ?


  • Moderators

    @Dariusz
    if you react to those events , accept them. If not just pass them to the base class implementation.


Log in to reply