【问题标题】:Qt: Track mouse position while QDrag is runningQt:在 QDrag 运行时跟踪鼠标位置
【发布时间】:2024-01-21 10:08:01
【问题描述】:

我正在开发一个具有多个窗口的 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的某个地方...你得到onMouseXChanged,onMouseYChanged??
  • 只要光标在我的一个窗口上,这可能会起作用,但是我需要在屏幕上的任何位置获取事件以使工具提示跟随鼠标,即使鼠标光标在外面我所有的窗户。
  • 在 QML 中,您可以拖动到窗口外 .. 这就是为什么光标坐标应该始终可用
  • 你在上面说它的一个应用程序有多个窗口,在那种情况下我从 qml 方面没有问题

标签: qt events window qml mouse


【解决方案1】:

更新:

我认为不使用操作系统库或每隔 X 毫秒获取一次鼠标位置就无法做到这一点。它看起来像是 Qt 框架没有考虑的一个非常具体的问题。您需要编写自己的类来控制它,使用 windows 的 win32、linux 的 x11 和 Mac 的等价物。

如果您想在窗口处于活动状态并且正在拖动某些东西时获取鼠标位置,请检查:

搜索了一下,我找到了一个解决方案,可以在您的窗口使用 QObject::eventFilter 获得焦点时获取它。

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

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 鼠标位置。

ma​​in.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。
  • 嗯......我的错,我误解了你的问题。因此,当它拖动某些东西并且您的程序没有焦点时,您需要鼠标位置,对吗?
  • 是的,我希望有一种 Qt 方法可以做到这一点,而不是高频轮询当前鼠标位置。否则我需要求助于系统库来通知我全局鼠标移动。
  • 我认为没有使用操作系统库或每隔 X 毫秒获取鼠标位置的方法。这看起来像是 Qt 框架没有考虑到的一个非常具体的问题。
  • 问题是 QDrag 给元素附加了一个事件过滤器,它吃掉了所有的 MouseMove 事件。因此,即使您在 QDrag 初始化之前附加了自己的事件过滤器,QDrag 事件过滤器也会首先获取事件并防止进一步传播。现在,我只满足于在鼠标光标位于我的一个窗口上时显示工具提示。我可能必须更改 Qt 源代码才能完全解决问题。如果 Qt 开发人员可以为这些事件提供自己的 API,那就太好了。
最近更新 更多