【问题标题】:Get all widgets under cursor获取光标下的所有小部件
【发布时间】:2015-02-06 10:04:30
【问题描述】:

widgetAt 函数为我提供了光标正下方的小部件,位于最高 z 顺序。

pos = QtGui.QCursor.pos()
widget = QtGui.qApp.widgetAt(pos)

但是我怎样才能获得光标下的 all 小部件?包括那些在最上面的人?比如:

pos = QtGui.QCursor.pos()
widgets = QtGui.qApp.widgetsAt(pos)

示例

作为一个使用示例,想象一个叠加层,它在用户点击的任何位置添加动画图形,例如水面上的涟漪。自然,叠加层需要接收并响应点击,但它必须这样做,而不会干扰传播到其下方小部件的点击。

另一个例子是使用跟踪;为了拦截和记录点击进行分析,可以使用相同的叠加层,但这次不绘制任何内容,只收集。

在这两种情况下,如果只有一个小部件获得点击事件,我需要一种方法来区分位于其下方的小部件以便传递事件。

【问题讨论】:

  • 您需要循环浏览所有小部件并将它们的坐标与鼠标坐标相对应...我想,无论如何。
  • 这是个好主意,当然很脏,但可行。您介意将其发布为答案吗?

标签: qt pyqt pyqt4 pyside pyqt5


【解决方案1】:

这是另一种可能的解决方案,多次调用现有的QApplication.widgetAt

import sys
from PySide import QtGui, QtCore


def widgets_at(pos):
    """Return ALL widgets at `pos`

    Arguments:
        pos (QPoint): Position at which to get widgets

    """

    widgets = []
    widget_at = QtGui.qApp.widgetAt(pos)

    while widget_at:
        widgets.append(widget_at)

        # Make widget invisible to further enquiries
        widget_at.setAttribute(QtCore.Qt.WA_TransparentForMouseEvents)
        widget_at = QtGui.qApp.widgetAt(pos)

    # Restore attribute
    for widget in widgets:
        widget.setAttribute(QtCore.Qt.WA_TransparentForMouseEvents, False)

    return widgets

示例

class Overlay(QtGui.QWidget):
    def __init__(self, parent=None):
        super(Overlay, self).__init__(parent)
        self.setAttribute(QtCore.Qt.WA_StyledBackground)
        self.setStyleSheet("QWidget { background-color: rgba(0, 255, 0, 50) }")

    def mousePressEvent(self, event):
        pos = QtGui.QCursor.pos()
        print [w.objectName() for w in widgets_at(pos)]
        return super(Overlay, self).mousePressEvent(event)


app = QtGui.QApplication(sys.argv)
window = QtGui.QWidget()
window.setObjectName("Window")
window.setFixedSize(200, 100)

button = QtGui.QPushButton("Button 1", window)
button.setObjectName("Button 1")
button.move(10, 10)
button = QtGui.QPushButton("Button 2", window)
button.setObjectName("Button 2")
button.move(50, 15)
overlay = Overlay(window)
overlay.setObjectName("Overlay")
overlay.setFixedSize(window.size())

window.show()
app.exec_()

这是一些输出。

[u'Overlay', u'Window'] # Clicking an empty area
[u'Overlay', u'Button 1', u'Window'] # Button 1
[u'Overlay', u'Button 2', u'Window'] # Button 2
[u'Overlay', u'Button 2', u'Button 1', u'Window'] # Overlap

看起来结果是 O(1),另外还有一个好处是可以按照它们重叠的顺序返回每个小部件。然而,当它们的属性被更改和恢复时,它确实会导致重叠的窗口闪烁(Windows 8)。

【讨论】:

    【解决方案2】:

    不漂亮,但我想不通:您需要循环浏览所有小部件并将它们的坐标与鼠标坐标相对应。

    编辑: 如果你想要 O(1),你可以这样做,但它需要更多的开销,并且你需要在每次移动后存储坐标。我实际上有一个这样做的类,因为我希望可移动(由用户)小部件以它们以前的状态出现,并且我不想在我的所有项目中经历编码这个的麻烦。因此,我将 QDialog (等)子类化,并将其添加到一个便利类中,该类本质上是一个美化的 QSettings 对象。当 QDialog 发出任何有用的信息时,便利类的插槽会将这些值(基本上是 Geometry())存储在 QSettings 中。对于使用此行为的任何项目,一个简单的 ConvenienceClass::addWidget() 调用 w/ QWidget 和一个 QString 作为参数(作为键),它都存储在工作表下。如果您想对此进行扩展,您可以将所有 QWidget 的坐标存储在这个便利类中,并在您认为必要时简单地获取它们 O(1)。

    坐标的存储是微不足道的,因为计算坐标的工作已经完成。包含这些的便利类并不太难,但确实增加了一些复杂性。

    【讨论】:

    • 除非出现更好的情况,否则我会尽快将其标记为已接受。理想情况下,希望有一个 O(1) 性能的更简洁的解决方案,而不是 O(n)。
    • 但我没有做任何动作?
    • 好吧...不确定另一个小部件下会出现什么小部件(除了对话框顶部的按钮或画布或其他东西)。在这种情况下,您必须在它的 show()'n 或之前的线性方法之后立即抓住它。
    • Gotcha -- 我认为在这种情况下,您可以在添加它们的坐标时存储它们(到叠加层)。除此之外,我能想到的只有 O(n) 方法。
    猜你喜欢
    • 2021-02-10
    • 1970-01-01
    • 1970-01-01
    • 2022-01-07
    • 2015-07-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多