class StateTooltip(QWidget): """ 进度提示框 """ closedSignal = pyqtSignal() def __init__(self, title="", content="", parent=None): super().__init__(parent) self.title = title self.content = content # 实例化小部件 self.__createWidgets() # 初始化参数 self.isDone = False self.rotateAngle = 0 self.deltaAngle = 18 # 初始化 self.__initWidget() def __createWidgets(self): """ 创建小部件 """ icon_path = { "normal": r"app\resource\images\createPlaylistPanel\stateToolTip_closeBt_normal_14_14.png", "hover": r"app\resource\images\createPlaylistPanel\stateToolTip_closeBt_hover_14_14.png", "pressed": r"app\resource\images\createPlaylistPanel\stateToolTip_closeBt_hover_14_14.png", } 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"app\resource\images\createPlaylistPanel\running_22_22.png" ) self.doneImage = QPixmap( r"app\resource\images\createPlaylistPanel\complete_20_20.png" ) self.closeButton = ThreeStateButton(icon_path, self, (14, 14)) def __initWidget(self): """ 初始化小部件 """ self.setFixedSize(370, 60) self.setWindowFlags(Qt.FramelessWindowHint) self.setAttribute(Qt.WA_StyledBackground) 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.__closeButtonClickedSlot) self.rotateTimer.timeout.connect(self.__rotateTimerFlowSlot) self.closeTimer.timeout.connect(self.__slowlyClose) self.__initLayout() self.__setQss() # 打开定时器 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"app\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 __closeButtonClickedSlot(self): """ 按下关闭按钮前摧毁相关线程 """ self.closedSignal.emit() 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 __rotateTimerFlowSlot(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()函数 """ self.move(self.window().width() - self.width() - 30, 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"app\resource\images\playing_interface\全部随机播放_normal_256_39.png", "hover": r"app\resource\images\playing_interface\全部随机播放_hover_256_39.png", "pressed": r"app\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"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 SubPlayWindow(QWidget): """ 桌面左上角子播放窗口 """ def __init__(self, parent=None, songInfo: dict = None): super().__init__(parent) self.songInfo = {} if songInfo: self.songInfo = songInfo self.__lastSongIconPath = { "normal": r"app\resource\images\sub_play_window\lastSong_50_50_normal.png", "hover": r"app\resource\images\sub_play_window\lastSong_50_50_hover.png", "pressed": r"app\resource\images\sub_play_window\lastSong_50_50_pressed.png", } self.__nextSongIconPath = { "normal": r"app\resource\images\sub_play_window\nextSong_50_50_normal.png", "hover": r"app\resource\images\sub_play_window\nextSong_50_50_hover.png", "pressed": r"app\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"app\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"app\resource\images\createPlaylistPanel\清空按钮_normal_50_50.png", "hover": r"app\resource\images\createPlaylistPanel\清空按钮_hover_50_50.png", "pressed": r"app\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"app\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"app\resource\images\createPlaylistPanel\pencil_noFocus_hover_50_50.png" )) def leaveEvent(self, e): """ 鼠标离开更新样式 """ if self.property("noText") == "true": self.pencilPic.setPixmap( QPixmap( r"app\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"app\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"app\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("app\\resource\\css\\lineEdit.qss", encoding="utf-8") as f: self.setStyleSheet(f.read())
class SearchLineEdit(QLineEdit): """ 单行搜索框 """ def __init__(self, parent=None): super().__init__(parent) # 按钮图标位置 clear_iconPath_dict = { "normal": r"app\resource\images\searchLineEdit\搜索框清空按钮_normal_45_45.png", "hover": r"app\resource\images\searchLineEdit\搜索框清空按钮_hover_45_45.png", "pressed": r"app\resource\images\searchLineEdit\搜索框清空按钮_pressed_45_45.png", } self.__search_iconPath_dict = { "normal": r"app\resource\images\searchLineEdit\搜索框透明搜索按钮_normal_46_45.png", "hover": r"app\resource\images\searchLineEdit\搜索框透明搜索按钮_hover_46_45.png", "pressed": r"app\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())