def __init__(self, parent=None): super().__init__(parent) # 按钮图标位置 clear_iconPath_dict = { 'normal': r'resource\images\searchLineEdit\搜索框清空按钮_normal_45_45.png', 'hover': r'resource\images\searchLineEdit\搜索框清空按钮_hover_45_45.png', 'pressed': r'resource\images\searchLineEdit\搜索框清空按钮_pressed_45_45.png' } self.__search_iconPath_dict = { 'normal': r'resource\images\searchLineEdit\搜索框透明搜索按钮_normal_46_45.png', 'hover': r'resource\images\searchLineEdit\搜索框透明搜索按钮_hover_46_45.png', 'pressed': r'resource\images\searchLineEdit\搜索框搜索按钮_pressed_46_45.png' } # 实例化按钮 self.clearButton = ThreeStateButton(clear_iconPath_dict, self, (46, 45)) self.searchButton = ThreeStateButton(self.__search_iconPath_dict, self, (46, 45)) # 实例化右击菜单 self.menu = LineEditMenu(self) # 初始化界面 self.__initWidget()
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 = ThreeStateButton( { 'normal': r'resource\images\playing_interface\全部随机播放_normal_256_39.png', 'hover': r'resource\images\playing_interface\全部随机播放_hover_256_39.png', 'pressed': r'resource\images\playing_interface\全部随机播放_pressed_256_39.png' }, self, (256, 39)) # 创建定时器 self.showPlaylistTimer = QTimer(self) self.hidePlaylistTimer = QTimer(self) # 初始化 self.__initWidget()
def __init__(self, text='', parent=None): super().__init__(text, parent) iconPath_dict = { 'normal': r'resource\images\createPlaylistPanel\清空按钮_normal_50_50.png', 'hover': r'resource\images\createPlaylistPanel\清空按钮_hover_50_50.png', 'pressed': r'resource\images\createPlaylistPanel\清空按钮_pressed_50_50.png' } # 创建小部件 self.clearButton = ThreeStateButton(iconPath_dict, self, (50, 50)) self.pencilPic = QLabel(self) self.menu = LineEditMenu(self) # 初始化 self.initWidget() self.setQss()
def __init__(self, string=None, parent=None, isNeedClearBt: bool = True): super().__init__(string, parent) self.isNeedClearBt = isNeedClearBt # 设置提示条和鼠标点击次数 self.customToolTip = None self.clickedTime = 0 iconPath_dict = { 'normal': r'resource\images\lineEdit\clearInfo_cross_normal.png', 'hover': r'resource\images\lineEdit\clearInfo_cross_hover.png', 'pressed': r'resource\images\lineEdit\clearInfo_cross_pressed.png' } # 实例化一个用于清空内容的按钮 self.clearButton = ThreeStateButton(iconPath_dict, self) # 实例化右击菜单 self.menu = LineEditMenu(self) # 实例化布局 self.h_layout = QHBoxLayout() 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 = ThreeStateButton( { 'normal': r'resource\images\playlist_card_interface\newPlaylist_normal.png', 'hover': r'resource\images\playlist_card_interface\newPlaylist_hover.png', 'pressed': r'resource\images\playlist_card_interface\newPlaylist_pressed.png' }, self, (129, 19)) # 创建导航标签 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 createWidgets(self): """ 创建小部件 """ icon_path = { 'normal': r'resource\images\createPlaylistPanel\stateToolTip_closeBt_normal_14_14.png', 'hover': r'resource\images\createPlaylistPanel\stateToolTip_closeBt_hover_14_14.png', 'pressed': r'resource\images\createPlaylistPanel\stateToolTip_closeBt_hover_14_14.png' } self.closeButton = ThreeStateButton(icon_path, self, (14, 14)) self.titleLabel = QLabel(self.title, self) self.contentLabel = QLabel(self.content, self) self.rotateTimer = QTimer(self) self.closeTimer = QTimer(self) self.animation = QPropertyAnimation(self, b'windowOpacity') self.busyImage = QPixmap( r'resource\images\createPlaylistPanel\running_22_22.png') self.doneImage = QPixmap( r'resource\images\createPlaylistPanel\complete_20_20.png')
def __init__(self, parent=None, songInfo: dict = None): super().__init__(parent) self.songInfo = {} if songInfo: self.songInfo = songInfo self.__lastSongIconPath = { 'normal': r'resource\images\sub_play_window\lastSong_50_50_normal.png', 'hover': r'resource\images\sub_play_window\lastSong_50_50_hover.png', 'pressed': r'resource\images\sub_play_window\lastSong_50_50_pressed.png' } self.__nextSongIconPath = { 'normal': r'resource\images\sub_play_window\nextSong_50_50_normal.png', 'hover': r'resource\images\sub_play_window\nextSong_50_50_hover.png', 'pressed': r'resource\images\sub_play_window\nextSong_50_50_pressed.png' } # 创建小部件 self.volumeSlider = Slider(Qt.Vertical, self) self.volumeLabel = QLabel(self) self.lastSongButton = ThreeStateButton(self.__lastSongIconPath, self, (50, 50)) self.playButton = PlayButton(self) self.nextSongButton = ThreeStateButton(self.__nextSongIconPath, self, (50, 50)) self.albumPic = QLabel(self) self.songNameLabel = QLabel(self) self.songerNameLabel = QLabel(self) self.ani = QPropertyAnimation(self, b'windowOpacity') self.timer = QTimer(self) # 系统音量控制类 self.systemVolume = SystemVolume() # 初始化 self.__initWidget()
class SearchLineEdit(QLineEdit): """ 单行搜索框 """ def __init__(self, parent=None): super().__init__(parent) # 按钮图标位置 clear_iconPath_dict = { 'normal': r'resource\images\searchLineEdit\搜索框清空按钮_normal_45_45.png', 'hover': r'resource\images\searchLineEdit\搜索框清空按钮_hover_45_45.png', 'pressed': r'resource\images\searchLineEdit\搜索框清空按钮_pressed_45_45.png' } self.__search_iconPath_dict = { 'normal': r'resource\images\searchLineEdit\搜索框透明搜索按钮_normal_46_45.png', 'hover': r'resource\images\searchLineEdit\搜索框透明搜索按钮_hover_46_45.png', 'pressed': r'resource\images\searchLineEdit\搜索框搜索按钮_pressed_46_45.png' } # 实例化按钮 self.clearButton = ThreeStateButton(clear_iconPath_dict, self, (46, 45)) self.searchButton = ThreeStateButton(self.__search_iconPath_dict, self, (46, 45)) # 实例化右击菜单 self.menu = LineEditMenu(self) # 初始化界面 self.__initWidget() def __initWidget(self): """ 初始化小部件 """ self.resize(370, 45) self.clearButton.hide() self.setAttribute(Qt.WA_StyledBackground) # 设置提示文字 self.setPlaceholderText('搜索') self.textChanged.connect(self.textChangedEvent) self.setWindowFlags(Qt.FramelessWindowHint) # 设置外边距 self.setTextMargins(0, 0, self.clearButton.width() + self.searchButton.width(), 0) # 调整按钮位置 self.__adjustButtonPos() # 安装监听 self.clearButton.installEventFilter(self) self.searchButton.installEventFilter(self) def textChangedEvent(self): """ 编辑框的文本改变时选择是否显示清空按钮 """ if self.text(): self.clearButton.show() else: self.clearButton.hide() def __adjustButtonPos(self): """ 调整按钮的位置 """ # 需要补上margin的位置 self.searchButton.move(self.width() - self.searchButton.width() - 8, 0) self.clearButton.move(self.searchButton.x() - self.clearButton.width(), 0) def resizeEvent(self, e): """ 调整大小的同时改变按钮位置 """ self.__adjustButtonPos() def mousePressEvent(self, e): if e.button() != Qt.LeftButton: return # 需要调用父类的鼠标点击事件,不然无法部分选中 super().mousePressEvent(e) # 如果输入框中有文本,就设置为只读并显示清空按钮 if self.text(): self.clearButton.show() def focusOutEvent(self, e): """ 当焦点移到别的输入框时隐藏按钮 """ # 调用父类的函数,消除焦点 super().focusOutEvent(e) self.clearButton.hide() def eventFilter(self, obj, e): """ 过滤事件 """ if obj == self.clearButton: if e.type() == QEvent.MouseButtonRelease and e.button( ) == Qt.LeftButton: self.clear() self.clearButton.hide() return True elif obj == self.searchButton: if e.type() == QEvent.MouseButtonRelease and e.button( ) == Qt.LeftButton: self.searchButton.setIcon( QIcon(self.__search_iconPath_dict['hover'])) return True return super().eventFilter(obj, e) def contextMenuEvent(self, e): """ 设置右击菜单 """ self.menu.exec_(e.globalPos())
class LineEdit(QLineEdit): """ 包含清空按钮的单行输入框 """ def __init__(self, string=None, parent=None, isNeedClearBt: bool = True): super().__init__(string, parent) self.isNeedClearBt = isNeedClearBt # 设置提示条和鼠标点击次数 self.customToolTip = None self.clickedTime = 0 iconPath_dict = { 'normal': r'resource\images\lineEdit\clearInfo_cross_normal.png', 'hover': r'resource\images\lineEdit\clearInfo_cross_hover.png', 'pressed': r'resource\images\lineEdit\clearInfo_cross_pressed.png' } # 实例化一个用于清空内容的按钮 self.clearButton = ThreeStateButton(iconPath_dict, self) # 实例化右击菜单 self.menu = LineEditMenu(self) # 实例化布局 self.h_layout = QHBoxLayout() self.initWidget() def initWidget(self): """ 初始化小部件 """ self.resize(500, 40) self.clearButton.hide() self.textChanged.connect(self.textChangedEvent) self.clearButton.move(self.width() - self.clearButton.width(), 0) if self.isNeedClearBt: self.setTextMargins(0, 0, self.clearButton.width(), 0) # 安装事件过滤器 self.clearButton.installEventFilter(self) def mousePressEvent(self, e): if e.button() == Qt.LeftButton: # 如果已经全选了再次点击就取消全选 if self.clickedTime == 0: self.selectAll() else: # 需要调用父类的鼠标点击事件,不然无法部分选中 super().mousePressEvent(e) self.setFocus() # 如果输入框中有文本,就设置为只读并显示清空按钮 if self.text() and self.isNeedClearBt: self.clearButton.show() self.clickedTime += 1 def contextMenuEvent(self, e: QContextMenuEvent): """ 设置右击菜单 """ self.menu.exec_(e.globalPos()) def focusOutEvent(self, e): """ 当焦点移到别的输入框时隐藏按钮 """ # 调用父类的函数,消除焦点 super().focusOutEvent(e) self.clickedTime = 0 self.clearButton.hide() def enterEvent(self, e): """ 鼠标进入时显示提示条 """ if self.customToolTip: self.customToolTip.setText(self.customToolTipText) self.customToolTip.move( e.globalX() - int(self.customToolTip.width() / 2), e.globalY() - 140 - self.customToolTip.isWordWrap * 30) self.customToolTip.show() def leaveEvent(self, e): """ 判断鼠标是否离开标签 """ if self.parent() and self.customToolTip: notLeave = isNotLeave(self) if notLeave: return self.customToolTip.hide() def setCustomToolTip(self, toolTip, text: str): """ 设置提示条和提示条内容 """ self.customToolTip = toolTip self.customToolTipText = text def textChangedEvent(self): """ 如果输入框中文本改变且此时清空按钮不可见,就显示清空按钮 """ if self.text( ) and not self.clearButton.isVisible() and self.isNeedClearBt: self.clearButton.show() def resizeEvent(self, e): """ 改变大小时需要移动按钮的位置 """ self.clearButton.move(self.width() - self.clearButton.width(), 0) def eventFilter(self, obj, e): """ 清空按钮按下时清空内容并隐藏按钮 """ if obj == self.clearButton: if e.type() == QEvent.MouseButtonRelease and e.button( ) == Qt.LeftButton: self.clear() self.clearButton.hide() return True return super().eventFilter(obj, e)
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 = ThreeStateButton( { 'normal': r'resource\images\playlist_card_interface\newPlaylist_normal.png', 'hover': r'resource\images\playlist_card_interface\newPlaylist_hover.png', 'pressed': r'resource\images\playlist_card_interface\newPlaylist_pressed.png' }, self, (129, 19)) # 创建导航标签 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.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'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'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'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)
class SubPlayWindow(QWidget): """ 桌面左上角子播放窗口 """ def __init__(self, parent=None, songInfo: dict = None): super().__init__(parent) self.songInfo = {} if songInfo: self.songInfo = songInfo self.__lastSongIconPath = { 'normal': r'resource\images\sub_play_window\lastSong_50_50_normal.png', 'hover': r'resource\images\sub_play_window\lastSong_50_50_hover.png', 'pressed': r'resource\images\sub_play_window\lastSong_50_50_pressed.png' } self.__nextSongIconPath = { 'normal': r'resource\images\sub_play_window\nextSong_50_50_normal.png', 'hover': r'resource\images\sub_play_window\nextSong_50_50_hover.png', 'pressed': r'resource\images\sub_play_window\nextSong_50_50_pressed.png' } # 创建小部件 self.volumeSlider = Slider(Qt.Vertical, self) self.volumeLabel = QLabel(self) self.lastSongButton = ThreeStateButton(self.__lastSongIconPath, self, (50, 50)) self.playButton = PlayButton(self) self.nextSongButton = ThreeStateButton(self.__nextSongIconPath, self, (50, 50)) self.albumPic = QLabel(self) self.songNameLabel = QLabel(self) self.songerNameLabel = QLabel(self) self.ani = QPropertyAnimation(self, b'windowOpacity') self.timer = QTimer(self) # 系统音量控制类 self.systemVolume = SystemVolume() # 初始化 self.__initWidget() def __initWidget(self): """ 初始化小部件 """ self.resize(635, 175) self.__initLayout() self.setAttribute(Qt.WA_TranslucentBackground) self.setWindowFlags(Qt.FramelessWindowHint | Qt.Window | Qt.WindowStaysOnTopHint) # 初始化音量滑块 self.volumeSlider.setRange(0, 100) self.volumeSlider.setSingleStep(1) # 初始化动画和定时器 self.timer.setInterval(3000) self.ani.setEasingCurve(QEasingCurve.Linear) self.ani.setDuration(700) self.ani.setStartValue(1) self.ani.setEndValue(0) # 分配ID self.volumeLabel.setObjectName('volumeLabel') self.songNameLabel.setObjectName('songNameLabel') self.songerNameLabel.setObjectName('songerNameLabel') # 设置层叠样式 self.__setQss() # 将信号连接到槽 self.__connectSignalToSlot() self.volumeSlider.setValue(self.systemVolume.getVolume()) # 设置封面和标签 self.updateWindow(self.songInfo) # 引用方法 self.setPlay = self.playButton.setPlay def __initLayout(self): """ 初始化布局 """ self.move(62, 75) self.albumPic.move(478, 25) self.playButton.move(222, 26) self.volumeLabel.move(32, 140) self.volumeSlider.move(34, 25) self.songNameLabel.move(122, 93) self.lastSongButton.move(122, 26) self.nextSongButton.move(322, 26) self.songerNameLabel.move(122, 135) self.albumPic.setFixedSize(125, 125) self.songNameLabel.setFixedWidth(285) self.songerNameLabel.setFixedWidth(290) self.volumeLabel.setFixedWidth(65) def updateWindow(self, songInfo: dict): """ 设置窗口内容 """ self.songInfo = songInfo self.songName = self.songInfo.get('songName', '未知歌曲') self.songerName = self.songInfo.get('songer', '未知歌手') # 调整长度 self.__adjustText() # 更新标签和专辑封面 self.songNameLabel.setText(self.songName) self.songerNameLabel.setText(self.songerName) self.__setAlbumCover() def timerSlot(self): """ 定时器溢出时间 """ self.timer.stop() self.ani.start() def enterEvent(self, e): """ 鼠标进入时停止动画并重置定时器 """ self.timer.stop() if self.ani.state() == QAbstractAnimation.Running: self.ani.stop() self.setWindowOpacity(1) def leaveEvent(self, e): """ 鼠标离开窗口时打开计时器 """ # 判断事件发生的位置发生在自己所占的rect内 notLeave = isNotLeave(self) if not notLeave: self.timer.start() def show(self): """ show()时重置透明度并根据鼠标位置决定是否打开计时器 """ self.setWindowOpacity(1) self.volumeSlider.setValue(self.systemVolume.getVolume()) super().show() notLeave = isNotLeave(self) if not notLeave: self.timer.start() def paintEvent(self, e): """ 绘制背景色 """ painter = QPainter(self) painter.setRenderHints(QPainter.Antialiasing) painter.setPen(Qt.NoPen) brush = QBrush(Qt.black) painter.setBrush(brush) # 绘制音量滑动条的背景 painter.drawRect(0, 0, 81, 175) # 绘制控制面板的背景 painter.drawRect(86, 0, 549, 175) def __connectSignalToSlot(self): """ 将信号连接到槽 """ self.ani.finished.connect(self.hide) self.timer.timeout.connect(self.timerSlot) self.volumeSlider.valueChanged.connect(self.sliderValueChangedSlot) def __setQss(self): """ 设置层叠样式 """ with open(r'resource\css\subPlayWindow.qss', encoding='utf-8') as f: self.setStyleSheet(f.read()) def __setAlbumCover(self): """ 设置封面 """ # 如果专辑信息为空就直接隐藏 self.coverPath = getCoverPath(self.songInfo.get('modifiedAlbum')) self.albumPic.setPixmap( QPixmap(self.coverPath).scaled(125, 125, Qt.KeepAspectRatio, Qt.SmoothTransformation)) def __adjustText(self): """ 根据文本长度决定是否显示省略号 """ fontMetrics_1 = QFontMetrics(QFont('Microsoft YaHei', 17, 63)) self.songName = fontMetrics_1.elidedText(self.songName, Qt.ElideRight, 285) fontMetrics_2 = QFontMetrics(QFont('Microsoft YaHei', 9)) self.songerName = fontMetrics_2.elidedText(self.songerName, Qt.ElideRight, 290) def __adjustVolumeLabelPos(self): """ 调整音量标签的位置 """ if 10 <= int(self.volumeLabel.text()) <= 99: self.volumeLabel.move(32, 140) elif 0 <= int(self.volumeLabel.text()) <= 9: self.volumeLabel.move(37, 140) else: self.volumeLabel.move(27, 140) def sliderValueChangedSlot(self, value): """ 音量改变时调整系统音量并更新标签 """ self.volumeLabel.setText(str(value)) self.__adjustVolumeLabelPos() self.systemVolume.setVolume(value)
class LineEdit(QLineEdit): """ 编辑框 """ def __init__(self, text='', parent=None): super().__init__(text, parent) iconPath_dict = { 'normal': r'resource\images\createPlaylistPanel\清空按钮_normal_50_50.png', 'hover': r'resource\images\createPlaylistPanel\清空按钮_hover_50_50.png', 'pressed': r'resource\images\createPlaylistPanel\清空按钮_pressed_50_50.png' } # 创建小部件 self.clearButton = ThreeStateButton(iconPath_dict, self, (50, 50)) self.pencilPic = QLabel(self) self.menu = LineEditMenu(self) # 初始化 self.initWidget() self.setQss() def initWidget(self): """ 初始化小部件 """ self.setFixedSize(484, 70) self.adjustButtonPos() self.textChanged.connect(self.textChangedEvent) self.setObjectName('createPlaylistPanelLineEdit') # 初始化按钮 self.clearButton.hide() self.clearButton.installEventFilter(self) self.pencilPic.setPixmap( QPixmap(r'resource\images\createPlaylistPanel\pencil_50_50.png')) # 设置文字的外间距,防止文字和文本重叠 self.setTextMargins( 0, 0, self.clearButton.width() + self.pencilPic.pixmap().width() + 1, 0) def textChangedEvent(self): """ 编辑框的文本改变时选择是否显示清空按钮 """ if self.text(): self.clearButton.show() else: self.clearButton.hide() def enterEvent(self, e): """ 鼠标进入更新样式 """ if self.property('noText') == 'true': self.pencilPic.setPixmap( QPixmap( r'resource\images\createPlaylistPanel\pencil_noFocus_hover_50_50.png' )) def leaveEvent(self, e): """ 鼠标离开更新样式 """ if self.property('noText') == 'true': self.pencilPic.setPixmap( QPixmap( r'resource\images\createPlaylistPanel\pencil_noFocus_50_50.png' )) def focusOutEvent(self, e): """ 当焦点移到别的输入框时隐藏按钮 """ super().focusOutEvent(e) if not self.text(): self.setProperty('noText', 'true') self.setStyle(QApplication.style()) self.setText(' 命名此播放列表') self.clearButton.hide() self.pencilPic.setPixmap( QPixmap( r'resource\images\createPlaylistPanel\pencil_noFocus_50_50.png' )) def focusInEvent(self, e): """ 焦点进入时更换样式并取消提示文字 """ super().focusInEvent(e) # 必须有判断的一步,不然每次右击菜单执行完都会触发focusInEvent()导致菜单功能混乱 if self.property('noText') == 'true': self.clear() self.setProperty('noText', 'false') self.setStyle(QApplication.style()) self.pencilPic.setPixmap( QPixmap(r'resource\images\createPlaylistPanel\pencil_50_50.png')) def mousePressEvent(self, e): """ 鼠标点击事件 """ if e.button() == Qt.LeftButton: # 需要调用父类的鼠标点击事件,不然无法部分选中 super().mousePressEvent(e) # 如果输入框中有文本,就设置为只读并显示清空按钮 if self.text(): self.clearButton.show() def contextMenuEvent(self, e): """ 设置右击菜单 """ self.menu.exec_(e.globalPos()) def resizeEvent(self, e): """ 调整大小的同时改变按钮位置 """ self.adjustButtonPos() def eventFilter(self, obj, e): """ 过滤事件 """ if obj == self.clearButton: if e.type() == QEvent.MouseButtonRelease and e.button( ) == Qt.LeftButton: self.clear() self.clearButton.hide() return True return super().eventFilter(obj, e) def adjustButtonPos(self): """ 调整按钮的位置 """ self.clearButton.move(self.width() - 101, 10) self.pencilPic.move(self.width() - 51, 10) def setQss(self): """ 设置层叠样式 """ with open('resource\\css\\lineEdit.qss', encoding='utf-8') as f: self.setStyleSheet(f.read())
class StateToolTip(QWidget): """ 进度提示框 """ def __init__(self, title='', content='', associatedThread=None, parent=None): super().__init__(parent) self.title = title self.content = content self.associatedThread = associatedThread # 实例化小部件 self.createWidgets() # 初始化参数 self.isDone = False self.rotateAngle = 0 self.deltaAngle = 18 # 初始化 self.initWidget() self.initLayout() self.setQss() def createWidgets(self): """ 创建小部件 """ icon_path = { 'normal': r'resource\images\createPlaylistPanel\stateToolTip_closeBt_normal_14_14.png', 'hover': r'resource\images\createPlaylistPanel\stateToolTip_closeBt_hover_14_14.png', 'pressed': r'resource\images\createPlaylistPanel\stateToolTip_closeBt_hover_14_14.png' } self.closeButton = ThreeStateButton(icon_path, self, (14, 14)) self.titleLabel = QLabel(self.title, self) self.contentLabel = QLabel(self.content, self) self.rotateTimer = QTimer(self) self.closeTimer = QTimer(self) self.animation = QPropertyAnimation(self, b'windowOpacity') self.busyImage = QPixmap( r'resource\images\createPlaylistPanel\running_22_22.png') self.doneImage = QPixmap( r'resource\images\createPlaylistPanel\complete_20_20.png') def initWidget(self): """ 初始化小部件 """ self.setFixedSize(370, 60) self.setWindowFlags(Qt.FramelessWindowHint | Qt.Window) self.rotateTimer.setInterval(50) self.closeTimer.setInterval(3000) self.contentLabel.setMinimumWidth(200) # 分配ID self.titleLabel.setObjectName('titleLabel') self.contentLabel.setObjectName('contentLabel') # 将信号连接到槽函数 self.closeButton.clicked.connect(self.closeButtonEvent) self.rotateTimer.timeout.connect(self.timeOutEvent) self.closeTimer.timeout.connect(self.slowlyClose) # 打开定时器 self.rotateTimer.start() def initLayout(self): """ 初始化布局 """ self.titleLabel.move(39, 11) self.contentLabel.move(15, 34) self.closeButton.move(self.width() - 29, 23) def setQss(self): """ 设置层叠样式 """ with open(r'resource\css\stateToolTip.qss', encoding='utf-8') as f: self.setStyleSheet(f.read()) def setTitle(self, title): """ 更新提示框的标题 """ self.title = title self.titleLabel.setText(title) def setContent(self, content): """ 更新提示框内容 """ self.content = content self.contentLabel.setText(content) def setState(self, isDone=False): """ 设置运行状态 """ self.isDone = isDone self.update() # 运行完成后主动关闭窗口 if self.isDone: self.closeTimer.start() def closeButtonEvent(self): """ 按下关闭按钮前摧毁相关线程 """ if self.associatedThread: self.associatedThread.stop() self.associatedThread.requestInterruption() self.associatedThread.wait() self.deleteLater() def slowlyClose(self): """ 缓慢关闭窗口 """ self.rotateTimer.stop() self.animation.setEasingCurve(QEasingCurve.Linear) self.animation.setDuration(500) self.animation.setStartValue(1) self.animation.setEndValue(0) self.animation.finished.connect(self.deleteLater) self.animation.start() def timeOutEvent(self): """ 定时器溢出时旋转箭头 """ self.rotateAngle = (self.rotateAngle + self.deltaAngle) % 360 self.update() def paintEvent(self, QPaintEvent): """ 绘制背景 """ super().paintEvent(QPaintEvent) # 绘制旋转箭头 painter = QPainter(self) painter.setRenderHints(QPainter.SmoothPixmapTransform) painter.setPen(Qt.NoPen) if not self.isDone: painter.translate(24, 20) # 原点平移到旋转中心 painter.rotate(self.rotateAngle) # 坐标系旋转 painter.drawPixmap(-int(self.busyImage.width() / 2), -int(self.busyImage.height() / 2), self.busyImage) else: painter.drawPixmap(14, 13, self.doneImage.width(), self.doneImage.height(), self.doneImage) def show(self): """ 重写show()函数 """ if self.parent(): self.move( self.parent().x() + self.parent().width() - self.width() - 30, self.parent().y() + 70) super().show()
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 = ThreeStateButton( { 'normal': r'resource\images\playing_interface\全部随机播放_normal_256_39.png', 'hover': r'resource\images\playing_interface\全部随机播放_hover_256_39.png', 'pressed': r'resource\images\playing_interface\全部随机播放_pressed_256_39.png' }, self, (256, 39)) # 创建定时器 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.__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'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 mousePressEvent(self, e: QMouseEvent): """ 鼠标点击界面其他位置时隐藏音量条 """ condX = 166 < e.pos().x() <= self.playBar.volumeSlider.width() + 166 condY = self.playBar.y() <= e.pos().y() <= self.playBar.y() + \ self.playBar.volumeSlider.height() if not (condX and condY): self.playBar.volumeSlider.hide() 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 : 播放列表,每一个元素都是songInfo字典\n isResetIndex : 是否将下标重置为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)