Get all widgets under the cursor

The function widgetAtgives me a widget directly below the cursor in the uppermost z-order.

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

But how can I get all the widgets under the cursor? Including those who stand at the very top? Sort of:

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

Example

As an example of use, draw an overlay that adds animated graphics, such as ripples on the surface of the water, wherever the user is. Naturally, the overlay should receive and respond to clicks, but it should do this without interfering with the clicks that extend to the widgets below it.

Another example is usage tracking; to intercept and record clicks for analysis, you can use the same overlay, but this time nothing is drawn, only assembled.

In both cases, if only one widget receives a click event, I will need to determine which widgets are below it in order to convey the event.

+4
source share
2 answers

Here is another possible solution that calls the existing one QApplication.widgetAtseveral times.

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

Example

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_()

Here are some results.

[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

It seems that the results are O (1), with the added benefit of returning each widget in the order in which they overlap. However, they overlap overlapping windows (Windows 8) because their attribute is changed and restored.

+5
source

, . .

EDIT: O (1), , , . , , , ( ) , . , QDialog ( ..), , QSettings. QDialog - , ( ()) QSettings. , , ConvenienceClass:: addWidget() w/QWidget QString ( ), . , QWidget grap O (1), .

, , , . , , , .

+1

All Articles