Open ghost opened 10 years ago
I've done a quick test with some promising results (avoiding duplicates when events are unhandled).
Here is what I've done:
def mousePressEvent(self, QMouseEvent):
mouse_button = None
if QMouseEvent.button() == QtCore.Qt.LeftButton:
mouse_button = 'left'
if QMouseEvent.button() == QtCore.Qt.RightButton:
mouse_button = 'right'
if QMouseEvent.button() == QtCore.Qt.MiddleButton:
mouse_button = 'middle'
cursor = QtGui.QCursor()
app = QtCore.QCoreApplication.instance()
widget = app.widgetAt(cursor.pos()).objectName()
if not widget:
widget = '<Unknown objectName>'
print mouse_button + ' mouse button pressed.'
print 'Coordinates:', cursor.pos().x(), cursor.pos().y()
print 'Widget Name', widget
def log_usage(app):
handler = Handler(app)
for widget in QtGui.QApplication.allWidgets():
newMousePressEvent = functools.partial(mousePressEvent, widget)
widget.mousePressEvent = newMousePressEvent
The output that I get when using this looks like this:
left mouse button pressed.
Coordinates: 592 372
Widget Name Btn00
The next step will be to see how to avoid breaking functionality in the case that the widgets already overloaded the mousePressEvent
method. What I have in mind is this:
mousePressEvent
of the methodI'm not sure if this will work. I'll keep you updated.
For the record, those are not part of the repository yet... It's a local test that I'm doing. :-)
This is close to what I had in mind, but there is a slight difference.
This, and what you've done previously, is a push method. You instruct each widget to push the given information to you, so that you can make a note of it. What I meant was for you to instead try a pull method. I.e. instead of telling each widget to let you know when an event has happened to them, you ask for it.
In the case of generating a heat map, you could do something like this.
# Pulling events from a Qt application
from PySide import QtCore, QtGui
timer = QtCore.QTimer()
timer.setInterval(100) # 10/sec
heatmap = {}
def record_hovering():
cursor = QtGui.QCursor()
app = QtCore.QCoreApplication.instance()
widget = app.widgetAt(cursor.pos()).objectName()
if not widget:
widget = '<Unknown objectName>'
# Is this the first time we hover this widget?
if not widget in heatmap:
heatmap[widget] = 0
# Keep track of how long we're hovering this widget.
heatmap[widget] += 1
timer.timeout.connect(record_hovering)
timer.start()
Let it run for a few seconds, then collect the results. E.g., here's what it looks like from Maya.
for key in sorted(heatmap, key=heatmap.__getitem__, reverse=True):
print "%s = %s" % (key, heatmap[key])
<Unknown objectName> = 150
mayaLayoutInternalWidget = 37
qt_scrollarea_viewport = 33
MayaWindow = 22
m_menubar_OptionBoxWindow = 21
check1 = 14
field = 13
OptionBoxWindow = 11
flowLayout6 = 10
menuMode = 9
modelPanel4 = 8
slider = 8
toolBar4 = 5
radio2 = 4
Looks like I spent most of my time hovering unnamed widget(s).
Not particularly helpful of course, a heatmap will probably be better off graphically visualised, but the point is that here, we're pulling information from widgets, as opposed to pushing information from them, and in doing so we leave widgets un-altered and thus aren't affecting performance, are guaranteed a widget with the highest z-order and can also capture widgets that wouldn't normally allow you to override their mousePressedEvent
- which may possibly be the majority of Maya's or any other native GUI widgets (as they're coming from C++).
Excellent write-up Marcus!
we're pulling information from widgets, as opposed to pushing information from them, and in doing so we leave widgets un-altered and thus aren't affecting performance, are guaranteed a widget with the highest z-order and can also capture widgets that wouldn't normally allow you to override their
mousePressedEvent
- which may possibly be the majority of Maya's or any other native GUI widgets (as they're coming from C++)
I see what you mean and it makes a lot of sense. I will keep experimenting with the idea and keep you updated about my progress.
Here is one suggested by Marcus Ottosson,
Instead of getting a list of
QWidgets
from a givenQApplication
, it might be a good idea to get the widget under the cursor when the mouse is clicked. By doing that, we don't have to worry about overlapping widgets and/or events not being handled. Currently unhandledQEvents
are affecting the output as specified in #1.In order to implement that, we could use widgetAt(pos) which will also be a step in the right direction if we want to aim for heatmaps as explained in #2.
I'll do a few tests to see if this is a feasible option. Let me know what you think.