You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

实现QComboBox项背景的Hover平滑过渡动画(PySide6)

实现QComboBox项悬停时的背景色平滑过渡动画

要实现QComboBox列表项在鼠标悬停时300ms内从灰色(QColor(198, 198, 198))平滑过渡到白色(QColor(255, 255, 255)),你需要在QStyledItemDelegate中结合QVariantAnimation管理颜色渐变过程,而非直接切换颜色。以下是完整实现方案:

核心思路

  • 用字典记录每个列表项的动画实例和当前颜色状态,确保每项动画独立运行
  • 重写editorEvent捕获鼠标进入/离开事件,触发对应方向的颜色动画
  • 在paint函数中使用动画的当前颜色绘制背景,实现平滑过渡

完整代码

from PySide6.QtCore import *
from PySide6.QtWidgets import *
from PySide6.QtGui import *
import sys

class ComboBoxDelegate(QStyledItemDelegate):
    def __init__(self, parent=None):
        super().__init__(parent)
        # 存储每个项的动画和当前颜色:key为index的row,value为(animation, current_color)
        self._animations = {}
        self.base_color = QColor(198, 198, 198)
        self.hover_color = QColor(255, 255, 255)
        self.animation_duration = 300  # 动画时长,单位ms

    def editorEvent(self, event, model, option, index):
        # 捕获鼠标进入和离开事件
        if event.type() == QEvent.MouseMove or event.type() == QEvent.MouseButtonPress:
            # 鼠标移动到项上,启动从base到hover的动画
            self._start_animation(index, self.base_color, self.hover_color)
        elif event.type() == QEvent.Leave:
            # 鼠标离开项,启动从hover到base的动画
            self._start_animation(index, self.hover_color, self.base_color)
        return super().editorEvent(event, model, option, index)

    def _start_animation(self, index, start_color, end_color):
        row = index.row()
        # 如果已有该项的动画,先停止并移除
        if row in self._animations:
            anim, _ = self._animations[row]
            anim.stop()
            anim.deleteLater()
            del self._animations[row]
        
        # 创建颜色动画
        anim = QVariantAnimation(self)
        anim.setStartValue(start_color)
        anim.setEndValue(end_color)
        anim.setDuration(self.animation_duration)
        anim.setEasingCurve(QEasingCurve.InOutQuad)  # 平滑缓动曲线
        
        # 初始化当前颜色为起始色
        current_color = start_color
        self._animations[row] = (anim, current_color)
        
        # 动画值变化时更新颜色并触发重绘
        def update_color(value):
            nonlocal current_color
            current_color = value
            self._animations[row] = (anim, current_color)
            # 触发该项重绘
            parent = self.parent()
            if isinstance(parent, QComboBox):
                view = parent.view()
                view.update(index)
        
        anim.valueChanged.connect(update_color)
        # 动画结束后清理资源
        anim.finished.connect(lambda: self._clean_up_animation(row))
        anim.start()

    def _clean_up_animation(self, row):
        if row in self._animations:
            anim, _ = self._animations[row]
            anim.deleteLater()
            del self._animations[row]

    def paint(self, painter, option, index):
        row = index.row()
        # 获取当前颜色:有动画用动画当前值,否则用基础色
        if row in self._animations:
            _, current_color = self._animations[row]
        else:
            # 检查是否处于悬停状态(处理动画未启动的边界情况)
            if option.state & QStyle.State_MouseOver:
                current_color = self.hover_color
            else:
                current_color = self.base_color
        
        # 绘制背景
        painter.fillRect(option.rect, current_color)
        # 绘制文本(保留原有样式的对齐等)
        painter.drawText(option.rect, Qt.AlignVCenter | Qt.AlignLeft, index.data())


class MainWidget(QWidget):
    def __init__(self):
        super().__init__()
        self.combo_box = QComboBox(self)
        self.combo_box.addItems(['Item 1','Item 2', 'Item 3'])
        # 确保下拉视图能正确接收鼠标事件
        self.combo_box.view().setMouseTracking(True)
        
        self.main_layout = QVBoxLayout()
        self.main_layout.addWidget(self.combo_box)
        self.setLayout(self.main_layout)

        delegate = ComboBoxDelegate(self.combo_box)
        self.combo_box.setItemDelegate(delegate)


app = QApplication(sys.argv)
window = MainWidget()
window.show()
sys.exit(app.exec())

关键细节说明

  • 启用view().setMouseTracking(True):确保QComboBox的下拉视图能捕获鼠标移动事件,否则仅点击时才会触发悬停状态
  • 使用QEasingCurve.InOutQuad:让动画的开始和结束阶段速度更平缓,视觉效果更自然
  • 动画结束后清理资源:避免内存泄漏,同时保证项在无动画时恢复到正确的基础色
  • 边界状态处理:在paint中直接检查悬停状态,防止动画未及时启动时出现颜色跳变

内容的提问来源于stack exchange,提问作者yakubszatkowski

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 20:42:07