如何有选择地让 QWidget 接受鼠标点击的焦点?

Posted

技术标签:

【中文标题】如何有选择地让 QWidget 接受鼠标点击的焦点?【英文标题】:How can I selectively make QWidget accept focus from a mouse click? 【发布时间】:2014-06-03 19:13:01 【问题描述】:

我正在创建一个应用程序,该应用程序提供节点和将它们连接在一起的线的可视化表示。例如,节点和线都由自定义 QWidget、WidgetNode 和 WidgetLine 表示。

我已经将 WidgetLine 实现为一个透明的小部件,它大到足以包含线条的起点和终点,以及一个用于绘制线条本身的自定义函数。

我希望这样,如果用户单击该行或在该行旁边单击,则 WidgetLine 将获得焦点,但如果他们单击远离该行(但仍位于 WidgetLine 几何图形覆盖的矩形区域),则单击被 WidgetLine 完全忽略并传递给下面的小部件。

我首先尝试使用 WidgetLine 上的自定义 focusInEvent() 函数来执行此操作,但发现鼠标点击没有在下方传播。然后我尝试将焦点策略设置为Qt::NoFocus 并使用自定义mousePressEvent() 使用setFocus() 在适当的时候手动设置焦点,但是即使我调用ignore() on,鼠标事件仍然没有传播到上面的小部件他们。

最后,我尝试安装一个事件过滤器来拒绝鼠标事件,使用这个事件过滤器功能

bool WidgetLineFilter::eventFilter(QObject* object, QEvent* event)

    assert(object == mCord);
    if (event->type() == QEvent::MouseButtonPress)
    
        QMouseEvent* e = dynamic_cast<QMouseEvent*>(event);
        assert(e);
        if (e)
        
            QPoint mouseRelativeToParent = mCord->mapToParent(e->pos());
            // calculate distance of mouse click to patch cord
            QLineF line(mCord->line());
            float distance = distanceFromPointToLine(QVector2D(line.p1()), QVector2D(line.p2()), QVector2D(mouseRelativeToParent));
            qDebug() << distance;
            const float distanceThreshold = 2.f;
            if (distance < distanceThreshold)
            
                qDebug() << "consuming mouse click for focus";
                mCord->setFocus(Qt::MouseFocusReason);
                return true;
            
            else
                qDebug() << "mousepressevent too far for focus";
                return QObject::eventFilter(object, event);
            
        
    
    return false;

但这仍然不会在“mousepressevent too far for focus”的情况下将鼠标事件传播到父级。我也尝试从这里返回 false 和 true,并在 e 上调用 ignore,但下面的小部件没有收到点击。

(注意,上述方法确实有效,因为 WidgetLine 只在正确的时间获得焦点,只是下面的小部件在没有获得焦点时没有接收到新闻事件。)

关于如何解决这个问题的任何想法?

【问题讨论】:

【参考方案1】:

将鼠标位置存储在全局变量中。让您的所有小部件都具有如下所示的进入/离开事件,并在您运行 func 时使用它来检查您所在/附近的小部件。

void QGLWidget::enterEvent(QEvent *)

    setFocus();


void QGLWidget::leaveEvent(QEvent *)

    clearFocus();

【讨论】:

感谢您的回答。我想我可以完成这项工作,但它非常残酷。我需要遍历所有小部件以检查它们的 z 顺序(许多 WidgetLines 可能堆叠在一起)。然后递归遍历所有小部件的子部件以检查它们是否在鼠标下(WidgetNodes 包含许多子小部件)。【参考方案2】:

最后,我创建了一个事件过滤器来选择性地拦截鼠标事件,并将其安装在基本窗口上,并递归地安装在基本窗口的每个子窗口小部件上(在创建新的子窗口小部件时将其安装)。此过滤器使用每个鼠标按下事件调用基本窗口,然后遍历每个 WidgetLine,测试它们是否应该被鼠标按下并设置焦点在它们上。如果它们都测试为 false,则过滤器释放事件,否则过滤器消耗它。

WidgetLine 被设置为对鼠标事件透明

setAttribute(Qt::WA_TransparentForMouseEvents);

实现这一点比它应该做的要复杂,但可以解决问题。

【讨论】:

以上是关于如何有选择地让 QWidget 接受鼠标点击的焦点?的主要内容,如果未能解决你的问题,请参考以下文章

使自定义 QWidget 可选

QWidget 键盘事件 焦点(QApplication源码)

如何失去 QTextEdit 的选择焦点?

文本框获得焦点时,显示旁边的文字

MFC 对话框 位图按钮 失去焦点

如何在 MFC 中的对话框上阻止鼠标输入