PySide2 复合小部件悬停效果 [英] PySide2 composite widget Hover Effect

查看:60
本文介绍了PySide2 复合小部件悬停效果的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如何在鼠标指针进入或离开 QListWidgetItem 时为自定义小部件内的组合元素的位置、缩放或任何其他属性设置动画?(见下方参考图片)

How to animate position,scale or any other attributes of composed elements inside custom widget, on mouse pointer enters or leaves the QListWidgetItem ? (see reference image below)

有没有更好的方法来管理 ListWidgetItem 周围的空间?item_widget.sizeHint() 提供了不需要的额外空间,这就是我向 setSizeHint 添加硬编码值的原因.

And is there any better way to manage space around ListWidgetItem ? item_widget.sizeHint() gives unwanted extra space, which is why i added hard coded value to setSizeHint.

ProductMainWindow.py

from PySide2 import QtCore, QtGui, QtWidgets
import productThumbnailWidget
import sys
sys.path.append('E:/code')

class prodMainWindowUI(QtWidgets.QMainWindow):
    def __init__(self):
        QtWidgets.QMainWindow.__init__(self)
        self.setupUi(self)    
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(800, 600)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.verticalLayout = QtWidgets.QVBoxLayout(self.centralwidget)
        self.verticalLayout.setObjectName("verticalLayout")
        self.productListWidget = QtWidgets.QListWidget(self.centralwidget)
        self.productListWidget.setObjectName("productListWidget")
        self.productListWidget.setViewMode(QtWidgets.QListWidget.IconMode)
        self.productListWidget.setIconSize(QtCore.QSize(256,256))
        self.productListWidget.setResizeMode(QtWidgets.QListWidget.Adjust)
        self.productListWidget.setMovement(QtWidgets.QListWidget.Static) # disable drag and drop
        self.productListWidget.setGridSize(QtCore.QSize(256 + 5, 256 + 5))

        self.verticalLayout.addWidget(self.productListWidget)
        MainWindow.setCentralWidget(self.centralwidget)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

        for i in range(6):
            item = QtWidgets.QListWidgetItem(self.productListWidget)
            item_widget = productThumbnailWidget.productThumbWidget()
            #item.setSizeHint(item_widget.sizeHint())
            item.setSizeHint(QtCore.QSize(256,256))
            self.productListWidget.addItem(item)
            self.productListWidget.setItemWidget(item, item_widget)

    def retranslateUi(self, MainWindow):
        MainWindow.setWindowTitle(QtWidgets.QApplication.translate("MainWindow", "MainWindow", None, -1))


app = QtWidgets.QApplication(sys.argv)
prodUI = prodMainWindowUI()
prodUI.show()
sys.exit(app.exec_())

productThumbnailWidget

from PySide2 import QtCore, QtGui, QtWidgets

