获取光标下的所有小部件

时间:2014-12-08 17:15:43

标签: qt pyqt pyqt4 pyside pyqt5

widgetAt函数直接在光标下给我一个小部件,最高的z顺序。

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

但是如何在光标下获取所有小部件?包括最顶端的那些?类似的东西:

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

实施例

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

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

在这两种情况下,如果只有一个小部件获得了click事件,我需要一种方法来区分它下面的小部件,以便传递事件。

2 个答案:

答案 0 :(得分:6)

这是另一种可能的解决方案,多次调用现有的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

实施例

image

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),因为它们的属性已更改并恢复。

答案 1 :(得分:1)

不是很漂亮,但不在我的脑海中:您需要循环浏览所有小部件并将其坐标用于鼠标坐标。

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

坐标的存储是微不足道的,因为无论如何都要完成计算它们的工作。持有这些的便利类并不太难,但确实增加了一些复杂性。