def __init__(self, playlist: list = None, parent=None): super().__init__(parent) self.playlist = playlist.copy() self.currentIndex = 0 self.isPlaylistVisible = False # 创建小部件 self.blurPixmap = None self.blurBackgroundPic = QLabel(self) self.blurCoverThread = BlurCoverThread(self) self.songInfoCardChute = SongInfoCardChute(self, self.playlist) self.parallelAniGroup = QParallelAnimationGroup(self) self.songInfoCardChuteAni = QPropertyAnimation(self.songInfoCardChute, b"geometry") self.playBar = PlayBar(self) self.songListWidget = SongListWidget(self.playlist, self) self.smallestModeInterface = SmallestPlayModeInterface(playlist, self) self.playBarAni = QPropertyAnimation(self.playBar, b"geometry") self.songListWidgetAni = QPropertyAnimation(self.songListWidget, b"geometry") self.guideLabel = QLabel("在这里,你将看到正在播放的歌曲以及即将播放的歌曲。", self) self.randomPlayAllButton = ThreeStatePushButton( { "normal": r"app\resource\images\playing_interface\全部随机播放_normal.png", "hover": r"app\resource\images\playing_interface\全部随机播放_hover.png", "pressed": r"app\resource\images\playing_interface\全部随机播放_pressed.png", }, " 随机播放你收藏中的所有内容", (30, 22), self) # 创建定时器 self.showPlaylistTimer = QTimer(self) self.hidePlaylistTimer = QTimer(self) # 初始化 self.__initWidget()
def __createWidgets(self): """ 创建小部件 """ self.myMusicLabel = QLabel("我的音乐", self) # 创建导航按钮 self.songTabButton = TabButton("歌曲", self, 0) self.songerTabButton = TabButton("歌手", self, 1) self.albumTabButton = TabButton("专辑", self, 1) # 创建底部按钮 self.randomPlayAllButton = ThreeStatePushButton( { "normal": r"app\resource\images\ramdom_play_all\无序播放所有.png", "hover": r"app\resource\images\ramdom_play_all\无序播放所有_hover.png", "pressed": r"app\resource\images\ramdom_play_all\无序播放所有_pressed.png", }, parent=self, text=" 无序播放所有", iconSize=(19, 15), ) self.sortModeLabel = QLabel("排序依据:", self) self.songSortModeButton = QPushButton("添加日期", self) self.albumSortModeButton = QPushButton("添加日期", self) # 创建菜单 self.songSortModeMenu = AeroMenu(parent=self) self.albumSortModeMenu = AeroMenu(parent=self) # 创建动作 self.songSortBySongerAct = QAction("歌手", self) self.songSortByDictOrderAct = QAction("A到Z", self) self.songSortByCratedTimeAct = QAction("添加日期", self) self.albumSortByDictOrderAct = QAction("A到Z", self) self.albumSortByCratedTimeAct = QAction("添加日期", self) self.albumSortByYearAct = QAction("发行年份", self) self.albumSortBySongerAct = QAction("歌手", self) self.songSortAction_list = [ self.songSortByCratedTimeAct, self.songSortByDictOrderAct, self.songSortBySongerAct, ] self.albumSortAction_list = [ self.albumSortByCratedTimeAct, self.albumSortByDictOrderAct, self.albumSortByYearAct, self.albumSortBySongerAct, ]
def __createWidgets(self): """ 创建小部件 """ # 创建磨砂背景 self.scrollArea = ScrollArea(self) self.scrollWidget = QWidget(self) self.gridLayout = GridLayout() self.blurBackground = BlurBackground(self.scrollWidget) # 创建播放列表卡 self.__createPlaylistCards() # 创建白色遮罩 self.whiteMask = QWidget(self) self.sortModeLabel = QLabel("排序依据:", self) self.playlistLabel = QLabel("播放列表", self) self.sortModeButton = QPushButton("修改日期", self) self.createPlaylistButton = ThreeStatePushButton( { "normal": r"app\resource\images\playlist_card_interface\newPlaylist_normal.png", "hover": r"app\resource\images\playlist_card_interface\newPlaylist_hover.png", "pressed": r"app\resource\images\playlist_card_interface\newPlaylist_pressed.png", }, " 新的播放列表", (19, 19), self, ) # 创建导航标签 self.guideLabel = QLabel("这里没有可显示的内容。请尝试其他筛选器。", self) self.guideLabel.setStyleSheet("color: black; font: 25px 'Microsoft YaHei'") self.guideLabel.resize(500, 26) self.guideLabel.move(44, 196) # 创建排序菜单 self.sortModeMenu = AeroMenu(parent=self) self.sortByModifiedTimeAct = QAction( "修改时间", self, triggered=lambda: self.__sortPlaylist("modifiedTime") ) self.sortByAToZAct = QAction( "A到Z", self, triggered=lambda: self.__sortPlaylist("AToZ") ) self.sortAct_list = [self.sortByModifiedTimeAct, self.sortByAToZAct] # 创建选择状态栏 self.selectionModeBar = SelectionModeBar(self) # 记录当前的排序方式 self.currentSortAct = self.sortByModifiedTimeAct
class ToolBar(QWidget): """ 工具栏 """ def __init__(self, parent=None): super().__init__(parent) # 创建小部件 self.__createWidgets() # 初始化 self.__initWidget() def __createWidgets(self): """ 创建小部件 """ self.myMusicLabel = QLabel("我的音乐", self) # 创建导航按钮 self.songTabButton = TabButton("歌曲", self, 0) self.songerTabButton = TabButton("歌手", self, 1) self.albumTabButton = TabButton("专辑", self, 1) # 创建底部按钮 self.randomPlayAllButton = ThreeStatePushButton( { "normal": r"app\resource\images\ramdom_play_all\无序播放所有.png", "hover": r"app\resource\images\ramdom_play_all\无序播放所有_hover.png", "pressed": r"app\resource\images\ramdom_play_all\无序播放所有_pressed.png", }, parent=self, text=" 无序播放所有", iconSize=(19, 15), ) self.sortModeLabel = QLabel("排序依据:", self) self.songSortModeButton = QPushButton("添加日期", self) self.albumSortModeButton = QPushButton("添加日期", self) # 创建菜单 self.songSortModeMenu = AeroMenu(parent=self) self.albumSortModeMenu = AeroMenu(parent=self) # 创建动作 self.songSortBySongerAct = QAction("歌手", self) self.songSortByDictOrderAct = QAction("A到Z", self) self.songSortByCratedTimeAct = QAction("添加日期", self) self.albumSortByDictOrderAct = QAction("A到Z", self) self.albumSortByCratedTimeAct = QAction("添加日期", self) self.albumSortByYearAct = QAction("发行年份", self) self.albumSortBySongerAct = QAction("歌手", self) self.songSortAction_list = [ self.songSortByCratedTimeAct, self.songSortByDictOrderAct, self.songSortBySongerAct, ] self.albumSortAction_list = [ self.albumSortByCratedTimeAct, self.albumSortByDictOrderAct, self.albumSortByYearAct, self.albumSortBySongerAct, ] def __initWidget(self): """ 初始化小部件 """ self.__initLayout() self.resize(1200, 245) self.setAttribute(Qt.WA_StyledBackground) # 隐藏专辑排序按钮 self.albumSortModeButton.hide() # 将动作添加到菜单中 self.songSortModeMenu.addActions(self.songSortAction_list) self.albumSortModeMenu.addActions(self.albumSortAction_list) # 分配ID self.myMusicLabel.setObjectName("myMusicLabel") self.sortModeLabel.setObjectName("sortModeLabel") self.songSortModeMenu.setObjectName("sortModeMenu") self.albumSortModeMenu.setObjectName("sortModeMenu") self.albumSortModeMenu.setProperty("modeNumber", "4") self.songSortModeButton.setObjectName("sortModeButton") self.albumSortModeButton.setObjectName("sortModeButton") self.randomPlayAllButton.setObjectName("randomPlayButton") # 设置层叠样式 self.__setQss() def __initLayout(self): """ 初始化布局 """ self.myMusicLabel.move(30, 54) self.songTabButton.move(33, 136) self.albumTabButton.move(239, 136) self.songerTabButton.move(136, 136) self.randomPlayAllButton.move(31, 199) self.sortModeLabel.move(231, 204) self.songSortModeButton.move(306, 199) self.albumSortModeButton.move(306, 199) def paintEvent(self, QPaintEvent): """ 绘制背景 """ super().paintEvent(QPaintEvent) painter = QPainter(self) painter.setPen(QPen(QColor(229, 229, 229))) painter.drawLine(30, 176, self.width(), 176) def __setQss(self): """ 设置层叠样式 """ with open(r"app\resource\css\myMusicInterfaceToolBar.qss", encoding="utf-8") as f: self.setStyleSheet(f.read())
class PlayingInterface(QWidget): """ 正在播放界面 """ nextSongSig = pyqtSignal() # 点击下一首或者上一首按钮时由主界面的播放列表决定下一首的Index lastSongSig = pyqtSignal() switchPlayStateSig = pyqtSignal() randomPlayAllSignal = pyqtSignal() removeMediaSignal = pyqtSignal(int) # 点击歌曲卡或者滑动歌曲信息卡滑槽时直接设置新的index,index由自己决定 currentIndexChanged = pyqtSignal(int) switchToAlbumInterfaceSig = pyqtSignal(str, str) # 发出进入最小模式的信号 smallestModeStateChanged = pyqtSignal(bool) # 退出全屏信号 exitFullScreenSig = pyqtSignal() def __init__(self, playlist: list = None, parent=None): super().__init__(parent) self.playlist = playlist.copy() self.currentIndex = 0 self.isPlaylistVisible = False # 创建小部件 self.blurPixmap = None self.blurBackgroundPic = QLabel(self) self.blurCoverThread = BlurCoverThread(self) self.songInfoCardChute = SongInfoCardChute(self, self.playlist) self.parallelAniGroup = QParallelAnimationGroup(self) self.songInfoCardChuteAni = QPropertyAnimation(self.songInfoCardChute, b"geometry") self.playBar = PlayBar(self) self.songListWidget = SongListWidget(self.playlist, self) self.smallestModeInterface = SmallestPlayModeInterface(playlist, self) self.playBarAni = QPropertyAnimation(self.playBar, b"geometry") self.songListWidgetAni = QPropertyAnimation(self.songListWidget, b"geometry") self.guideLabel = QLabel("在这里,你将看到正在播放的歌曲以及即将播放的歌曲。", self) self.randomPlayAllButton = ThreeStatePushButton( { "normal": r"app\resource\images\playing_interface\全部随机播放_normal.png", "hover": r"app\resource\images\playing_interface\全部随机播放_hover.png", "pressed": r"app\resource\images\playing_interface\全部随机播放_pressed.png", }, " 随机播放你收藏中的所有内容", (30, 22), self) # 创建定时器 self.showPlaylistTimer = QTimer(self) self.hidePlaylistTimer = QTimer(self) # 初始化 self.__initWidget() def __initWidget(self): """ 初始化小部件 """ self.resize(1100, 870) self.currentSmallestModeSize = QSize(340, 340) self.setAttribute(Qt.WA_StyledBackground) self.guideLabel.move(45, 62) self.randomPlayAllButton.move(45, 117) self.playBar.move(0, self.height() - self.playBar.height()) # 隐藏部件 self.smallestModeInterface.hide() self.randomPlayAllButton.hide() self.guideLabel.hide() self.playBar.hide() # 设置层叠样式 self.setObjectName("playingInterface") self.guideLabel.setObjectName("guideLabel") self.randomPlayAllButton.setObjectName("randomPlayAllButton") self.__setQss() # 开启磨砂线程 if self.playlist: self.startBlurThread( self.songInfoCardChute.curSongInfoCard.albumCoverPath) # 将信号连接到槽 self.__connectSignalToSlot() # 初始化动画 self.playBarAni.setDuration(350) self.songListWidgetAni.setDuration(350) self.songListWidgetAni.setEasingCurve(QEasingCurve.InOutQuad) self.playBarAni.setEasingCurve(QEasingCurve.InOutQuad) self.parallelAniGroup.addAnimation(self.playBarAni) self.parallelAniGroup.addAnimation(self.songInfoCardChuteAni) # 初始化定时器 self.showPlaylistTimer.setInterval(120) self.hidePlaylistTimer.setInterval(120) self.showPlaylistTimer.timeout.connect(self.showPlayListTimerSlot) self.hidePlaylistTimer.timeout.connect(self.hidePlayListTimerSlot) def __setQss(self): """ 设置层叠样式 """ with open(r"app\resource\css\playInterface.qss", encoding="utf-8") as f: self.setStyleSheet(f.read()) def setBlurPixmap(self, blurPixmap): """ 设置磨砂pixmap """ self.blurPixmap = blurPixmap # 更新背景 self.__resizeBlurPixmap() def __resizeBlurPixmap(self): """ 调整背景图尺寸 """ maxWidth = max(self.width(), self.height()) if self.blurPixmap: self.blurBackgroundPic.setPixmap( self.blurPixmap.scaled( maxWidth, maxWidth, Qt.KeepAspectRatioByExpanding, Qt.SmoothTransformation, )) def startBlurThread(self, albumCoverPath): """ 开启磨砂线程 """ blurRadius = [6, 40][self.smallestModeInterface.isVisible()] self.blurCoverThread.setTargetCover(albumCoverPath, blurRadius=blurRadius) self.blurCoverThread.start() def resizeEvent(self, e): """ 改变尺寸时也改变小部件的大小 """ super().resizeEvent(e) self.__resizeBlurPixmap() self.songInfoCardChute.resize(self.size()) self.blurBackgroundPic.setFixedSize(self.size()) self.playBar.resize(self.width(), self.playBar.height()) self.songListWidget.resize(self.width(), self.height() - 382) self.smallestModeInterface.resize(self.size()) if self.isPlaylistVisible: self.playBar.move(0, 190) self.songListWidget.move(0, 382) self.songInfoCardChute.move(0, 258 - self.height()) else: self.playBar.move(0, self.height() - self.playBar.height()) self.songListWidget.move(0, self.height()) def showPlayBar(self): """ 显示播放栏 """ # 只在播放栏不可见的时候显示播放栏和开启动画 if not self.playBar.isVisible(): self.playBar.show() self.songInfoCardChuteAni.setDuration(450) self.songInfoCardChuteAni.setEasingCurve(QEasingCurve.OutCubic) self.songInfoCardChuteAni.setStartValue( self.songInfoCardChute.rect()) self.songInfoCardChuteAni.setEndValue( QRect(0, -self.playBar.height() + 68, self.width(), self.height())) self.songInfoCardChuteAni.start() def hidePlayBar(self): """ 隐藏播放栏 """ if self.playBar.isVisible() and not self.isPlaylistVisible: self.playBar.hide() self.songInfoCardChuteAni.setEasingCurve(QEasingCurve.OutCirc) self.songInfoCardChuteAni.setStartValue( QRect(0, -self.playBar.height() + 68, self.width(), self.height())) self.songInfoCardChuteAni.setEndValue( QRect(0, 0, self.width(), self.height())) self.songInfoCardChuteAni.start() def showPlaylist(self): """ 显示播放列表 """ if self.songListWidgetAni.state() != QAbstractAnimation.Running: self.songInfoCardChuteAni.setDuration(350) self.songInfoCardChuteAni.setEasingCurve(QEasingCurve.InOutQuad) self.songInfoCardChuteAni.setStartValue( QRect(0, self.songInfoCardChute.y(), self.width(), self.height())) self.songInfoCardChuteAni.setEndValue( QRect(0, 258 - self.height(), self.width(), self.height())) self.playBarAni.setStartValue( QRect(0, self.playBar.y(), self.width(), self.playBar.height())) self.playBarAni.setEndValue( QRect(0, 190, self.width(), self.playBar.height())) self.songListWidgetAni.setStartValue( QRect( self.songListWidget.x(), self.songListWidget.y(), self.songListWidget.width(), self.songListWidget.height(), )) self.songListWidgetAni.setEndValue( QRect( self.songListWidget.x(), 382, self.songListWidget.width(), self.songListWidget.height(), )) if self.sender() == self.playBar.showPlaylistButton: self.playBar.pullUpArrowButton.timer.start() self.playBar.show() self.parallelAniGroup.start() self.blurBackgroundPic.hide() self.showPlaylistTimer.start() def showPlayListTimerSlot(self): """ 显示播放列表定时器溢出槽函数 """ self.showPlaylistTimer.stop() self.songListWidgetAni.start() self.isPlaylistVisible = True def hidePlayListTimerSlot(self): """ 显示播放列表定时器溢出槽函数 """ self.hidePlaylistTimer.stop() self.parallelAniGroup.start() def hidePlaylist(self): """ 隐藏播放列表 """ if self.parallelAniGroup.state() != QAbstractAnimation.Running: self.songInfoCardChuteAni.setDuration(350) self.songInfoCardChuteAni.setEasingCurve(QEasingCurve.InOutQuad) self.songInfoCardChuteAni.setStartValue( QRect(0, self.songInfoCardChute.y(), self.width(), self.height())) self.songInfoCardChuteAni.setEndValue( QRect(0, -self.playBar.height() + 68, self.width(), self.height())) self.playBarAni.setStartValue( QRect(0, 190, self.width(), self.playBar.height())) self.playBarAni.setEndValue( QRect( 0, self.height() - self.playBar.height(), self.width(), self.playBar.height(), )) self.songListWidgetAni.setStartValue( QRect( self.songListWidget.x(), self.songListWidget.y(), self.songListWidget.width(), self.songListWidget.height(), )) self.songListWidgetAni.setEndValue( QRect( self.songListWidget.x(), self.height(), self.songListWidget.width(), self.songListWidget.height(), )) if self.sender() == self.playBar.showPlaylistButton: self.playBar.pullUpArrowButton.timer.start() # self.parallelAniGroup.start() self.songListWidgetAni.start() self.hidePlaylistTimer.start() self.blurBackgroundPic.show() self.isPlaylistVisible = False def showPlaylistButtonSlot(self): """ 显示或隐藏播放列表 """ if not self.isPlaylistVisible: self.showPlaylist() else: self.hidePlaylist() def setCurrentIndex(self, index): """ 更新播放列表下标 """ # 下标大于等于0时才更新 if self.currentIndex != index and index > -1: # 在播放列表的最后一首歌被移除时不更新样式 if index >= len(self.playlist): return if self.smallestModeInterface.isVisible(): self.smallestModeInterface.setCurrentIndex(index) self.currentIndex = index self.songListWidget.setCurrentIndex(index) self.songInfoCardChute.setCurrentIndex(index) def setPlaylist(self, playlist: list, isResetIndex: bool = True): """ 更新播放列表 Parameters ---------- playlist: list 播放列表,每一个元素都是songInfo字典 isResetIndex: bool 是否将下标重置为0 """ self.playlist = deepcopy(playlist) self.currentIndex = 0 if isResetIndex else self.currentIndex if playlist: self.songInfoCardChute.setPlaylist(self.playlist, isResetIndex) self.smallestModeInterface.setPlaylist(self.playlist, isResetIndex) self.songListWidget.updateSongCards(self.playlist) # 如果小部件不可见就显示 if playlist and not self.songListWidget.isVisible(): self.__setGuideLabelHidden(True) def __settleDownPlayBar(self): """ 定住播放栏 """ self.songInfoCardChute.stopSongInfoCardTimer() def __startSongInfoCardTimer(self): """ 重新打开歌曲信息卡的定时器 """ if not self.playBar.volumeSlider.isVisible(): # 只有音量滑动条不可见才打开计时器 self.songInfoCardChute.startSongInfoCardTimer() def __songListWidgetCurrentChangedSlot(self, index): """ 歌曲列表当前下标改变插槽 """ self.currentIndex = index self.songInfoCardChute.setCurrentIndex(index) self.currentIndexChanged.emit(index) def __songInfoCardChuteCurrentChangedSlot(self, index): """ 歌曲列表当前下标改变插槽 """ self.currentIndex = index self.songListWidget.setCurrentIndex(index) self.currentIndexChanged.emit(index) def __removeSongFromPlaylist(self, index): """ 从播放列表中移除选中的歌曲 """ lastSongRemoved = False if self.currentIndex > index: self.currentIndex -= 1 self.songInfoCardChute.currentIndex -= 1 elif self.currentIndex == index: # 如果被移除的是最后一首需要将当前下标-1 if index == self.songListWidget.currentIndex + 1: self.currentIndex -= 1 self.songInfoCardChute.currentIndex -= 1 lastSongRemoved = True else: self.songInfoCardChute.setCurrentIndex(self.currentIndex) self.removeMediaSignal.emit(index) # 如果播放列表为空,隐藏小部件 if len(self.playlist) == 0: self.__setGuideLabelHidden(False) # 如果被移除的是最后一首就将当前播放歌曲置为被移除后的播放列表最后一首 """ if lastSongRemoved: self.currentIndexChanged.emit(self.currentIndex) """ def clearPlaylist(self): """ 清空歌曲卡 """ self.playlist.clear() self.songListWidget.clearSongCards() # 显示随机播放所有按钮 self.__setGuideLabelHidden(False) def __setGuideLabelHidden(self, isHidden): """ 设置导航标签和随机播放所有按钮的可见性 """ self.randomPlayAllButton.setHidden(isHidden) self.guideLabel.setHidden(isHidden) self.songListWidget.setHidden(not isHidden) if isHidden: # 隐藏导航标签时根据播放列表是否可见设置磨砂背景和播放栏的可见性 self.blurBackgroundPic.setHidden(self.isPlaylistVisible) self.playBar.setHidden(not self.isPlaylistVisible) else: # 显示导航标签时隐藏磨砂背景 self.blurBackgroundPic.hide() self.playBar.hide() # 最后再显示歌曲信息卡 self.songInfoCardChute.setHidden(not isHidden) def updateOneSongCard(self, oldSongInfo: dict, newSongInfo): """ 更新一个歌曲卡 """ self.songListWidget.updateOneSongCard(oldSongInfo, newSongInfo) self.playlist = self.songListWidget.playlist self.songInfoCardChute.playlist = self.playlist def updateMultiSongCards(self, oldSongInfo_list: list, newSongInfo_list: list): """ 更新多个歌曲卡 """ self.songListWidget.updateMultiSongCards(oldSongInfo_list, newSongInfo_list) self.playlist = self.songListWidget.playlist self.songInfoCardChute.playlist = self.playlist def showSmallestModeInterface(self): """ 显示最小播放模式界面 """ self.exitFullScreenSig.emit() # 记录下正常尺寸 self.currentGeometry = self.window().geometry() # type:QRect # 更新磨砂半径 self.blurCoverThread.setTargetCover( self.blurCoverThread.albumCoverPath, 40, (350, 350)) self.blurCoverThread.start() self.playBar.hide() self.songListWidget.hide() self.songInfoCardChute.hide() self.blurBackgroundPic.show() # 先更新歌曲信息卡再显示界面 self.smallestModeInterface.setCurrentIndex(self.currentIndex) self.smallestModeInterface.show() # 发出隐藏标题栏按钮的信号 self.smallestModeStateChanged.emit(True) self.window().setMinimumSize(206, 197) self.window().setGeometry( self.currentGeometry.x() + self.currentGeometry.width() - self.currentSmallestModeSize.width(), self.currentGeometry.y(), self.currentSmallestModeSize.width(), self.currentSmallestModeSize.height(), ) def __hideSmallestModeInterface(self): """ 隐藏最小播放模式界面 """ # 记录下最小播放模式的尺寸 self.currentSmallestModeSize = self.window().size() # type:QSize # 更新磨砂半径 self.blurCoverThread.setTargetCover( self.blurCoverThread.albumCoverPath, 6, (450, 450)) self.blurCoverThread.start() self.smallestModeInterface.hide() self.window().setMinimumSize(1030, 850) self.window().setGeometry(self.currentGeometry) # 发出显示标题栏按钮的信号 self.smallestModeStateChanged.emit(False) self.blurBackgroundPic.setHidden(self.isPlaylistVisible) self.playBar.show() self.songListWidget.show() self.songInfoCardChute.show() def __connectSignalToSlot(self): """ 将信号连接到槽 """ self.blurCoverThread.blurDone.connect(self.setBlurPixmap) # 更新背景封面和下标 self.songInfoCardChute.currentIndexChanged[int].connect( self.__songInfoCardChuteCurrentChangedSlot) self.songInfoCardChute.currentIndexChanged[str].connect( self.startBlurThread) # 显示和隐藏播放栏 self.songInfoCardChute.showPlayBarSignal.connect(self.showPlayBar) self.songInfoCardChute.hidePlayBarSignal.connect(self.hidePlayBar) # 将播放栏的信号连接到槽 self.playBar.lastSongButton.clicked.connect(self.lastSongSig) self.playBar.nextSongButton.clicked.connect(self.nextSongSig) self.playBar.playButton.clicked.connect(self.switchPlayStateSig) self.playBar.pullUpArrowButton.clicked.connect( self.showPlaylistButtonSlot) self.playBar.showPlaylistButton.clicked.connect( self.showPlaylistButtonSlot) self.playBar.smallPlayModeButton.clicked.connect( self.showSmallestModeInterface) self.playBar.enterSignal.connect(self.__settleDownPlayBar) self.playBar.leaveSignal.connect(self.__startSongInfoCardTimer) # 将歌曲列表的信号连接到槽函数 self.songListWidget.currentIndexChanged.connect( self.__songListWidgetCurrentChangedSlot) self.songListWidget.removeItemSignal.connect( self.__removeSongFromPlaylist) self.randomPlayAllButton.clicked.connect(self.randomPlayAllSignal) # 将最小化播放界面的信号连接到槽函数 self.smallestModeInterface.lastSongButton.clicked.connect( self.lastSongSig) self.smallestModeInterface.nextSongButton.clicked.connect( self.nextSongSig) self.smallestModeInterface.playButton.clicked.connect( self.switchPlayStateSig) self.smallestModeInterface.exitSmallestModeButton.clicked.connect( self.__hideSmallestModeInterface) # 切换到专辑界面 self.songInfoCardChute.switchToAlbumInterfaceSig.connect( self.switchToAlbumInterfaceSig) self.songListWidget.switchToAlbumInterfaceSig.connect( self.switchToAlbumInterfaceSig)
class PlaylistCardInterface(QWidget): """ 播放列表卡界面 """ playSig = pyqtSignal(list) nextToPlaySig = pyqtSignal(list) deletePlaylistSig = pyqtSignal(dict) renamePlaylistSig = pyqtSignal(dict, dict) selectionModeStateChanged = pyqtSignal(bool) def __init__(self, playlists: list, parent=None): super().__init__(parent) self.columnNum = 1 self.sortMode = "modifiedTime" self.playlists = deepcopy(playlists) self.playlistCard_list = [] self.playlistCardDict_list = [] # type:list[dict] self.checkedPlaylistCard_list = [] self.isInSelectionMode = False self.isAllPlaylistCardChecked = False # 创建小部件 self.__createWidgets() # 初始化 self.__initWidget() def __createWidgets(self): """ 创建小部件 """ # 创建磨砂背景 self.scrollArea = ScrollArea(self) self.scrollWidget = QWidget(self) self.gridLayout = GridLayout() self.blurBackground = BlurBackground(self.scrollWidget) # 创建播放列表卡 self.__createPlaylistCards() # 创建白色遮罩 self.whiteMask = QWidget(self) self.sortModeLabel = QLabel("排序依据:", self) self.playlistLabel = QLabel("播放列表", self) self.sortModeButton = QPushButton("修改日期", self) self.createPlaylistButton = ThreeStatePushButton( { "normal": r"app\resource\images\playlist_card_interface\newPlaylist_normal.png", "hover": r"app\resource\images\playlist_card_interface\newPlaylist_hover.png", "pressed": r"app\resource\images\playlist_card_interface\newPlaylist_pressed.png", }, " 新的播放列表", (19, 19), self, ) # 创建导航标签 self.guideLabel = QLabel("这里没有可显示的内容。请尝试其他筛选器。", self) self.guideLabel.setStyleSheet("color: black; font: 25px 'Microsoft YaHei'") self.guideLabel.resize(500, 26) self.guideLabel.move(44, 196) # 创建排序菜单 self.sortModeMenu = AeroMenu(parent=self) self.sortByModifiedTimeAct = QAction( "修改时间", self, triggered=lambda: self.__sortPlaylist("modifiedTime") ) self.sortByAToZAct = QAction( "A到Z", self, triggered=lambda: self.__sortPlaylist("AToZ") ) self.sortAct_list = [self.sortByModifiedTimeAct, self.sortByAToZAct] # 创建选择状态栏 self.selectionModeBar = SelectionModeBar(self) # 记录当前的排序方式 self.currentSortAct = self.sortByModifiedTimeAct def __createPlaylistCards(self): """ 创建播放列表卡 """ # 创建并行动画组 self.hideCheckBoxAniGroup = QParallelAnimationGroup(self) self.hideCheckBoxAni_list = [] for playlist in self.playlists: self.__createOnePlaylistCard(playlist) def __createOnePlaylistCard(self, playlist: dict): """ 创建一个播放列表卡 """ playlistCard = PlaylistCard(playlist, self) self.playlistCard_list.append(playlistCard) self.playlistCardDict_list.append( {"playlistCard": playlistCard, "playlist": playlist} ) # 创建动画 hideCheckBoxAni = QPropertyAnimation( playlistCard.checkBoxOpacityEffect, b"opacity" ) self.hideCheckBoxAniGroup.addAnimation(hideCheckBoxAni) self.hideCheckBoxAni_list.append(hideCheckBoxAni) # 信号连接到槽 playlistCard.showBlurBackgroundSig.connect(self.__showBlurBackground) playlistCard.hideBlurBackgroundSig.connect(self.blurBackground.hide) playlistCard.renamePlaylistSig.connect(self.__showRenamePlaylistPanel) playlistCard.deleteCardSig.connect(self.__showDeleteCardPanel) playlistCard.playSig.connect(self.playSig) playlistCard.checkedStateChanged.connect( self.__playlistCardCheckedStateChangedSlot ) playlistCard.nextToPlaySig.connect(self.nextToPlaySig) def __initWidget(self): """ 初始化小部件 """ # 隐藏小部件 self.blurBackground.hide() self.selectionModeBar.hide() self.guideLabel.setHidden(bool(self.playlistCard_list)) # 初始化滚动条 self.scrollArea.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) # 将动作添加到菜单中 self.sortModeMenu.addActions(self.sortAct_list) # 分配ID和属性 self.scrollWidget.setObjectName("scrollWidget") self.playlistLabel.setObjectName("playlistLabel") self.sortModeLabel.setObjectName("sortModeLabel") self.sortModeButton.setObjectName("sortModeButton") self.createPlaylistButton.setObjectName("createPlaylistButton") self.sortModeMenu.setObjectName("sortModeMenu") self.sortModeMenu.setProperty("modeNumber", "2") self.__setQss() self.__initLayout() self.resize(1270, 760) self.__connectSignalToSlot() def __initLayout(self): """ 初始化布局 """ self.scrollArea.move(0, 0) self.playlistLabel.move(30, 54) self.sortModeLabel.move(190, 135) self.sortModeButton.move(264, 130) self.createPlaylistButton.move(30, 135) self.selectionModeBar.move(0, self.height() - self.selectionModeBar.height()) # 设置布局的间距和外边距 self.gridLayout.setVerticalSpacing(20) self.gridLayout.setHorizontalSpacing(10) self.gridLayout.setContentsMargins(15, 175, 15, 120) self.scrollArea.setWidget(self.scrollWidget) self.scrollWidget.setLayout(self.gridLayout) # 如果没有播放列表就直接返回 if not self.playlistCard_list: return # 按照修改日期排序播放列表 self.__sortPlaylist("modifiedTime") def __setQss(self): """ 设置层叠样式 """ with open(r"app\resource\css\playlistCardInterface.qss", encoding="utf-8") as f: self.setStyleSheet(f.read()) def resizeEvent(self, e): """ 调整小部件尺寸和位置 """ super().resizeEvent(e) self.scrollArea.resize(self.size()) self.whiteMask.resize(self.width() - 15, 175) self.scrollArea.verticalScrollBar().resize(4, self.height() - 116) self.scrollArea.verticalScrollBar().move(-1, 40) self.selectionModeBar.resize(self.width(), self.selectionModeBar.height()) if self.width() < 641 and self.columnNum != 1: self.__setColumnNum(1) elif 641 <= self.width() < 954 and self.columnNum != 2: self.__setColumnNum(2) elif 954 <= self.width() < 1267 and self.columnNum != 3: self.__setColumnNum(3) elif 1267 <= self.width() < 1580 and self.columnNum != 4: self.__setColumnNum(4) elif 1580 <= self.width() < 1893 and self.columnNum != 5: self.__setColumnNum(5) elif self.width() >= 1893 and self.columnNum != 6: self.__setColumnNum(6) def __setColumnNum(self, columnNum: int): """ 设置网格列数 """ self.columnNum = columnNum self.gridLayout.updateColumnNum(columnNum, 298, 288) self.scrollWidget.resize( self.width(), 175 + self.gridLayout.rowCount() * 298 + 120 ) def __sortPlaylist(self, key): """ 排序播放列表 """ self.sortMode = key if key == "modifiedTime": self.sortModeButton.setText("修改时间") self.currentSortAct = self.sortByModifiedTimeAct self.playlistCardDict_list.sort( key=self.__sortPlaylistByModifiedTime, reverse=True ) else: self.sortModeButton.setText("A到Z") self.currentSortAct = self.sortByAToZAct self.playlistCardDict_list.sort( key=self.__sortPlaylistByAToZ, reverse=False ) # 先将小部件布局中移除 self.gridLayout.removeAllWidgets() # 将小部件添加到布局中 for index, playlistCard_dict in enumerate(self.playlistCardDict_list): row = index // self.columnNum column = index - self.columnNum * row playlistCard = playlistCard_dict["playlistCard"] self.gridLayout.addWidget(playlistCard, row, column, Qt.AlignLeft) def __showBlurBackground(self, pos: QPoint, playlistCoverPath: str): """ 显示磨砂背景 """ # 将全局坐标转换为窗口坐标 pos = self.scrollWidget.mapFromGlobal(pos) self.blurBackground.setBlurPic(playlistCoverPath, 40) self.blurBackground.move(pos.x() - 30, pos.y() - 20) self.blurBackground.show() def __showSortModeMenu(self): """ 显示排序方式菜单 """ # 设置默认选中动作 self.sortModeMenu.setDefaultAction(self.currentSortAct) actIndex = self.sortAct_list.index(self.currentSortAct) self.sortModeMenu.exec( self.mapToGlobal( QPoint(self.sender().x(), self.sender().y() - 37 * actIndex - 1) ) ) def __playlistCardCheckedStateChangedSlot( self, playlistCard: PlaylistCard, isChecked: bool ): """ 播放列表卡选中状态改变槽函数 """ # 如果专辑信息不在选中的专辑信息列表中且对应的专辑卡变为选中状态就将专辑信息添加到列表中 if playlistCard not in self.checkedPlaylistCard_list and isChecked: self.checkedPlaylistCard_list.append(playlistCard) self.__checkPlaylistCardNumChangedSlot(len(self.checkedPlaylistCard_list)) # 如果专辑信息已经在列表中且该专辑卡变为非选中状态就弹出该专辑信息 elif playlistCard in self.checkedPlaylistCard_list and not isChecked: self.checkedPlaylistCard_list.pop( self.checkedPlaylistCard_list.index(playlistCard) ) self.__checkPlaylistCardNumChangedSlot(len(self.checkedPlaylistCard_list)) # 如果先前不处于选择模式那么这次发生选中状态改变就进入选择模式 if not self.isInSelectionMode: # 所有专辑卡进入选择模式 self.__setAllPlaylistCardSelectionModeOpen(True) # 发送信号要求主窗口隐藏播放栏 self.selectionModeStateChanged.emit(True) self.selectionModeBar.show() # 更新标志位 self.isInSelectionMode = True else: if not self.checkedPlaylistCard_list: # 所有专辑卡退出选择模式 self.__setAllPlaylistCardSelectionModeOpen(False) # 发送信号要求主窗口显示播放栏 self.selectionModeBar.hide() self.selectionModeStateChanged.emit(False) # 更新标志位 self.isInSelectionMode = False def __setAllPlaylistCardSelectionModeOpen(self, isOpenSelectionMode: bool): """ 设置所有播放列表卡是否进入选择模式 """ for playlistCard in self.playlistCard_list: playlistCard.setSelectionModeOpen(isOpenSelectionMode) # 退出选择模式时开启隐藏所有复选框的动画 if not isOpenSelectionMode: self.__startHideCheckBoxAni() def __startHideCheckBoxAni(self): """ 开始隐藏复选框动画 """ for ani in self.hideCheckBoxAni_list: ani.setStartValue(1) ani.setEndValue(0) ani.setDuration(140) self.hideCheckBoxAniGroup.start() def __hideAllCheckBox(self): """ 隐藏所有复选框 """ for playlistCard in self.playlistCard_list: playlistCard.checkBox.hide() def __unCheckPlaylistCards(self): """ 取消所有已处于选中状态的播放列表卡的选中状态 """ checkedPlaylistCard_list_copy = self.checkedPlaylistCard_list.copy() for playlistCard in checkedPlaylistCard_list_copy: playlistCard.setChecked(False) # 更新按钮的图标为全选 self.selectionModeBar.checkAllButton.setCheckedState(True) def setAllPlaylistCardCheckedState(self, isAllChecked: bool): """ 设置所有的专辑卡checked状态 """ if self.isAllPlaylistCardChecked == isAllChecked: return self.isAllPlaylistCardChecked = isAllChecked for playlistCard in self.playlistCard_list: playlistCard.setChecked(isAllChecked) def __checkPlaylistCardNumChangedSlot(self, num: int): """ 选中的歌曲卡数量改变对应的槽函数 """ self.selectionModeBar.setPartButtonHidden(num > 1) self.selectionModeBar.move(0, self.height() - self.selectionModeBar.height()) def __checkAllButtonSlot(self): """ 全选/取消全选按钮槽函数 """ self.setAllPlaylistCardCheckedState(not self.isAllPlaylistCardChecked) def __sortPlaylistByModifiedTime(self, playlistCard_dict: dict) -> str: return playlistCard_dict["playlist"]["modifiedTime"] def __sortPlaylistByAToZ(self, playlistCard_dict: dict) -> str: return pinyin.get_initial(playlistCard_dict["playlist"]["playlistName"])[ 0 ].lower() def addOnePlaylistCard(self, playlist: dict): """ 添加一个播放列表卡 """ self.__createOnePlaylistCard(playlist) self.playlists.append(playlist) self.guideLabel.hide() # 向布局添加小部件 self.gridLayout.appendWidget(self.playlistCard_list[-1]) self.scrollWidget.resize( self.width(), 175 + self.gridLayout.rowCount() * 298 + 120 ) # 按照当前排序方式重新排序播放列表卡 self.__sortPlaylist(self.sortMode) def __showRenamePlaylistPanel( self, oldPlaylist: dict, playlistCard: PlaylistCard = None ): """ 显示重命名播放列表面板 """ playlistCard = ( self.sender() if not playlistCard else playlistCard ) # type:PlaylistCard renamePlaylistPanel = RenamePlaylistPanel(oldPlaylist, self.window()) renamePlaylistPanel.renamePlaylistSig.connect( lambda oldPlaylist, newPlaylist: self.__renamePlaylistSlot( oldPlaylist, newPlaylist, playlistCard ) ) renamePlaylistPanel.exec() def __renamePlaylistSlot( self, oldPlaylist: dict, newPlaylist: dict, playlistCard: PlaylistCard ): """ 重命名播放列表槽函数 """ playlistCard.updateWindow(newPlaylist) # 重新排序播放列表卡 index = self.playlists.index(oldPlaylist) self.playlists[index] = newPlaylist index = self.getIndexByPlaylist(oldPlaylist) self.playlistCardDict_list[index]["playlist"] = newPlaylist self.__sortPlaylist(self.sortMode) # 发送信号 self.renamePlaylistSig.emit(oldPlaylist, newPlaylist) def __showDeleteCardPanel(self, playlist: dict, playlistCard: PlaylistCard = None): """ 显示删除播放列表卡对话框 """ playlistCard = self.sender() if not playlistCard else playlistCard title = "是否确定要删除此项?" content = f"""如果删除"{playlist['playlistName']}",它将不再位于此设备上。""" deleteCardPanel = DeleteCardPanel(title, content, self.window()) deleteCardPanel.deleteCardSig.connect( lambda: self.__deleteOnePlaylistCard(playlistCard, playlist) ) deleteCardPanel.exec() def __deleteOnePlaylistCard(self, playlistCard: PlaylistCard, playlist: dict): """ 删除一个播放列表卡 """ # 从布局中移除播放列表卡 self.gridLayout.removeWidget(playlistCard) # 从列表中弹出小部件 self.playlists.remove(playlist) self.playlistCard_list.remove(playlistCard) self.playlistCardDict_list.pop(self.getIndexByPlaylistCard(playlistCard)) # 删除播放列表卡 playlistCard.deleteLater() # 调整高度 self.scrollWidget.resize( self.width(), 175 + self.gridLayout.rowCount() * 298 + 120 ) # 删除json文件并发送删除播放列表的信号 remove(f'app\\Playlists\\{playlist["playlistName"]}.json') self.deletePlaylistSig.emit(playlist) # 如果没有专辑卡就显示导航标签 self.guideLabel.setHidden(bool(self.playlistCard_list)) def __deleteMultiPlaylistCards(self, playlistCard_list: list, playlists: list): """ 删除多个播放列表卡 """ for playlistCard, playlist in zip(playlistCard_list, playlists): self.__deleteOnePlaylistCard(playlistCard, playlist) def __emitCheckedPlaylists(self): """ 发送选中的播放列表中的歌曲 """ # 发送播放列表 playlist = [] for playlistCard in self.checkedPlaylistCard_list: playlist.extend(playlistCard.songInfo_list) # 取消所有播放列表卡的选中 self.__unCheckPlaylistCards() if self.sender() is self.selectionModeBar.playButton: self.playSig.emit(playlist) elif self.sender() is self.selectionModeBar.nextToPlayButton: self.nextToPlaySig.emit(playlist) def __selectionBarRenameButtonSlot(self): """ 选择栏重命名按钮的槽函数 """ playlistCard = self.checkedPlaylistCard_list[0] self.__unCheckPlaylistCards() self.__showRenamePlaylistPanel(playlistCard.playlist, playlistCard) def __selectionModeBarDeleteButtonSlot(self): """ 选择栏删除按钮槽函数 """ if len(self.checkedPlaylistCard_list) == 1: playlistCard = self.checkedPlaylistCard_list[0] # 取消所有歌曲卡的选中 self.__unCheckPlaylistCards() self.__showDeleteCardPanel(playlistCard.playlist, playlistCard) else: title = "确定要删除这些项?" content = f"若删除这些播放列表,它们将不再位于此设备上。" playlistCard_list = self.checkedPlaylistCard_list[:] playlists = [playlistCard.playlist for playlistCard in playlistCard_list] # 取消所有歌曲卡的选中 self.__unCheckPlaylistCards() # 显示删除对话框 deleteCardPanel = DeleteCardPanel(title, content, self.window()) deleteCardPanel.deleteCardSig.connect( lambda: self.__deleteMultiPlaylistCards(playlistCard_list, playlists) ) deleteCardPanel.exec() def addSongsToPlaylist(self, playlistName: str, songInfo_list: list) -> dict: """ 将歌曲添加到播放列表中,返回修改后的播放列表 """ # 直接修改播放列表卡字典中的播放列表 index = self.getIndexByPlaylistName(playlistName) playlistCard_dict = self.playlistCardDict_list[index] playlist = playlistCard_dict["playlist"] # 更新播放列表 playlist["modifiedTime"] = QDateTime.currentDateTime().toString(Qt.ISODate) playlist["songInfo_list"] = songInfo_list + playlist["songInfo_list"] playlistCard_dict["playlistCard"].updateWindow(playlist) # 更新json文件 with open(f"app\\Playlists\\{playlistName}.json", "w", encoding="utf-8") as f: dump(playlist, f) return playlist def getIndexByPlaylistName(self, playlistName: str) -> int: """ 通过播放列表名字获取播放列表在播放列表卡字典列表中的下标 """ for index, playlistCard_dict in enumerate(self.playlistCardDict_list): if playlistCard_dict["playlist"]["playlistName"] == playlistName: return index raise Exception(f'指定的播放列表"{playlistName}"不存在') def getIndexByPlaylistCard(self, playlistCard: PlaylistCard) -> int: """ 通过播放列表卡获取播放列表在播放列表卡字典列表中的下标 """ for index, playlistCard_dict in enumerate(self.playlistCardDict_list): if playlistCard_dict["playlistCard"] is playlistCard: return index raise Exception(f'指定的播放列表卡"{playlistCard.playlistName}"不存在') def getIndexByPlaylist(self, playlist: dict) -> int: """ 通过播放列表获取播放列表在播放列表卡字典列表中的下标 """ for index, playlistCard_dict in enumerate(self.playlistCardDict_list): if playlistCard_dict["playlist"] == playlist: return index raise Exception(f"""指定的播放列表"{playlist['playlistName']}"不存在""") def exitSelectionMode(self): """ 退出选择模式 """ self.__unCheckPlaylistCards() def __connectSignalToSlot(self): """ 将信号连接到槽 """ self.sortModeButton.clicked.connect(self.__showSortModeMenu) self.selectionModeBar.cancelButton.clicked.connect(self.__unCheckPlaylistCards) self.selectionModeBar.checkAllButton.clicked.connect(self.__checkAllButtonSlot) self.selectionModeBar.playButton.clicked.connect(self.__emitCheckedPlaylists) self.selectionModeBar.nextToPlayButton.clicked.connect( self.__emitCheckedPlaylists ) self.selectionModeBar.renameButton.clicked.connect( self.__selectionBarRenameButtonSlot ) self.selectionModeBar.deleteButton.clicked.connect( self.__selectionModeBarDeleteButtonSlot )