Ejemplo n.º 1
0
class AlbumCard(PerspectiveWidget):
    """ 定义包含专辑歌手名的窗口 """

    playSignal = pyqtSignal(list)
    deleteCardSig = pyqtSignal(dict)
    nextPlaySignal = pyqtSignal(list)
    addToPlayingSignal = pyqtSignal(list)  # 将专辑添加到正在播放
    hideBlurAlbumBackgroundSig = pyqtSignal()
    saveAlbumInfoSig = pyqtSignal(dict, dict)
    switchToAlbumInterfaceSig = pyqtSignal(dict)
    checkedStateChanged = pyqtSignal(QWidget, bool)
    addAlbumToNewCustomPlaylistSig = pyqtSignal(list)  # 将专辑添加到新建的播放列表
    addAlbumToCustomPlaylistSig = pyqtSignal(str, list)  # 将专辑添加到已存在的自定义播放列表
    showBlurAlbumBackgroundSig = pyqtSignal(QPoint, str)  # 发送专辑卡全局坐标
    showAlbumInfoEditPanelSig = pyqtSignal(AlbumInfoEditPanel)  # 发送显示专辑信息面板信号

    def __init__(self, albumInfo: dict, parent):
        super().__init__(parent, True)
        self.albumInfo = deepcopy(albumInfo)
        self.songInfo_list = self.albumInfo.get("songInfo_list")  # type:list
        self.picPath = self.albumInfo.get("cover_path")  # type:str
        # 初始化标志位
        self.isChecked = False
        self.isInSelectionMode = False
        # 创建小部件
        self.__createWidgets()
        # 初始化
        self.__initWidget()

    def __createWidgets(self):
        """ 创建小部件 """
        # 实例化专辑名和歌手名
        self.albumNameLabel = ClickableLabel(self.albumInfo["album"], self)
        self.songerNameLabel = ClickableLabel(self.albumInfo["songer"], self)
        # 实例化封面和按钮
        self.albumPic = QLabel(self)
        self.playButton = BlurButton(
            self,
            (30, 65),
            r"app\resource\images\album_tab_interface\播放按钮_70_70.png",
            self.picPath,
        )
        self.addToButton = BlurButton(
            self,
            (100, 65),
            r"app\resource\images\album_tab_interface\添加到按钮_70_70.png",
            self.picPath,
        )
        # 创建复选框
        self.checkBox = CheckBox(self, forwardTargetWidget=self.albumPic)
        # 创建动画和窗口特效
        self.checkBoxOpacityEffect = QGraphicsOpacityEffect(self)

    def __initWidget(self):
        """ 初始化小部件 """
        self.setFixedSize(210, 290)
        self.setAttribute(Qt.WA_StyledBackground)
        self.albumPic.setFixedSize(200, 200)
        self.playButton.move(35, 70)
        self.addToButton.move(105, 70)
        self.albumPic.setPixmap(
            QPixmap(self.picPath).scaled(
                200, 200, Qt.KeepAspectRatioByExpanding, Qt.SmoothTransformation
            )
        )
        # 给小部件添加特效
        self.checkBox.setGraphicsEffect(self.checkBoxOpacityEffect)
        # 隐藏按钮
        self.playButton.hide()
        self.addToButton.hide()
        # 设置鼠标光标
        self.songerNameLabel.setCursor(Qt.PointingHandCursor)
        # 设置部件位置
        self.__initLayout()
        # 分配ID和属性
        self.setObjectName("albumCard")
        self.albumNameLabel.setObjectName("albumName")
        self.songerNameLabel.setObjectName("songerName")
        self.setProperty("isChecked", "False")
        self.albumNameLabel.setProperty("isChecked", "False")
        self.songerNameLabel.setProperty("isChecked", "False")
        # 将信号连接到槽函数
        self.playButton.clicked.connect(
            lambda: self.playSignal.emit(self.songInfo_list)
        )
        self.addToButton.clicked.connect(self.__showAddToMenu)
        self.checkBox.stateChanged.connect(self.__checkedStateChangedSlot)

    def __initLayout(self):
        """ 初始化布局 """
        self.albumPic.move(5, 5)
        self.albumNameLabel.move(5, 213)
        self.songerNameLabel.move(5, 239)
        self.checkBox.move(178, 8)
        self.checkBox.hide()
        self.__adjustLabel()

    def enterEvent(self, e):
        """ 鼠标进入窗口时显示磨砂背景和按钮 """
        # 显示磨砂背景
        albumCardPos = self.mapToGlobal(QPoint(0, 0))  # type:QPoint
        self.showBlurAlbumBackgroundSig.emit(albumCardPos, self.picPath)
        # 处于选择模式下按钮不可见
        self.playButton.setHidden(self.isInSelectionMode)
        self.addToButton.setHidden(self.isInSelectionMode)

    def leaveEvent(self, e):
        """ 鼠标离开时隐藏磨砂背景和按钮 """
        # 隐藏磨砂背景
        self.hideBlurAlbumBackgroundSig.emit()
        self.addToButton.hide()
        self.playButton.hide()

    def contextMenuEvent(self, event: QContextMenuEvent):
        """ 显示右击菜单 """
        # 创建菜单
        menu = AlbumCardContextMenu(parent=self)
        menu.playAct.triggered.connect(lambda: self.playSignal.emit(self.songInfo_list))
        menu.nextToPlayAct.triggered.connect(
            lambda: self.nextPlaySignal.emit(self.songInfo_list)
        )
        menu.addToMenu.playingAct.triggered.connect(
            lambda: self.addToPlayingSignal.emit(self.songInfo_list)
        )
        menu.editInfoAct.triggered.connect(self.showAlbumInfoEditPanel)
        menu.selectAct.triggered.connect(self.__selectActSlot)
        menu.addToMenu.addSongsToPlaylistSig.connect(
            lambda name: self.addAlbumToCustomPlaylistSig.emit(name, self.songInfo_list)
        )
        menu.addToMenu.newPlayList.triggered.connect(
            lambda: self.addAlbumToNewCustomPlaylistSig.emit(self.songInfo_list)
        )
        menu.deleteAct.triggered.connect(
            lambda: self.deleteCardSig.emit(self.albumInfo)
        )
        menu.exec(event.globalPos())

    def __adjustLabel(self):
        """ 根据专辑名的长度决定是否换行和添加省略号 """
        newText, isWordWrap = autoWrap(self.albumNameLabel.text(), 22)
        if isWordWrap:
            # 添加省略号
            index = newText.index("\n")
            fontMetrics = QFontMetrics(QFont("Microsoft YaHei", 10, 75))
            secondLineText = fontMetrics.elidedText(
                newText[index + 1 :], Qt.ElideRight, 200
            )
            newText = newText[: index + 1] + secondLineText
            self.albumNameLabel.setText(newText)
        # 给歌手名添加省略号
        fontMetrics = QFontMetrics(QFont("Microsoft YaHei", 10, 25))
        newSongerName = fontMetrics.elidedText(
            self.songerNameLabel.text(), Qt.ElideRight, 200
        )
        self.songerNameLabel.setText(newSongerName)
        self.songerNameLabel.adjustSize()
        self.albumNameLabel.adjustSize()
        self.songerNameLabel.move(
            5, self.albumNameLabel.y() + self.albumNameLabel.height() - 4
        )

    def mouseReleaseEvent(self, e):
        """ 鼠标松开发送切换到专辑界面信号或者取反选中状态 """
        super().mouseReleaseEvent(e)
        if e.button() == Qt.LeftButton:
            if self.isInSelectionMode:
                self.setChecked(not self.isChecked)
            else:
                # 不处于选择模式时且鼠标松开事件不是复选框发来的才发送切换到专辑界面的信号
                self.switchToAlbumInterfaceSig.emit(self.albumInfo)

    def updateWindow(self, newAlbumInfo: dict):
        """ 更新专辑卡窗口信息 """
        if newAlbumInfo == self.albumInfo:
            return
        self.albumInfo = deepcopy(newAlbumInfo)
        self.songInfo_list = self.albumInfo["songInfo_list"]
        self.picPath = newAlbumInfo["cover_path"]
        self.albumPic.setPixmap(
            QPixmap(self.picPath).scaled(
                200, 200, Qt.KeepAspectRatioByExpanding, Qt.SmoothTransformation
            )
        )
        self.albumNameLabel.setText(newAlbumInfo["album"])
        self.songerNameLabel.setText(newAlbumInfo["songer"])
        self.playButton.setBlurPic(newAlbumInfo["cover_path"], 30)
        self.addToButton.setBlurPic(newAlbumInfo["cover_path"], 30)
        self.__adjustLabel()

    def showAlbumInfoEditPanel(self):
        """ 显示专辑信息编辑面板 """
        oldAlbumInfo = deepcopy(self.albumInfo)
        infoEditPanel = AlbumInfoEditPanel(self.albumInfo, self.window())
        infoEditPanel.saveInfoSig.connect(
            lambda newAlbumInfo: self.__saveAlbumInfoSlot(oldAlbumInfo, newAlbumInfo)
        )
        self.showAlbumInfoEditPanelSig.emit(infoEditPanel)
        infoEditPanel.setStyle(QApplication.style())
        infoEditPanel.exec_()

    def __saveAlbumInfoSlot(self, oldAlbumInfo: dict, newAlbumInfo: dict):
        """ 保存专辑信息并更新界面 """
        newAlbumInfo_copy = deepcopy(newAlbumInfo)
        # self.updateWindow(newAlbumInfo)
        self.saveAlbumInfoSig.emit(oldAlbumInfo, newAlbumInfo_copy)
        self.albumInfo["songInfo_list"].sort(
            key=lambda songInfo: int(songInfo["tracknumber"])
        )
        # 更新专辑封面
        self.updateAlbumCover(newAlbumInfo["cover_path"])

    def updateAlbumCover(self, coverPath: str):
        """ 更新专辑封面 """
        self.picPath = coverPath
        self.albumPic.setPixmap(
            QPixmap(self.picPath).scaled(
                200, 200, Qt.KeepAspectRatioByExpanding, Qt.SmoothTransformation
            )
        )
        self.playButton.setBlurPic(coverPath, 30)
        self.addToButton.setBlurPic(coverPath, 30)

    def __checkedStateChangedSlot(self):
        """ 复选框选中状态改变对应的槽函数 """
        self.isChecked = self.checkBox.isChecked()
        # 发送信号
        self.checkedStateChanged.emit(self, self.isChecked)
        # 更新属性和背景色
        self.setProperty("isChecked", str(self.isChecked))
        self.albumNameLabel.setProperty("isChecked", str(self.isChecked))
        self.songerNameLabel.setProperty("isChecked", str(self.isChecked))
        self.setStyle(QApplication.style())

    def setChecked(self, isChecked: bool):
        """ 设置歌曲卡的选中状态 """
        self.checkBox.setChecked(isChecked)

    def setSelectionModeOpen(self, isOpenSelectionMode: bool):
        """ 设置是否进入选择模式, 处于选择模式下复选框一直可见,按钮不可见 """
        if self.isInSelectionMode == isOpenSelectionMode:
            return
        # 进入选择模式时显示复选框
        if isOpenSelectionMode:
            self.checkBoxOpacityEffect.setOpacity(1)
            self.checkBox.show()
        self.isInSelectionMode = isOpenSelectionMode

    def __selectActSlot(self):
        """ 右击菜单选择动作对应的槽函数 """
        self.setSelectionModeOpen(True)
        self.setChecked(True)

    def __showAddToMenu(self):
        """ 显示添加到菜单 """
        addToMenu = AddToMenu(parent=self)
        addToGlobalPos = self.mapToGlobal(QPoint(0, 0)) + QPoint(
            self.addToButton.x(), self.addToButton.y()
        )
        x = addToGlobalPos.x() + self.addToButton.width() + 5
        y = addToGlobalPos.y() + int(
            self.addToButton.height() / 2 - (13 + 38 * addToMenu.actionCount()) / 2
        )
        addToMenu.playingAct.triggered.connect(
            lambda: self.addToPlayingSignal.emit(self.songInfo_list)
        )
        addToMenu.newPlayList.triggered.connect(
            lambda: self.addAlbumToNewCustomPlaylistSig.emit(self.songInfo_list)
        )
        addToMenu.addSongsToPlaylistSig.connect(
            lambda name: self.addAlbumToCustomPlaylistSig.emit(name, self.songInfo_list)
        )
        addToMenu.exec(QPoint(x, y))
