I want a button. I'm trying to make a state machine, so it will be used in QState class.
If Mouse cursor is hovering, the button changes it's color.
If left mouse button is released on the button, it should work.
If mouse left button is pressed on the button but released on outside of the button, (drag cursor to outside while pressing) the button shouldn't work. (so users can cancel their miss click by dragging out.)
Here is my code but it doesn't work well. When I drag cursor outside and release mouse button, the button works.
I tested several things and now I understand something.
While mouse button is pressed, QPushButton.enterEvent and QPushButton.leaveEvent doesn't work.
When cursor leaves QPushButton while the button is pressed, it seems that QPushButton.released emit signal.
How can I make this button?
class MyButton(QtWidgets.QPushButton):
selected = QtCore.pyqtSignal(bool)
def __init__(self, parent=None):
super().__init__(parent)
self.setMouseTracking(True)
self.is_hover = False
def enterEvent(self, event):
self.setStyleSheet\
("color: black; border-style: solid;\
border-width: 2px; border-color: white;\
background-color: rgba(250,0,0,1);")
self.is_hover = True
print('is_hover = True')
def leaveEvent(self, event):
self.setStyleSheet\
("color: white; border-style: solid;\
border-width: 2px; border-color: white;\
background-color: rgba(250,50,150,0.5);")
self.is_hover = False
print('is_hover = False')
def mouseReleaseEvent(self, event):
if event.button() == QtCore.Qt.LeftButton:
if self.is_hover:
self.selected.emit(True)
else:
return
else:
return
Subclassing and method overriding should only be done when the class doesn't provide what you need, and that's not your case.
The clicked
signal does exactly what you describe: the signal is emitted when the user presses the left mouse button and releases it while the cursor is inside the button (and when click()
or animateClick()
are called programmatically, but that's not the point).
If you want to also change the appearance while the button is hovered, you must use the :hover pseudo state. Note that to correctly use stylesheets selectors, you cannot use simple stylesheet syntax like you did, but the class must be specified exactly as it happens with standard CSS.
self.button = QPushButton()
self.button.setStyleSheet('''
QPushButton {
color: white;
border: 2px solid white;
background-color: rgba(250, 50, 150, 128);
}
QPushButton:hover {
background-color: rgba(250, 0, 0, 255);
}
''')
self.button.clicked.connect(self.someFunction)
Some considerations:
rgb()
and rgba()
syntax accepts integers between 0 and 255, or percentages, so the alpha values you used (0.5 and 1) won't work as expected, since they will be considered practically transparent (0 and 1, respectively) since non percentage values are always in the 0-255 range;:hover
and :pressed
:pressed
state; also, using the inset
and outset
border style (instead of solid
) is also considered good practice; note that it only works well with darker colors, so if you want a "whitish" border, use the following: self.button.setStyleSheet('''
QPushButton {
color: white;
border: 2px outset lightGray;
background-color: rgba(250, 50, 150, 128);
}
QPushButton:pressed {
border-style: inset;
}
QPushButton:hover {
background-color: rgba(250, 0, 0, 255);
}
QPushButton:hover:pressed {
/* since no background was set for pressed, hover state takes
precedence, so we need to specify the "default" color again here */
background-color: rgba(250, 50, 150, 128);
}
''')
setMouseTracking()
is useful only when you need to track mouse movements when no mouse button is pressed, and are intercepted by mouseMoveEvent()
(which you didn't implement) which is normally always called for mouse movements after a mouse button has been pressed;mousePressEvent()
, that widget will become the "mouse grabber" (see mouseGrabber()
and grabMouse()
) and all mouse movements will be received by that widget only: no enter/leave/hover events will be received by any other widget until the mouse button is released;