Qt5-触摸屏输入事件分析

本文深入分析了Qt库中触摸屏事件的处理流程,从源码角度解释了如何通过libinput进行触摸识别,并详细介绍了触摸事件的触发、移动、松开及取消过程。

一、引言

    由于需要分析qtbase中的触摸屏处理事件,所以下载源码进行分析。思路为:widgets窗口线程起来之后,怎样通过libinput进行触摸识别。(理解了触摸屏的识别之后,键盘、鼠标、案件的识别也是类似的)

二、源码分析 

路径:qtbase/src/widgets/kernel/qgesturemanager.cpp 
 for (ContextIterator context = contexts.begin(); context != contextEnd; ++context) {
        Qt::GestureType gestureType = context.value();
        const QMap<Qt::GestureType, QGestureRecognizer *> &const_recognizers = m_recognizers;
        QMap<Qt::GestureType, QGestureRecognizer *>::const_iterator
                typeToRecognizerIterator = const_recognizers.lowerBound(gestureType),
                typeToRecognizerEnd = const_recognizers.upperBound(gestureType);
        for (; typeToRecognizerIterator != typeToRecognizerEnd; ++typeToRecognizerIterator) {
            QGestureRecognizer *recognizer = typeToRecognizerIterator.value();
            QObject *target = context.key();  //获取事件key码
            QGesture *state = getState(target, recognizer, gestureType);//通过key码来确定事件类型的状态
            if (!state)
                continue;
            QGestureRecognizer::Result recognizerResult = recognizer->recognize(state, target, event);
            QGestureRecognizer::Result recognizerState = recognizerResult & QGestureRecognizer::ResultState_Mask;
            QGestureRecognizer::Result resultHint = recognizerResult & QGestureRecognizer::ResultHint_Mask;
            if (recognizerState == QGestureRecognizer::TriggerGesture) {
                qCDebug(lcGestureManager) << "QGestureManager:Recognizer: gesture triggered: " << state << event;
                triggeredGestures << state;
            } else if (recognizerState == QGestureRecognizer::FinishGesture) {
                qCDebug(lcGestureManager) << "QGestureManager:Recognizer: gesture finished: " << state << event;
                finishedGestures << state;
            } else if (recognizerState == QGestureRecognizer::MayBeGesture) {
                qCDebug(lcGestureManager) << "QGestureManager:Recognizer: maybe gesture: " << state << event;//触摸屏事件类型输出
                newMaybeGestures << state;
            } else if (recognizerState == QGestureRecognizer::CancelGesture) {
                qCDebug(lcGestureManager) << "QGestureManager:Recognizer: not gesture: " << state << event;
                notGestures << state;
            } else if (recognizerState == QGestureRecognizer::Ignore) {
                qCDebug(lcGestureManager) << "QGestureManager:Recognizer: ignored the event: " << state << event;
            } else {
                qCDebug(lcGestureManager) << "QGestureManager:Recognizer: hm, lets assume the recognizer"
                        << "ignored the event: " << state << event;
            }
            if (resultHint & QGestureRecognizer::ConsumeEventHint) {
                qCDebug(lcGestureManager) << "QGestureManager: we were asked to consume the event: "
                        << state << event;
                consumeEventHint = true;
            }
        }
    }