class productThumbWidget(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(productThumbWidget, self).__init__(parent)
        self.setObjectName("Form")
        self.resize(256, 256)
        self.setMinimumSize(QtCore.QSize(256, 256))
        self.setMaximumSize(QtCore.QSize(256, 256))

        self.verticalLayout = QtWidgets.QVBoxLayout()
        self.verticalLayout.setContentsMargins(0, 0, 0, 0)
        self.verticalLayout.setSpacing(0)
        self.verticalLayout.setObjectName("verticalLayout")

        self.frame = QtWidgets.QFrame()
        self.frame.setFrameShape(QtWidgets.QFrame.StyledPanel)
        self.frame.setFrameShadow(QtWidgets.QFrame.Raised)
        self.frame.setObjectName("frame")

        self.thumbnailLabel = QtWidgets.QLabel("", self.frame)
        self.thumbnailLabel.setObjectName("thumbnailLabel")
        self.thumbnailLabel.setScaledContents(False)
        self.thumbnailLabel.setGeometry(QtCore.QRect(0, 0, 256, 256))
        self.thumbnailLabel.setMinimumSize(QtCore.QSize(256, 256))
        self.thumbnailLabel.setMaximumSize(QtCore.QSize(256, 256))
        self.thumbnailLabel.setPixmap(QtGui.QPixmap("E:/code/android.png"))    

        self.backgroundLabel = QtWidgets.QLabel("", self.frame)
        self.backgroundLabel.setObjectName("backgroundLabel")
        self.backgroundLabel.setGeometry(QtCore.QRect(0, 206, 256, 50))
        self.backgroundLabel.setMinimumSize(QtCore.QSize(256, 50))
        self.backgroundLabel.setMaximumSize(QtCore.QSize(256, 50))        
        self.backgroundLabel.setStyleSheet("QLabel#backgroundLabel{\n"
"    background: #32353B;\n"
"}")        

        self.titleLabel = QtWidgets.QLabel("Title", self.frame)
        self.titleLabel.setObjectName("titleLabel")
        self.titleLabel.setGeometry(QtCore.QRect(10, 218, 246, 25))
        self.titleLabel.setMinimumSize(QtCore.QSize(246, 25))
        self.titleLabel.setMaximumSize(QtCore.QSize(246, 25))
        font = QtGui.QFont()
        font.setFamily("SF Pro Display")
        font.setPointSize(16)
        font.setWeight(75)
        font.setBold(True)
        self.titleLabel.setFont(font)
        self.titleLabel.setStyleSheet("QLabel#titleLabel {\n"
"    color: #FFFFFF;\n"
"}")
        self.verticalLayout.addWidget(self.frame)
        self.setLayout(self.verticalLayout)

输出

推荐答案

解决方案是使用 QPropertyAnimation 动画矩形的位置,并使用事件 QEvent::Enter 和 QEvent::Leave 激活动画:

The solution is to animate the position of the rectangle using QPropertyAnimation, and to activate the animations using the events QEvent::Enter and QEvent::Leave:

import shiboken2
from PySide2 import QtCore, QtGui, QtWidgets


class RectangleHoverEffect(QtCore.QObject):
    def __init__(self, rectangle, parent):
        super().__init__(parent)
        if not isinstance(rectangle, QtWidgets.QWidget):
            raise TypeError(f"{rectangle} must be a QWidget")
        if rectangle.parent() is None:
            raise ValueError(f"{rectangle} must have a parent")
        self.m_rectangle = rectangle
        self.m_rectangle.parent().installEventFilter(self)
        self.m_animation = QtCore.QPropertyAnimation(
            self,
            targetObject=self.m_rectangle,
            propertyName=b"pos",
            duration=300,
            easingCurve=QtCore.QEasingCurve.OutQuad,
        )

    def eventFilter(self, obj, event):
        if shiboken2.isValid(self.m_rectangle):
            if self.m_rectangle.parent() is obj:
                y0 = self.m_rectangle.parent().height()
                y1 = self.m_rectangle.parent().height() - self.m_rectangle.height()

                if event.type() == QtCore.QEvent.Enter:
                    self._start_animation(y0, y1)
                elif event.type() == QtCore.QEvent.Leave:
                    self._start_animation(y1, y0)
        return super().eventFilter(obj, event)

    def _start_animation(self, y0, y1):
        self.m_animation.setStartValue(QtCore.QPoint(0, y0))
        self.m_animation.setEndValue(QtCore.QPoint(0, y1))
        self.m_animation.start()


class ThumbWidget(QtWidgets.QFrame):
    def __init__(self, title, pixmap, parent=None):
        super().__init__(parent)
        self.setFrameShape(QtWidgets.QFrame.StyledPanel)
        self.setFrameShadow(QtWidgets.QFrame.Raised)

        pixmap_label = QtWidgets.QLabel(pixmap=pixmap, scaledContents=True)
        title_label = QtWidgets.QLabel(title)
        title_label.setStyleSheet("""color: #FFFFFF""")
        font = QtGui.QFont()
        font.setFamily("SF Pro Display")
        font.setPointSize(16)
        font.setWeight(75)
        font.setBold(True)
        title_label.setFont(font)

        background_label = QtWidgets.QLabel(pixmap_label)
        background_label.setStyleSheet("background: #32353B;")
        background_label.setFixedSize(self.width(), 50)
        background_label.move(0, self.height())

        background_lay = QtWidgets.QVBoxLayout(background_label)
        background_lay.addWidget(title_label)

        self.setFixedSize(256, 256)
        lay = QtWidgets.QVBoxLayout(self)
        lay.setContentsMargins(0, 0, 0, 0)
        lay.setSpacing(0)
        lay.addWidget(pixmap_label)

        effect = RectangleHoverEffect(background_label, self)


class ProductMainWindow(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)

        self.product_listwidget = QtWidgets.QListWidget(
            viewMode=QtWidgets.QListWidget.IconMode,
            iconSize=QtCore.QSize(256, 256),
            resizeMode=QtWidgets.QListWidget.Adjust,
            movement=QtWidgets.QListWidget.Static,
        )
        self.product_listwidget.setGridSize(QtCore.QSize(256 + 5, 256 + 5))

        for i in range(6):
            item = QtWidgets.QListWidgetItem()
            item_widget = ThumbWidget(f"Title {i}", QtGui.QPixmap("E:/code/android.png"))
            item.setSizeHint(QtCore.QSize(256, 256))
            self.product_listwidget.addItem(item)
            self.product_listwidget.setItemWidget(item, item_widget)

        central_widget = QtWidgets.QWidget()
        self.setCentralWidget(central_widget)
        lay = QtWidgets.QVBoxLayout(central_widget)
        lay.addWidget(self.product_listwidget)

        self.resize(960, 480)


if __name__ == "__main__":
    import sys

    app = QtWidgets.QApplication(sys.argv)
    w = ProductMainWindow()
    w.show()
    sys.exit(app.exec_())

这篇关于PySide2 复合小部件悬停效果的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