PySide2 复合小部件悬停效果
Posted
技术标签:
【中文标题】PySide2 复合小部件悬停效果【英文标题】:PySide2 composite widget Hover Effect 【发布时间】:2019-09-21 15:07:42 【问题描述】:如何在鼠标指针进入或离开 QListWidgetItem 时为自定义小部件内的组合元素的位置、比例或任何其他属性设置动画? (见下图)
还有没有更好的方法来管理 ListWidgetItem 周围的空间? item_widget.sizeHint() 提供了不需要的额外空间,这就是我向 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)
输出
【问题讨论】:
【参考方案1】:解决方案是使用 QPropertyAnimation 为矩形的位置设置动画,并使用事件 QEvent::Enter 和 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_())
【讨论】:
在向 QListWidget 填充 2000-3000 个项目时,RAM 使用率变高(直到我关闭应用程序才下降)。如何解决这个问题?以上是关于PySide2 复合小部件悬停效果的主要内容,如果未能解决你的问题,请参考以下文章
仅为 QTabWidget 中的选项卡显示工具提示,而不是整个小部件
子类化并覆盖 PySide2 小部件方法;我在哪里可以找到参考资料?