state << event类型的来源:qtbase/src/platformsupport/input/libinput/qlibinputtouch.cpp
//触屏被按下
void QLibInputTouch::processTouchDown(libinput_event_touch *e)
{
    int slot = libinput_event_touch_get_slot(e);//输入事件的获取是通过libinput
    DeviceState *state = deviceState(e);
    QWindowSystemInterface::TouchPoint *tp = state->point(slot);
    if (tp) {
        qWarning("Incorrect touch state");
    } else {
        QWindowSystemInterface::TouchPoint newTp;
        newTp.id = qMax(0, slot);
        newTp.state = Qt::TouchPointPressed;//手指按下
        newTp.area = QRect(0, 0, 8, 8);
        newTp.area.moveCenter(getPos(e));
        state->m_points.append(newTp);//将按下的状态数据存储到state中
    }
}
//手指松开
void QLibInputTouch::processTouchUp(libinput_event_touch *e)
{
    int slot = libinput_event_touch_get_slot(e);
    DeviceState *state = deviceState(e);
    QWindowSystemInterface::TouchPoint *tp = state->point(slot);
    if (tp) {
        tp->state = Qt::TouchPointReleased;
        // There may not be a Frame event after the last Up. Work this around.
        Qt::TouchPointStates s = 0;
        for (int i = 0; i < state->m_points.count(); ++i)//触控的点数
            s |= state->m_points.at(i).state;
        if (s == Qt::TouchPointReleased)
            processTouchFrame(e);//多点触控处理
    } else {
        qWarning("Inconsistent touch state (got 'up' without 'down')");
    }
}
void QLibInputTouch::processTouchFrame(libinput_event_touch *e)
{
    DeviceState *state = deviceState(e);
    if (!state->m_touchDevice) {
        qWarning("TouchFrame without registered device"); 
        return;
    }
    if (state->m_points.isEmpty())
        return;

    QWindowSystemInterface::handleTouchEvent(Q_NULLPTR, state->m_touchDevice, state->m_points,
                                             QGuiApplication::keyboardModifiers());

    for (int i = 0; i < state->m_points.count(); ++i) {
        QWindowSystemInterface::TouchPoint &tp(state->m_points[i]);
        if (tp.state == Qt::TouchPointReleased)
            state->m_points.removeAt(i--);//每松开一个手指,则触控的点数减1
        else if (tp.state == Qt::TouchPointPressed)
            tp.state = Qt::TouchPointStationary;
    }
}

processTouchDown和processTouchUp事件处理是怎么来的呢?

见路径:qtbase/src/platformsupport/input/libinput/qlibinputhandler.cpp

首先看一下头文件定义:

class QLibInputHandler : public QObject
{
    Q_OBJECT

public:
    QLibInputHandler(const QString &key, const QString &spec);
    ~QLibInputHandler();

signals:
    void deviceAdded(const QString &sysname, const QString &name);
    void deviceRemoved(const QString &sysname, const QString &name);

private slots:
    void onReadyRead();
    void onCursorPositionChangeRequested(const QPoint &pos);

private:
    void processEvent(libinput_event *ev);

    udev *m_udev;
    libinput *m_li;  //libinput定义
    int m_liFd;
    QScopedPointer<QSocketNotifier> m_notifier;//监听系统文件操作,将操作转换为Qt事件进入系统的消息循环队列
    QScopedPointer<QLibInputPointer> m_pointer;
    QScopedPointer<QLibInputKeyboard> m_keyboard;
    QScopedPointer<QLibInputTouch> m_touch;
    QMap<int, int> m_devCount;
};

然后.cpp文件分析:

m_li = libinput_udev_create_context(&liInterface, Q_NULLPTR, m_udev);
    if (Q_UNLIKELY(!m_li))
        qFatal("Failed to get libinput context");

    libinput_log_set_handler(m_li, liLogHandler);
    if (qLcLibInput().isDebugEnabled())
        libinput_log_set_priority(m_li, LIBINPUT_LOG_PRIORITY_DEBUG);

    if (Q_UNLIKELY(libinput_udev_assign_seat(m_li, "seat0")))
        qFatal("Failed to assign seat");

    m_liFd = libinput_get_fd(m_li);
	qDebug() << "[qlibinputhandler.cpp] m_liFd is: " << m_liFd;
    m_notifier.reset(new QSocketNotifier(m_liFd, QSocketNotifier::Read));
//若监听到发生触摸、按键等事件,立即通知onReadyRead
    connect(m_notifier.data(), SIGNAL(activated(int)), SLOT(onReadyRead()));
void QLibInputHandler::onReadyRead()
{
    qDebug() << "have a event";
	qDebug() << "[qlibinputhandler.cpp] enter to onReadyRead";
    if (libinput_dispatch(m_li)) {
        qWarning("libinput_dispatch failed");
        return;
    }

    libinput_event *ev;
    while ((ev = libinput_get_event(m_li)) != Q_NULLPTR) {
        processEvent(ev);  //按键处理事件
        libinput_event_destroy(ev);
    }
}

在processEvent(ev)中进行了一系列的处理:

