I have a grid of QWidget()
s in a QScrollArea()
. I override their leaveEvent()
for a particular reason (more on that later). When moving around with the mouse pointer, the leaveEvent()
is triggered just fine. But when I keep the mouse pointer still and scroll through the QScrollArea()
with my scrollwheel, the mouse pointer effectively leaves the widget without triggering a leaveEvent()
.
1. Some context
I'm working on an application to browse through Arduino libraries:
What you see in the screenshot is a QScrollArea()
in the middle with a scrollbar on the right and one at the bottom. This QScrollArea()
contains a large QFrame()
that holds all the widgets in its QGridLayout()
. Each widget - usually a QLabel()
or QPushButton()
- has a lightgrey border. This way, I visualize the grid they're in.
2. Lighting up a whole row
When clicking on a widget, I want the whole row to light up. To achieve this, I override the mousePressEvent()
for each of them:
Note: In practice, I just make each widget a subclasses from a custom class
CellWidget()
, such that I need this method-override only once.
def mousePressEvent(self, e:QMouseEvent) -> None:
'''
Set 'blue' property True for all neighbors.
'''
for w in self.__neighbors:
w.setProperty("blue", True)
w.style().unpolish(w)
w.style().polish(w)
w.update()
super().mousePressEvent(e)
return
The variable self.__neighbors
is a reference each widget keeps to all its neighboring widgets at the left and right side. This way, each widget has access to all widgets from its own row.
As you can see, I set the property "blue"
from each widget. This has an effect in the StyleSheet. For example, this is the stylesheet I give to the QLabel()
widgets:
# Note: 'self' is the QLabel() widget.
self.setStyleSheet(f"""
QLabel {
background-color: #ffffff;
color: #2e3436;
border-left: 1px solid #eeeeec;
border-top: 1px solid #eeeeec;
border-right: 1px solid #d3d7cf;
border-bottom: 1px solid #d3d7cf;
}
QLabel[blue = true] {
background-color: #d5e1f0;
}
""")
The unpolish()
and polish()
methods ensure that the stylesheet gets reloaded (I think?) and the update()
method invokes a repaint. The procedure works nice. I click on any given widget, and the whole row lights up blue!
3. Turn off the (blue) light
To turn it off, I override the leaveEvent()
method:
def leaveEvent(self, e:QEvent) -> None:
'''
Clear 'blue' property for all neighbors.
'''
for w in self.__neighbors:
w.setProperty("blue", False)
w.style().unpolish(w)
w.style().polish(w)
w.update()
super().leaveEvent(e)
return
So the whole row remains lighted up until you leave the widget with your mouse pointer. The leaveEvent()
clears the 'blue'
property for all neighbors and forces a repaint on them.
4. The problem
But there is a weakness. When I keep my mouse at the same position and I scroll the mousewheel down, the mouse pointer effectively leaves the widget without triggering a leaveEvent()
. The absence of such leaveEvent()
breaks my solution. While keeping the mouse still and scrolling down, you can click several rows and they all remain blue.
5. System info
I'm working on an application for both Windows and Linux. The application is written in Python 3.8
and uses PyQt5
.
question from:
https://stackoverflow.com/questions/65922274/widgets-leaveevent-is-missed-when-scrolling-through-the-encapsulating-qscroll