问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

Qt开发经验:鼠标悬停在QMenu上时导致状态栏信息消失

创作时间:
作者:
@小白创作中心

Qt开发经验:鼠标悬停在QMenu上时导致状态栏信息消失

引用
CSDN
1.
https://m.blog.csdn.net/chenai886/article/details/144055559

本文将围绕鼠标悬停在QMenu上时导致状态栏信息消失的情况,整合事件信息流,对流程进行详细解读,并通过代码剖析每个关键环节的行为。

1. 问题描述

当鼠标悬停在QMenu的空白区域、分隔符或禁用菜单项时:

  • 当前的QAction被置为nullptr。
  • QMenu调用previousAction->showStatusText("", ...),发送空字符串的QStatusTipEvent。
  • 状态栏(QStatusBar)接收到该事件后清空状态栏内容。

2. 信息流的完整路径

整个信息流从鼠标事件开始,到状态栏显示或清空结束,共分为以下5个阶段:

  1. 鼠标移动事件触发(QMenu)
  • 鼠标移动到QMenu内部,触发QMenu::mouseMoveEvent。
  • 检测鼠标悬停位置,并调用QMenuPrivate::setCurrentAction更新当前QAction。
  1. 当前QAction状态更新(QMenuPrivate)
  • 如果鼠标在空白区域,调用showStatusText("", ...),生成空字符串的QStatusTipEvent。
  1. 状态提示事件发送(QActionPrivate)
  • 将QStatusTipEvent发送到QMainWindow或父控件。
  1. 事件捕获与处理(QMainWindow)
  • QMainWindow::event捕获QEvent::StatusTip,并调用QStatusBar::showMessage。
  1. 状态栏显示更新(QStatusBar)
  • 接收到空字符串消息,调用clearMessage清空状态栏。

3. 信息流的代码与详细解读

3.1 鼠标移动事件触发

当鼠标移动到QMenu内部时,QMenu::mouseMoveEvent被触发。

核心代码:QMenu::mouseMoveEvent

void QMenu::mouseMoveEvent(QMouseEvent *e) {
    Q_D(QMenu);
    QAction *action = d->actionAt(e->pos()); // 获取鼠标悬停的 QAction
    if ((!action || action->isSeparator()) && !d->sloppyState.enabled()) {
        d->setCurrentAction(action); // 鼠标在空白区域,action 为 nullptr
        return;
    }
    d->setCurrentAction(action); // 鼠标悬停在有效菜单项上
}
  • 行为分析:
  • d->actionAt(e->pos()):检测鼠标当前位置是否有对应的QAction。
  • 如果鼠标在空白区域或分隔符上,action == nullptr。
  • 调用d->setCurrentAction(action),将当前激活的菜单项设置为nullptr。

3.2 当前QAction状态更新

QMenuPrivate::setCurrentAction是管理QAction激活状态的核心逻辑。

核心代码:QMenuPrivate::setCurrentAction

void QMenuPrivate::setCurrentAction(QAction *action, int popup, SelectionReason reason, bool activateFirst) {
    QAction *previousAction = currentAction;
    currentAction = action; // 更新当前 QAction
    if (action) {
        activateAction(action, QAction::Hover); // 激活当前 QAction
    } else if (previousAction) {
        // 当前 QAction 被清空,调用 previousAction 的 showStatusText
        previousAction->d_func()->showStatusText(topCausedWidget(), QString());
    }
}
  • 行为分析:
  • 如果currentAction == nullptr,说明鼠标离开了有效的菜单项。
  • 调用previousAction->showStatusText("", ...),生成空字符串状态提示。

3.3 状态提示事件发送

QActionPrivate::showStatusText用于生成并分发QStatusTipEvent。

核心代码:QActionPrivate::showStatusText

bool QActionPrivate::showStatusText(QWidget *widget, const QString &str) {
    if (QObject *object = widget ? widget : parent) {
        QStatusTipEvent tip(str); // 创建状态提示事件
        QApplication::sendEvent(object, &tip); // 发送事件到目标控件
        return true;
    }
    return false;
}
  • 行为分析:
  • str是状态提示内容,此处为""(空字符串)。
  • 使用QApplication::sendEvent分发QStatusTipEvent,通常发送到QMainWindow。

3.4 事件捕获与处理

QMainWindow是QStatusTipEvent的中继点,它捕获事件并调用状态栏的showMessage。

核心代码:QMainWindow::event

bool QMainWindow::event(QEvent *event) {
#if QT_CONFIG(statustip)
    case QEvent::StatusTip:
#if QT_CONFIG(statusbar)
        if (QStatusBar *sb = d->layout->statusBar())
            sb->showMessage(static_cast<QStatusTipEvent *>(event)->tip()); // 更新状态栏
        else
#endif
            static_cast<QStatusTipEvent *>(event)->ignore(); // 忽略事件
        return true;
#endif
    return QMainWindow::event(event); // 其他事件交由父类处理
}
  • 行为分析:
  • 捕获QEvent::StatusTip类型事件。
  • 调用QStatusBar::showMessage,将事件中的tip内容传递给状态栏。
  • 如果tip == "",状态栏会显示空消息并清空内容。

3.5 状态栏显示更新

状态栏(QStatusBar)是信息流的终点,通过showMessage和clearMessage更新显示内容。

核心代码:QStatusBar::showMessage

void QStatusBar::showMessage(const QString &message, int timeout) {
    Q_D(QStatusBar);
    if (message.isEmpty()) {
        clearMessage(); // 如果消息为空,清空状态栏
        return;
    }
    d->tempItem = message; // 更新临时消息
    hideOrShow(); // 根据消息状态更新状态栏显示
}

核心代码:QStatusBar::clearMessage

void QStatusBar::clearMessage() {
    Q_D(QStatusBar);
    d->tempItem.clear(); // 清空临时消息
    hideOrShow(); // 隐藏状态栏
}
  • 行为分析:
  • 如果message是空字符串,调用clearMessage清空状态栏。
  • 调用hideOrShow更新状态栏的可见性。

4. 问题的根本原因

  1. 鼠标悬停在空白区域或分隔符时:
  • QMenu的currentAction被置为nullptr。
  • 调用previousAction->showStatusText("", ...),生成空字符串状态提示。
  1. 状态栏响应空字符串事件:
  • QMainWindow捕获QStatusTipEvent,将tip内容传递给状态栏。
  • 如果tip == "",状态栏调用clearMessage清空显示内容。

5. 解决方案

方法1:拦截空字符串事件

通过事件过滤器,阻止空字符串的QStatusTipEvent被传递到状态栏。

bool MainWindow::eventFilter(QObject *obj, QEvent *event) {
    if (event->type() == QEvent::StatusTip) {
        QStatusTipEvent *se = static_cast<QStatusTipEvent *>(event);
        if (se->tip().isEmpty()) {
            return true; // 阻止空字符串事件
        }
    }
    return QMainWindow::eventFilter(obj, event);
}

6. 总结

事件信息流的完整路径

  1. 鼠标移动到QMenu内部。
  2. QMenu更新currentAction,调用showStatusText("")。
  3. QActionPrivate发送空字符串的QStatusTipEvent。
  4. QMainWindow捕获事件并调用状态栏的showMessage。
  5. QStatusBar接收空字符串,调用clearMessage清空显示。

问题的根因

  • 鼠标悬停在空白区域时,QMenu发送空字符串事件。
  • 状态栏默认响应空字符串事件,清空内容。

通过优化QMenu和拦截空字符串事件,可以有效解决鼠标悬停在QMenu上导致状态栏信息消失的问题。

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号