Ejemplo n.º 2
0
class SongInfoCard(QWidget):
    """ 歌曲信息卡 """

    showPlayBarSignal = pyqtSignal()
    hidePlayBarSignal = pyqtSignal()
    switchToAlbumInterfaceSig = pyqtSignal(str, str)

    def __init__(self, parent=None, songInfo: dict = None):
        super().__init__(parent)
        self.setSongInfo(songInfo)
        self.timer = QTimer(self)
        self.albumCoverLabel = QLabel(self)
        self.songNameLabel = ClickableLabel(parent=self)
        self.songerAlbumLabel = ClickableLabel(parent=self)
        # 初始化标志位
        self.isPlayBarVisible = False
        # 初始化
        self.__initWidget()

    def __initWidget(self):
        """ 初始化小部件 """
        self.resize(1307, 136)
        self.setFixedHeight(136)
        self.setMinimumWidth(400)
        self.albumCoverLabel.move(30, 0)
        self.albumCoverLabel.setFixedSize(136, 136)
        self.setAttribute(Qt.WA_TranslucentBackground)
        self.updateCard(self.songInfo)
        # 初始化定时器
        self.timer.setInterval(3000)
        self.timer.timeout.connect(self.timerSlot)
        # 分配ID
        self.songNameLabel.setObjectName("songNameLabel")
        self.songerAlbumLabel.setObjectName("songerAlbumLabel")
        # 设置属性
        self.songNameLabel.setProperty("state", "normal")
        self.songerAlbumLabel.setProperty("state", "normal")
        # self.__initLayout()
        self.__setQss()
        # 安装事件过滤器
        self.songNameLabel.installEventFilter(self)
        self.songerAlbumLabel.installEventFilter(self)
        # 信号连接到槽
        def slot():
            return self.switchToAlbumInterfaceSig.emit(self.album, self.songerName)

        self.songNameLabel.clicked.connect(slot)
        self.songerAlbumLabel.clicked.connect(slot)

    def __initLayout(self):
        """ 初始化布局 """
        self.songNameLabel.move(186, 30)
        self.songerAlbumLabel.move(186, 82)

    def setSongInfo(self, songInfo: dict):
        """ 设置歌曲信息 """
        self.songInfo = songInfo
        if not self.songInfo:
            self.songInfo = {}
        self.album = self.songInfo.get("album", "未知专辑")
        self.songName = self.songInfo.get("songName", "未知歌名")
        self.songerName = self.songInfo.get("songer", "未知歌手")
        self.albumCoverPath = getCoverPath(self.songInfo.get("modifiedAlbum"))

    def updateCard(self, songInfo: dict):
        """ 更新歌曲信息卡 """
        self.setSongInfo(songInfo)
        self.songNameLabel.setText(self.songName)
        self.songerAlbumLabel.setText(self.songerName + " • " + self.album)
        self.albumCoverLabel.setPixmap(
            QPixmap(self.albumCoverPath).scaled(
                136, 136, Qt.KeepAspectRatio, Qt.SmoothTransformation
            )
        )
        # 调整文本
        self.adjustText()

    def __setQss(self):
        """ 设置层叠样式 """
        with open(
            r"app\resource\css\playInterfaceSongInfoCard.qss", encoding="utf-8"
        ) as f:
            self.setStyleSheet(f.read())

    def eventFilter(self, obj, e: QEvent):
        """ 重写事件过滤器 """
        if obj in [self.songNameLabel, self.songerAlbumLabel]:
            if e.type() == QEvent.MouseButtonPress:
                self.songNameLabel.setProperty("state", "pressed")
                self.songerAlbumLabel.setProperty("state", "pressed")
                self.setStyle(QApplication.style())
            elif e.type() == QEvent.Enter:
                self.songNameLabel.setProperty("state", "hover")
                self.songerAlbumLabel.setProperty("state", "hover")
                self.setStyle(QApplication.style())
            elif e.type() in [QEvent.MouseButtonRelease, QEvent.Leave]:
                self.songNameLabel.setProperty("state", "normal")
                self.songerAlbumLabel.setProperty("state", "normal")
                self.setStyle(QApplication.style())
        return super().eventFilter(obj, e)

    def enterEvent(self, e):
        """ 鼠标进入时打开计时器并显示播放栏 """
        if not self.isPlayBarVisible:
            if not self.timer.isActive():
                # 显示播放栏
                self.timer.start()
                self.showPlayBarSignal.emit()
            else:
                # 重置定时器
                self.timer.stop()
                self.timer.start()

    def timerSlot(self):
        """ 定时器溢出时隐藏播放栏 """
        self.timer.stop()
        self.hidePlayBarSignal.emit()

    def adjustText(self):
        """ 根据文本长度决定是否插入换行符 """
        maxWidth = self.width() - 232
        # 设置专辑名歌手名标签的长度
        fontMetrics = QFontMetrics(QFont("Microsoft YaHei", 13))
        songerAlbumWidth = sum(
            [fontMetrics.width(i) for i in self.songerAlbumLabel.text()]
        )
        self.songerAlbumLabel.setFixedWidth(min(maxWidth, songerAlbumWidth))
        # 调整专辑名标签
        fontMetrics = QFontMetrics(QFont("Microsoft YaHei", 21, 75))
        newSongName_list = list(self.songName)  # type:list
        totalWidth = 0
        isWrap = False
        for i in range(len(self.songName)):
            totalWidth += fontMetrics.width(self.songName[i])
            if totalWidth > maxWidth:
                newSongName_list.insert(i, "\n")
                isWrap = True
                break
        if isWrap:
            self.songNameLabel.setText("".join(newSongName_list))
            self.songNameLabel.move(186, 6)
            self.songerAlbumLabel.move(186, 101)
            self.songNameLabel.setFixedSize(maxWidth, 83)
        else:
            self.songNameLabel.move(186, 26)
            self.songerAlbumLabel.move(186, 82)
            self.songNameLabel.setFixedSize(totalWidth, 46)
            self.songNameLabel.setText(self.songName)
