Pyside2/Qt : Image as button with hover state

1,286 views
Skip to first unread message

johancc

unread,
Jun 25, 2022, 12:12:58 AM6/25/22
to Python Programming for Autodesk Maya
Q: How to add the hover state to this button I have (as an image)? I need it to have the "hand icon" when hovering and also some sort of overlay like a darkening/lighting effect. Is it possible with my current code? I am using pyside/qt inside a DCC application, in this case Autodesk Maya 2022. Thank you.

from PySide2.QtWidgets import QApplication, QPushButton
from PySide2.QtGui import QPixmap, QIcon
from PySide2.QtCore import QSize

def clicked():
    print("Button clicked!")

window_wid = QtWidgets.QWidget()
vlayout_wid = QtWidgets.QVBoxLayout()


# Set Push Button
button = QPushButton()

button.setMaximumSize(200,100)

vlayout_wid.addWidget(button)

# Set image in Push Button
pixmap = QPixmap("my_image.jpg")
button_icon = QIcon(pixmap)
button.setIcon(button_icon)
button.setIconSize(QSize(200,100))

#button.show()
button.clicked.connect(clicked)

window_wid.setLayout(vlayout_wid)
window_wid.show()

qt.png

Justin Israel

unread,
Jun 25, 2022, 12:42:03 AM6/25/22
to python_in...@googlegroups.com
QIcon has an "active" mode state but the widget has to support it and it seems QPushButton does not use the active state of the QIcon during hover.
You could use a custom stylesheet, but that has the effect of needing to be fully styled or your button looks weird.

So a pretty easy solution is to just have a custom button class, where you have full control over what to do when hover starts and stops:

class MyPushButton(QPushButton):

    def __init__(self, *a, **kw):
        super(MyPushButton, self).__init__(*a, **kw)
        self._icon_normal = QIcon(QPixmap("my_image.jpg"))
        self._icon_over = QIcon(QPixmap("my_image_over.jpg"))

    def enterEvent(self, event):
        self.setIcon(self._icon_over)
        return super(MyPushButton, self).enterEvent(event)

    def leaveEvent(self, event):
        self.setIcon(self._icon_normal)
        return super(MyPushButton, self).enterEvent(event)

You can do any other settings you want in addition to changing the icon.

Justin
 

--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_m...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/28a6b738-6028-4504-b057-6c526bc37da9n%40googlegroups.com.

johancc

unread,
Jun 25, 2022, 12:50:59 AM6/25/22
to Python Programming for Autodesk Maya
Hi, thanks for the reply. Honestly I didn't want to have another image on the hover effect, I just wanted to darken it or light it somehow with code. Just not sure how.

Anyways, I ended up changing a bit the code. Here's my current setup:

from PySide2 import QtWidgets
from PySide2 import QtGui
from PySide2 import QtCore

class PicButton(QtWidgets.QAbstractButton):
    def __init__(self, pixmap, parent=None):
        super(PicButton, self).__init__(parent)
        self.pixmap = pixmap

    def paintEvent(self, event):
        painter = QtGui.QPainter(self)
        painter.drawPixmap(event.rect(), self.pixmap)

    def sizeHint(self):
        return self.pixmap.size()


window_wid = QtWidgets.QWidget()
vlayout_wid = QtWidgets.QVBoxLayout()

myPixmap = QPixmap(""my_image.jpg")
my_button = PicButton(myPixmap)
my_button.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
my_button.setMaximumSize(200,100)


vlayout_wid.addWidget(my_button)
window_wid.setLayout(vlayout_wid)
window_wid.show()

Justin Israel

unread,
Jun 25, 2022, 1:06:47 AM6/25/22
to python_in...@googlegroups.com


On Sat, 25 Jun 2022, 4:51 pm johancc, <joao.c...@gmail.com> wrote:
Hi, thanks for the reply. Honestly I didn't want to have another image on the hover effect, I just wanted to darken it or light it somehow with code. Just not sure how.

If the hover state doesn't need to be recalculated all the time, you could just generate a lighter version of the original pixmap using QPainter and then keep using my original example. 
A custom paint event gets called often. So it only makes sense if you want to actively control how it repaints. It would work as well though. 

johancc

unread,
Jun 25, 2022, 1:07:57 AM6/25/22
to Python Programming for Autodesk Maya
I was able to darken the image within the paint event, but can't get it to work inside the enterEvent.

from PySide2 import QtWidgets
from PySide2 import QtGui
from PySide2 import QtCore

class PicButton(QtWidgets.QAbstractButton):
    def __init__(self, pixmap, parent=None):
        super(PicButton, self).__init__(parent)
        self.pixmap = pixmap

    def paintEvent(self, event):
        painter = QtGui.QPainter(self)
        painter.drawPixmap(event.rect(), self.pixmap)
       
        #painter.setCompositionMode(QtGui.QPainter.CompositionMode_DestinationIn)
        painter.fillRect(event.rect(), QtGui.QBrush(QtGui.QColor (0,0,0,128)))
       
    def enterEvent(self, event):
        print('hovering')  

    def sizeHint(self):
        return self.pixmap.size()

window_wid = QtWidgets.QWidget()
vlayout_wid = QtWidgets.QVBoxLayout()

myPixmap = QtGui.QPixmap("D:\DOWNLOADS\GSG Assets S22-R25\GSG Assets\HDRIS\Hdris 1\Commercial Locations\PlusThumbnails\Concrete_Office_Outside.jpg")


my_button = PicButton(myPixmap)
my_button.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
my_button.setMaximumSize(200,100)


vlayout_wid.addWidget(my_button)
window_wid.setLayout(vlayout_wid)
window_wid.show()

Justin Israel

unread,
Jun 25, 2022, 3:19:58 PM6/25/22
to python_in...@googlegroups.com


On Sat, 25 Jun 2022, 5:07 pm johancc, <joao.c...@gmail.com> wrote:
I was able to darken the image within the paint event, but can't get it to work inside the enterEvent.

In your init for the custom button, you can generate the darkened pixmap once and then use it over and over in the enterEvent to set the icon. 

Create a new QPixmap from the source via calling source_pixmap.copy()
Start a QPainter with the copy, and darken it. 
Now you have a darkened copy of the pixmap and do not have to keep recreating it. Use it in the event handlers. 



Justin Israel

unread,
Jun 25, 2022, 9:21:17 PM6/25/22
to python_in...@googlegroups.com
I was back at my computer today so I figured I would give you a complete example of using the event handlers:
class MyPushButton(QPushButton):

    def __init__(self, *a, **kw):
        super(MyPushButton, self).__init__(*a, **kw)

        pix_normal = QPixmap("my_image.jpg")
        pix_over = pix_normal.copy()
        painter = QtGui.QPainter(pix_over)
        painter.fillRect(pix_over.rect(), QtGui.QBrush(QtGui.QColor(0,0,0,128)))
        painter.end()

        self._icon_normal = QIcon(pix_normal)
        self._icon_over = QIcon(pix_over)
        self.setIcon(self._icon_normal)


    def enterEvent(self, event):
        self.setIcon(self._icon_over)
        return super(MyPushButton, self).enterEvent(event)

    def leaveEvent(self, event):
        self.setIcon(self._icon_normal)
        return super(MyPushButton, self).leaveEvent(event)
Justin

Reply all
Reply to author
Forward
0 new messages