5

In order to create a test case for a drag and drop bug in the QTreeView widget I tried to simulate a drag and drop mouse movement behavior.

I basically select the first element in the QTreeView and want it to drag and drop on the third element. I did this by using a combination of QTest::mousePress and QTest::mouseMove. At the end there should be of course a QTest::mouseRelease, but I already failed in reproducing the mousePress and mouseMove.

These are steps necessary to reproduce:

  1. User moves mouse on center of first item
  2. User presses the left mouse button
  3. User holds the left mouse button pressed and moves the mouse to the center of the third item
  4. User releases the left mouse button

If one does these action as described I can see, that the QTreeView Widgets reacts appropriately, and indicating special highlights and vertical lines, in case the item will be move between to items.

Unfortunately, my automatized test fails to reproduce this behavior. It seems that calling QTest::mousePress in sequence does something different. Also using a pair of QTest::mousePress and QTest::mouseMove is something differently.

This is my code:

main.cpp

#include "TestObject.h"
#include <QTest>

QTEST_MAIN(TestObject)

TestObject.h

#include <QtTest/QtTest>

class TestObject : public QObject
{
    Q_OBJECT

private slots:
    void dragAndDrop();
};

TestObject.cpp

#include "TestObject.h"
#include "TestObject.moc"
#include <QTest>
#include <QTreeView>
#include <QStandardItemModel>
#include <QPropertyAnimation>
#include "MouseMover.h"

void TestObject::dragAndDrop() {
    qDebug() << "Hello";
    QStandardItemModel model;
    QTreeView view;
    view.setModel(&model);
    view.show();
    view.setHeaderHidden(true);
    view.setDragDropMode(QAbstractItemView::DragDropMode::DragDrop);
    view.setDefaultDropAction(Qt::DropAction::MoveAction);
    view.setColumnHidden(1, true);
    for (auto rowIter = 0; rowIter < 3; rowIter++) {
        QList<QStandardItem*> items;
        for (auto colIter = 0; colIter < 2; colIter++) {
            items << new QStandardItem(QString("%1-%2").arg(rowIter).arg(colIter));
        }
        model.appendRow(items);
    }

    MouseMover mover;
    mover.setWidget(view.viewport());
    QPropertyAnimation anim(&mover, "mousePosition");

    QTimer::singleShot(0, [&]() {
        auto startValue = view.visualRect(model.index(0, 0)).center();
        auto endValue = view.visualRect(model.index(2, 0)).center();

        QTest::mousePress(view.viewport(), Qt::MouseButton::LeftButton, Qt::KeyboardModifier::NoModifier, startValue);
        anim.setStartValue(startValue);
        anim.setEndValue(endValue);
        anim.setDuration(500);
        anim.start();
    });
    qApp->exec();
}

MouseMover.h

#pragma once
#include <QObject>
#include <QTest>

class MouseMover : public QObject {
Q_OBJECT
public:
    Q_PROPERTY(QPoint mousePosition READ mousePosition WRITE setMousePosition MEMBER mMousePosition)

    void setWidget(QWidget* widget) {
        mWidget = widget;
    }
    QPoint mousePosition() const {
        return mMousePosition;
    }

    void setMousePosition(const QPoint& pos) {
        mMousePosition = pos;
        if (mWidget) {
            QTest::mousePress(mWidget, Qt::MouseButton::LeftButton, Qt::KeyboardModifier::NoModifier, mMousePosition);
            QTest::mouseMove(mWidget, mMousePosition);

        }
    }

private:
    QPoint mMousePosition;
    QWidget* mWidget{ nullptr };
};

MouseMover.cpp

#include "MouseMover.h"
Aleph0
  • 5,816
  • 4
  • 29
  • 80

1 Answers1

0

mouseMoveEvent() handler starts and blocks at QDrag.exec(). In order to simulate the drop, you have to schedule it e.g. with QTimer:

def complete_qdrag_exec():
    QTest.mouseMove(drop_target)
    QTest.qWait(50)
    QTest.mouseClick(drop_target, Qt.MouseButton.LeftButton)

QTimer.singleShot(1000, complete_qdrag_exec)
QTest.mousePress(drag_source, Qt.MouseButton.LeftButton, Qt.KeyboardModifier.NoModifier, QPoint(10, 10))
QTest.mouseMove(drag_source, QPoint(50, 50))  # Ensure distance sufficient for DND start threshold

Above is plain Python (PyQt6/PySide6). I assume it's similar for C++ ...

K3---rnc
  • 6,717
  • 3
  • 31
  • 46