Qt:在QDrag运行时跟踪鼠标位置

3
我正在开发一个具有多个窗口的Qt应用程序,并希望为程序中的某些元素实现跨窗口拖放功能。为此,我将事件过滤器附加到要拖动的QML元素上,并监听MousePress/MouseMove事件以启动拖动过程,具体操作如下:
QDrag *drag = new QDrag(quickItem);
QMimeData* mimeData = new QMimeData();
mimeData->setText("Test");
drag->setHotSpot(QPoint(0, 0));
drag->setMimeData(mimeData);
drag->exec();

这很好运行,但现在我想在拖动时显示一个小工具提示(作为QWidget),跟随鼠标光标并根据鼠标当前停留的元素显示短文本(类似于在Windows资源管理器中拖动文件时出现的“复制到…”或“移动到…”标签)。
然而,在拖动元素时,我既没有在QDrag对象上,也没有在quickItem本身上收到任何MouseMove事件,这使得无法跟踪鼠标位置。由于在拖动过程中鼠标被抓住,因此Qt应该有一些事件能够频繁报告鼠标位置,无论鼠标在屏幕的什么位置。
我知道QDrag::setPixmap方法,但是这不允许我在拖动过程中更改我的工具提示文本,并且存在其他我想避免的限制。
是否有一种在QDrag运行时监听鼠标移动事件的方法,而不使用特定于平台的系统API?

quickItem 中可能会有一个 _mousearea_,你可以在那里获取 onMouseXChangedonMouseYChanged 吗? - Mohammad Kanan
只有当光标位于我的窗口之一上时,这可能有效,但是我需要获取屏幕上的任何位置的事件,以使工具提示跟随鼠标移动,即使鼠标光标在所有窗口之外。 - Œlrim
在QML中,您可以拖动窗口外部..这就是为什么光标坐标应始终可用的原因。 - Mohammad Kanan
你之前说过这是一个带有多个窗口的应用程序,在这种情况下,我不认为 QML 方面会有问题。 - Mohammad Kanan
1个回答

0

更新:

我认为没有办法在不使用操作系统库或每隔X毫秒获取鼠标位置的情况下完成此操作。这似乎是Qt框架没有考虑到的一个非常特定的问题。您需要编写自己的类来使用win32控制Windows,使用Linux的x11以及Mac的等效方法。

如果您想在窗口处于活动状态并拖动某些内容时获取鼠标位置,请检查以下内容:

经过一番搜索,我找到了一种解决方案,可以在窗口具有焦点时使用QObject::eventFilter获取它。

创建一个类(例如EventListener),该类继承自QObject并覆盖eventFilter和一种方法,将其设置为您的qml窗口(继承自QObject)事件过滤器,并使用installEventFilter

eventslistener.h:

#include <QEvent>
#include <QObject>
#include <QDebug>
#include <QDropEvent>

class EventsListener : public QObject
{
    Q_OBJECT

public:
    EventsListener(QObject * ptr) : QObject (ptr) {
    }

    Q_INVOKABLE void handleEventsOf(QObject *object) {
        if (object)
            object->installEventFilter(this);
    }

    bool eventFilter(QObject *object, QEvent *event) override {
        if(event->type() == QEvent::DragMove) {
            QDragMoveEvent *mouseEvent = static_cast<QDragMoveEvent*>(event);

            qDebug() << "Mouse position dragging (x, y): (" << mouseEvent->pos().x() << ", " << mouseEvent->pos().y() << ")";
            return false; //this is must return false or drop event will be handled by this method and drag&drop won't work correctly
        }

        return false;
    }
};

现在我们需要访问这个类的一个实例(在这种情况下是单例),使用qmlRegisterSingletonType。 你可能希望使用qmlRegisterType来注册此事件监听器作为类型(而不是单例),并使用信号直接通知qml鼠标位置。

main.cpp:

#include <QGuiApplication>
#include <QQmlApplicationEngine>

#include "eventlistener.h"

static QObject *eventsListenerInstance(QQmlEngine *qmlEngine, QJSEngine *engine)
{
    return new EventsListener(engine);
}

int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);

    QGuiApplication app(argc, argv);

    qmlRegisterSingletonType<EventsListener>("AppEventListener", 1, 0, "EventsListener", eventsListenerInstance);

    QQmlApplicationEngine engine;
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    if (engine.rootObjects().isEmpty())
        return -1;

    return app.exec();
}

main.qml:

import ...
import AppEventListener 1.0

ApplicationWindow {
    visible: true
    width: 640
    height: 480
    id: item
    property string display
    property alias dropEnabled: acceptDropCB.checked
    color: dropArea.containsDrag ? "#CFC" : "#EEE"

    Component.onCompleted: EventsListener.handleEventsOf(item)

...
}

不确定你是否正确理解了我的问题:我知道如何拦截事件,问题是:在拖放操作期间,我没有收到任何MouseMove事件。你的解决方案只会在光标位于我的应用程序窗口上方时给我事件,但我需要它们出现在任何地方,即使鼠标在我的应用程序之外。这应该是可能的,因为grabMouse在拖放期间被执行。 - Œlrim
嗯...我的错,我误解了你的问题。所以你需要鼠标拖动某物时的位置,而你的程序没有焦点,是吗? - Abdelilah El Aissaoui
是的,我希望有一种Qt的方法来完成它,而不是以高频轮询当前鼠标位置的方式。否则,我需要使用系统库通知我全局鼠标移动的情况。 - Œlrim
我认为没有不使用操作系统库或每隔 X 毫秒获取鼠标位置的方法来解决这个问题。看起来这是一个 Qt 框架没有考虑到的非常特定的问题。 - Abdelilah El Aissaoui
问题在于QDrag会将事件过滤器附加到元素上,从而吞噬所有MouseMove事件。因此,即使在QDrag初始化之前附加自己的事件过滤器,QDrag事件过滤器也会首先获取事件并阻止进一步传播。目前,我只想在鼠标光标悬停在我的窗口之一上时显示工具提示。我可能需要更改Qt源代码来完全解决这个问题。如果Qt开发人员能够为这些事件提供自己的API,那就太好了。 - Œlrim
抱歉回复晚了,我错过了通知。我不确定在Qt框架本身中是否值得实现它。不幸的是,正如我所提到的,您需要为每个操作系统使用API或使用检查其位置的替代方法,每隔X毫秒进行一次检查。 - Abdelilah El Aissaoui

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接