Ejemplo n.º 3
0
class SongCard(QWidget):
    """ 歌曲卡 """

    clicked = pyqtSignal(int)
    switchToAlbumInterfaceSig = pyqtSignal(str, str)  # 发送专辑名和歌手名

    def __init__(self, songInfo: dict, parent=None):
        super().__init__(parent)
        self.__getInfo(songInfo)
        self.__resizeTime = 0
        # 记录播放状态
        self.isPlaying = False
        self.__currentState = "leave-notPlay"
        # 记录下每个小部件所占的最大宽度
        self.__maxSongNameCardWidth = 420
        self.__maxSongerLabelWidth = 284
        self.__maxAlbumLabelWidth = 284
        # 记录songCard对应的item的下标
        self.itemIndex = None
        # 创建小部件
        self.songNameCard = SongNameCard(songInfo["songName"], self)
        self.songerLabel = ClickableLabel(songInfo["songer"], self, False)
        self.albumLabel = ClickableLabel(songInfo["album"], self, False)
        self.yearLabel = QLabel(songInfo["year"], self)
        self.durationLabel = QLabel(songInfo["duration"], self)
        self.buttonGroup = self.songNameCard.buttonGroup
        self.playButton = self.songNameCard.playButton
        self.addToButton = self.songNameCard.addToButton
        self.__label_list = [
            self.songerLabel,
            self.albumLabel,
            self.yearLabel,
            self.durationLabel,
        ]
        # 创建动画
        self.__createAnimations()
        # 初始化
        self.__initWidget()

    def __initWidget(self):
        """ 初始化小部件 """
        self.__getLabelWidth()
        self.resize(1234, 60)
        self.resize(1234, 60)
        self.setFixedHeight(60)
        self.albumLabel.setCursor(Qt.PointingHandCursor)
        self.songerLabel.setCursor(Qt.PointingHandCursor)
        self.setAttribute(Qt.WA_StyledBackground)
        # 分配ID和属性
        self.setObjectName("songCard")
        self.albumLabel.setObjectName("clickableLabel")
        self.songerLabel.setObjectName("clickableLabel")
        self.setDynamicProperty(self.__currentState)
        # self.__setQss()
        # 安装事件过滤器
        self.installEventFilter(self)
        # 信号连接到槽
        self.playButton.clicked.connect(lambda: self.clicked.emit(self.itemIndex))
        self.albumLabel.clicked.connect(
            lambda: self.switchToAlbumInterfaceSig.emit(
                self.albumLabel.text(), self.songerLabel.text()
            )
        )

    def __setQss(self):
        """ 设置层叠样式 """
        with open(r"app\resource\css\playInterfaceSongCard.qss", encoding="utf-8") as f:
            self.setStyleSheet(f.read())

    def __getLabelWidth(self):
        """ 计算标签的长度 """
        fontMetrics = QFontMetrics(QFont("Microsoft YaHei", 9))
        self.songerWidth = fontMetrics.width(self.songInfo["songer"])
        self.albumWidth = fontMetrics.width(self.songInfo["album"])

    def setDynamicProperty(self, state: str):
        """ 设置动态属性,总共4状态,分别为enter-notPlay、enter-play、leave-notPlay、leave-play """
        self.yearLabel.setProperty("state", state)
        self.albumLabel.setProperty("state", state)
        self.songerLabel.setProperty("state", state)
        self.durationLabel.setProperty("state", state)
        if state.endswith("play"):
            self.isPlaying = True
            self.songNameCard.setPlay(True)
        if state.startswith("enter"):
            self.setProperty("state", "hover")
            self.songNameCard.buttonGroup.setProperty("state", "hover")
        else:
            self.setProperty("state", "leave")
            self.songNameCard.buttonGroup.setProperty("state", "leave")
        self.setStyle(QApplication.style())

    def resizeEvent(self, e):
        """ 改变窗口大小时移动标签 """
        # super().resizeEvent(e)
        self.__resizeTime += 1
        if self.__resizeTime == 1:
            self.originalWidth = self.width()
            width = self.width() - 246
            # 计算各个标签所占的最大宽度
            self.__maxSongNameCardWidth = int(42 / 99 * width)
            self.__maxSongerLabelWidth = int((width - self.__maxSongNameCardWidth) / 2)
            self.__maxAlbumLabelWidth = self.__maxSongerLabelWidth
            # 如果实际尺寸大于可分配尺寸,就调整大小
            self.__adjustWidgetWidth()
        elif self.__resizeTime > 1:
            deltaWidth = self.width() - self.originalWidth
            self.originalWidth = self.width()
            # 分配多出来的宽度
            threeEqualWidth = int(deltaWidth / 3)
            self.__maxSongNameCardWidth += threeEqualWidth
            self.__maxSongerLabelWidth += threeEqualWidth
            self.__maxAlbumLabelWidth += deltaWidth - 2 * threeEqualWidth
            self.__adjustWidgetWidth()
        # 移动标签
        self.durationLabel.move(self.width() - 45, 20)
        self.yearLabel.move(self.width() - 190, 20)
        self.songerLabel.move(self.__maxSongNameCardWidth + 26, 20)
        self.albumLabel.move(self.songerLabel.x() + self.__maxSongerLabelWidth + 15, 20)
        # 更新动画目标移动位置
        self.__getAniTargetX_list()

    def eventFilter(self, obj, e: QEvent):
        """ 更新样式 """
        if obj == self:
            if e.type() in [QEvent.Enter, QEvent.MouseButtonRelease]:
                self.songNameCard.setWidgetHidden(False)
                state = "enter-play" if self.isPlaying else "enter-notPlay"
                self.setDynamicProperty(state)
            elif e.type() == QEvent.Leave:
                self.songNameCard.setWidgetHidden(True)
                state = "leave-play" if self.isPlaying else "leave-notPlay"
                self.setDynamicProperty(state)
            elif e.type() == QEvent.MouseButtonPress:
                self.setProperty("state", "pressed")
                self.songNameCard.buttonGroup.setProperty("state", "pressed")
                self.setStyle(QApplication.style())
        return super().eventFilter(obj, e)

    def mousePressEvent(self, e):
        """ 鼠标按下时移动小部件 """
        super().mousePressEvent(e)
        # 移动小部件
        if self.aniGroup.state() == QAbstractAnimation.Stopped:
            for deltaX, widget in zip(self.__deltaX_list, self.__aniWidget_list):
                widget.move(widget.x() + deltaX, widget.y())
        else:
            self.aniGroup.stop()
            for targetX, widget in zip(self.__aniTargetX_list, self.__aniWidget_list):
                widget.move(targetX, widget.y())

    def mouseReleaseEvent(self, e: QMouseEvent):
        """ 鼠标松开时开始动画 """
        for ani, widget, deltaX in zip(
            self.__ani_list, self.__aniWidget_list, self.__deltaX_list
        ):
            ani.setStartValue(
                QRect(widget.x(), widget.y(), widget.width(), widget.height())
            )
            ani.setEndValue(
                QRect(widget.x() - deltaX, widget.y(), widget.width(), widget.height())
            )
        self.aniGroup.start()
        # 左键点击时才更新样式
        if e.button() == Qt.LeftButton:
            if not self.isPlaying:
                self.aniGroup.finished.connect(self.aniFinishedSlot)

    def aniFinishedSlot(self):
        """ 动画完成时更新样式 """
        # 发送点击信号
        self.setDynamicProperty("enter-play")
        self.songNameCard.checkBox.hide()
        self.clicked.emit(self.itemIndex)
        # 动画完成后需要断开连接,为下一次样式更新做准备
        self.aniGroup.disconnect()

    def __adjustWidgetWidth(self):
        """ 调整小部件宽度 """
        # if self.songNameCard.songNameWidth + 41 > self.__maxSongNameCardWidth:
        self.songNameCard.resize(self.__maxSongNameCardWidth, 60)
        if self.songerWidth > self.__maxSongerLabelWidth:
            self.songerLabel.setFixedWidth(self.__maxSongerLabelWidth)
        else:
            self.songerLabel.setFixedWidth(self.songerWidth)
        if self.albumWidth > self.__maxAlbumLabelWidth:
            self.albumLabel.setFixedWidth(self.__maxAlbumLabelWidth)
        else:
            self.albumLabel.setFixedWidth(self.albumWidth)

    def __createAnimations(self):
        """ 创建动画 """
        self.aniGroup = QParallelAnimationGroup(self)
        self.__deltaX_list = [13, 5, -3, -11, -13]
        self.__aniWidget_list = [
            self.songNameCard,
            self.songerLabel,
            self.albumLabel,
            self.yearLabel,
            self.durationLabel,
        ]
        self.__ani_list = [
            QPropertyAnimation(widget, b"geometry") for widget in self.__aniWidget_list
        ]
        for ani in self.__ani_list:
            ani.setDuration(400)
            ani.setEasingCurve(QEasingCurve.OutQuad)
            self.aniGroup.addAnimation(ani)
        # 记录下移动目标位置
        self.__getAniTargetX_list()

    def __getAniTargetX_list(self):
        """ 计算动画的初始值 """
        self.__aniTargetX_list = []
        for deltaX, widget in zip(self.__deltaX_list, self.__aniWidget_list):
            self.__aniTargetX_list.append(deltaX + widget.x())

    def setPlay(self, isPlay: bool):
        """ 设置歌曲卡的播放状态 """
        self.isPlaying = isPlay
        self.songNameCard.setPlay(isPlay)
        if isPlay:
            self.setDynamicProperty("enter-play")
        else:
            self.setDynamicProperty("leave-notPlay")

    def updateSongCard(self, songInfo: dict):
        """ 更新歌曲卡信息 """
        self.resize(self.size())
        self.__getInfo(songInfo)
        self.songNameCard.setSongName(songInfo["songName"])
        self.songerLabel.setText(songInfo["songer"])
        self.albumLabel.setText(songInfo["album"])
        self.yearLabel.setText(songInfo["year"])
        self.durationLabel.setText(songInfo["duration"])
        # 调整宽度
        self.__getLabelWidth()
        songerWidth = (
            self.songerWidth
            if self.songerWidth <= self.__maxSongerLabelWidth
            else self.__maxSongerLabelWidth
        )
        albumWidth = (
            self.albumWidth
            if self.albumWidth <= self.__maxAlbumLabelWidth
            else self.__maxAlbumLabelWidth
        )
        self.songerLabel.setFixedWidth(songerWidth)
        self.albumLabel.setFixedWidth(albumWidth)

    def __getInfo(self, songInfo: dict):
        """ 从歌曲信息中分离信息 """
        self.songInfo = songInfo
        self.year = songInfo["year"]  # type:str
        self.songer = songInfo["songer"]  # type:str
        self.album = songInfo["album"]  # type:str
        self.duration = songInfo["duration"]  # type:str
        self.songName = songInfo["songName"]  # type:str