void QLibInputHandler::processEvent(libinput_event *ev)
{
    libinput_event_type type = libinput_event_get_type(ev); //按键的事件类型
    qDebug() << "[qlibinputhandler.cpp] libinput_event_type is: " << type;
    libinput_device *dev = libinput_event_get_device(ev);
	
    switch (type) {
    case LIBINPUT_EVENT_DEVICE_ADDED:
    {
       .
       .
       .
    case LIBINPUT_EVENT_TOUCH_DOWN://触摸事件处理
        m_touch->processTouchDown(libinput_event_get_touch_event(ev));
        break;
    case LIBINPUT_EVENT_TOUCH_MOTION://触摸移动处理
        m_touch->processTouchMotion(libinput_event_get_touch_event(ev));
        break;
    case LIBINPUT_EVENT_TOUCH_UP://触摸松开处理
        m_touch->processTouchUp(libinput_event_get_touch_event(ev));
        break;
    case LIBINPUT_EVENT_TOUCH_CANCEL:
        m_touch->processTouchCancel(libinput_event_get_touch_event(ev));
        break;
    case LIBINPUT_EVENT_TOUCH_FRAME:
        m_touch->processTouchFrame(libinput_event_get_touch_event(ev));
        break;
    default:
        break;
    }
}

到此与qtbase/src/platformsupport/input/libinput/qlibinputtouch.cpp中的各类触摸事件结合了起来。

上述事件的流程图如下所示:

<think>首先,用户的问题是:"我想查找Qt触摸屏事件与鼠标事件合并效果相同问题原因及解决办法 请问Qt触摸屏事件与鼠标事件合并后效果相同问题相同问题原因及解决方案"。用户提供了三个引用,我需要基于这些引用来回答。 从引用中提取关键信息: - 引用[1]:介绍了Qt触摸屏鼠标事件的捕获与处理机制,包括click、press、pressup事件。提到了触摸屏与鼠标事件的基本概念和Qt事件处理流程。 - 引用[2]:讨论了Qt在单指触摸时默认转换鼠标事件的问题。用户尝试设置属性`Qt::AA_SynthesizeMouseForUnhandledTouchEvents`和`Qt::AA_SynthesizeTouchForUnhandledMouseEvents`,但效果不理想。 - 引用[3]:提到了在Linux开发中,Qt支持鼠标和触摸屏的配置,通过环境变量`QWS_MOUSE_PROTO`来设置。 用户的核心问题是:触摸屏事件与鼠标事件合并后效果相同的原因及解决方案。意思是,在Qt中,触摸屏事件被处理为鼠标事件,导致两者行为相同,用户可能想区分它们。 系统级指令: - 所有行内数学表达式必须使用`$...$`,但在这个上下文中,可能没有数学表达式。 - 独立公式用`$$...$$`,但这里可能不需要。 - LaTeX语法正确。 - 使用中文回答。 - 生成相关问题。 - 回答中引用的段落末尾自然地添加引用标识,例如[^1]。 - 回答结构清晰,帮助用户逐步解决问题。 用户可见层指令:回答结构清晰,逐步解决问题,保证真实可靠。 我的回答结构: 1. **理解问题**:解释用户的问题,即触摸屏事件和鼠标事件Qt中被合并或转换,导致相同效果。 2. **原因分析**:基于引用,解释为什么会出现这种合并效果。 - 引用[1]提到Qt事件模型,触摸屏事件可能被转换为鼠标事件- 引用[2]显示,Qt默认会合成鼠标事件来处理未处理的触摸事件(`Qt::AA_SynthesizeMouseForUnhandledTouchEvents`)。 - 引用[3]涉及环境变量配置,用于支持多输入设备。 3. **解决方案**:提供方法来解决这个问题,比如禁用自动转换或手动处理事件- 从引用[2],设置`qApp->setAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents, false);`可能有效,但用户说无效,所以需要其他方法。 - 引用[3]建议使用环境变量配置输入设备。 - 其他可能的方案:在事件处理函数中区分事件类型。 4. **生成相关问题**:在末尾添加相关问题。 确保引用标识: - 在回答中引用段落时,末尾添加, [^2], [^3]。 逐步解决: - 步骤1:问题描述。 - 步骤2:原因。 - 步骤3:解决方案。 - 步骤4:总结。 相关问题: - 基于主题,生成2-3个相关问题。 最终输出: - 用中文。 - 结构清晰。 </think> ### Qt触摸屏事件与鼠标事件合并效果相同的原因及解决方案 在Qt开发中,触摸屏事件和鼠标事件合并后效果相同的问题常见于应用程序对两种输入事件的处理机制。这会导致触摸操作(如点击或滑动)被错误地识别为鼠标操作,影响用户体验。下面我将逐步分析原因并提供解决方案,确保回答基于可靠的引用信息。 #### 1. **问题原因分析** Qt事件系统默认将触摸屏事件自动转换为鼠标事件,以实现兼容性。这主要是因为Qt事件处理模型会为未处理的触摸事件合成相应的鼠标事件。具体原因包括: - **默认事件转换机制**:Qt通过属性`Qt::AA_SynthesizeMouseForUnhandledTouchEvents`(默认为true)自动将触摸事件转换为鼠标事件。例如,单指触摸操作(如press或release)会被映射为鼠标的click或move事件,导致两者行为一致[^2]。 - **事件处理优先级**:在Qt事件循环中,如果触摸事件未被应用程序处理,系统会将其“降级”为鼠标事件。引用[2]中提到,即使尝试禁用该属性(`qApp->setAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents, false);`),在某些平台或配置下可能无效,因为底层驱动或事件过滤器可能强制转换。 - **输入设备配置冲突**:当同时支持鼠标和触摸屏时,环境变量设置不当会导致事件冲突。引用[3]指出,在Linux环境下,变量`QWS_MOUSE_PROTO`用于配置输入设备协议;如果只设置单一设备(如触摸屏),Qt可能优先使用鼠标事件模拟触摸行为。 总之,核心原因是Qt的默认行为旨在简化开发,但可能导致触摸事件被“同质化”为鼠标事件。 #### 2. **解决方案** 要解决触摸屏事件和鼠标事件合并的问题,需要显式区分事件类型或调整事件处理逻辑。以下是几种有效方法: - **禁用事件自动转换**: 在应用程序初始化时(如`main.cpp`),设置属性以阻止Qt合成鼠标事件。从引用[2]的实验可知,直接设置属性可能无效,但可结合事件过滤器: ```cpp // 在 main() 函数中 qApp->setAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents, false); // 禁用触摸转鼠标 qApp->setAttribute(Qt::AA_SynthesizeTouchForUnhandledMouseEvents, false); // 禁用鼠标转触摸 ``` 如果上述代码无效(如引用[2]所述),在事件处理函数(如`eventFilter`或`event()`)中手动处理事件: ```cpp bool YourWidget::event(QEvent *event) { if (event->type() == QEvent::TouchBegin || event->type() == QEvent::TouchUpdate) { // 处理触摸事件,阻止其转换为鼠标事件 handleTouchEvent(static_cast<QTouchEvent*>(event)); return true; // 返回true表示事件已处理,不转换 } return QWidget::event(event); // 其他事件正常处理 } ``` 此方法确保触摸事件被独立处理,避免与鼠标事件混淆[^1]。 - **配置输入设备环境变量**: 如果开发环境涉及多输入设备(如Linux),引用[3]建议通过环境变量指定设备协议。在启动程序前设置: ```bash export QWS_MOUSE_PROTO="MouseMan:/dev/input/mice Tslib:/dev/input/touchscreen0" # 同时支持鼠标和触摸屏 ``` 这强制Qt区分事件源,减少自动转换。 - **事件类型区分与处理**: 在事件处理代码中,使用Qt事件类型标识符来区分来源: - 触摸事件:使用`QTouchEvent`(如`TouchBegin`, `TouchEnd`)。 - 鼠标事件:使用`QMouseEvent`(如`MouseButtonPress`, `MouseMove`)。 例如: ```cpp void YourWidget::mousePressEvent(QMouseEvent *event) { if (event->source() == Qt::MouseEventSynthesizedBySystem) { // 忽略系统合成的鼠标事件 return; } // 处理真实鼠标事件 } ``` 这能精准控制事件流,避免误处理。 #### 3. **实际注意事项** - **兼容性测试**:不同Qt版本(如Qt5Qt6)行为可能不同。建议在目标平台(如嵌入式Linux或Windows)进行测试。 - **性能影响**:手动处理事件可能增加开销,需优化事件过滤器性能。 - **默认行为恢复**:如果方案无效,检查是否有第三方插件或驱动覆盖了Qt设置;从引用[1]看,深入理解Qt事件模型能帮助调试。 通过这些方案,您可以有效解决事件合并问题,提升输入处理的准确性。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值