即使 QTabletEvent 被接受,QWidget 的 mousePressEvent 也会被调用

mousePressEvent of QWidget gets called even though QTabletEvent was accepted

在 QWidget 派生的 class 对象中实现了 tabletEvent(QTabletEvent *event) 和 mousePressEvent(QMouseEvent *event),每次调用类型 TabletEvent::TabletPress 的 tabletEvent 时都会调用 mousePressEvent。根据 Qt documentation,这不应该发生:

The event handler QWidget::tabletEvent() receives TabletPress, TabletRelease and TabletMove events. Qt will first send a tablet event, then if it is not accepted by any widget, it will send a mouse event.

mainwindow.cpp

#include "mainwindow.h"
#include "tabletwidget.h"

MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
{
    TabletWidget* tw = new TabletWidget(this);
    setCentralWidget(tw);
}

tabletwidget.h

#ifndef TABLETWIDGET_H
#define TABLETWIDGET_H

#include <QWidget>

class TabletWidget : public QWidget
{
    Q_OBJECT
public:
    explicit TabletWidget(QWidget *parent = 0);

protected:
    void tabletEvent(QTabletEvent *event) Q_DECL_OVERRIDE;
    void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE;

signals:

public slots:
};

#endif // TABLETWIDGET_H

tabletwidget.cpp

#include "tabletwidget.h"
#include <QDebug>
#include <QTabletEvent>

TabletWidget::TabletWidget(QWidget *parent) : QWidget(parent)
{

}

void TabletWidget::tabletEvent(QTabletEvent *event)
{
    event->accept();
    qDebug() << "tabletEvent: " << event->type();
}

void TabletWidget::mousePressEvent(QMouseEvent *event)
{
    qDebug() << "mousePressEvent";
}

如果我使用笔尖或按 Wacom Intuos CTH-680S-DEIT 的任意按钮生成的输出是:

tabletEvent:  92
mousePressEvent
tabletEvent:  87
tabletEvent:  87
tabletEvent:  87
tabletEvent:  87
tabletEvent:  93

所以首先调用 tabletEvent,即使我接受了事件,mousePressEvent 仍然被调用。接下来的每个 tabletEvent 都是 QTabletEvent::TabletMove 类型,最后一个是 QTabletEvent::TabletRelease。来自 Qt documentation:

QEvent::TabletMove 87
QEvent::TabletPress 92
QEvent::TabletRelease 93

我在 Mac OS 10.10.3 和 Windows 7 上测试过,结果相同。这是一个错误还是我做错了?

这是在 Qt 5.4.2 上测试的。

确实,根据 Qt 文档,Qt 不应在使用平板电脑时发送鼠标事件。但它似乎无论如何都可以(我使用的是 5.5 版)。

绕过它的一种方法是重新实现 QApplicationevent() 方法 - 这是发送 TabletEnterProximityTabletLeaveProximity 的地方;这些功能不会发送到 QWidgetevent()

因此,每当应用程序捕捉到 TabletEnterProximityTabletLeaveProximity 事件时,您可以向 TabletWidget 发送信号以更改私有 bool 变量 _deviceActive。然后,在 TabletWidget 中,为每个 MousePressEvent(和 MouseReleaseEvent)添加一个检查,以查看 _deviceActive 是否为真;并且仅当标志为 false 时才执行事件。

为了说明,继承的 TabletApplication 看起来像这样:

class TabletApplication : public QApplication {
    Q_OBJECT
public:
    TabletApplication(int& argv, char** argc): QApplication(argv,argc){}
    bool event(QEvent* event){
        if (event->type() == QEvent::TabletEnterProximity || event->type() == QEvent::TabletLeaveProximity) {
            bool active = event->type() == QEvent::TabletEnterProximity? 1:0;
            emit sendTabletDevice(active);
            return true; 
        }
        return QApplication::event(event);
}
signals:
    void sendTabletActive(bool active);
};

以及tabletwidget.h里面的附加部分:

class TabletWidget : public QWidget {
// ...
public slots:
     void setTabletDeviceActive(bool active){
          _deviceActive = active;
     }
// ...
private:
     bool _deviceActive;
};

然后在设备处于活动状态时检查鼠标事件:

void TabletWidget::mousePressEvent(QMouseEvent *event)
{
     if (!_deviceActive) 
         qDebug() << "mousePressEvent";
}

当然不要忘记将相应的信号与插槽相连。希望对你有帮助。

参考:TabletApplication from Qt tablet example