我在孩子 A 和孩子 B 的窗口中有两个重叠的小部件。孩子 A 在 B 之上并获得鼠标事件,但有时点击应该通过并最终击中孩子 B.
理想的解决方案是使用
event->ignore()
但这会将事件传递给父部件,而不是兄弟部件。
“全部通过”解决方案
setAttribute(Qt::WA_TransparentForMouseEvents);
也不起作用,因为孩子 A 需要捕获一些事件。
我如何告诉 Qt “我不想处理这个事件,就像我不在那里一样”?
更简单的解决方案(如果适用)是根据您的点击条件设置
WA_TransparentForMouseEvents
。
比如你想点击Child-A的某些区域,你可以使用它的
mouseMoveEvent()
来检查是否需要设置或取消WA_TransparentForMouseEvents
。然后点击事件自动通过。
如果你不能确定一个点击事件在它被实际触发之前是否应该被接受,你可以这样做:
void ChildA::mousePressEvent(QMouseEvent* event) {
const bool accepted = ...; //insert custom logic here
if (accepted) {
... //handle event here
} else {
//not accepting event -> resend a copy of this event...
QMouseEvent* eventCopy = new QMouseEvent(*event);
QApplication::instance()->postEvent(eventCopy);
//...but this time, ignore it
setAttribute(Qt::WA_TransparentForMouseEvents, true);
QTimer::singleSlot(1, this, SLOT(resetClickTransparency()));
//don't propagate original event any further
event->accept();
}
}
void ChildA::resetClickTransparency() { //slot
setAttribute(Qt::WA_TransparentForMouseEvents, false);
}
免责声明: 在一年没有使用 Qt 后,所有这些都记在心里,所以请纠正我的名称或类型错误。
你也可以从另一个角度来处理这个问题。您可以让其他小部件使用事件过滤器侦听第一个小部件的事件,而不是将事件转发给“其他”小部件。我不确定这是否适合您的用例,这取决于什么/谁决定什么事件将由什么对象处理。
如果事件必须被 Child-A 忽略,发出一个信号并与其父级一起捕获它,然后父级发出一个新信号以被 Child-B 捕获
Child-A -> Parent -> Child-B
(信号)->(时隙)(信号)->(时隙)
这是一个可能的选择:
例如:
bool WidgetA::event(QEvent *event)
{
QWidget::event( event);
QEvent::Type type = event->type();
if ( (type != QEvent::KeyPress) &&
(type != QEvent::Wheel) &&
(type != QEvent::MouseButtonDblClick) &&
(type != QEvent::MouseMove) &&
(type != QEvent::MouseButtonPress) )
return true;
//forward the event
if ( m_pChildB )
m_pChildB->event( event);
return true;
}
希望这有帮助。
对于当前的 Qt5(当然这也适用于 Qt6;我不知道它是否适用于 Qt4)如果你想将几个半透明的小部件堆叠在一起(例如自定义在媒体查看器上方绘制画布或 QMdiArea 等)。
一般的假设是从事件处理的意义上来看待QObject的父子关系(如果孩子不能处理一个事件,父母会处理它)。
在我的例子中,这看起来类似于以下内容:
class MyCentralWidget(QWidget):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# helper class - initializes a layout suitable for widget stacking
class _Layout(QGridLayout):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setContentsMargins(0, 0, 0, 0)
# set layout for this widget
layout = _Layout(self)
# initialize layer widgets
layer0 = ... # bottom most layer
layer1 = ...
layer2 = ...
layer3 = ... # top layer
# Stack entities on top of another
layout.addWidget(layer0, 0, 0) # this is shielded by layer1 and will never receive mouse events
layout.addWidget(layer1, 0, 0) # this will receive all mouse events not handled by layers above
# Parenting-based stacking using QGridLayout
# to enable proper event propagation (events flow in
# child->parent direction if unused by child)
_Layout(layer1).addWidget(layer2, 0, 0)
_Layout(layer2).addWidget(layer3, 0, 0)
# Resulting mouse event propagation: layer3 -> layer2 -> layer1
# possibly add some other floating widget for floating menus...
toolbox = MyToolbox(parent=self)
现在为了将事件传播到较低的小部件(例如从第 3 层到第 2 层),您必须确保小部件不使用事件。通常,当一个小部件对事件不感兴趣时,它只是通过在其基础上调用 event() 将其传递给其父级(例如,QMdiArea 在没有点击 QMdiSubWindow 或按钮时执行此操作):
# example implementation of event() method
def event(self, e) -> bool:
if self.consumeEvent(e):
# return if event was consumed
return True
# event was not consumed - send to parent via super call
return super().event(e)