class Music(QMainWindow): def __init__(self, parent=None): super().__init__() uic.loadUi(QDir.current().absoluteFilePath('music.ui'), self) #print(QDir.current().absoluteFilePath('music.ui')) self.player = QMediaPlayer() self.playlist = QMediaPlaylist() self.player.setPlaylist(self.playlist) self.playMusicBtn.clicked.connect(self.player.play) self.pauseMusicBtn.clicked.connect(self.player.pause) self.stopMusicBtn.clicked.connect(self.player.stop) self.nextMusicBtn.clicked.connect(self.playlist.next) self.previousMusicButton.clicked.connect(self.playlist.previous) self.openFilesBtn.clicked.connect(self.openFiles) self.openDirectoryBtn.clicked.connect(self.openDir) self.playlist.currentMediaChanged.connect(self.updateLabels) self.show() def buildPlayList(self, fileNames): for name in fileNames: print(name) url = QUrl.fromLocalFile(name) self.playlist.addMedia(QMediaContent(url)) #self.playlist.setPlaybackMode(QMediaPlaylist.Loop) def openFiles(self): #fileNames, _ = QFileDialog.getOpenFileNames(self, "Open Files") music = QStandardPaths.writableLocation(QStandardPaths.MusicLocation) path, _ = QFileDialog.getOpenFileName( self, "Open file", directory=music, options=QFileDialog.DontUseNativeDialog) print(type(path)) print(path) self.playlist.addMedia(QMediaContent(QUrl.fromLocalFile(path))) print(type(path)) print(path) def openDir(self): #dir _ = QtGui.QFileDialog.getExistingDirectory(None, 'Select a directory:', 'C:\\', QtGui.QFileDialog.ShowDirsOnly) home = QStandardPaths.writableLocation(QStandardPaths.HomeLocation) d = QFileDialog.getExistingDirectory( caption="Choose Directory", directory=home, options=QFileDialog.DontUseNativeDialog) #print(x) files = os.listdir(d) #print(type(files)) for i in files: path = os.path.join(d, i) self.playlist.addMedia(QMediaContent(QUrl.fromLocalFile(path))) #print('{} {}'.format(path, type(i))) #print(os.listdir(path=x)) #self.buildPlayList(os.listdir(path=x)) songCount = self.playlist.mediaCount() self.songsLoadedLbl.setText('Songs Loaded = {}'.format(songCount)) def updateLabels(self, media=None): #print(media) self.statusBar().showMessage(str(media.canonicalUrl().fileName()))
class Start(QMainWindow): def __init__(self): super(Start, self).__init__() self.titles = "Media Player" self.left = 500 self.top = 300 self.width = 400 self.height = 200 self.window_main() self.adding_menus() def openMultipleFile(self): dialogs = QFileDialog(self) self.fnames, _ = dialogs.getOpenFileNames( self, 'Open Media Files', QDir.homePath(), "Videos (*.mp4 *.mkv *.3pg)") if self.fnames != '': self.playlist = QMediaPlaylist(self) self.fnamelist = [] for playlst in self.fnames: self.fnamelist.append( QMediaContent(QUrl.fromLocalFile(playlst))) self.playlist.addMedia(self.fnamelist) self.playlist.setCurrentIndex(1) self.videoWidget = QVideoWidget(self) self.mediaPlayer.setVideoOutput(self.videoWidget) # self.videoWidget.setAspectRatioMode(60, 60,Qt.KeepAspectRatioByExpanding) self.mediaPlayer.setPlaylist(self.playlist) self.playlist.currentIndexChanged.connect(self.mediaNameChange) self.mediaPlayer.play() self.play.setIcon(self.style().standardIcon(QStyle.SP_MediaPause)) self.play.setEnabled(True) self.stop.setEnabled(True) self.loop.setEnabled(True) if (len(self.fnamelist) > 1): self.forw.setEnabled(True) self.shuffl.setEnabled(True) self.l1.setText("00:00") mediaName = self.fnames[0].rsplit('/', 1)[-1] self.fulltitle = mediaName + " - " + self.titles self.setWindowTitle(self.fulltitle) self.mediaPlayer.durationChanged.connect(self.sliderDuration) def openFile(self): self.fname, _ = QFileDialog.getOpenFileName( self, 'Open Media Files', QDir.homePath(), "Videos (*.mp4 *.mkv *.3pg)") if self.fname != '': mediaName = self.fname.rsplit('/', 1)[-1] self.fulltitle = mediaName + " - " + self.titles self.setWindowTitle(self.fulltitle) self.playlist = QMediaPlaylist(self) self.playlist.addMedia( QMediaContent(QUrl.fromLocalFile(self.fname))) self.playlist.setCurrentIndex(1) self.mediaPlayer.setPlaylist(self.playlist) self.playlist.currentIndexChanged.connect(self.mediaNameChange) self.mediaPlayer.play() self.play.setIcon(self.style().standardIcon(QStyle.SP_MediaPause)) self.play.setEnabled(True) self.stop.setEnabled(True) self.loop.setEnabled(True) self.l1.setText("00:00") self.mediaPlayer.durationChanged.connect(self.sliderDuration) def window_main(self): self.setWindowTitle(self.titles) qw = QWidget() self.setGeometry(self.left, self.top, qw.maximumWidth(), qw.maximumHeight()) self.setMinimumSize(540, 0) self.setWindowIcon(QIcon("mediaplayer.png")) self.video() self.show() def sliderChanged(self, position): pos = position * 1000 self.mediaPlayer.setPosition(pos) self.slider.setValue(position) def adding_menus(self): menu = Allmenu(self) def volumeChange(self, vol): self.mediaPlayer.setVolume(vol) def sliderDuration(self, duratn): milisec = self.mediaPlayer.duration() sec = int(milisec / 1000) hour = int(sec / 3600) min = int((sec / 60) - (hour * 60)) secs = int(sec - (min * 60) - (hour * 60 * 60)) self.l2.setText(str(hour) + ":" + str(min) + ":" + str(secs)) self.slider.setMaximum(sec) def sliderDuration2(self, duratn): second = int(duratn / 1000) self.slider.setValue(second) hour = int(second / 3600) min = int((second / 60) - (hour * 60)) secs = int(second - (min * 60) - (hour * 60 * 60)) if (min < 10): min = "0" + str(min) else: min = str(min) if (secs < 10): secs = "0" + str(secs) else: secs = str(secs) if (hour == 0): self.l1.setText(min + ":" + secs) else: self.l1.setText(str(hour) + ":" + min + ":" + secs) def mediaNameChange(self, index): mediaName = self.fnames[index].rsplit('/', 1)[-1] self.fulltitle = mediaName + " - " + self.titles self.setWindowTitle(self.fulltitle) if (self.playlist.playbackMode() == 4): self.forw.setEnabled(True) self.back.setEnabled(True) else: if ((index + 1) == self.playlist.mediaCount()): self.forw.setEnabled(False) self.back.setEnabled(True) else: self.back.setEnabled(True) def video(self): self.mediaPlayer = QMediaPlayer(None, QMediaPlayer.VideoSurface) self.mediaPlayer.positionChanged.connect(self.sliderDuration2) self.mediaPlayer.setVolume(10) videoWidget = QVideoWidget() layout = QVBoxLayout() wid = QWidget(self) self.play = QPushButton() self.play.setEnabled(False) self.play.setFixedWidth(40) self.play.setFixedHeight(30) self.play.setIcon(self.style().standardIcon(QStyle.SP_MediaPlay)) self.play.setIconSize(QSize(20, 20)) self.play.clicked.connect(self.playAction) self.play.setShortcut(QKeySequence("Space")) self.back = QPushButton() self.back.setEnabled(False) self.back.setFixedWidth(40) self.back.setFixedHeight(25) self.back.setStyleSheet("margin-left: 10px") self.back.setIcon(self.style().standardIcon( QStyle.SP_MediaSeekBackward)) self.back.setIconSize(QSize(14, 14)) self.back.clicked.connect(self.prevAction) self.back.setShortcut(QKeySequence("Ctrl+b")) self.stop = QPushButton() self.stop.setEnabled(False) self.stop.setFixedWidth(40) self.stop.setFixedHeight(25) self.stop.setStyleSheet("margin-left: 0px") self.stop.setIcon(self.style().standardIcon(QStyle.SP_MediaStop)) self.stop.setIconSize(QSize(14, 14)) self.stop.clicked.connect(self.stopAction) self.stop.setShortcut(QKeySequence("s")) self.forw = QPushButton() self.forw.setEnabled(False) self.forw.setFixedWidth(40) self.forw.setFixedHeight(25) self.forw.setStyleSheet("margin-left: 0px") self.forw.setIcon(self.style().standardIcon( QStyle.SP_MediaSeekForward)) self.forw.setIconSize(QSize(14, 14)) self.forw.clicked.connect(self.forwAction) self.forw.setShortcut(QKeySequence("Ctrl+f")) self.loop = QPushButton() self.loop.setEnabled(False) self.loop.setFixedWidth(40) self.loop.setFixedHeight(25) self.loop.setStyleSheet("margin-left: 10px") self.loop.setIcon(QIcon(QPixmap("loop.svg"))) self.loop.setIconSize(QSize(14, 14)) self.loop.clicked.connect(self.loopAction) self.loop.setShortcut(QKeySequence("Ctrl+l")) self.shuffl = QPushButton() self.shuffl.setEnabled(False) self.shuffl.setFixedHeight(25) self.shuffl.setStyleSheet("margin-left: 0px") self.shuffl.setFixedWidth(40) self.shuffl.setFixedHeight(25) self.shuffl.setStyleSheet("margin-left: 0px") self.shuffl.setIcon(QIcon(QPixmap("shuffl.svg"))) self.shuffl.setIconSize(QSize(14, 14)) self.shuffl.clicked.connect(self.shufflAction) self.shuffl.setShortcut(QKeySequence("Ctrl+shift+s")) spacer = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) self.volume = QDial() self.volume.setFixedWidth(40) self.volume.setFixedHeight(40) self.volume.setMaximum(100) self.volume.setMinimum(0) self.volume.setToolTip("Volume") self.volume.valueChanged.connect(self.volumeChange) hlayout = QHBoxLayout() hlayout.addWidget(self.play) hlayout.addWidget(self.back) hlayout.addWidget(self.stop) hlayout.addWidget(self.forw) hlayout.addWidget(self.loop) hlayout.addWidget(self.shuffl) hlayout.addItem(spacer) hlayout.addWidget(self.volume) hslayout = QHBoxLayout() self.slider = QSlider(Qt.Horizontal) self.slider.setMinimum(0) self.slider.setMaximum(0) self.l1 = QLabel() self.l1.setText("--:--:--") self.l2 = QLabel() self.l2.setText("--:--:--") self.slider.sliderMoved.connect(self.sliderChanged) hslayout.addWidget(self.l1) hslayout.addWidget(self.slider) hslayout.addWidget(self.l2) layout.addWidget(videoWidget) layout.addLayout(hslayout) layout.addLayout(hlayout) wid.setLayout(layout) self.setCentralWidget(wid) self.mediaPlayer.setVideoOutput(videoWidget) def playAction(self): if (self.mediaPlayer.state() == 1): self.mediaPlayer.pause() self.play.setIcon(self.style().standardIcon(QStyle.SP_MediaPlay)) elif (self.mediaPlayer.state() == 2): self.mediaPlayer.play() self.play.setIcon(self.style().standardIcon(QStyle.SP_MediaPause)) else: self.back.setEnabled(False) self.play.setIcon(self.style().standardIcon(QStyle.SP_MediaPlay)) def stopAction(self): self.mediaPlayer.stop() self.play.setEnabled(False) self.play.setIcon(self.style().standardIcon(QStyle.SP_MediaPlay)) self.setWindowTitle(self.titles) self.l1.setText("--:--:--") self.l2.setText("--:--:--") def forwAction(self): if (self.playlist.playbackMode() == 4): self.forw.setEnabled(True) self.back.setEnabled(True) indexes = random.randint(0, (self.playlist.mediaCount() - 1)) self.playlist.setCurrentIndex(indexes) elif (self.playlist.playbackMode() == 1): self.playlist.next() else: print(self.playlist.currentIndex()) if ((self.playlist.currentIndex() + 2) == self.playlist.mediaCount()): self.forw.setEnabled(False) self.playlist.next() self.back.setEnabled(True) else: self.playlist.next() self.back.setEnabled(True) def prevAction(self): if (self.playlist.playbackMode() == 4): self.forw.setEnabled(True) self.back.setEnabled(True) indexes = random.randint(0, (self.playlist.mediaCount() - 1)) self.playlist.setCurrentIndex(indexes) elif (self.playlist.playbackMode() == 1): self.playlist.previous() else: if (self.playlist.currentIndex() == 1): self.forw.setEnabled(True) self.playlist.previous() self.back.setEnabled(False) else: self.playlist.previous() self.forw.setEnabled(True) def loopAction(self): if (self.playlist.playbackMode() != 1): self.playlist.setPlaybackMode(QMediaPlaylist.CurrentItemInLoop) self.loop.setIcon(QIcon(QPixmap("greenloop.svg"))) self.shuffl.setIcon(QIcon(QPixmap("shuffl.svg"))) else: self.playlist.setPlaybackMode(QMediaPlaylist.Sequential) self.loop.setIcon(QIcon(QPixmap("loop.svg"))) def shufflAction(self): if (self.playlist.playbackMode() != 4): self.playlist.setPlaybackMode(QMediaPlaylist.Random) self.shuffl.setIcon(QIcon(QPixmap("greenshuffl.svg"))) self.loop.setIcon(QIcon(QPixmap("loop.svg"))) else: self.playlist.setPlaybackMode(QMediaPlaylist.Sequential) self.shuffl.setIcon(QIcon(QPixmap("shuffl.svg"))) def close(self): sys.exit(1)
class Window(QtWidgets.QMainWindow, Ui_MainWindow): def __init__(self, parent=None): super().__init__(parent) # load config self.data = yaml_loader() # load ui self.setupUi(self) # load icons self.setWindowTitle("Sputofy") self.setWindowIcon(QIcon(os.path.join(RES_PATH, "logo.svg"))) loopIcon = QIcon() loopIcon.addPixmap(QPixmap(os.path.join(RES_PATH, "loopIconOFF.svg"))) self.loopBtn.setIcon(loopIcon) prevIcon = QIcon() prevIcon.addPixmap(QPixmap(os.path.join(RES_PATH, "backwardIcon.svg"))) self.prevBtn.setIcon(prevIcon) playIcon = QIcon() playIcon.addPixmap(QPixmap(os.path.join(RES_PATH, "playIcon.svg"))) self.playBtn.setIcon(playIcon) nextIcon = QIcon() nextIcon.addPixmap(QPixmap(os.path.join(RES_PATH, "forwardIcon.svg"))) self.nextBtn.setIcon(nextIcon) randomIcon = QIcon() randomIcon.addPixmap( QPixmap(os.path.join(RES_PATH, "randomIconOFF.svg"))) self.randomBtn.setIcon(randomIcon) volumeIcon = QIcon() volumeIcon.addPixmap(QPixmap(os.path.join(RES_PATH, "volumeIcon.svg"))) self.volumeBtn.setIcon(volumeIcon) # window's settings self.xCor = self.data['last_position']['xPos'] self.yCor = self.data['last_position']['yPos'] self.widthSize = self.data['last_window_size']['width'] self.heightSize = self.data['last_window_size']['height'] self.setGeometry(self.xCor, self.yCor, self.widthSize, self.heightSize) # load YouTubeToMP3 self.YouTubeToMP3 = YouTubeToMP3Window() # open YouTubeToMP3 using button self.actionYT_MP3.triggered.connect(self.YouTubeToMP3.show_window) # info action self.actionInfo.triggered.connect(self.info_handle) #=========================== mediaplayer ============================== # create media player object self.mediaPlayer = QMediaPlayer(None) # open button self.actionOpen_Song.triggered.connect(self.open_song) self.actionOpen_Folder.triggered.connect(self.open_folder) # play button self.playBtn.setEnabled(False) self.playBtn.clicked.connect( self.play_video ) # when btn is pressed: if it is playing it pause, if it is paused it plays # QShortcut(QKeySequence("Space"), self).activated.connect(self.play_video)metodo da ricordare in caso di problemi #TODO # duration slider self.durationSlider.setEnabled(False) self.durationSliderMaxValue = 0 self.durationSlider.valueChanged.connect( self.mediaPlayer.setPosition ) # set mediaPlayer position using the value took from the slider QShortcut('Right', self, lambda: self.durationSlider.setValue( self.durationSlider.value() + 10000)) # 1s = 1000ms QShortcut('Left', self, lambda: self.durationSlider.setValue( self.durationSlider.value() - 10000)) # 1s = 1000ms QShortcut('Shift+Right', self, lambda: self.durationSlider.setValue( self.durationSliderMaxValue - 1000)) # jump to the end-1s of song QShortcut('Shift+Left', self, lambda: self.durationSlider.setValue(0)) # restart song # volumeSlider self.volumeSlider.setProperty("value", 100) self.volumeSlider.setRange(0, 100) self.volumeSlider.setValue( self.data['volume'] if self.data['volume'] != 0 else self.data['volume'] + 1 ) # set slider value | if saved volume is equal to 0 load with volume = 1 else load the saved volume self.mediaPlayer.setVolume( self.data['volume'] if self.data['volume'] != 0 else self.data['volume'] + 1 ) # set mediaPlayer volume | if saved volume is equal to 0 load with volume = 1 else load the saved volume self.volumeLabel.setText( f"{self.data['volume']}%" if self.data['volume'] != 0 else f"{self.data['volume']+1}%" ) # set volume label text | if saved volume is equal to 0 load with volume = 1 else load the saved volume self.volumeSlider.valueChanged.connect( self.mediaPlayer.setVolume ) # set mediaPlayer volume using the value took from the slider QShortcut('Up', self, lambda: self.volumeSlider.setValue( self.volumeSlider.value() + 1)) # volume + 1 QShortcut('Down', self, lambda: self.volumeSlider.setValue( self.volumeSlider.value() - 1)) # volume - 1 QShortcut( 'Shift+Up', self, lambda: self.volumeSlider.setValue(100)) # set maximum volume QShortcut( 'Shift+Down', self, lambda: self.volumeSlider.setValue(0)) # set minimun volume(mute) # volumeBtn self.volumeBtn.clicked.connect( self.volume_toggle) # mute/unmute volume pressing btn self.isMuted = False # starting with a non-muted volume self.previousVolume = self.data[ 'volume'] # loading last registered volume # media player signals self.mediaPlayer.durationChanged.connect( self.duration_changed) # set range of duration slider self.mediaPlayer.positionChanged.connect( self.position_changed) # duration slider progress self.mediaPlayer.stateChanged.connect( self.player_state) # see when it's playing or in pause self.mediaPlayer.volumeChanged.connect( self.volume_icon) # change volumebtn icon #=========================== playlist ============================== # create the playlist self.playlist = QMediaPlaylist() self.playlist.setPlaybackMode(2) self.mediaPlayer.setPlaylist(self.playlist) # clear the playlist self.playlistIsEmpty = True # playlistList model self.model = PlaylistModel(self.playlist) self.playlistView.setModel(self.model) self.playlist.currentIndexChanged.connect( self.playlist_position_changed) selection_model = self.playlistView.selectionModel() selection_model.selectionChanged.connect( self.playlist_selection_changed) #=========================== playlist function ============================== self.mediaList = [] # array of loaded songs self.currentPlaylist = "" # current loaded playlist name self.isCustomPlaylist = False # add song name on title self.playlist.currentMediaChanged.connect(self.set_title) # playlist buttons self.nextBtn.clicked.connect(self.next_song) # seek track forward self.prevBtn.clicked.connect(self.prev_song) # seek track backward self.mediaPlayer.mediaStatusChanged.connect( self.auto_next_track ) # once song is ended seek track forward and play it self.actionLoopIt.triggered.connect( self.loop_song) # (1) loop the same song self.actionShuffle.triggered.connect( self.shuffle_playlist) # change song's order self.loopBtn.clicked.connect(self.loop) # (3) loop the playlist self.randomBtn.clicked.connect( self.random) # (4) play random song without end # create new playlist self.actionCreatePlaylist.triggered.connect(self.custom_playlist) # delete current playlist self.actionDeletePlaylist.triggered.connect(self.delete_playlist) # remove all songs self.actionClearQueue.triggered.connect(self.clear_queue) # load playlist self.actionDict = {} # dictionary of action Objects for action in self.data['playlistList']: self.actionDict[action] = self.menuPlaylist.addAction( action, partial(self.load_playlist, action)) if len(self.data['playlistList']) == 0: self.menuPlaylist.menuAction().setVisible(False) #================== Songs opening ==================# def open_folder(self): foldername = QFileDialog.getExistingDirectory(self, "Open folder", "c:\\") if foldername: self.playlist.clear() self.mediaList.clear() for song in os.listdir(foldername): media = f"{foldername}/{song}" self.playlist.addMedia(QMediaContent(QUrl(media))) self.mediaList.append(media) self.playlist.setCurrentIndex(0) self.playBtn.setEnabled(True) self.durationSlider.setEnabled(True) self.playlistIsEmpty = False self.isCustomPlaylist = False self.model.layoutChanged.emit() # load songs in list view self.set_title() self.mediaPlayer.pause() # adjust play/pause icon def open_song(self): filename, _ = QFileDialog.getOpenFileName(self, "Open Song", "c:\\") if filename: if self.playlistIsEmpty == False: self.playlist.clear() self.mediaList.clear() self.playlistIsEmpty = True self.playlist.addMedia(QMediaContent(QUrl.fromLocalFile(filename))) self.mediaList.append(filename) self.playBtn.setEnabled(True) self.durationSlider.setEnabled(True) self.isCustomPlaylist = False self.model.layoutChanged.emit() # load song in list view self.set_title() # adjust play/pause icon if self.playlist.mediaCount( ) == 1: # if there is 1 song and you add another self.playlist.setCurrentIndex(0) self.mediaPlayer.pause() def load_playlist(self, playlistName): self.playlist.clear() self.mediaList.clear() # reload config self.data = yaml_loader() for song in self.data['playlistList'][playlistName]: self.playlist.addMedia(QMediaContent(QUrl.fromLocalFile(song))) self.mediaList.append(song) self.playlist.setCurrentIndex(0) self.playBtn.setEnabled(True) self.durationSlider.setEnabled(True) self.playlistIsEmpty = False self.isCustomPlaylist = True self.model.layoutChanged.emit() # load songs in list view self.currentPlaylist = playlistName # name of current loaded playlist self.set_title() self.statusbar.showMessage(f'Playlist "{playlistName}" loaded', 4000) self.menuPlaylist.menuAction().setVisible(True) # adjust play/pause icon self.mediaPlayer.pause() def set_title(self): if self.playlist.mediaCount() == 0: self.setWindowTitle("Sputofy") else: if self.isCustomPlaylist == False: self.setWindowTitle( f"Sputofy - {os.path.splitext(self.playlist.currentMedia().canonicalUrl().fileName())[0]} - {self.playlist.currentIndex()+1}/{self.playlist.mediaCount()}" ) else: self.setWindowTitle( f"Sputofy - {self.currentPlaylist} - {os.path.splitext(self.playlist.currentMedia().canonicalUrl().fileName())[0]} - {self.playlist.currentIndex()+1}/{self.playlist.mediaCount()}" ) #=======================================================# #================== Player Functions ==================# def play_video(self): if self.durationSlider.isEnabled(): # if slider was enabled if self.mediaPlayer.state() == QMediaPlayer.PlayingState: self.mediaPlayer.pause() else: self.mediaPlayer.play() def duration_changed(self, duration): self.durationSlider.setRange(0, duration) if duration > 0: self.totalTime_Label.setText(time_format(round( duration / 1000))) # duration is in ms self.durationSliderMaxValue = duration def position_changed(self, position): if position >= 0: self.elapsedTime_Label.setText(time_format( (position / 1000))) # position is in ms # Disable the events to prevent updating triggering a setPosition event (can cause stuttering). self.durationSlider.blockSignals(True) self.durationSlider.setValue(position) self.durationSlider.blockSignals(False) #=======================================================# #================== Playlist Settings ==================# #TODO Work in progress def playlist_array(self): index = self.playlist.mediaCount() mediaList = [] for i in range(index): # songPath = (self.playlist.media(path).canonicalUrl().path())#.split("/",1)[1] # mediaList.append(songPath) # print(self.playlist.media(i).canonicalUrl().path()) mediaList.append(self.playlist.media(i).canonicalUrl().fileName()) return mediaList def custom_playlist(self): if not self.playlist.mediaCount() == 0: name, is_notEmpty = QInputDialog.getText(self, "playlist", "save playlist as:") if name: if name in self.data['playlistList']: self.statusbar.showMessage( "playlist not created (name is already used)", 4000) else: self.data['playlistList'][name] = self.mediaList yaml_dump(self.data) # add new action Object to dictionary self.actionDict[name] = self.menuPlaylist.addAction( name, partial(self.load_playlist, name)) self.load_playlist( name) # instantly loading the new playlist else: self.statusbar.showMessage( "playlist not created (you should give a name to your baby :/)", 4000) else: self.statusbar.showMessage("there are no songs to playlist", 4000) def delete_playlist(self): if self.isCustomPlaylist: if len(self.data['playlistList']) == 1: self.menuPlaylist.menuAction().setVisible(False) self.data['playlistList'].pop( self.currentPlaylist) # remove playlist from dictionary self.menuPlaylist.removeAction(self.actionDict[ self.currentPlaylist]) # remove relative action self.actionDict.pop( self.currentPlaylist) # remove relative action Object self.playlist.clear() self.model.layoutChanged.emit() self.setWindowTitle("Sputofy") yaml_dump(self.data) self.statusbar.showMessage( 'succesfully deleted "' + self.currentPlaylist + '" playlist', 4000) else: self.statusbar.showMessage("cannot delete a non custom playlist", 4000) def clear_queue(self): self.playlist.clear() self.mediaList.clear() self.playBtn.setEnabled(False) self.model.layoutChanged.emit() def playlist_position_changed(self, i): if i > -1: ix = self.model.index(i) self.playlistView.setCurrentIndex(ix) def playlist_selection_changed(self, ix): # We receive a QItemSelection from selectionChanged. i = ix.indexes()[0].row() self.posizione = i self.playlist.setCurrentIndex(i) self.mediaPlayer.play() #=======================================================# #================== Playback Settings ==================# def next_song(self): if self.playlist.currentIndex() == self.playlist.mediaCount() - 1: self.playlist.setCurrentIndex(0) else: self.playlist.next() def prev_song(self): if self.playlist.currentIndex() == 0: self.playlist.setCurrentIndex(self.playlist.mediaCount() - 1) else: self.playlist.previous() def loop_song(self): if self.playlist.playbackMode() != 1: self.playlist.setPlaybackMode(1) self.actionLoopIt.setText("Loop it: ON") self.loopBtn.setIcon( QIcon(os.path.join(RES_PATH, "loopIconOFF.svg"))) self.randomBtn.setIcon( QIcon(os.path.join(RES_PATH, "randomIconOFF.svg"))) else: self.playlist.setPlaybackMode(2) self.actionLoopIt.setText("Loop it: OFF") def shuffle_playlist(self): if self.playlist.mediaCount(): self.playlist.shuffle() self.model.layoutChanged.emit() else: self.statusbar.showMessage("there are no songs to shuffle", 4000) def loop(self): if self.playlist.playbackMode() != 3: self.playlist.setPlaybackMode(3) self.loopBtn.setIcon( QIcon(os.path.join(RES_PATH, "loopIconON.svg"))) self.randomBtn.setIcon( QIcon(os.path.join(RES_PATH, "randomIconOFF.svg"))) self.actionLoopIt.setText("Loop it: OFF") else: self.playlist.setPlaybackMode(2) self.loopBtn.setIcon( QIcon(os.path.join(RES_PATH, "loopIconOFF.svg"))) def random(self): if self.playlist.playbackMode() != 4: self.playlist.setPlaybackMode(4) self.randomBtn.setIcon( QIcon(os.path.join(RES_PATH, "randomIconON.svg"))) self.loopBtn.setIcon( QIcon(os.path.join(RES_PATH, "loopIconOFF.svg"))) self.actionLoopIt.setText("Loop it: OFF") else: self.playlist.setPlaybackMode(2) self.randomBtn.setIcon( QIcon(os.path.join(RES_PATH, "randomIconOFF.svg"))) def auto_next_track(self): if self.mediaPlayer.mediaStatus() == QMediaPlayer.EndOfMedia: if self.playlist.playbackMode() == 2: # index starts from 0 mediacount starts from 1 if self.playlist.currentIndex( ) != self.playlist.mediaCount() - 1: self.playlist.next() self.mediaPlayer.play() else: # if ended song was the last one set the index to the first one and pause self.playlist.setCurrentIndex(0) self.mediaPlayer.pause() # loop playlist elif self.playlist.playbackMode() == 3: self.playlist.next() self.mediaPlayer.play() # random song elif self.playlist.playbackMode() == 4: while self.playlist.previousIndex( ) == self.playlist.currentIndex( ): # preventing repeating the same song self.playlist.setCurrentIndex( random.randint(0, self.playlist.mediaCount() - 1)) #=======================================================# #================== Volume Settings ==================# def volume_icon(self, volume): self.volumeLabel.setText(f"{volume}%") if volume: volumeIcon = QIcon() volumeIcon.addPixmap( QPixmap(os.path.join(RES_PATH, "volumeIcon.svg")), QIcon.Normal, QIcon.Off) self.volumeBtn.setIcon(volumeIcon) self.previousVolume = self.volumeSlider.value() self.isMuted = False else: volumeMutedIcon = QIcon() volumeMutedIcon.addPixmap( QPixmap(os.path.join(RES_PATH, "volumeMutedIcon.svg")), QIcon.Normal, QIcon.Off) self.volumeBtn.setIcon(volumeMutedIcon) self.isMuted = True def volume_toggle(self): if self.isMuted == False: self.volumeSlider.setValue(0) self.isMuted = True elif self.isMuted == True: if self.previousVolume == 0: self.volumeSlider.setValue(10) else: self.volumeSlider.setValue(self.previousVolume) self.isMuted = False #=======================================================# def mousePressEvent(self, event): ''' remove the border around the buttons created by using tab key ''' focused_widget = QtWidgets.QApplication.focusWidget() try: focused_widget.clearFocus() except: pass QMainWindow.mousePressEvent(self, event) def player_state(self, event): ''' event handler that adjust the play/pause icon ''' if event == QMediaPlayer.PlayingState: pauseIcon = QIcon() pauseIcon.addPixmap( QPixmap(os.path.join(RES_PATH, "pauseIcon.svg")), QIcon.Normal, QIcon.Off) self.playBtn.setIcon(pauseIcon) elif event == QMediaPlayer.PausedState: playIcon = QIcon() playIcon.addPixmap(QPixmap(os.path.join(RES_PATH, "playIcon.svg")), QIcon.Normal, QIcon.Off) self.playBtn.setIcon(playIcon) def closeEvent(self, event): ''' event handler that take window information and save it in config before the window close ''' # retrieve position xAxis = self.geometry().x() yAxis = self.geometry().y() self.data['last_position']['xPos'] = xAxis self.data['last_position']['yPos'] = yAxis # retrieve size width = self.width() height = self.height() self.data['last_window_size']['width'] = width self.data['last_window_size']['height'] = height # retrieve volume self.data['volume'] = self.mediaPlayer.volume() # retrieve user user = os.getlogin() self.data[ 'default_folder'] = f"C:\\Users\\{user}\\Desktop\\sputofy_songs" yaml_dump(self.data) def info_handle(self): info = "Sputofy\n1.0.0\n©2020 "+\ "Sputofy is a free audio player based on the converted youtube songs made by a_str0\n\n"+\ "Sputofy is written using python 3.x and PyQt5 modules" msg = QMessageBox.about(self, "About", info)
class FmvManager(QDockWidget, Ui_ManagerWindow): ''' Video Manager ''' def __init__(self, iface, parent=None): super().__init__(parent) self.setupUi(self) self.parent = parent self.iface = iface self._PlayerDlg = None self.meta_reader = [] self.initialPt = [] self.pass_time = 250 self.buf_interval = 2000 self.update_interval = 2000 self.loading = False self.playlist = QMediaPlaylist() self.VManager.viewport().installEventFilter(self) # Context Menu self.VManager.customContextMenuRequested.connect(self.__context_menu) self.removeAct = QAction( QIcon(":/imgFMV/images/mActionDeleteSelected.svg"), QCoreApplication.translate("ManagerDock", "Remove from list"), self, triggered=self.remove) self.VManager.setColumnWidth(1, 250) self.VManager.setColumnWidth(2, 140) self.VManager.setColumnWidth(3, 600) self.VManager.setColumnWidth(4, 600) self.VManager.setColumnWidth(5, 130) self.VManager.verticalHeader().setDefaultAlignment(Qt.AlignHCenter) self.VManager.hideColumn(0) self.videoPlayable = [] self.videoIsStreaming = [] self.dtm_path = parser['GENERAL']['DTM_file'] draw.setValues() self.setAcceptDrops(True) def loadVideosFromSettings(self): # Get Video Manager List VideoList = getVideoManagerList() for load_id in VideoList: filename = s.value(getNameSpace() + "/Manager_List/" + load_id) _, name = os.path.split(filename) folder = getVideoFolder(filename) klv_folder = os.path.join(folder, "klv") exist = os.path.exists(klv_folder) if exist: self.AddFileRowToManager(name, filename, load_id, exist, klv_folder) else: if os.path.isfile(filename): self.AddFileRowToManager(name, filename, load_id) def eventFilter(self, source, event): ''' Event Filter ''' if (event.type() == QEvent.MouseButtonPress and source is self.VManager.viewport() and self.VManager.itemAt(event.pos()) is None): self.VManager.clearSelection() return QDockWidget.eventFilter(self, source, event) @pyqtSlot(QPoint) def __context_menu(self, position): ''' Context Menu Manager Rows ''' if self.VManager.itemAt(position) is None: return menu = QMenu() menu.addAction(self.removeAct) menu.exec_(self.VManager.mapToGlobal(position)) def remove(self): ''' Remove current row ''' if self.loading: return # close video player (safer because it changes playlist internals) if self._PlayerDlg is not None: self._PlayerDlg.close() for cr in self.VManager.selectedItems(): idx = 0 # we browse cells but we need lines, so ignore already deleted rows try: idx = cr.row() except Exception: continue row_id = self.VManager.item(idx, 0).text() row_text = self.VManager.item(idx, 1).text() self.VManager.removeRow(idx) self.videoPlayable.pop(idx) self.videoIsStreaming.pop(idx) self.initialPt.pop(idx) # Remove video to Settings List RemoveVideoToSettings(row_id) # Remove folder if is local RemoveVideoFolder(row_text) if self.meta_reader[idx] is not None: self.meta_reader[idx].dispose() self.meta_reader.pop(idx) # remove from playlist self.playlist.removeMedia(idx) def closePlayer(self): ''' Close FMV ''' try: self._PlayerDlg.close() except Exception: None def closeFMV(self): ''' Close FMV ''' try: self._PlayerDlg.close() except Exception: None self.close() return def openStreamDialog(self): ''' Open Stream Dialog ''' self.OpenStream = OpenStream(self.iface, parent=self) self.OpenStream.setWindowFlags(Qt.Window | Qt.WindowCloseButtonHint) self.OpenStream.exec_() return def openMuiltiplexorDialog(self): ''' Open Multiplexor Dialog ''' self.Muiltiplexor = Multiplexor(self.iface, parent=self, Exts=ast.literal_eval( parser.get("FILES", "Exts"))) self.Muiltiplexor.setWindowFlags(Qt.Window | Qt.WindowCloseButtonHint) self.Muiltiplexor.exec_() return def AddFileRowToManager(self, name, filename, load_id=None, islocal=False, klv_folder=None): ''' Add file Video to new Row ''' # We limit the number of videos due to the buffer self.loading = True if self.VManager.rowCount() > 5: qgsu.showUserAndLogMessage(QCoreApplication.translate( "ManagerDock", "You must delete some video from the list before adding a new one" ), level=QGis.Warning) self.loading = False return self.islocal = islocal self.klv_folder = klv_folder w = QWidget() layout = QVBoxLayout() pbar = QProgressBar() layout.addWidget(pbar) w.setLayout(layout) rowPosition = self.VManager.rowCount() self.videoPlayable.append(False) pbar.setGeometry(0, 0, 300, 30) pbar.setValue(0) pbar.setMaximumHeight(30) if load_id is None: row_id = 0 if rowPosition != 0: row_id = int(self.VManager.item(rowPosition - 1, 0).text()) + 1 else: row_id = load_id self.VManager.insertRow(rowPosition) self.VManager.setItem(rowPosition, 0, QTableWidgetItem(str(row_id))) self.VManager.setItem(rowPosition, 1, QTableWidgetItem(name)) self.VManager.setItem( rowPosition, 2, QTableWidgetItem( QCoreApplication.translate("ManagerDock", "Loading"))) self.VManager.setItem(rowPosition, 3, QTableWidgetItem(filename)) self.VManager.setItem(rowPosition, 4, QTableWidgetItem("-")) self.VManager.setCellWidget(rowPosition, 5, w) self.VManager.setVisible(False) self.VManager.horizontalHeader().setStretchLastSection(True) self.VManager.setVisible(True) # resolve if it is a stream if "://" in filename: self.videoIsStreaming.append(True) else: self.videoIsStreaming.append(False) if not self.videoIsStreaming[-1]: # Disable row if not exist video file if not os.path.exists(filename): self.ToggleActiveRow(rowPosition, value="Missing source file") for j in range(self.VManager.columnCount()): try: self.VManager.item(rowPosition, j).setFlags(Qt.NoItemFlags | Qt.ItemIsEnabled) self.VManager.item(rowPosition, j).setBackground( QColor(211, 211, 211)) except Exception: self.VManager.cellWidget(rowPosition, j).setStyleSheet( "background-color:rgb(211,211,211);") pass self.loading = False return pbar.setValue(30) info = FFMpeg().probe(filename) if info is None: qgsu.showUserAndLogMessage( QCoreApplication.translate("ManagerDock", "Failed loading FFMPEG ! ")) klvIdx = getKlvStreamIndex(filename, islocal) # init non-blocking metadata buffered reader self.meta_reader.append( BufferedMetaReader(filename, klv_index=klvIdx, pass_time=self.pass_time, interval=self.buf_interval)) pbar.setValue(60) try: # init point we can center the video on self.initialPt.append( getVideoLocationInfo(filename, islocal, klv_folder)) if not self.initialPt[rowPosition]: self.VManager.setItem( rowPosition, 4, QTableWidgetItem( QCoreApplication.translate( "ManagerDock", "Start location not available."))) self.ToggleActiveRow(rowPosition, value="Video not applicable") pbar.setValue(100) else: self.VManager.setItem( rowPosition, 4, QTableWidgetItem(self.initialPt[rowPosition][2])) pbar.setValue(90) self.videoPlayable[rowPosition] = True except Exception: qgsu.showUserAndLogMessage( QCoreApplication.translate( "ManagerDock", "This video doesn't have Metadata ! ")) pbar.setValue(100) self.ToggleActiveRow(rowPosition, value="Video not applicable") else: self.meta_reader.append(StreamMetaReader(filename)) qgsu.showUserAndLogMessage("", "StreamMetaReader initialized.", onlyLog=True) self.initialPt.append(None) self.videoPlayable[rowPosition] = True url = "" if self.videoIsStreaming[-1]: # show video from splitter (port +1) oldPort = filename.split(":")[2] newPort = str(int(oldPort) + 10) proto = filename.split(":")[0] url = QUrl(proto + "://127.0.0.1:" + newPort) else: url = QUrl.fromLocalFile(filename) self.playlist.addMedia(QMediaContent(url)) if self.videoPlayable[rowPosition]: pbar.setValue(100) if islocal: self.ToggleActiveRow(rowPosition, value="Ready Local") else: self.ToggleActiveRow(rowPosition, value="Ready") # Add video to settings list AddVideoToSettings(str(row_id), filename) self.loading = False def openVideoFileDialog(self): ''' Open video file dialog ''' if self.loading: return Exts = ast.literal_eval(parser.get("FILES", "Exts")) filename, _ = askForFiles(self, QCoreApplication.translate( "ManagerDock", "Open video"), exts=Exts) if filename: if not self.isFileInPlaylist(filename): _, name = os.path.split(filename) self.AddFileRowToManager(name, filename) else: qgsu.showUserAndLogMessage( QCoreApplication.translate( "ManagerDock", "File is already loaded in playlist: " + filename)) return def isFileInPlaylist(self, filename): mcount = self.playlist.mediaCount() for x in range(mcount): if filename in self.playlist.media(x).canonicalUrl().toString(): return True return False def PlayVideoFromManager(self, model): ''' Play video from manager dock. Manager row double clicked ''' # Don't enable Play if video doesn't have metadata if not self.videoPlayable[model.row()]: return try: if self._PlayerDlg.isVisible(): self._PlayerDlg.close() except Exception: None path = self.VManager.item(model.row(), 3).text() text = self.VManager.item(model.row(), 1).text() folder = getVideoFolder(text) klv_folder = os.path.join(folder, "klv") exist = os.path.exists(klv_folder) # First time we open the player if self._PlayerDlg is None: if exist: self.CreatePlayer(path, self.update_interval, model.row(), islocal=True, klv_folder=klv_folder) else: self.CreatePlayer(path, self.update_interval, model.row()) self.SetupPlayer(model.row()) if exist: self._PlayerDlg.playFile(path, islocal=True, klv_folder=klv_folder) else: self._PlayerDlg.playFile(path) def SetupPlayer(self, row): ''' Play video from manager dock. Manager row double clicked ''' self.ToggleActiveRow(row) self.playlist.setCurrentIndex(row) # qgsu.CustomMessage("QGIS FMV", path, self._PlayerDlg.fileName, icon="Information") # if path != self._PlayerDlg.fileName: self._PlayerDlg.setMetaReader(self.meta_reader[row]) self.ToggleActiveFromTitle() self._PlayerDlg.show() self._PlayerDlg.activateWindow() # zoom to map zone curAuthId = self.iface.mapCanvas().mapSettings().destinationCrs( ).authid() if self.initialPt[row][1] is not None and self.initialPt[row][ 0] is not None: map_pos = QgsPointXY(self.initialPt[row][1], self.initialPt[row][0]) if curAuthId != "EPSG:4326": trgCode = int(curAuthId.split(":")[1]) xform = QgsCoordinateTransform( QgsCoordinateReferenceSystem(4326), QgsCoordinateReferenceSystem(trgCode), QgsProject().instance()) map_pos = xform.transform(map_pos) self.iface.mapCanvas().setCenter(map_pos) self.iface.mapCanvas().zoomScale(50000) def CreatePlayer(self, path, interval, row, islocal=False, klv_folder=None): ''' Create Player ''' self._PlayerDlg = QgsFmvPlayer(self.iface, path, interval, parent=self, meta_reader=self.meta_reader[row], pass_time=self.pass_time, islocal=islocal, klv_folder=klv_folder) self._PlayerDlg.player.setPlaylist(self.playlist) self._PlayerDlg.setWindowFlags(Qt.Dialog | Qt.WindowCloseButtonHint) self._PlayerDlg.show() self._PlayerDlg.activateWindow() def ToggleActiveFromTitle(self): ''' Toggle Active video status ''' column = 2 for row in range(self.VManager.rowCount()): if self.VManager.item(row, column) is not None: v = self.VManager.item(row, column).text() text = self.VManager.item(row, 1).text() if v == "Playing": folder = getVideoFolder(text) klv_folder = os.path.join(folder, "klv") exist = os.path.exists(klv_folder) if exist: self.ToggleActiveRow(row, value="Ready Local") else: self.ToggleActiveRow(row, value="Ready") return def ToggleActiveRow(self, row, value="Playing"): ''' Toggle Active row manager video status ''' self.VManager.setItem( row, 2, QTableWidgetItem(QCoreApplication.translate("ManagerDock", value))) return def closeEvent(self, _): ''' Close Manager Event ''' FmvDock = qgis.utils.plugins[getNameSpace()] FmvDock._FMVManager = None try: if self._PlayerDlg.isVisible(): self._PlayerDlg.close() except Exception: None return def dragEnterEvent(self, e): check = True Exts = ast.literal_eval(parser.get("FILES", "Exts")) if e.mimeData().hasUrls(): for url in e.mimeData().urls(): fileIsOk = False for ext in Exts: if url.fileName().lower().endswith(ext): fileIsOk = True break if not fileIsOk: check = False break # Only accept if all files match a required extension if check: e.acceptProposedAction() # Ignore and stop propagation else: e.setDropAction(Qt.IgnoreAction) e.accept() def dropEvent(self, e): for url in e.mimeData().urls(): # local files if "file:///" in url.toString(): if not self.isFileInPlaylist(url.toString()[8:]): self.AddFileRowToManager(url.fileName(), url.toString()[8:]) # network drives else: if not self.isFileInPlaylist(url.toString()[5:]): self.AddFileRowToManager(url.fileName(), url.toString()[5:])
class MyWidget(QMainWindow): def __init__(self): super().__init__() # !Костыль # Запрещаю менять размер окна, т.к. не хочу работать с layout`ами self.setFixedSize(656, 532) self.setWindowIcon(QIcon('icons/icon.ico')) # Добавляем эквалайзер громкости self.equalizer = QVolumeEq((255, 255, 102), (255, 26, 10), bg_color=(0, 0, 0, 20)) self.equalizer.setParent(self) self.equalizer.move(20, 391) self.equalizer.resize(341, 31) uic.loadUi('icons/djap.ui', self) self.lightThemeAction.triggered.connect(self.change_theme) self.darkThemeAction.triggered.connect(self.change_theme) # Создаём организатор плейлистов self.playlist_handler = QPlaylistHandler() self.playlist_handler.setParent(self) self.playlist_handler.move(380, 40) # Настраиваем меню self.saveOption.triggered.connect(self.playlist_handler.save_playlist) self.saveAsOption.triggered.connect(self.playlist_handler.save_playlist_as) self.deleteOption.triggered.connect(self.playlist_handler.delete_playlist) self.aboutOption.triggered.connect(self.about) self.helpOption.triggered.connect(self.help) self.repeatModeOption.triggered.connect(self.playlist_handler.change_playmode) self.oneModeOption.triggered.connect(self.playlist_handler.change_playmode) self.randomModeOption.triggered.connect(self.playlist_handler.change_playmode) # Реализация проигрывателя и основного плейлиста self.player = QMediaPlayer() self.player.durationChanged.connect(self.update_song) self.player.setVolume(25) self.player.positionChanged.connect(self.update_timeline_position) self.queue = QMediaPlaylist(self.player) self.player.setPlaylist(self.queue) self.queue.setPlaybackMode(QMediaPlaylist.Loop) self.is_looped_queue = True # Загрузка музыки из плейлиста self.load_songs_from_playlist() # Подключаем кнопки к их функциям self.is_playing = False self.playBtn.clicked.connect(self.play_or_pause) self.queue.currentMediaChanged.connect(self.check_to_stop) self.nextBtn.clicked.connect(self.next_audio) self.prevBtn.clicked.connect(self.prev_audio) self.is_looped_current_track = False self.loopBtn.clicked.connect(self.loop_or_unloop_current_track) # Настройка слайдера звука self.volumeSlider.hide() self.volumeSlider.setValue(25) self.equalizer.setValue(25) self.volumeSlider.sliderReleased.connect(self.volumeSlider.hide) self.volumeSlider.valueChanged.connect(self.change_volume) self.volumeBtn.clicked.connect(self.change_volume) # Настройка таймлайна self.is_timeline_dragged = False self.timeline.sliderReleased.connect(self.timeline_changed) self.timeline.sliderPressed.connect(self.timeline_is_dragged) # !Костыль # Имитируем запуск аудиодорожки, чтобы подгрузить данные self.play_or_pause() self.play_or_pause() self.icon_changed() def palette(self): """Функция для создания темы""" con = sqlite3.connect('user_data.sqlite') cur = con.cursor() if int(cur.execute('SELECT * FROM theme').fetchone()[0]): palette = QPalette() palette.setColor(QPalette.Window, QColor(240, 240, 240)) palette.setColor(QPalette.WindowText, Qt.black) palette.setColor(QPalette.Base, Qt.white) palette.setColor(QPalette.AlternateBase, QColor(246, 246, 246)) palette.setColor(QPalette.ToolTipBase, QColor(255, 255, 240)) palette.setColor(QPalette.ToolTipText, Qt.black) palette.setColor(QPalette.Text, Qt.black) palette.setColor(QPalette.Button, QColor(240, 240, 240)) palette.setColor(QPalette.ButtonText, Qt.black) palette.setColor(QPalette.BrightText, Qt.white) palette.setColor(QPalette.Highlight, QColor(0, 120, 215).lighter()) palette.setColor(QPalette.HighlightedText, Qt.white) else: palette = QPalette() palette.setColor(QPalette.Window, QColor(53, 53, 53)) palette.setColor(QPalette.WindowText, Qt.white) palette.setColor(QPalette.Base, QColor(15, 15, 15)) palette.setColor(QPalette.AlternateBase, QColor(53, 53, 53)) palette.setColor(QPalette.ToolTipBase, Qt.white) palette.setColor(QPalette.ToolTipText, Qt.white) palette.setColor(QPalette.Text, Qt.white) palette.setColor(QPalette.Button, QColor(53, 53, 53)) palette.setColor(QPalette.ButtonText, Qt.white) palette.setColor(QPalette.BrightText, Qt.red) palette.setColor(QPalette.Highlight, QColor(142, 45, 197).lighter()) palette.setColor(QPalette.HighlightedText, Qt.black) return palette def change_theme(self): """Функция для смены темы""" con = sqlite3.connect('user_data.sqlite') cur = con.cursor() if self.sender() == self.lightThemeAction: cur.execute('UPDATE theme SET isit = 1') else: cur.execute('UPDATE theme SET isit = 0') con.commit() QMessageBox.warning(self, 'DJust Audio Player', 'Чтобы изменения вступили в силу, ' 'перезапустите приложение.', QMessageBox.Ok) def help(self): """Функция для показа формы справки""" self.help = QHelp() def about(self): """Функция для показа формы О программе""" self.about = QAbout() def delete_song_from_mediaplayer(self, index): """Удалить трек из медиаплеера""" self.queue.removeMedia(index) def add_song_to_mediaplayer(self, url): """Добавить трек в медиаплеер""" self.queue.addMedia(QMediaContent(QUrl(url))) def change_track_by_click(self, row): """Функция для смены трека по нажатию в списке""" self.queue.setCurrentIndex(row) def check_to_stop(self): """Проверяем, чтобы плеер не играл с пустым плейлистом""" if self.queue.isEmpty() and self.is_playing: self.play_or_pause() def load_songs_from_playlist(self): """Загрузка музыки из БД в плейлист""" self.queue.clear() for url in self.playlist_handler.urls(): self.queue.addMedia(QMediaContent(QUrl(url))) def icon_changed(self): """Функция для вывода обложки трека""" def noicon(): """В случае отсутствия обложки эта функция ставить свою заглушку""" try: pixmap = QPixmap() if pixmap.load('icons/noicon.png'): self.audioPic.setPixmap(pixmap) except Exception as e: print(e.__class__.__name__, ': ', e, sep='') try: # Читаем метаданные трека n = self.queue.currentIndex() url = self.playlist_handler.urls()[n if n > -1 else 0] file = File(url) for i in file.tags.keys(): if 'APIC:' in i: artwork = file.tags[i].data break else: raise KeyError # Переводим их в байтовый массив ba = QByteArray(artwork) # И загружаем на форму, если это возможно pixmap = QPixmap() if pixmap.loadFromData(ba): self.audioPic.setPixmap(pixmap.scaled(341, 341, Qt.IgnoreAspectRatio)) else: raise KeyError except Exception as e: # print(e.__class__.__name__, ': ', e, sep='') noicon() def timeline_is_dragged(self): """Мини-функция для изменения переменной""" # При вызове этой функции мы понимаем, что пользователь # взаимодействует с таймлайном трека self.is_timeline_dragged = True def timeline_changed(self): """Функция для пользовательской перемотки данного трека""" # Перематываем позицию плеера self.player.setPosition(self.timeline.value() * 1000) # Пользователь отпустил язычок слайдера и больше не взаимодействует с таймлайном self.is_timeline_dragged = False # Вызываем обновление временной шкалы, т.к. произошла перемотка self.update_timeline_position() def update_song(self): """Изменение данных о треке""" # !Костыль # Проматываем несколько миллисекунд, чтобы избежать повреждённых треков self.player.setPosition(10) # Меняем обложку self.icon_changed() # Выделяем в списке новый трек self.playlist_handler.set_current_select(self.queue.currentIndex()) # Меняем название окна в соответствии с играющим треком title = self.queue.currentMedia().canonicalUrl().path().split('/')[-1] if title: self.setWindowTitle('DJust Audio Player | %s' % title) else: self.setWindowTitle('DJust Audio Player') # Изменяем длину таймлайна на длину трека self.timeline.setMaximum(self.player.duration() // 1000) # Выводим длину трека в минутах/секундах на экран minutes, seconds = self.player.duration() // 1000 // 60, self.player.duration() // 1000 % 60 self.endTimeLabel.setText(str(minutes) + ':{:02d}'.format(seconds)) def update_timeline_position(self): """Функция для обновления временной шкалы""" # Выводим текущее положение плеера в минутах/секундах minutes, seconds = self.player.position() // 1000 // 60, self.player.position() // 1000 % 60 self.currentTimeLabel.setText(str(minutes) + ':{:02d}'.format(seconds)) # Чтобы не "вырывать" язычок слайдера из рук пользователя, # проверяем, что пользователь НЕ взаимодейтсвует с таймлайном. if not self.is_timeline_dragged: self.timeline.setValue(minutes * 60 + seconds) def loop_or_unloop_current_track(self): """Мини-функция для зацикливания данного трека""" if not self.is_looped_current_track: # Зацикливаем self.is_looped_current_track = True self.queue.setPlaybackMode(QMediaPlaylist.CurrentItemInLoop) # Меняем картинку на перечёркнутый круг self.loopBtn.setStyleSheet('background-image: url(icons/unloop.png)') else: # убираем цикл self.is_looped_current_track = False self.queue.setPlaybackMode(self.playlist_handler.mode) self.loopBtn.setStyleSheet('background-image: url(icons/loop.png)') def next_audio(self): """Функция для перемотки на следующий в списке трек""" if self.queue.mediaCount() - 1 == self.queue.currentIndex(): # Если трек ппоследний в списке, возвращаемся к первому self.queue.setCurrentIndex(0) else: # Иначе просто перемещаемся к следующему self.queue.next() def prev_audio(self): """Функция для перемотки на предыдущий в списке трек""" if 0 == self.queue.currentIndex(): # Если трек первый в списке, возвращаемся к последнему self.queue.setCurrentIndex(self.queue.mediaCount() - 1) else: # Иначе просто возвращаемся к предыдущему self.queue.previous() def play_or_pause(self): """Функция для запуска или остановки проигрывателя""" if self.queue.isEmpty(): # Меняем картинку на стрелочку (проигрывание) self.is_playing = False self.playBtn.setStyleSheet('background-image: url(icons/play.png)') # Ставим на паузу self.player.pause() return if self.is_playing: # Меняем картинку на стрелочку (проигрывание) self.is_playing = False self.playBtn.setStyleSheet('background-image: url(icons/play.png)') # Ставим на паузу self.player.pause() else: # Меняем картинку на 2 палочки (пауза) self.is_playing = True self.playBtn.setStyleSheet('background-image: url(icons/pause.png)') # Запускаем звук self.player.play() def change_volume(self): """Мини-Функция для передачи уровня громкости в плеер""" # Функцию использует несколько объектов сразу, проверяем вызывателя if self.sender() == self.volumeBtn: # Если функцию вызвала кнопка, то включаем/выключаем отображение слайдера if not self.volumeSlider.isHidden(): self.volumeSlider.hide() else: self.volumeSlider.show() else: # Иначе функцию вызвал сам слайдер. Значит, меняем значение self.player.setVolume(self.volumeSlider.value()) self.equalizer.setValue(self.volumeSlider.value())
class App(QMainWindow): def __init__(self): super().__init__() self.player = QMediaPlayer() # 播放器 self.playlist = QMediaPlaylist() # 播放列表 self.title = '音乐播放器' self.volumeHint = QLabel('音量: 99%') self.mInfo = { 'cover': './default_cover.jpg', 'music': '', 'singer': '', 'duration': 0 } self.aboutWin = AboutWindow() self.cover = QLabel() self.listWid = QListWidget() # 设置主窗口位置 self.left = 200 self.top = 100 self.width = 500 self.height = 430 self.font = QFont('SansSerif', 10.5) self.color = 1 # 0 - 黑色界面, 1 - 白色界面 self.userAction = 0 # 0 - 停止中, 1 - 播放中 2 - 暂停中 self.initUI() def initUI(self): # 添加文件菜单 menubar = self.menuBar() menubar.setNativeMenuBar(False) # 不使用本地菜单栏以获得全平台统一的效果 filemenu = menubar.addMenu('文件') windowmenu = menubar.addMenu('窗口') fileAct = QAction('打开文件', self) folderAct = QAction('打开文件夹', self) themeAct = QAction('切换[亮/暗]主题', self) aboutAct = QAction('关于', self) fileAct.setShortcut('Ctrl+O') folderAct.setShortcut('Ctrl+D') themeAct.setShortcut('Ctrl+T') aboutAct.setShortcut('Ctrl+H') filemenu.addAction(fileAct) filemenu.addAction(folderAct) windowmenu.addAction(themeAct) windowmenu.addAction(aboutAct) fileAct.triggered.connect(self.openFile) folderAct.triggered.connect(self.addFiles) themeAct.triggered.connect(self.toggleColors) aboutAct.triggered.connect(self.viewAbout) self.listWid.itemDoubleClicked.connect(self.quickplayhandler) self.listWid.setFont(self.font) self.playlist.setPlaybackMode(2) self.setLayout() self.setWindowFlags(Qt.WindowMinimizeButtonHint | Qt.WindowCloseButtonHint) # 禁用窗口最大化按钮 self.setFont(self.font) self.setWindowTitle(self.title) self.setWindowIcon(QIcon('icon.ico')) self.setGeometry(self.left, self.top, self.width, self.height) self.setFixedSize(self.width, self.height) self.toggleColors() self.show() def setLayout(self): wid = QWidget(self) self.setCentralWidget(wid) # 添加音量控制 volumeslider = QSlider(Qt.Horizontal, self) volumeslider.setFocusPolicy(Qt.NoFocus) volumeslider.valueChanged[int].connect(self.changeVolume) volumeslider.setValue(100) # 添加歌曲播放控制 playBtn = QPushButton('播放') # 播放 pauseBtn = QPushButton('暂停') # 暂停 stopBtn = QPushButton('清空列表') # 停止 prevBtn = QPushButton('上一首') # 上一首 playback = QComboBox() # 回放模式 nextBtn = QPushButton('下一首') # 下一首 playback.addItem(' 单曲播放') playback.addItem(' 单曲循环') playback.addItem(' 列表播放') playback.addItem(' 列表循环') playback.addItem(' 列表随机') playback.setCurrentIndex(2) # 添加封面 jpg = QPixmap(self.mInfo['cover']).scaled(300, 300) self.cover.setPixmap(jpg) # 添加布局 Area = QVBoxLayout() # centralWidget controls = QHBoxLayout() showLayout = QHBoxLayout() playlistCtrlLayout = QHBoxLayout() # 歌曲播放控制布局 controls.addWidget(playBtn) controls.addWidget(pauseBtn) controls.addWidget(stopBtn) # 播放列表控制布局 playlistCtrlLayout.addWidget(prevBtn) playlistCtrlLayout.addWidget(playback) playlistCtrlLayout.addWidget(nextBtn) # 显示布局 showLayout.addWidget(self.cover) showLayout.addWidget(self.listWid) # 竖直排列 Area.addStretch(1) Area.addLayout(showLayout) Area.addWidget(self.volumeHint) Area.addWidget(volumeslider) Area.addLayout(controls) Area.addLayout(playlistCtrlLayout) wid.setLayout(Area) # 事件绑定 playBtn.clicked.connect(self.playhandler) pauseBtn.clicked.connect(self.pausehandler) playback.currentIndexChanged.connect(self.playbackhandler) stopBtn.clicked.connect(self.stophandler) prevBtn.clicked.connect(self.prevSong) nextBtn.clicked.connect(self.nextSong) self.statusBar().showMessage('文件-打开文件(夹)-选择-开始播放') self.playlist.currentMediaChanged.connect(self.songChanged) def openFile(self): print("点击了文件按钮") song = QFileDialog.getOpenFileName(self, "打开音频", "github-lkxed", "音频文件 (*.mp3 *.ogg *.wav *.m4a)") if song[0] != '': url = QUrl.fromLocalFile(song[0]) if self.playlist.mediaCount() == 0: self.playlist.addMedia(QMediaContent(url)) self.player.setPlaylist(self.playlist) self.player.play() self.userAction = 1 self.listWid.addItem(song[0].split('/')[-1].split('.')[0]) print(self.playlist.mediaCount()) else: self.playlist.addMedia(QMediaContent(url)) print(self.playlist.mediaCount()) def addFiles(self): print("点击了文件夹按钮") if self.playlist.mediaCount() != 0: self.folderIterator() print(self.playlist.mediaCount()) else: self.folderIterator() self.player.setPlaylist(self.playlist) self.player.playlist().setCurrentIndex(0) self.player.play() print(self.playlist.mediaCount()) self.userAction = 1 def folderIterator(self): folderChosen = QFileDialog.getExistingDirectory(self, '打开音频文件夹', '.') if folderChosen != None: it = QDirIterator(folderChosen) it.next() while it.hasNext(): if it.fileInfo().isDir() == False and it.filePath() != '.': fInfo = it.fileInfo() print(it.filePath(), fInfo.suffix()) if fInfo.suffix() in ('mp3', 'ogg', 'wav', 'm4a'): print('added file', fInfo.fileName()) self.listWid.addItem(fInfo.fileName()) self.playlist.addMedia(QMediaContent(QUrl.fromLocalFile(it.filePath()))) it.next() if it.fileInfo().isDir() == False and it.filePath() != '.': fInfo = it.fileInfo() print(it.filePath(), fInfo.suffix()) if fInfo.suffix() in ('mp3', 'ogg', 'wav', 'm4a'): print('added file', fInfo.fileName()) self.listWid.addItem(fInfo.fileName()) self.playlist.addMedia(QMediaContent(QUrl.fromLocalFile(it.filePath()))) # 设置ListItem高度 for x in range(self.listWid.count()): self.listWid.item(x).setSizeHint(QSize(100, 30)) # 处理双击播放事件 def quickplayhandler(self, item): # 若播放列表没有音频了就跳出打开文件对话框 if self.playlist.mediaCount() == 0: self.openFile() # 若播放列表不为空则播放 elif self.playlist.mediaCount() != 0: index = self.listWid.currentRow() self.player.playlist().setCurrentIndex(index) self.player.play() self.userAction = 1 # 处理按钮播放事件 def playhandler(self): if self.userAction == 2: self.player.play() elif self.userAction == 0: # 若播放列表没有音频了就跳出打开文件对话框 if self.playlist.mediaCount() == 0: self.openFile() # 若播放列表不为空则播放 elif self.playlist.mediaCount() != 0: index = self.listWid.currentRow() self.player.playlist().setCurrentIndex(index) self.player.play() self.userAction = 1 else: self.statusBar().showMessage('已经在播放中!') # 处理暂停事件 def pausehandler(self): self.userAction = 2 self.player.pause() # 处理清空列表事件 def stophandler(self): self.userAction = 0 self.player.stop() self.playlist.clear() self.listWid.clear() self.mInfo['cover'] = 'default_cover.jpg' jpg = QPixmap(self.mInfo['cover']).scaled(300, 300) self.cover.setPixmap(jpg) print("Playlist cleared!") self.statusBar().showMessage("列表已清空") def changeVolume(self, value): text = '音量: ' + str(value) + '%' self.volumeHint.setText(text) self.player.setVolume(value) # 上一首事件 def prevSong(self): if self.playlist.currentIndex() == 0: self.playlist.setCurrentIndex(self.playlist.mediaCount()-1) else: self.player.playlist().previous() # 随机播放事件 def playbackhandler(self, index): self.playlist.setPlaybackMode(index) print(self.playlist.playbackMode()) # 下一首事件 def nextSong(self): if self.playlist.mediaCount() == self.playlist.currentIndex()+1: self.playlist.setCurrentIndex(0) else: self.player.playlist().next() # 音频切换事件 def songChanged(self, media): if not media.isNull(): url = media.canonicalUrl() self.showInfo(url) self.statusBar().showMessage(url.fileName()) index = self.player.playlist().currentIndex() self.listWid.setCurrentRow(index) def showInfo(self, url): filepath = url.path()[1:] filename = url.fileName()[:-4] print(filename) if filepath[-3:] == 'm4a': file = mutagen.MP4(filepath) try: img = file.tags['covr'][0] self.mInfo['cover'] = './cover/'+filename+'.jpg' if not os.path.exists('./cover'): os.mkdir('./cover') if not os.path.exists(self.mInfo['cover']): with open(self.mInfo['cover'], 'wb') as f: f.write(img) except: print('找不到封面') self.mInfo['cover'] = './default_cover.jpg' elif filepath[-3:] == 'mp3': file = mutagen.File(filepath) try: img = file.tags['APIC:'].data self.mInfo['cover'] = './cover/'+filename+'.jpg' if not os.path.exists('./cover'): os.mkdir('./cover') if not os.path.exists(self.mInfo['cover']): with open(self.mInfo['cover'], 'wb') as f: f.write(img) except: print('找不到封面') self.mInfo['cover'] = './default_cover.jpg' else: print('音频文件不支持提取封面') self.mInfo['cover'] = './default_cover.jpg' jpg = QPixmap(self.mInfo['cover']).scaled(300, 300) self.cover.setPixmap(jpg) # 主题切换事件 def toggleColors(self): app.setStyle("Fusion") palette = QPalette() # 明亮主题 if self.color == 0: palette.setColor(QPalette.Window, QColor(53, 53, 53)) palette.setColor(QPalette.WindowText, Qt.white) palette.setColor(QPalette.Base, QColor(25, 25, 25)) palette.setColor(QPalette.AlternateBase, QColor(53, 53, 53)) palette.setColor(QPalette.ToolTipBase, Qt.white) palette.setColor(QPalette.ToolTipText, Qt.white) palette.setColor(QPalette.Text, Qt.white) palette.setColor(QPalette.Button, QColor(53, 53, 53)) palette.setColor(QPalette.ButtonText, Qt.white) palette.setColor(QPalette.BrightText, Qt.red) palette.setColor(QPalette.Link, QColor(235, 101, 54)) palette.setColor(QPalette.Highlight, QColor(235, 101, 54)) palette.setColor(QPalette.HighlightedText, Qt.black) app.setPalette(palette) self.color = 1 # 黑暗主题 elif self.color == 1: palette.setColor(QPalette.Window, Qt.white) palette.setColor(QPalette.WindowText, Qt.black) palette.setColor(QPalette.Base, QColor(240, 240, 240)) palette.setColor(QPalette.AlternateBase, Qt.white) palette.setColor(QPalette.ToolTipBase, Qt.white) palette.setColor(QPalette.ToolTipText, Qt.white) palette.setColor(QPalette.Text, Qt.black) palette.setColor(QPalette.Button, Qt.white) palette.setColor(QPalette.ButtonText, Qt.black) palette.setColor(QPalette.BrightText, Qt.red) palette.setColor(QPalette.Link, QColor(66, 155, 248)) palette.setColor(QPalette.Highlight, QColor(66, 155, 248)) palette.setColor(QPalette.HighlightedText, Qt.black) app.setPalette(palette) self.color = 0 def viewAbout(self): self.aboutWin.show()
class AudioPlayer(QMainWindow): def __init__(self): super(AudioPlayer, self).__init__() self.setGeometry(150, 150, 600, 450) self.setWindowTitle('Prototype!!') self.isButtonClick = False self.current_time = 0 self.media_obj = audiosegment.empty() self.play_process = threading.Thread(target=play, args=(self.media_obj,)) self.initUI() def initUI(self): menu = self.menuBar() # create menu bar file_menu = menu.addMenu('File') # create file button options_menu = menu.addMenu('Options') # create options button wid = QWidget() self.setCentralWidget(wid) # create 'Open FIle' actions, add to 'File' button open_file = QAction('Open File', self) open_file.triggered.connect(self.openFile) open_file.setShortcut("Ctrl+O") file_menu.addAction(open_file) # create 'Close File' action, add to 'File' button close_file = QAction('Close File', self) close_file.triggered.connect(self.exit) close_file.setShortcut("Ctrl+W") file_menu.addAction(close_file) # create 'Exit' action, add to 'File' button exit_file = QAction('Close File', self) exit_file.triggered.connect(self.exit) exit_file.setShortcut("Ctrl+W") file_menu.addAction(exit_file) # create 'Color' action, add to 'Options' button color_option = QAction('Color', self) color_option.triggered.connect(self.show_color_dialog) color_option.setShortcut("Ctrl+2") options_menu.addAction(color_option) # create 'Shape' action, add to 'Options' button shape_option = QAction('Shape', self) # shape_option.triggered.connect() shape_option.setShortcut("Ctrl+3") options_menu.addAction(shape_option) # create 'Foliage' action, add to 'Options' button foliage_option = QAction('Foliage', self) # foliage_option.triggered.connect() foliage_option.setShortcut("Ctrl+3") options_menu.addAction(foliage_option) # Creating button for pause/play self.player = QMediaPlayer() self.playlist = QMediaPlaylist() videoWidget = QVideoWidget() self.pal = QPalette() self.pal.setColor(QPalette.Foreground, Qt.green) videoWidget.setPalette(self.pal) self.playButton = QPushButton() self.playButton.setEnabled(False) self.playButton.setIcon(self.style().standardIcon(QStyle.SP_MediaPlay)) self.playButton.clicked.connect(self.play) self.positionSlider = QSlider(Qt.Horizontal) self.positionSlider.setRange(0, 0) self.positionSlider.sliderMoved.connect(self.setPosition) self.errorLabel = QLabel() self.errorLabel.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Maximum) self.songLabel = QLabel() self.songLabel.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Maximum) controlLayout = QHBoxLayout() controlLayout.setContentsMargins(0, 0, 0, 0) self.playButton.setShortcut("Space") controlLayout.addWidget(self.playButton) controlLayout.addWidget(self.positionSlider) layout = QVBoxLayout() layout.addWidget(videoWidget) layout.addLayout(controlLayout) layout.addWidget(self.songLabel) layout.addWidget(self.errorLabel) # Set widget to contain window contents wid.setLayout(layout) # self.mediaPlayer.setVideoOutput(videoWidget) self.player.stateChanged.connect(self.mediaStateChanged) self.player.positionChanged.connect(self.positionChanged) self.player.durationChanged.connect(self.durationChanged) self.player.error.connect(self.handleError) def openFile(self): song = QFileDialog.getOpenFileName(self, "Open Song", "~", "Sound Files (*.mp3 *.ogg *.wav *.m4a)") songName = song[0].split("/") songName = songName[-1].split(".") self.songLabel.setText(songName[0]) if song[0] != '': url = QUrl.fromLocalFile(song[0]) if self.playlist.mediaCount() == 0: self.playButton.setEnabled(True) self.playlist.addMedia(QMediaContent(url)) self.player.setPlaylist(self.playlist) self.player.setVolume(100) self.player.play() else: self.playlist.addMedia(QMediaContent(url)) def exit(self): sys.exit() def play(self): if self.player.state() == QMediaPlayer.PlayingState: self.player.pause() else: self.player.play() def mediaStateChanged(self, state): if self.player.state() == QMediaPlayer.PlayingState: self.playButton.setIcon( self.style().standardIcon(QStyle.SP_MediaPause)) else: self.playButton.setIcon( self.style().standardIcon(QStyle.SP_MediaPlay)) def positionChanged(self, position): self.positionSlider.setValue(position) def durationChanged(self, duration): self.positionSlider.setRange(0, duration) def setPosition(self, position): self.player.setPosition(position) def handleError(self): self.playButton.setEnabled(False) self.errorLabel.setText("Error: " + self.player.errorString()) def show_color_dialog(self): QColorDialog.getColor()
class MediaPlayerTab(GalacteekTab): statePlaying = QMediaPlayer.PlayingState statePaused = QMediaPlayer.PausedState stateStopped = QMediaPlayer.StoppedState def __init__(self, *args, **kw): super(MediaPlayerTab, self).__init__(*args, **kw) self.playlistIpfsPath = None self.playlist = QMediaPlaylist() self.model = ListModel(self.playlist) self.playlistsMenu = QMenu(self) self.playlistsMenu.triggered.connect(self.onPlaylistsMenu) self.pListWidget = QWidget(self) self.uipList = ui_mediaplaylist.Ui_MediaPlaylist() self.uipList.setupUi(self.pListWidget) self.uipList.savePlaylistButton.clicked.connect(self.onSavePlaylist) self.uipList.savePlaylistButton.setEnabled(False) self.uipList.loadPlaylistButton.setPopupMode(QToolButton.InstantPopup) self.uipList.loadPlaylistButton.setMenu(self.playlistsMenu) self.clipMenu = QMenu(self) self.copyPathAction = QAction(getIconIpfsIce(), iCopyPlaylistPath(), self, triggered=self.onCopyPlaylistPath) self.loadPathAction = QAction(getIconIpfsIce(), iLoadPlaylistFromPath(), self, triggered=self.onLoadPlaylistPath) self.copyPathAction.setEnabled(False) self.clipMenu.addAction(self.copyPathAction) self.clipMenu.addAction(self.loadPathAction) self.uipList.clipPlaylistButton.setPopupMode(QToolButton.InstantPopup) self.uipList.clipPlaylistButton.setMenu(self.clipMenu) self.uipList.clearButton.clicked.connect(self.onClearPlaylist) self.uipList.nextButton.clicked.connect(self.onPlaylistNext) self.uipList.previousButton.clicked.connect(self.onPlaylistPrevious) self.uipList.nextButton.setIcon(self.style().standardIcon( QStyle.SP_MediaSkipForward)) self.uipList.previousButton.setIcon(self.style().standardIcon( QStyle.SP_MediaSkipBackward)) self.pListView = self.uipList.listView self.pListView.mousePressEvent = self.playlistMousePressEvent self.pListView.setModel(self.model) self.pListView.setResizeMode(QListView.Adjust) self.pListView.setMinimumWidth(self.width() / 2) self.duration = None self.playerState = None self.player = QMediaPlayer(self) self.player.setPlaylist(self.playlist) self.videoWidget = MPlayerVideoWidget(self.player, self) self.useUpdates(True) self.videoWidget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.player.setVideoOutput(self.videoWidget) self.player.error.connect(self.onError) self.player.stateChanged.connect(self.onStateChanged) self.player.metaDataChanged.connect(self.onMetaData) self.player.durationChanged.connect(self.mediaDurationChanged) self.player.positionChanged.connect(self.mediaPositionChanged) self.player.videoAvailableChanged.connect(self.onVideoAvailable) self.pListView.activated.connect(self.onListActivated) self.playlist.currentIndexChanged.connect(self.playlistPositionChanged) self.playlist.currentMediaChanged.connect(self.playlistMediaChanged) self.playlist.mediaInserted.connect(self.playlistMediaInserted) self.playlist.mediaRemoved.connect(self.playlistMediaRemoved) self.togglePList = QToolButton(self) self.togglePList.setIcon(self.style().standardIcon( QStyle.SP_ArrowRight)) self.togglePList.setFixedSize(32, 128) self.togglePList.clicked.connect(self.onTogglePlaylist) self.clipboardMediaItem = None self.clipboardButton = QToolButton(clicked=self.onClipboardClicked) self.clipboardButton.setIcon(getIconClipboard()) self.clipboardButton.setEnabled(False) self.clipboardButton.setToolTip('Load media from clipboard') self.pinButton = QToolButton(clicked=self.onPinMediaClicked) self.pinButton.setIcon(getIcon('pin.png')) self.processClipboardItem(self.app.clipTracker.current, force=True) self.app.clipTracker.currentItemChanged.connect(self.onClipItemChange) self.playButton = QToolButton(clicked=self.onPlayClicked) self.playButton.setIcon(self.style().standardIcon(QStyle.SP_MediaPlay)) self.pauseButton = QToolButton(clicked=self.onPauseClicked) self.pauseButton.setIcon(self.style().standardIcon( QStyle.SP_MediaPause)) self.pauseButton.setEnabled(False) self.stopButton = QToolButton(clicked=self.onStopClicked) self.stopButton.setIcon(self.style().standardIcon(QStyle.SP_MediaStop)) self.stopButton.setEnabled(False) self.fullscreenButton = QToolButton(clicked=self.onFullScreen) self.fullscreenButton.setIcon(getIcon('fullscreen.png')) self.fullscreenButton.setToolTip(iFullScreen()) self.seekSlider = QSlider(Qt.Horizontal, sliderMoved=self.onSeek) self.seekSlider.sliderReleased.connect(self.onSliderReleased) self.seekSlider.setObjectName('mediaPlayerSlider') self.durationLabel = QLabel() vLayout = QVBoxLayout() hLayoutControls = QHBoxLayout() hLayoutControls.setContentsMargins(0, 0, 0, 0) hLayoutControls.addWidget(self.clipboardButton) hLayoutControls.addWidget(self.pinButton) hLayoutControls.addWidget(self.playButton) hLayoutControls.addWidget(self.pauseButton) hLayoutControls.addWidget(self.stopButton) hLayoutControls.addWidget(self.seekSlider) hLayoutControls.addWidget(self.durationLabel) hLayoutControls.addWidget(self.fullscreenButton) vLayout.addWidget(self.videoWidget) vLayout.addLayout(hLayoutControls) hLayout = QHBoxLayout() hLayout.addLayout(vLayout) hLayout.addWidget(self.pListWidget) hLayout.addWidget(self.togglePList) self.pListWidget.hide() self.vLayout.addLayout(hLayout) self.update() self.videoWidget.changeFocus() @property def isPlaying(self): return self.playerState == self.statePlaying @property def isPaused(self): return self.playerState == self.statePaused @property def isStopped(self): return self.playerState == self.stateStopped def useUpdates(self, updates=True): # Enable widget updates or not on the video widget self.videoWidget.setUpdatesEnabled(updates) def update(self): self.app.task(self.updatePlaylistsMenu) def onFullScreen(self): self.videoWidget.viewFullScreen(True) def onClearPlaylist(self): self.copyPathAction.setEnabled(False) self.player.stop() self.clearPlaylist() def onLoadPlaylistPath(self): current = self.app.clipTracker.getCurrent() if current: self.app.task(self.loadPlaylistFromPath, current.path) def onCopyPlaylistPath(self): if self.playlistIpfsPath: self.app.setClipboardText(self.playlistIpfsPath) def onPinMediaClicked(self): currentMedia = self.playlist.currentMedia() if currentMedia.isNull(): return messageBox(iNoMediaInPlaylist()) ensure(self.pinMedia(currentMedia)) @ipfsOp async def pinMedia(self, ipfsop, media): mediaUrl = qurlPercentDecode(media.canonicalUrl()) path = IPFSPath(mediaUrl, autoCidConv=True) if path.valid: await ipfsop.ctx.pin(str(path), qname='mediaplayer') @ipfsOp async def updatePlaylistsMenu(self, ipfsop): currentList = [ action.text() for action in self.playlistsMenu.actions() ] listing = await ipfsop.filesList(self.profile.pathPlaylists) for entry in listing: if entry['Name'] in currentList: continue action = QAction(entry['Name'], self) action.setData(entry) self.playlistsMenu.addAction(action) def playlistShowContextMenu(self, event): selModel = self.pListView.selectionModel() idx = self.pListView.indexAt(event.pos()) if not idx.isValid(): return path = self.model.data(idx) if path: selModel.reset() selModel.select(idx, QItemSelectionModel.Select) menu = QMenu(self) menu.addAction(getIcon('clear-all.png'), iPlaylistRemoveMedia(), functools.partial(self.onRemoveMediaFromIndex, idx)) menu.exec_(event.globalPos()) def onRemoveMediaFromIndex(self, idx): self.playlist.removeMedia(idx.row()) def playlistMousePressEvent(self, event): if event.button() == Qt.RightButton: self.pListView.selectionModel().reset() self.playlistShowContextMenu(event) else: if not self.pListView.indexAt(event.pos()).isValid(): self.deselectPlaylistItems() QListView.mousePressEvent(self.pListView, event) def onPlaylistsMenu(self, action): entry = action.data() self.app.task(self.loadPlaylistFromPath, joinIpfs(entry['Hash'])) def onSavePlaylist(self): paths = self.playlistGetPaths() listName = inputText(title=iPlaylistName(), label=iPlaylistName()) if not listName: return obj = JSONPlaylistV1(listName=listName, itemPaths=paths) self.app.task(self.savePlaylist, obj, listName) @ipfsOp async def savePlaylist(self, ipfsop, obj, name): objPath = os.path.join(self.profile.pathPlaylists, name) exists = await ipfsop.filesStat(objPath) if exists: await ipfsop.filesRm(objPath) ent = await ipfsop.client.core.add_json(obj.root) if ent: await ipfsop.filesLinkFp(ent, objPath) self.playlistIpfsPath = joinIpfs(ent['Hash']) self.copyPathAction.setEnabled(True) self.update() @ipfsOp async def loadPlaylistFromPath(self, ipfsop, path): try: obj = await ipfsop.jsonLoad(path) except Exception: return messageBox(iCannotLoadPlaylist()) if obj is None: return messageBox(iCannotLoadPlaylist()) try: # Assume v1 format for now, when the format evolves we'll just # use json validation pList = JSONPlaylistV1(data=obj) self.clearPlaylist() for item in pList.items(): self.queueFromPath(item['path']) self.playlistIpfsPath = path self.copyPathAction.setEnabled(True) except Exception: return messageBox(iCannotLoadPlaylist()) def playlistMediaInserted(self, start, end): self.uipList.savePlaylistButton.setEnabled( self.playlist.mediaCount() > 0) def playlistMediaRemoved(self, start, end): self.uipList.savePlaylistButton.setEnabled( self.playlist.mediaCount() > 0) self.model.modelReset.emit() def playlistGetPaths(self): return [u.path() for u in self.playlistGetUrls()] def playlistGetUrls(self): urls = [] for idx in range(0, self.playlist.mediaCount()): media = self.playlist.media(idx) urls.append(media.canonicalUrl()) return urls def onClipItemChange(self, item): self.processClipboardItem(item) def processClipboardItem(self, item, force=False): if not item: return def analyzeMimeType(cItem): if cItem.mimeCategory in ['audio', 'video', 'image']: self.clipboardMediaItem = cItem self.clipboardButton.setEnabled(True) self.clipboardButton.setToolTip(cItem.path) else: self.clipboardButton.setEnabled(False) self.clipboardButton.setToolTip(iClipboardEmpty()) if force: analyzeMimeType(item) else: item.mimeTypeAvailable.connect(lambda mType: analyzeMimeType(item)) def onClipboardClicked(self): if self.clipboardMediaItem: self.playFromPath(self.clipboardMediaItem.path) else: messageBox('Not a multimedia resource') def onSliderReleased(self): pass def onPlaylistNext(self): self.playlist.next() def onPlaylistPrevious(self): self.playlist.previous() def onPlayClicked(self): self.player.play() def onPauseClicked(self): if self.isPlaying: self.player.pause() elif self.isPaused: self.player.play() def onStopClicked(self): self.player.stop() self.player.setPosition(0) self.seekSlider.setValue(0) self.seekSlider.setRange(0, 0) def onSeek(self, seconds): if self.player.isSeekable(): self.player.setPosition(seconds * 1000) def onTogglePlaylist(self): self.pListWidget.setVisible(self.pListWidget.isHidden()) def onError(self, error): messageBox(iPlayerError(error)) def onStateChanged(self, state): self.playerState = state self.updateControls(state) def updateControls(self, state): if self.isStopped: self.stopButton.setEnabled(False) self.pauseButton.setEnabled(False) self.playButton.setEnabled(True) self.seekSlider.setEnabled(False) self.duration = None elif self.isPlaying: self.seekSlider.setRange(0, self.player.duration() / 1000) self.seekSlider.setEnabled(True) self.pauseButton.setEnabled(True) self.playButton.setEnabled(False) self.stopButton.setEnabled(True) def onListActivated(self, index): if index.isValid(): self.playlist.setCurrentIndex(index.row()) self.player.play() def onMetaData(self): # Unfinished if self.player.isMetaDataAvailable(): availableKeys = self.player.availableMetaData() for key in availableKeys: self.player.metaData(key) def playFromUrl(self, url, mediaName=None): if self.isPlaying: self.player.stop() cUrls = self.playlistGetUrls() for u in cUrls: if u.toString() == url.toString(): return messageBox(iAlreadyInPlaylist()) media = QMediaContent(url) if self.playlist.addMedia(media): count = self.model.rowCount() if count > 0: self.playlist.setCurrentIndex(count - 1) self.player.play() def playFromPath(self, path, mediaName=None): mediaUrl = self.app.subUrl(path) self.playFromUrl(mediaUrl) def queueFromPath(self, path, playLast=False, mediaName=None): mediaUrl = self.app.subUrl(path) self.playlist.addMedia(QMediaContent(mediaUrl)) if playLast: count = self.playlist.mediaCount() if count > 0: self.player.stop() self.playlist.setCurrentIndex(count - 1) self.player.play() def clearPlaylist(self): self.playlist.clear() self.pListView.reset() def playlistPositionChanged(self, position): self.pListView.setCurrentIndex(self.model.index(position, 0)) def deselectPlaylistItems(self): self.pListView.selectionModel().reset() def playlistMediaChanged(self, media): selModel = self.pListView.selectionModel() self.deselectPlaylistItems() self.model.modelReset.emit() idx = self.model.index(self.playlist.currentIndex(), 0) if idx.isValid(): selModel.select(idx, QItemSelectionModel.Select) def onVideoAvailable(self, available): if available: if self.isPlaying: self.useUpdates(False) elif self.isStopped or self.isPaused: self.useUpdates(True) else: self.useUpdates(True) def mediaDurationChanged(self, duration): self.duration = duration / 1000 self.seekSlider.setMaximum(self.duration) def mediaPositionChanged(self, progress): progress /= 1000 if self.duration: cTime = durationConvert(progress) tTime = durationConvert(self.duration) self.durationLabel.setText('{0} ({1})'.format( cTime.toString(), tTime.toString())) if not self.seekSlider.isSliderDown(): self.seekSlider.setValue(progress) async def onClose(self): self.player.stop() self.player.setMedia(QMediaContent(None)) return True def playerAvailable(self): return mediaPlayerAvailable(player=self.player)
class MusicPlayer(QMainWindow): """MusicPlayer houses all of elements that directly interact with the main window.""" def __init__(self, parent=None): """Initialize the QMainWindow widget. The window title, window icon, and window size are initialized here as well as the following widgets: QMediaPlayer, QMediaPlaylist, QMediaContent, QMenuBar, QToolBar, QLabel, QPixmap, QSlider, QDockWidget, QListWidget, QWidget, and QVBoxLayout. The connect signals for relavant widgets are also initialized. """ super(MusicPlayer, self).__init__(parent) self.setWindowTitle('Mosaic') window_icon = utilities.resource_filename('mosaic.images', 'icon.png') self.setWindowIcon(QIcon(window_icon)) self.resize(defaults.Settings().window_size, defaults.Settings().window_size + 63) # Initiates Qt objects to be used by MusicPlayer self.player = QMediaPlayer() self.playlist = QMediaPlaylist() self.playlist_location = defaults.Settings().playlist_path self.content = QMediaContent() self.menu = self.menuBar() self.toolbar = QToolBar() self.art = QLabel() self.pixmap = QPixmap() self.slider = QSlider(Qt.Horizontal) self.duration_label = QLabel() self.playlist_dock = QDockWidget('Playlist', self) self.library_dock = QDockWidget('Media Library', self) self.playlist_view = QListWidget() self.library_view = library.MediaLibraryView() self.library_model = library.MediaLibraryModel() self.preferences = configuration.PreferencesDialog() self.widget = QWidget() self.layout = QVBoxLayout(self.widget) self.duration = 0 self.playlist_dock_state = None self.library_dock_state = None # Sets QWidget() as the central widget of the main window self.setCentralWidget(self.widget) self.layout.setContentsMargins(0, 0, 0, 0) self.art.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored) # Initiates the playlist dock widget and the library dock widget self.addDockWidget(defaults.Settings().dock_position, self.playlist_dock) self.playlist_dock.setWidget(self.playlist_view) self.playlist_dock.setVisible(defaults.Settings().playlist_on_start) self.playlist_dock.setFeatures(QDockWidget.DockWidgetClosable) self.addDockWidget(defaults.Settings().dock_position, self.library_dock) self.library_dock.setWidget(self.library_view) self.library_dock.setVisible( defaults.Settings().media_library_on_start) self.library_dock.setFeatures(QDockWidget.DockWidgetClosable) self.tabifyDockWidget(self.playlist_dock, self.library_dock) # Sets the range of the playback slider and sets the playback mode as looping self.slider.setRange(0, self.player.duration() / 1000) self.playlist.setPlaybackMode(QMediaPlaylist.Sequential) # OSX system menu bar causes conflicts with PyQt5 menu bar if sys.platform == 'darwin': self.menu.setNativeMenuBar(False) # Initiates Settings in the defaults module to give access to settings.toml defaults.Settings() # Signals that connect to other methods when they're called self.player.metaDataChanged.connect(self.display_meta_data) self.slider.sliderMoved.connect(self.seek) self.player.durationChanged.connect(self.song_duration) self.player.positionChanged.connect(self.song_position) self.player.stateChanged.connect(self.set_state) self.playlist_view.itemActivated.connect(self.activate_playlist_item) self.library_view.activated.connect(self.open_media_library) self.playlist.currentIndexChanged.connect(self.change_index) self.playlist.mediaInserted.connect(self.initialize_playlist) self.playlist_dock.visibilityChanged.connect( self.dock_visiblity_change) self.library_dock.visibilityChanged.connect(self.dock_visiblity_change) self.preferences.dialog_media_library.media_library_line.textChanged.connect( self.change_media_library_path) self.preferences.dialog_view_options.dropdown_box.currentIndexChanged.connect( self.change_window_size) self.art.mousePressEvent = self.press_playback # Creating the menu controls, media controls, and window size of the music player self.menu_controls() self.media_controls() self.load_saved_playlist() def menu_controls(self): """Initiate the menu bar and add it to the QMainWindow widget.""" self.file = self.menu.addMenu('File') self.edit = self.menu.addMenu('Edit') self.playback = self.menu.addMenu('Playback') self.view = self.menu.addMenu('View') self.help_ = self.menu.addMenu('Help') self.file_menu() self.edit_menu() self.playback_menu() self.view_menu() self.help_menu() def media_controls(self): """Create the bottom toolbar and controls used for media playback.""" self.addToolBar(Qt.BottomToolBarArea, self.toolbar) self.toolbar.setMovable(False) play_icon = utilities.resource_filename('mosaic.images', 'md_play.png') self.play_action = QAction(QIcon(play_icon), 'Play', self) self.play_action.triggered.connect(self.player.play) stop_icon = utilities.resource_filename('mosaic.images', 'md_stop.png') self.stop_action = QAction(QIcon(stop_icon), 'Stop', self) self.stop_action.triggered.connect(self.player.stop) previous_icon = utilities.resource_filename('mosaic.images', 'md_previous.png') self.previous_action = QAction(QIcon(previous_icon), 'Previous', self) self.previous_action.triggered.connect(self.previous) next_icon = utilities.resource_filename('mosaic.images', 'md_next.png') self.next_action = QAction(QIcon(next_icon), 'Next', self) self.next_action.triggered.connect(self.playlist.next) repeat_icon = utilities.resource_filename('mosaic.images', 'md_repeat_none.png') self.repeat_action = QAction(QIcon(repeat_icon), 'Repeat', self) self.repeat_action.triggered.connect(self.repeat_song) self.toolbar.addAction(self.play_action) self.toolbar.addAction(self.stop_action) self.toolbar.addAction(self.previous_action) self.toolbar.addAction(self.next_action) self.toolbar.addAction(self.repeat_action) self.toolbar.addWidget(self.slider) self.toolbar.addWidget(self.duration_label) def file_menu(self): """Add a file menu to the menu bar. The file menu houses the Open File, Open Multiple Files, Open Playlist, Open Directory, and Exit Application menu items. """ self.open_action = QAction('Open File', self) self.open_action.setShortcut('O') self.open_action.triggered.connect(self.open_file) self.open_multiple_files_action = QAction('Open Multiple Files', self) self.open_multiple_files_action.setShortcut('M') self.open_multiple_files_action.triggered.connect( self.open_multiple_files) self.open_playlist_action = QAction('Open Playlist', self) self.open_playlist_action.setShortcut('CTRL+P') self.open_playlist_action.triggered.connect(self.open_playlist) self.open_directory_action = QAction('Open Directory', self) self.open_directory_action.setShortcut('D') self.open_directory_action.triggered.connect(self.open_directory) self.save_playlist_action = QAction('Save Playlist', self) self.save_playlist_action.setShortcut('CTRL+S') self.save_playlist_action.triggered.connect(self.save_playlist) self.exit_action = QAction('Quit', self) self.exit_action.setShortcut('CTRL+Q') self.exit_action.triggered.connect(self.closeEvent) self.file.addAction(self.open_action) self.file.addAction(self.open_multiple_files_action) self.file.addAction(self.open_playlist_action) self.file.addAction(self.open_directory_action) self.file.addSeparator() self.file.addAction(self.save_playlist_action) self.file.addSeparator() self.file.addAction(self.exit_action) def edit_menu(self): """Add an edit menu to the menu bar. The edit menu houses the preferences item that opens a preferences dialog that allows the user to customize features of the music player. """ self.preferences_action = QAction('Preferences', self) self.preferences_action.setShortcut('CTRL+SHIFT+P') self.preferences_action.triggered.connect( lambda: self.preferences.exec_()) self.edit.addAction(self.preferences_action) def playback_menu(self): """Add a playback menu to the menu bar. The playback menu houses """ self.play_playback_action = QAction('Play', self) self.play_playback_action.setShortcut('P') self.play_playback_action.triggered.connect(self.player.play) self.stop_playback_action = QAction('Stop', self) self.stop_playback_action.setShortcut('S') self.stop_playback_action.triggered.connect(self.player.stop) self.previous_playback_action = QAction('Previous', self) self.previous_playback_action.setShortcut('B') self.previous_playback_action.triggered.connect(self.previous) self.next_playback_action = QAction('Next', self) self.next_playback_action.setShortcut('N') self.next_playback_action.triggered.connect(self.playlist.next) self.playback.addAction(self.play_playback_action) self.playback.addAction(self.stop_playback_action) self.playback.addAction(self.previous_playback_action) self.playback.addAction(self.next_playback_action) def view_menu(self): """Add a view menu to the menu bar. The view menu houses the Playlist, Media Library, Minimalist View, and Media Information menu items. The Playlist item toggles the playlist dock into and out of view. The Media Library items toggles the media library dock into and out of view. The Minimalist View item resizes the window and shows only the menu bar and player controls. The Media Information item opens a dialog that shows information relevant to the currently playing song. """ self.dock_action = self.playlist_dock.toggleViewAction() self.dock_action.setShortcut('CTRL+ALT+P') self.library_dock_action = self.library_dock.toggleViewAction() self.library_dock_action.setShortcut('CTRL+ALT+L') self.minimalist_view_action = QAction('Minimalist View', self) self.minimalist_view_action.setShortcut('CTRL+ALT+M') self.minimalist_view_action.setCheckable(True) self.minimalist_view_action.triggered.connect(self.minimalist_view) self.view_media_info_action = QAction('Media Information', self) self.view_media_info_action.setShortcut('CTRL+SHIFT+M') self.view_media_info_action.triggered.connect( self.media_information_dialog) self.view.addAction(self.dock_action) self.view.addAction(self.library_dock_action) self.view.addSeparator() self.view.addAction(self.minimalist_view_action) self.view.addSeparator() self.view.addAction(self.view_media_info_action) def help_menu(self): """Add a help menu to the menu bar. The help menu houses the about dialog that shows the user information related to the application. """ self.about_action = QAction('About', self) self.about_action.setShortcut('H') self.about_action.triggered.connect( lambda: about.AboutDialog().exec_()) self.help_.addAction(self.about_action) def open_file(self): """Open the selected file and add it to a new playlist.""" filename, success = QFileDialog.getOpenFileName( self, 'Open File', '', 'Audio (*.mp3 *.flac)', '', QFileDialog.ReadOnly) if success: file_info = QFileInfo(filename).fileName() playlist_item = QListWidgetItem(file_info) self.playlist.clear() self.playlist_view.clear() self.playlist.addMedia( QMediaContent(QUrl().fromLocalFile(filename))) self.player.setPlaylist(self.playlist) playlist_item.setToolTip(file_info) self.playlist_view.addItem(playlist_item) self.playlist_view.setCurrentRow(0) self.player.play() def open_multiple_files(self): """Open the selected files and add them to a new playlist.""" filenames, success = QFileDialog.getOpenFileNames( self, 'Open Multiple Files', '', 'Audio (*.mp3 *.flac)', '', QFileDialog.ReadOnly) if success: self.playlist.clear() self.playlist_view.clear() for file in natsort.natsorted(filenames, alg=natsort.ns.PATH): file_info = QFileInfo(file).fileName() playlist_item = QListWidgetItem(file_info) self.playlist.addMedia( QMediaContent(QUrl().fromLocalFile(file))) self.player.setPlaylist(self.playlist) playlist_item.setToolTip(file_info) self.playlist_view.addItem(playlist_item) self.playlist_view.setCurrentRow(0) self.player.play() def open_playlist(self): """Load an M3U or PLS file into a new playlist.""" playlist, success = QFileDialog.getOpenFileName( self, 'Open Playlist', '', 'Playlist (*.m3u *.pls)', '', QFileDialog.ReadOnly) if success: playlist = QUrl.fromLocalFile(playlist) self.playlist.clear() self.playlist_view.clear() self.playlist.load(playlist) self.player.setPlaylist(self.playlist) for song_index in range(self.playlist.mediaCount()): file_info = self.playlist.media( song_index).canonicalUrl().fileName() playlist_item = QListWidgetItem(file_info) playlist_item.setToolTip(file_info) self.playlist_view.addItem(playlist_item) self.playlist_view.setCurrentRow(0) self.player.play() def save_playlist(self): """Save the media in the playlist dock as a new M3U playlist.""" playlist, success = QFileDialog.getSaveFileName( self, 'Save Playlist', '', 'Playlist (*.m3u)', '') if success: saved_playlist = "{}.m3u".format(playlist) self.playlist.save(QUrl().fromLocalFile(saved_playlist), "m3u") def load_saved_playlist(self): """Load the saved playlist if user setting permits.""" saved_playlist = "{}/.m3u".format(self.playlist_location) if os.path.exists(saved_playlist): playlist = QUrl().fromLocalFile(saved_playlist) self.playlist.load(playlist) self.player.setPlaylist(self.playlist) for song_index in range(self.playlist.mediaCount()): file_info = self.playlist.media( song_index).canonicalUrl().fileName() playlist_item = QListWidgetItem(file_info) playlist_item.setToolTip(file_info) self.playlist_view.addItem(playlist_item) self.playlist_view.setCurrentRow(0) def open_directory(self): """Open the selected directory and add the files within to an empty playlist.""" directory = QFileDialog.getExistingDirectory(self, 'Open Directory', '', QFileDialog.ReadOnly) if directory: self.playlist.clear() self.playlist_view.clear() for dirpath, __, files in os.walk(directory): for filename in natsort.natsorted(files, alg=natsort.ns.PATH): file = os.path.join(dirpath, filename) if filename.endswith(('mp3', 'flac')): self.playlist.addMedia( QMediaContent(QUrl().fromLocalFile(file))) playlist_item = QListWidgetItem(filename) playlist_item.setToolTip(filename) self.playlist_view.addItem(playlist_item) self.player.setPlaylist(self.playlist) self.playlist_view.setCurrentRow(0) self.player.play() def open_media_library(self, index): """Open a directory or file from the media library into an empty playlist.""" self.playlist.clear() self.playlist_view.clear() if self.library_model.fileName(index).endswith(('mp3', 'flac')): self.playlist.addMedia( QMediaContent(QUrl().fromLocalFile( self.library_model.filePath(index)))) self.playlist_view.addItem(self.library_model.fileName(index)) elif self.library_model.isDir(index): directory = self.library_model.filePath(index) for dirpath, __, files in os.walk(directory): for filename in natsort.natsorted(files, alg=natsort.ns.PATH): file = os.path.join(dirpath, filename) if filename.endswith(('mp3', 'flac')): self.playlist.addMedia( QMediaContent(QUrl().fromLocalFile(file))) playlist_item = QListWidgetItem(filename) playlist_item.setToolTip(filename) self.playlist_view.addItem(playlist_item) self.player.setPlaylist(self.playlist) self.player.play() def display_meta_data(self): """Display the current song's metadata in the main window. If the current song contains metadata, its cover art is extracted and shown in the main window while the track number, artist, album, and track title are shown in the window title. """ if self.player.isMetaDataAvailable(): file_path = self.player.currentMedia().canonicalUrl().toLocalFile() (album, artist, title, track_number, *__, artwork) = metadata.metadata(file_path) try: self.pixmap.loadFromData(artwork) except TypeError: self.pixmap = QPixmap(artwork) meta_data = '{} - {} - {} - {}'.format(track_number, artist, album, title) self.setWindowTitle(meta_data) self.art.setScaledContents(True) self.art.setPixmap(self.pixmap) self.layout.addWidget(self.art) def initialize_playlist(self, start): """Display playlist and reset playback mode when media inserted into playlist.""" if start == 0: if self.library_dock.isVisible(): self.playlist_dock.setVisible(True) self.playlist_dock.show() self.playlist_dock.raise_() if self.playlist.playbackMode() != QMediaPlaylist.Sequential: self.playlist.setPlaybackMode(QMediaPlaylist.Sequential) repeat_icon = utilities.resource_filename( 'mosaic.images', 'md_repeat_none.png') self.repeat_action.setIcon(QIcon(repeat_icon)) def press_playback(self, event): """Change the playback of the player on cover art mouse event. When the cover art is clicked, the player will play the media if the player is either paused or stopped. If the media is playing, the media is set to pause. """ if event.button() == 1 and configuration.Playback( ).cover_art_playback.isChecked(): if (self.player.state() == QMediaPlayer.StoppedState or self.player.state() == QMediaPlayer.PausedState): self.player.play() elif self.player.state() == QMediaPlayer.PlayingState: self.player.pause() def seek(self, seconds): """Set the position of the song to the position dragged to by the user.""" self.player.setPosition(seconds * 1000) def song_duration(self, duration): """Set the slider to the duration of the currently played media.""" duration /= 1000 self.duration = duration self.slider.setMaximum(duration) def song_position(self, progress): """Move the horizontal slider in sync with the duration of the song. The progress is relayed to update_duration() in order to display the time label next to the slider. """ progress /= 1000 if not self.slider.isSliderDown(): self.slider.setValue(progress) self.update_duration(progress) def update_duration(self, current_duration): """Calculate the time played and the length of the song. Both of these times are sent to duration_label() in order to display the times on the toolbar. """ duration = self.duration if current_duration or duration: time_played = QTime( (current_duration / 3600) % 60, (current_duration / 60) % 60, (current_duration % 60), (current_duration * 1000) % 1000) song_length = QTime((duration / 3600) % 60, (duration / 60) % 60, (duration % 60), (duration * 1000) % 1000) if duration > 3600: time_format = "hh:mm:ss" else: time_format = "mm:ss" time_display = "{} / {}".format(time_played.toString(time_format), song_length.toString(time_format)) else: time_display = "" self.duration_label.setText(time_display) def set_state(self, state): """Change the icon in the toolbar in relation to the state of the player. The play icon changes to the pause icon when a song is playing and the pause icon changes back to the play icon when either paused or stopped. """ if self.player.state() == QMediaPlayer.PlayingState: pause_icon = utilities.resource_filename('mosaic.images', 'md_pause.png') self.play_action.setIcon(QIcon(pause_icon)) self.play_action.triggered.connect(self.player.pause) elif (self.player.state() == QMediaPlayer.PausedState or self.player.state() == QMediaPlayer.StoppedState): self.play_action.triggered.connect(self.player.play) play_icon = utilities.resource_filename('mosaic.images', 'md_play.png') self.play_action.setIcon(QIcon(play_icon)) def previous(self): """Move to the previous song in the playlist. Moves to the previous song in the playlist if the current song is less than five seconds in. Otherwise, restarts the current song. """ if self.player.position() <= 5000: self.playlist.previous() else: self.player.setPosition(0) def repeat_song(self): """Set the current media to repeat and change the repeat icon accordingly. There are four playback modes: repeat none, repeat all, repeat once, and shuffle. Clicking the repeat button cycles through each playback mode. """ if self.playlist.playbackMode() == QMediaPlaylist.Sequential: self.playlist.setPlaybackMode(QMediaPlaylist.Loop) repeat_on_icon = utilities.resource_filename( 'mosaic.images', 'md_repeat_all.png') self.repeat_action.setIcon(QIcon(repeat_on_icon)) elif self.playlist.playbackMode() == QMediaPlaylist.Loop: self.playlist.setPlaybackMode(QMediaPlaylist.CurrentItemInLoop) repeat_on_icon = utilities.resource_filename( 'mosaic.images', 'md_repeat_once.png') self.repeat_action.setIcon(QIcon(repeat_on_icon)) elif self.playlist.playbackMode() == QMediaPlaylist.CurrentItemInLoop: self.playlist.setPlaybackMode(QMediaPlaylist.Random) repeat_icon = utilities.resource_filename('mosaic.images', 'md_shuffle.png') self.repeat_action.setIcon(QIcon(repeat_icon)) elif self.playlist.playbackMode() == QMediaPlaylist.Random: self.playlist.setPlaybackMode(QMediaPlaylist.Sequential) repeat_icon = utilities.resource_filename('mosaic.images', 'md_repeat_none.png') self.repeat_action.setIcon(QIcon(repeat_icon)) def activate_playlist_item(self, item): """Set the active media to the playlist item dobule-clicked on by the user.""" current_index = self.playlist_view.row(item) if self.playlist.currentIndex() != current_index: self.playlist.setCurrentIndex(current_index) if self.player.state() != QMediaPlayer.PlayingState: self.player.play() def change_index(self, row): """Highlight the row in the playlist of the active media.""" self.playlist_view.setCurrentRow(row) def minimalist_view(self): """Resize the window to only show the menu bar and audio controls.""" if self.minimalist_view_action.isChecked(): if self.playlist_dock.isVisible(): self.playlist_dock_state = True if self.library_dock.isVisible(): self.library_dock_state = True self.library_dock.close() self.playlist_dock.close() QTimer.singleShot(10, lambda: self.resize(500, 0)) else: self.resize(defaults.Settings().window_size, defaults.Settings().window_size + 63) if self.library_dock_state: self.library_dock.setVisible(True) if self.playlist_dock_state: self.playlist_dock.setVisible(True) def dock_visiblity_change(self, visible): """Change the size of the main window when the docks are toggled.""" if visible and self.playlist_dock.isVisible( ) and not self.library_dock.isVisible(): self.resize( defaults.Settings().window_size + self.playlist_dock.width() + 6, self.height()) elif visible and not self.playlist_dock.isVisible( ) and self.library_dock.isVisible(): self.resize( defaults.Settings().window_size + self.library_dock.width() + 6, self.height()) elif visible and self.playlist_dock.isVisible( ) and self.library_dock.isVisible(): self.resize( defaults.Settings().window_size + self.library_dock.width() + 6, self.height()) elif (not visible and not self.playlist_dock.isVisible() and not self.library_dock.isVisible()): self.resize(defaults.Settings().window_size, defaults.Settings().window_size + 63) def media_information_dialog(self): """Show a dialog of the current song's metadata.""" if self.player.isMetaDataAvailable(): file_path = self.player.currentMedia().canonicalUrl().toLocalFile() else: file_path = None dialog = information.InformationDialog(file_path) dialog.exec_() def change_window_size(self): """Change the window size of the music player.""" self.playlist_dock.close() self.library_dock.close() self.resize(defaults.Settings().window_size, defaults.Settings().window_size + 63) def change_media_library_path(self, path): """Change the media library path to the new path selected in the preferences dialog.""" self.library_model.setRootPath(path) self.library_view.setModel(self.library_model) self.library_view.setRootIndex(self.library_model.index(path)) def closeEvent(self, event): """Override the PyQt close event in order to handle save playlist on close.""" playlist = "{}/.m3u".format(self.playlist_location) if defaults.Settings().save_playlist_on_close: self.playlist.save(QUrl().fromLocalFile(playlist), "m3u") else: if os.path.exists(playlist): os.remove(playlist) QApplication.quit()
class MyWindow(QMainWindow): text_update = pyqtSignal(str) # Create main window def __init__(self, parent=None): QMainWindow.__init__(self, parent) self.central = QWidget(self) self.player = QMediaPlayer() self.playlist = QMediaPlaylist() self.inference = Inference() self.folder_chosen = False self.textbox = QTextEdit(self.central) self.textbox.setFont(TEXT_FONT) self.textbox.setMinimumSize(300, 100) self.text_update.connect(self.append_text) sys.stdout = self print("Camera number %u" % camera_num) print("Image size %u x %u" % IMG_SIZE) if DISP_SCALE > 1: print("Display scale %u:1" % DISP_SCALE) self.vlayout = QVBoxLayout() # Window layout self.displays = QHBoxLayout() self.disp = ImageWidget(self) self.displays.addWidget(self.disp) self.vlayout.addLayout(self.displays) self.label = QLabel(self) self.vlayout.addWidget(self.label) self.vlayout.addWidget(self.textbox) # TODO provision for allowing song to play # DONE self.current_song_start_time = datetime.now() volumeslider = QSlider(Qt.Horizontal, self) volumeslider.setFocusPolicy(Qt.NoFocus) volumeslider.valueChanged[int].connect(self.changeVolume) volumeslider.setValue(100) playBtn = QPushButton('Play') # play button pauseBtn = QPushButton('Pause') # pause button stopBtn = QPushButton('Stop') # stop button # Add playlist controls prevBtn = QPushButton('Prev') shuffleBtn = QPushButton('Shuffle') nextBtn = QPushButton('Next') # Add button layouts #controlArea = QVBoxLayout() # centralWidget controls = QHBoxLayout() playlistCtrlLayout = QHBoxLayout() # Add buttons to song controls layout controls.addWidget(playBtn) controls.addWidget(pauseBtn) controls.addWidget(stopBtn) # Add buttons to playlist controls layout playlistCtrlLayout.addWidget(prevBtn) playlistCtrlLayout.addWidget(shuffleBtn) playlistCtrlLayout.addWidget(nextBtn) # Add to vertical layout self.vlayout.addWidget(volumeslider) self.vlayout.addLayout(controls) self.vlayout.addLayout(playlistCtrlLayout) self.central.setLayout(self.vlayout) #self.central.setLayout(controlArea) self.setCentralWidget(self.central) # Connect each signal to their appropriate function playBtn.clicked.connect(self.playhandler) pauseBtn.clicked.connect(self.pausehandler) stopBtn.clicked.connect(self.stophandler) prevBtn.clicked.connect(self.prevSong) shuffleBtn.clicked.connect(self.shufflelist) nextBtn.clicked.connect(self.nextSong) self.statusBar() self.playlist.currentMediaChanged.connect(self.songChanged) self.mainMenu = self.menuBar() # Menu bar exitAction = QAction('&Exit', self) exitAction.setShortcut('Ctrl+Q') exitAction.triggered.connect(self.close) self.fileMenu = self.mainMenu.addMenu('&File') fileAct = QAction('Open File', self) folderAct = QAction('Open Folder', self) fileAct.setShortcut('Ctrl+O') folderAct.setShortcut('Ctrl+D') self.fileMenu.addAction(fileAct) self.fileMenu.addAction(folderAct) fileAct.triggered.connect(self.openFile) folderAct.triggered.connect(self.addFiles) self.fileMenu.addAction(exitAction) #self.addControls() # Start image capture & display def start(self): self.timer = QTimer(self) # Timer to trigger display self.timer.timeout.connect(lambda: self.show_image(image_queue, self.disp, DISP_SCALE)) self.timer.start(DISP_MSEC) self.capture_thread = threading.Thread(target=grab_images, args=(camera_num, image_queue)) self.capture_thread.start() # Thread to grab images # Fetch camera image from queue, and display it def show_image(self, imageq, display, scale): if not imageq.empty(): image = imageq.get() if image is not None and len(image) > 0: img = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) self.display_image(img, display, scale) # Display an image, reduce size if required def display_image(self, img, display, scale=1): disp_size = img.shape[1]//scale, img.shape[0]//scale disp_bpl = disp_size[0] * 3 if scale > 1: img = cv2.resize(img, disp_size, interpolation=cv2.INTER_CUBIC) global PREDICTION_STATUS if PREDICTION_STATUS != -1: # print(PREDICTION_STATUS) self.handle_inference(PREDICTION_STATUS) # img_try = cv2.imread('test_image.jpg') # img_try = cv2.cvtColor(img , cv2.COLOR_BGR2RGB) # self.inference.perform_inference(img) # if self.inference.network.wait() == 0 : # result = self.inference.network.extract_output() # pred = np.argmax(result[0, :]) # print(pred) # self.handle_inference( pred ) qimg = QImage(img.data, disp_size[0], disp_size[1], disp_bpl, IMG_FORMAT) display.setImage(qimg) # Handle sys.stdout.write: update text display def write(self, text): self.text_update.emit(str(text)) def flush(self): pass def check_song_is_playing(self): if (datetime.now() - self.current_song_start_time ).seconds >= 4: return False else: return True def handle_inference(self, result): if self.folder_chosen == True and self.check_song_is_playing() == False: global PREDICTION_STATUS if result == 1: #self.pausehandler() PREDICTION_STATUS = -1 pass elif result == 5: self.playhandler() elif result == 0: #self.stophandler() PREDICTION_STATUS = -1 pass elif result == 4: PREDICTION_STATUS = -1 elif result == 2: self.prevSong() elif result == 3: self.nextSong() # Append to text display def append_text(self, text): cur = self.textbox.textCursor() # Move cursor to end of text cur.movePosition(QTextCursor.End) s = str(text) while s: head,sep,s = s.partition("\n") # Split line at LF cur.insertText(head) # Insert text at cursor if sep: # New line if LF cur.insertBlock() self.textbox.setTextCursor(cur) # Update visible cursor # Window is closing: stop video capture def closeEvent(self, event): global capturing capturing = False self.capture_thread.join() def openFile(self): song = QFileDialog.getOpenFileName(self, "Open Song", "~", "Sound Files (*.mp3 *.ogg *.wav *.m4a)") if song[0] != '': url = QUrl.fromLocalFile(song[0]) if self.playlist.mediaCount() == 0: self.playlist.addMedia(QMediaContent(url)) self.player.setPlaylist(self.playlist) self.player.play() else: self.playlist.addMedia(QMediaContent(url)) def addFiles(self): if self.playlist.mediaCount() != 0: self.folderIterator() else: self.folderIterator() self.player.setPlaylist(self.playlist) self.player.playlist().setCurrentIndex(0) self.player.play() def folderIterator(self): folderChosen = QFileDialog.getExistingDirectory(self, 'Open Music Folder', '~') if folderChosen != None: it = QDirIterator(folderChosen) it.next() while it.hasNext(): if it.fileInfo().isDir() == False and it.filePath() != '.': fInfo = it.fileInfo() if fInfo.suffix() in ('mp3', 'ogg', 'wav', 'm4a'): self.playlist.addMedia(QMediaContent(QUrl.fromLocalFile(it.filePath()))) self.folder_chosen = True it.next() self.playlist.setPlaybackMode(QMediaPlaylist.Loop) if it.fileInfo().isDir() == False and it.filePath() != '.': fInfo = it.fileInfo() if fInfo.suffix() in ('mp3', 'ogg', 'wav', 'm4a'): self.playlist.addMedia(QMediaContent(QUrl.fromLocalFile(it.filePath()))) self.folder_chosen = True def playhandler(self): if self.playlist.mediaCount() == 0: self.folder_chosen = False self.addFiles() elif self.playlist.mediaCount() != 0: self.player.play() self.current_song_start_time = datetime.now() global PREDICTION_STATUS PREDICTION_STATUS = -1 def pausehandler(self): print('Stopped and cleared playlist') self.player.pause() global PREDICTION_STATUS PREDICTION_STATUS = -1 def stophandler(self): self.folder_chosen = False self.player.stop() self.playlist.clear() print('Stopped and cleared playlist') self.statusBar().showMessage("Stopped and cleared playlist") global PREDICTION_STATUS PREDICTION_STATUS = -1 def changeVolume(self, value): self.player.setVolume(value) def prevSong(self): print('playing previous song') if self.playlist.mediaCount() == 0: self.folder_chosen = False self.addFiles() elif self.playlist.mediaCount() != 0: self.player.playlist().previous() self.current_song_start_time = datetime.now() global PREDICTION_STATUS PREDICTION_STATUS = -1 def shufflelist(self): self.playlist.shuffle() def nextSong(self): print('playing next song') if self.playlist.mediaCount() == 0: self.folder_chosen = False self.addFiles() elif self.playlist.mediaCount() != 0: self.player.playlist().next() self.current_song_start_time = datetime.now() global PREDICTION_STATUS PREDICTION_STATUS = -1 def songChanged(self, media): if not media.isNull(): url = media.canonicalUrl() print('Now playing ' +url.fileName()) self.statusBar().showMessage(url.fileName())
class DBPlayer(QWidget): # signal signaltxt = pyqtSignal(str) signalnum = pyqtSignal(int) def __init__(self): super(DBPlayer, self).__init__() self.setMaximumSize(16777215, 35) # Init Player self.messtitle = TITL_PROG self.namemedia = '' self.albumname = '' self.currentPlaylist = QMediaPlaylist() self.player = QMediaPlayer() self.player.stateChanged.connect(self.qmp_stateChanged) self.player.positionChanged.connect(self.qmp_positionChanged) self.player.volumeChanged.connect(self.qmp_volumeChanged) self.player.durationChanged.connect(self.qmp_durationChanged) self.player.setVolume(60) # Init GUI self.setLayout(self.addControls()) self.infoBox = None def addControls(self): # buttons self.playBtn = QPushButton() self.playBtn.setIcon(self.style().standardIcon(QStyle.SP_MediaPlay)) self.playBtn.setStyleSheet('border: 0px;') stopBtn = QPushButton() stopBtn.setIcon(self.style().standardIcon(QStyle.SP_MediaStop)) stopBtn.setStyleSheet('border: 0px;') prevBtn = QPushButton() prevBtn.setIcon(self.style().standardIcon(QStyle.SP_MediaSkipBackward)) prevBtn.setStyleSheet('border: 0px;') nextBtn = QPushButton() nextBtn.setIcon(self.style().standardIcon(QStyle.SP_MediaSkipForward)) nextBtn.setStyleSheet('border: 0px;') volumeDescBtn = QPushButton('▼') volumeDescBtn.setIcon(self.style().standardIcon(QStyle.SP_MediaVolume)) volumeDescBtn.setMaximumWidth(30) volumeDescBtn.setStyleSheet('border: 0px;') volumeIncBtn = QPushButton('▲') volumeIncBtn.setIcon(self.style().standardIcon(QStyle.SP_MediaVolume)) volumeIncBtn.setMaximumWidth(40) volumeIncBtn.setStyleSheet('border: 0px;') infoBtn = QPushButton() infoBtn.setIcon(self.style().standardIcon( QStyle.SP_FileDialogContentsView)) infoBtn.setStyleSheet('border: 0px;') # seek slider self.seekSlider = QSlider(Qt.Horizontal, self) self.seekSlider.setMinimum(0) self.seekSlider.setMaximum(100) self.seekSlider.setTracking(False) # labels position start/end self.seekSliderLabel1 = QLabel('0:00') self.seekSliderLabel2 = QLabel('0:00') # layout controlArea = QHBoxLayout() controlArea.addWidget(prevBtn) controlArea.addWidget(self.playBtn) controlArea.addWidget(stopBtn) controlArea.addWidget(nextBtn) controlArea.addWidget(self.seekSliderLabel1) controlArea.addWidget(self.seekSlider) controlArea.addWidget(self.seekSliderLabel2) controlArea.addWidget(infoBtn) controlArea.addWidget(volumeDescBtn) controlArea.addWidget(volumeIncBtn) # link buttons to media self.seekSlider.sliderMoved.connect(self.seekPosition) self.playBtn.clicked.connect(self.playHandler) stopBtn.clicked.connect(self.stopHandler) volumeDescBtn.clicked.connect(self.decreaseVolume) volumeIncBtn.clicked.connect(self.increaseVolume) prevBtn.clicked.connect(self.prevItemPlaylist) nextBtn.clicked.connect(self.nextItemPlaylist) infoBtn.clicked.connect(self.displaySongInfo) return controlArea def playHandler(self): if self.player.state() == QMediaPlayer.PlayingState: self.player.pause() message = (' [Paused at position %s]' % self.seekSliderLabel1.text()) self.messtitle = self.namemedia + message self.signaltxt.emit(self.messtitle) else: if self.player.state() == QMediaPlayer.StoppedState: if self.player.mediaStatus() == QMediaPlayer.NoMedia: if self.currentPlaylist.mediaCount() != 0: self.player.setPlaylist(self.currentPlaylist) elif self.player.mediaStatus() == QMediaPlayer.LoadedMedia: self.player.play() elif self.player.mediaStatus() == QMediaPlayer.BufferedMedia: self.player.play() elif self.player.state() == QMediaPlayer.PlayingState: pass elif self.player.state() == QMediaPlayer.PausedState: self.player.play() if self.player.volume() is not None and self.player.state( ) == QMediaPlayer.PlayingState: message = ' [Volume %d]' % self.player.volume() self.messtitle = self.namemedia + message self.signaltxt.emit(self.messtitle) def stopHandler(self): if self.player.state() == QMediaPlayer.PlayingState: self.stopState = True self.player.stop() elif self.player.state() == QMediaPlayer.PausedState: self.player.stop() elif self.player.state() == QMediaPlayer.StoppedState: pass if self.player.volume() is not None and self.player.state( ) == QMediaPlayer.PlayingState: self.messtitle = self.namemedia + (' [Stopped]') self.signaltxt.emit(self.messtitle) def qmp_stateChanged(self): if self.player.state() == QMediaPlayer.StoppedState: self.player.stop() # buttons icon play/pause change if self.player.state() == QMediaPlayer.PlayingState: self.playBtn.setIcon(self.style().standardIcon( QStyle.SP_MediaPause)) else: self.playBtn.setIcon(self.style().standardIcon( QStyle.SP_MediaPlay)) def qmp_positionChanged(self, position): # update position slider self.seekSlider.setValue(position) # update the text label self.seekSliderLabel1.setText( '%d:%02d' % (int(position / 60000), int((position / 1000) % 60))) def seekPosition(self, position): sender = self.sender() if isinstance(sender, QSlider): if self.player.isSeekable(): self.player.setPosition(position) def qmp_volumeChanged(self): if self.player.volume() is not None: message = (' [Playing at Volume %d]' % (self.player.volume())) if self.namemedia != '': self.messtitle = self.namemedia + message else: self.messtitle = "Initialisation player " + message self.signaltxt.emit(self.messtitle) def qmp_durationChanged(self, duration): self.seekSlider.setRange(0, duration) self.seekSliderLabel2.setText( '%d:%02d' % (int(duration / 60000), int((duration / 1000) % 60))) nummedia = self.currentPlaylist.mediaCount() curmedia = self.currentPlaylist.currentIndex() #artist = self.player.metaData(QMediaMetaData.Author) #tittle = self.player.metaData(QMediaMetaData.Title) self.namemedia = path.basename(self.homMed[curmedia]) self.namemedia = '[%02d/%02d' % ( curmedia + 1, nummedia) + '] "' + self.namemedia + '"' self.buildPlaylist() message = (' [Playing at Volume %d]' % (self.player.volume())) if self.player.volume() is not None and self.player.state( ) == QMediaPlayer.PlayingState: self.messtitle = self.namemedia + message self.signaltxt.emit(self.messtitle) def buildPlaylist(self): """Build play list.""" nummedia = self.currentPlaylist.mediaCount() curmedia = self.currentPlaylist.currentIndex() + 1 compteur = 1 self.textplaylist = '<b>' + self.albumname + '</b>' self.textplaylist += '<table class="tftable" border="0">' for namemedia in self.homMed: media = path.basename(namemedia) media = '[%02d/%02d' % (compteur, nummedia) + '] "' + media + '"' if curmedia == compteur: self.textplaylist += '<tr><td><b>' + media + '</b></td></tr>' else: self.textplaylist += '<tr><td>' + media + '</td></tr>' compteur += 1 self.textplaylist = self.textplaylist + '</table>' self.playBtn.setToolTip(self.textplaylist) self.signalnum.emit(curmedia - 1) def increaseVolume(self): """Volume +.""" vol = self.player.volume() vol = min(vol + 5, 100) self.player.setVolume(vol) def decreaseVolume(self): """Volume -.""" vol = self.player.volume() vol = max(vol - 5, 0) self.player.setVolume(vol) def prevItemPlaylist(self): self.player.playlist().previous() if self.currentPlaylist.currentIndex() == -1: self.player.playlist().previous() def nextItemPlaylist(self): self.player.playlist().next() if self.currentPlaylist.currentIndex() == -1: self.player.playlist().next() def addMediaslist(self, listmedias, position, albumname): if self.currentPlaylist.mediaCount() > 0: self.currentPlaylist.removeMedia(0, self.currentPlaylist.mediaCount()) self.player.stop() self.stopHandler() self.currentPlaylist.removeMedia(0, self.currentPlaylist.mediaCount()) self.albumname = albumname if listmedias: self.homMed = listmedias for media in self.homMed: self.currentPlaylist.addMedia( QMediaContent(QUrl.fromLocalFile(media))) self.currentPlaylist.setCurrentIndex(position) self.playHandler() def displaySongInfo(self): # extract datas metaDataKeyList = self.player.availableMetaData() fullText = '<table class="tftable" border="0">' for key in metaDataKeyList: value = str(self.player.metaData(key)).replace("'", "").replace( "[", "").replace("]", "") if key == 'Duration': value = '%d:%02d' % (int( int(value) / 60000), int((int(value) / 1000) % 60)) fullText = fullText + '<tr><td>' + key + '</td><td>' + value + '</td></tr>' fullText = fullText + '</table>' # re-init if self.infoBox is not None: self.infoBox.destroy() # infos box self.infoBox = QMessageBox(self) self.infoBox.setWindowTitle('Detailed Song Information') self.infoBox.setTextFormat(Qt.RichText) self.infoBox.addButton('OK', QMessageBox.AcceptRole) self.infoBox.setText(fullText) self.infoBox.show()
class Music_App(QMainWindow): def __init__(self): super().__init__() self.title = 'Music Player' self.left = 20 self.top = 30 self.width = 600 self.height = 400 self.root_Gui() self.song_path = "" self.q_path = "" #initializing playlist and player self.Playlist = QMediaPlaylist() self.Player = QMediaPlayer() self.button_stat = -1 #self.sp_st_cam= -1 def root_Gui(self): #base of GUI self.setWindowTitle(self.title) self.setGeometry(self.left, self.top, self.width, self.height) self.menu() self.main_buttons() self.status_bar() self.show() def menu(self): #menu of GUI main_menu = self.menuBar() file_menu = main_menu.addMenu('File') tools_menu = main_menu.addMenu('Tools') help__menu = main_menu.addMenu('Help') exit_ = main_menu.addMenu('Exit') file_menu.addAction(self.open_file()) file_menu.addAction(self.exit_button()) exit_.addAction(self.exit_button()) def main_buttons(self): #different button available in GUI play_button = QPushButton('Play', self) play_button.setToolTip("To play the song click it.") play_button.move(100, 80) play_button.clicked.connect(self.play_button) pause_button = QPushButton('Pause', self) pause_button.setToolTip("To pause the song click it.") pause_button.move(200, 80) pause_button.clicked.connect(self.pause_button) next_button = QPushButton('Next', self) next_button.setToolTip('Play Next Song') next_button.move(300, 80) next_button.clicked.connect(self.next_button) prev_button = QPushButton('Prev', self) prev_button.setToolTip('Play Previous Song') prev_button.move(400, 80) prev_button.clicked.connect(self.prev_button) plusV_button = QPushButton('+ Vol', self) plusV_button.setToolTip('Increase Volume') plusV_button.move(100, 110) plusV_button.clicked.connect(self.plusV_button) minsV_button = QPushButton('- Vol', self) minsV_button.setToolTip('Decrease Volume') minsV_button.move(200, 110) minsV_button.clicked.connect(self.minsV_button) stop_button = QPushButton('Stop', self) stop_button.setToolTip('Stop Song') stop_button.move(300, 110) stop_button.clicked.connect(self.stop_button) info_button = QPushButton('Song Details', self) info_button.setToolTip('Get Song Details') info_button.move(400, 110) info_button.clicked.connect(self.info_button) #play button handler def play_button(self): print("Playing Music") if self.Playlist.mediaCount() == 0: self.file_dialog() elif self.Playlist.mediaCount() != 0: self.button_stat = 1 self.Player.play() #pause button handler def pause_button(self): print("Pause Music") self.button_stat = 2 self.Player.pause() #prev button handler def prev_button(self): print("Next Song") if self.Playlist.mediaCount() == 0: self.file_dialog() elif self.Playlist.mediaCount != 0: self.Player.playlist().previous() #next button handler def next_button(self): print("Prev Song") if self.Playlist.mediaCount() == 0: self.file_dialog() elif self.Playlist.mediaCount() != 0: self.Player.playlist().next() #info button handler def info_button(self): print("\n info button") song = eyed3.load(self.song_path) artist = song.tag.artist album = song.tag.album title = song.tag.title s_detail = 'Artist : ' + artist + '\n\tAlbum : ' + album + '\n\tTitle : ' + title song_det = QMessageBox(self) song_det.setWindowTitle('Song Details') song_det.setText(s_detail) song_det.show() #volume button handler def plusV_button(self): print("Plus Volume") vol_ = self.Player.volume() vol_ = min(100, vol_ + 5) self.Player.setVolume(vol_) def minsV_button(self): print("Minus Volume") vol_ = self.Player.volume() vol_ = max(100, vol_ - 5) self.Player.setVolume(vol_) #stop button handler def stop_button(self): print("Stopping Music") self.button_stat = 3 self.Player.stop() self.Playlist.clear() self.statusBar().showMessage("Music Stopped") #open file dialog def open_file(self): fopen_button = QAction('Open File', self) #fopen_button.setShortcut('CTRL+O') fopen_button.setStatusTip('Open Music File') fopen_button.triggered.connect(self.file_dialog) return fopen_button #EXIT button handler def exit_button(self): exit_Button = QAction('Exit', self) #exit_Button.setShortcut('CTRL+Q') exit_Button.setStatusTip('Exit Application') exit_Button.triggered.connect(self.close_msg) return exit_Button def show_qoute(self, q_path): q_img = Image.open(q_path) q_img.show() def song_play(self, song_path): self.button_stat = 1 self.Playlist.addMedia( QMediaContent(QUrl.fromLocalFile(self.song_path))) self.Player.setPlaylist(self.Playlist) self.Player.setVolume(50) self.Player.play() #file dialog handler def file_dialog(self): fName = QFileDialog.getOpenFileName( self, "Select A Song", "~", "All Files (*) ;; Mp3 Files(*.mp3)") if fName[0] == '': print("No file Selected") elif self.Playlist.mediaCount() == 0: #print(QUrl(fName[0])) self.song_path = fName[0] self.button_stat = 1 self.Playlist.addMedia(QMediaContent(QUrl.fromLocalFile(fName[0]))) self.Player.setPlaylist(self.Playlist) self.Player.setVolume(50) self.Player.play() else: self.Playlist.addMedia(QMediaContent(QUrl(fName[0]))) #close/exit handler def close_msg(self): msg = QMessageBox.question(self, 'Close Msg', 'Click Yes to Close', QMessageBox.Yes, QMessageBox.No) if msg == QMessageBox.Yes: self._obj.change_f(1) self.close() print('Closing App') else: print('Not Closing') #status handler def status_bar(self): self.statusBar().showMessage('ViAna Music Player')
class App(QMainWindow): def __init__(self): super().__init__() self.player = QMediaPlayer() self.playlist = QMediaPlaylist() self.playlist.setPlaybackMode(QMediaPlaylist.Sequential) self.title = 'JPR Reader GUI version2' self.left = 50 self.top = 100 self.width = 1200 self.height = 800 self.fileReady = False self.tableRow = 5 self.tableCol = 15 self.row = 2 self.audiolist = [] self.configFile = ".//configurationFile.xlsx" self.dict = { 'num': None, 'partner': None, 'parcel': None, 'exception': None, 'box': None } self.initUI() def initUI(self): self.setWindowTitle(self.title) self.setGeometry(self.left, self.top, self.width, self.height) # menu menubar = self.menuBar() filemenu = menubar.addMenu('File') fileAct = QAction('Open File', self) fileAct.setShortcut('Ctrl+O') filemenu.addAction(fileAct) fileAct.triggered.connect(self.openFileNameDialog) # status_bar self.statusbar = self.statusBar() # tabs self.tabs = QTabWidget() self.tab1 = QWidget(self) self.tab2 = QWidget(self) self.tabs.addTab(self.tab1, "조작") self.tabs.addTab(self.tab2, "설정") self.setCentralWidget(self.tabs) # tab1 gui self.ButtonGroupBox = self.createButtonGroupBox() self.createLogTable() self.tab1.layout = QVBoxLayout() self.tab1.layout.addWidget(self.ButtonGroupBox) self.tab1.layout.addWidget(self.logTable) self.tab1.setLayout(self.tab1.layout) # tab2 gui self.explanation = QLabel() self.explanation.setText("""<<< JPR reader 설정 테이블 >>> 이곳에서 JPR reader가 읽어주는 항목과 아이템을 설정할 수 있습니다. 항목과 아이템의 설정 방법은 셀을 더블 클릭한 후 이름을 입력하는 방식으로진행됩니다. 그 후 떠오른 파일 탐색기에서 지정할 mp3파일을 선택해 주세요. 설정의 확인은 셀의 색으로 확인 가능합니다. 지정이 완료된 항목은 노란색, 아이템은 하늘색으로 표시됩니다. 지정이 되지 않은 셀은 빨간색으로 표시됩니다. 행과 열 버튼으로 테이블의 크기를 조절할 수 있으며 초기화시 모든 설정은 사라지며 테이블은 5by5로 초기화 됩니다. <<<주의사항>>> 1. 첫번째 열은 반드시 항목을 입력해 주세요. 2. 입력되는 이름은 엑셀파일에서 표현된 명칭과 완벽히 일치해야 합니다.(엔터나 스페이스, 오타 주의!) 3. 초기화를 누르면 처음부터 모든 항목과 아이템을 지정해야 합니다. 4. 엑셀 파일의 ()나 /을 통해 구분한 아이템은 따로 입력해 주세요. 5. 사용하는 엑셀파일의 구조를 유지해 주세요. 변경시 프로그램의 수정이 필요할 수 있습니다.(예: '박스'항목은 항상 있을 것으로 간주됩니다.) 제작자 정보: 김현우([email protected])""") self.explanation.setAlignment(Qt.AlignCenter) self.createConfigTable() self.createHorizontalButtons2() self.tab2.layout = QVBoxLayout() self.tab2.layout.addWidget(self.explanation) self.tab2.layout.addWidget(self.configTable) self.tab2.layout.addWidget(self.horizontalButtons2) self.tab2.setLayout(self.tab2.layout) # Show widget self.show() def openFileNameDialog(self): fileName, _ = QFileDialog.getOpenFileName(self, "Open file", "", "All Files (*)") if not fileName: self.statusbar.showMessage('Fail to load file...') else: self.wb = load_workbook(fileName.strip()) self.ws = self.wb.active self.fileReady = True self.row = 2 self.statusbar.showMessage('succeed to load file...') self.logTable.clear() # set logTable's horizontal header self.logTable.setHorizontalHeaderItem(0, QTableWidgetItem("포장순번")) self.logTable.setHorizontalHeaderItem(1, QTableWidgetItem("거래처명")) self.logTable.setHorizontalHeaderItem(2, QTableWidgetItem("배송센터")) self.logTable.setHorizontalHeaderItem(3, QTableWidgetItem("특이사항")) self.logTable.setHorizontalHeaderItem(4, QTableWidgetItem("박스")) # initialize dictionary self.dict = { 'num': None, 'partner': None, 'parcel': None, 'exception': None, 'box': None } col = 6 num = 0 header = str(self.ws.cell(row=1, column=col + 1).value).strip() while header != 'None': self.dict[header] = None col = col + 1 num = num + 1 header = str(self.ws.cell(row=1, column=col + 1).value).strip() self.tableCol = 5 + num self.logTable.setColumnCount(self.tableCol) for c in range(5, self.tableCol): self.logTable.setHorizontalHeaderItem( c, QTableWidgetItem(list(self.dict.keys())[c])) def createButtonGroupBox(self): buttonGroupBox = QGroupBox("Controller") vLayout = QVBoxLayout() pre_button = QPushButton('w', self) pre_button.clicked.connect(self.pre_click) pre_button.setIcon(QIcon('.\\img\\up-arrow.png')) pre_button.setIconSize(QSize(600, 100)) pre_button.setShortcut('w') vLayout.addWidget(pre_button) hBottensWidget = self.createHButtons() vLayout.addWidget(hBottensWidget) next_button = QPushButton('x', self) next_button.clicked.connect(self.next_click) next_button.setIcon(QIcon('.\\img\\down-arrow.png')) next_button.setIconSize(QSize(600, 100)) next_button.setShortcut('x') vLayout.addWidget(next_button) buttonGroupBox.setLayout(vLayout) return buttonGroupBox def createHButtons(self): hBottensWidget = QWidget() hLayout = QHBoxLayout() back_button = QPushButton('a', self) back_button.clicked.connect(self.back_click) back_button.setIcon(QIcon('.\\img\\left-arrow.png')) back_button.setIconSize(QSize(200, 150)) back_button.setShortcut('a') hLayout.addWidget(back_button) cur_button = QPushButton('s', self) cur_button.clicked.connect(self.cur_click) cur_button.setIcon(QIcon('.\\img\\reload.png')) cur_button.setIconSize(QSize(200, 150)) cur_button.setShortcut('s') hLayout.addWidget(cur_button) forward_button = QPushButton('d', self) forward_button.clicked.connect(self.forward_click) forward_button.setIcon(QIcon('.\\img\\right-arrow.png')) forward_button.setIconSize(QSize(200, 150)) forward_button.setShortcut('d') hLayout.addWidget(forward_button) hBottensWidget.setLayout(hLayout) return hBottensWidget def createHorizontalButtons2(self): self.horizontalButtons2 = QGroupBox("설정 변경") layout = QHBoxLayout() plusRowButton = QPushButton('행+', self) plusRowButton.clicked.connect(self.plus_row) layout.addWidget(plusRowButton) plusColButton = QPushButton('열+', self) plusColButton.clicked.connect(self.plus_col) layout.addWidget(plusColButton) init_button = QPushButton('초기화', self) init_button.clicked.connect(self.initialize) layout.addWidget(init_button) self.horizontalButtons2.setLayout(layout) def createLogTable(self): # Create table self.logTable = QTableWidget() self.logTable.setRowCount(self.tableRow) self.logTable.setColumnCount(self.tableCol) self.logTable.move(0, 0) def createConfigTable(self): self.configTable = QTableWidget() try: # load configurationFile cwb = load_workbook(self.configFile.strip()) except: # message box string = "설정파일을 불러올 수 없습니다." QMessageBox.question(self, 'Error', string, QMessageBox.Ok, QMessageBox.Ok) else: # Get matadata cws = cwb.active self.crow = cws.cell(row=1, column=1).value self.ccol = cws.cell(row=1, column=2).value # Configure table self.configTable.setRowCount(self.crow) self.configTable.setColumnCount(self.ccol) self.configTable.move(0, 0) # Load data from configFile for i in range(self.crow): for j in range(self.ccol): item = str(cws.cell(row=i + 2, column=j + 1).value).strip() if item == 'None': item = '' self.configTable.setItem(i, j, QTableWidgetItem(item)) # check if files exist arr = listdir('./audio_clips') for row in range(self.crow): for col in range(self.ccol): # if 박스(0,0) if row == 0 and col == 0: continue # reset backgound color self.configTable.item(row, col).setBackground(QColor(255, 0, 0)) # if file exist, change background color fname = str(row) + '_' + str(col) + '.mp3' if fname in arr: if row == 0: self.configTable.item(row, col).setBackground( QColor(255, 255, 0)) else: self.configTable.item(row, col).setBackground( QColor(0, 255, 255)) # 박스(0,0) self.configTable.setItem(0, 0, QTableWidgetItem('박스')) self.configTable.item(0, 0).setBackground(QColor(255, 255, 255)) # link the callback function self.configTable.itemDoubleClicked.connect(self.item_doubleClicked) self.configTable.itemChanged.connect(self.item_changed) def setLogTable(self): # shifting other rows for r in range(1, self.tableRow): for c in range(self.tableCol): try: self.logTable.item(r, c).setBackground(QColor(255, 255, 255)) self.logTable.setItem(r - 1, c, self.logTable.item(r, c).clone()) except: pass # set current row for idx, key in enumerate(list(self.dict.keys())): if type(self.dict[key]) is list: self.logTable.setItem( self.tableRow - 1, idx, QTableWidgetItem(' '.join(self.dict[key]))) self.logTable.item(self.tableRow - 1, idx).setBackground(QColor(255, 255, 0)) else: self.logTable.setItem(self.tableRow - 1, idx, QTableWidgetItem(self.dict[key])) self.logTable.item(self.tableRow - 1, idx).setBackground(QColor(255, 255, 0)) def read(self): # 포장 순번 self.dict['num'] = str(self.ws.cell(row=self.row, column=3).value[2:]).strip() # 거래처명 self.dict['partner'] = str(self.ws.cell(row=self.row, column=5).value).strip() # 배송센터 self.dict['parcel'] = str(self.ws.cell(row=self.row, column=4).value).strip() # 특이사항 self.dict['exception'] = str( self.ws.cell(row=self.row, column=6).value).strip() # 박스 self.dict['box'] = str(self.ws.cell(row=self.row, column=2).value).strip() # left things print(len(self.dict)) for i in range(5, len(self.dict)): header = str(self.ws.cell(row=1, column=i + 2).value).strip() self.dict[header] = str( self.ws.cell(row=self.row, column=i + 2).value).strip() self.parsing(header, self.dict[header]) print(self.dict) self.setLogTable() def parsing(self, key, val): if val == 'None' or val == '': self.dict[key] = None else: if '(' in val or '/' in val: arr = re.split('[(/]', val) for i in range(len(arr)): if ')' in arr[i]: arr[i] = arr[i][:arr[i].index(')')] arr[i] = arr[i].strip() self.dict[key] = arr else: self.dict[key] = val def itemFromKeyVal(self, key, val): items = self.configTable.findItems(val, Qt.MatchExactly) if len(items) <= 0: # Error string = '(' + key + ', ' + val + ') ' + '아이템을 찾을 수 없습니다.' QMessageBox.question(self, 'Error', string, QMessageBox.Ok, QMessageBox.Ok) else: for item in items: if self.configTable.item(0, item.column()).data(0) == key: return item def load_audiolist(self): for key, val in self.dict.items(): # 포장순번 if key == 'num': for i in range(len(self.dict['num'])): self.audiolist.append('_' + val[i]) # beep self.audiolist.append('_beep') # 택배발송 elif key == 'parcel': if val == '택배발송': self.audiolist.append('_택배발송') # beep self.audiolist.append('_beep') # 박스 elif key == 'box': item = self.itemFromKeyVal('박스', val) if item: self.audiolist.append( str(item.row()) + '_' + str(item.column())) # beep self.audiolist.append('_beep') elif key in ['partner', 'exception']: pass # general case else: # The case(val == None) will be ignored if val == None: pass # when val is list elif type(val) == list: for idx, eachVal in enumerate(val): item = self.itemFromKeyVal(key, eachVal) if item: if idx == 0: self.audiolist.append( '0_' + str(item.column())) # key self.audiolist.append( str(item.row()) + '_' + str(item.column())) # val # beep self.audiolist.append('_beep') # when val is not list else: item = self.itemFromKeyVal(key, val) if item: if val == '1' or key == val: self.audiolist.append('0_' + str(item.column())) # key else: self.audiolist.append('0_' + str(item.column())) # key self.audiolist.append( str(item.row()) + '_' + str(item.column())) # val # beep self.audiolist.append('_beep') print(self.audiolist) def speak(self): self.playlist.clear() for clip in self.audiolist: url = QUrl.fromLocalFile('./audio_clips/' + clip + '.mp3') #print(url) self.playlist.addMedia(QMediaContent(url)) self.player.setPlaylist(self.playlist) self.player.play() #----------------------- Control button callback -----------------------# @pyqtSlot() def pre_click(self): if not self.fileReady: self.cur_click() else: if self.row == 2: self.statusbar.showMessage("Can't be previous.") else: self.row -= 1 self.dict = self.dict.fromkeys(self.dict, None) del self.audiolist[:] self.read() self.load_audiolist() self.speak() @pyqtSlot() def cur_click(self): if not self.fileReady: string = '파일이 준비되어 있지 않습니다.' QMessageBox.question(self, '경고', string, QMessageBox.Ok, QMessageBox.Ok) self.openFileNameDialog() else: self.dict = self.dict.fromkeys(self.dict, None) del self.audiolist[:] self.read() self.load_audiolist() self.speak() @pyqtSlot() def next_click(self): if not self.fileReady: self.cur_click() else: if self.row == self.ws.max_row: self.statusbar.showMessage("It's over.") else: self.row += 1 self.dict = self.dict.fromkeys(self.dict, None) del self.audiolist[:] self.read() self.load_audiolist() self.speak() @pyqtSlot() def back_click(self): if self.playlist.mediaCount() == 0: self.cur_click() elif self.playlist.mediaCount() != 0: p = re.compile('.+_beep.+') cnt = 0 for i in range(self.playlist.mediaCount()): # if it's start point, start at here if self.playlist.currentIndex() == 0: break # go backward self.playlist.setCurrentIndex(self.playlist.previousIndex(1)) # start at previous beep point if p.match(str(self.playlist.currentMedia().canonicalUrl())): cnt = cnt + 1 if cnt == 2: print(self.playlist.currentIndex()) if self.player.state() == QMediaPlayer.StoppedState: self.player.play() break @pyqtSlot() def forward_click(self): if self.playlist.mediaCount() == 0: self.cur_click() elif self.playlist.mediaCount() != 0: p = re.compile('.+_beep.+') for i in range(self.playlist.mediaCount()): # don't go further from end point if self.playlist.currentIndex() < 0: break # go forward self.playlist.setCurrentIndex(self.playlist.nextIndex(1)) # start at next beep point if p.match(str(self.playlist.currentMedia().canonicalUrl())): print(self.playlist.currentIndex()) break #----------------------- Configuration button callback -----------------------# @pyqtSlot() def plus_row(self): # change configTable self.crow = self.crow + 1 self.configTable.setRowCount(self.crow) # fill the generated cell self.configTable.itemChanged.disconnect(self.item_changed) for col in range(self.ccol): self.configTable.setItem(self.crow - 1, col, QTableWidgetItem('')) self.configTable.item(self.crow - 1, col).setBackground(QColor(255, 0, 0)) self.configTable.itemChanged.connect(self.item_changed) @pyqtSlot() def plus_col(self): # change configTable self.ccol = self.ccol + 1 self.configTable.setColumnCount(self.ccol) # fill the generated cell self.configTable.itemChanged.disconnect(self.item_changed) for row in range(self.crow): self.configTable.setItem(row, self.ccol - 1, QTableWidgetItem('')) self.configTable.item(row, self.ccol - 1).setBackground( QColor(255, 0, 0)) self.configTable.itemChanged.connect(self.item_changed) @pyqtSlot() def initialize(self): # remove configurable audio files arr = listdir('./audio_clips') p = re.compile('_.+') for file in arr: if p.match(file): continue remove('./audio_clips/' + file) # init configTable self.configTable.itemChanged.disconnect(self.item_changed) # lock self.configTable.clear() self.crow = 5 self.ccol = 5 self.configTable.setRowCount(self.crow) self.configTable.setColumnCount(self.ccol) # reset configTable item for row in range(self.crow): for col in range(self.ccol): self.configTable.setItem(row, col, QTableWidgetItem('')) self.configTable.item(row, col).setBackground(QColor(255, 0, 0)) self.configTable.setItem(0, 0, QTableWidgetItem('박스')) self.configTable.item(0, 0).setBackground(QColor(255, 255, 255)) self.configTable.itemChanged.connect(self.item_changed) # unlock # init configFile self.update_configFile() #---------------------- ConfigTable signal callback ------------------------# def item_doubleClicked(self, item): self.previousItem = item.data(0) print(self.previousItem) def update_configFile(self): cwb_w = Workbook(write_only=True) cws_w = cwb_w.create_sheet() cws_w.append([self.crow, self.ccol]) for row in range(self.crow): itemList = [] for col in range(self.ccol): itemList.append(self.configTable.item(row, col).data(0)) cws_w.append(itemList) try: cwb_w.save(self.configFile) except: string = """설정파일이 열려있을 수 있습니다. 설정파일을 닫은 후에 다시 시도하세요.""" QMessageBox.question(self, 'Error', string, QMessageBox.Ok, QMessageBox.Ok) def item_changed(self, item): self.configTable.itemChanged.disconnect(self.item_changed) # lock if item.row() == 0 and item.column() == 0: string = "이 항목은 바꿀 수 없습니다." QMessageBox.question(self, 'Error', string, QMessageBox.Ok, QMessageBox.Ok) self.configTable.setItem(item.row(), item.column(), QTableWidgetItem(self.previousItem)) else: # get file name fileName, _ = QFileDialog.getOpenFileName(self, "Open file", "", "All Files (*)") # count the number of key that has same name keys = self.configTable.findItems(item.data(0), Qt.MatchExactly) kcnt = 0 for key in keys: if key.row() == 0: kcnt = kcnt + 1 # count the number of atribute that has same name atributes = self.configTable.findItems(item.data(0), Qt.MatchExactly) acnt = 0 for atribute in atributes: if atribute.row() == 0: pass elif atribute.column() == item.column(): acnt = acnt + 1 # change is accepted only in case of uniqueness and existence and it is not 박스(0,0) if kcnt >= 2: string = "항목명이 같을 수 없습니다." QMessageBox.question(self, 'Error', string, QMessageBox.Ok, QMessageBox.Ok) self.configTable.setItem(item.row(), item.column(), QTableWidgetItem(self.previousItem)) elif acnt >= 2: string = "같은 항목에 같은 이름의 아이템을 둘 수 없습니다." QMessageBox.question(self, 'Error', string, QMessageBox.Ok, QMessageBox.Ok) self.configTable.setItem(item.row(), item.column(), QTableWidgetItem(self.previousItem)) elif fileName: # copy file to local dir dst = "./audio_clips./" + str(item.row()) + '_' + str( item.column()) + '.mp3' copyfile(fileName, dst) # change cell color if item.row() == 0: self.configTable.item(item.row(), item.column()).setBackground( QColor(255, 255, 0)) else: self.configTable.item(item.row(), item.column()).setBackground( QColor(0, 255, 255)) # update configFile self.update_configFile() else: string = "선택이 취소됨." QMessageBox.question(self, 'Error', string, QMessageBox.Ok, QMessageBox.Ok) self.configTable.setItem(item.row(), item.column(), QTableWidgetItem(self.previousItem)) self.configTable.itemChanged.connect(self.item_changed) # unlock
class MainUIClass(QMainWindow, mp3playerGUILayout3.Ui_MainWindow): def __init__(self, parent=None): super(MainUIClass, self).__init__(parent) self.setupUi(self) self.fixLayout() self.mediaPlayer = QMediaPlayer() self.mediaPlayer.error.connect(self.erroralert) self.playlist = QMediaPlaylist() self.mediaPlayer.setPlaylist(self.playlist) #Connecting button clicks and functions self.OpenButton.clicked.connect(self.addtoqueue) self.PlayButton.clicked.connect(self.play) self.StopButton.clicked.connect(self.mediaPlayer.stop) self.muteButton.clicked.connect(self.mute) self.ForwardButton.clicked.connect(self.playlist.next) self.BackwardButton.clicked.connect(self.playlist.previous) self.addToQueue.clicked.connect(self.addtoqueue) self.repeatButton.clicked.connect(self.repeat) self.shuffleButton.clicked.connect(self.shuffle) #Adding valuechange functionality self.Timeline.setRange(0, 100) self.Timeline.sliderReleased.connect(self.set_position) self.mediaPlayer.setVolume(50) self.volume_on = True self.Volumedial.setValue(50) self.Volumedial.valueChanged.connect(self.set_volume) self.displayfilename_thread = DisplayThread() self.displayfilename_thread.start() self.displayfilename_thread.labeltext.connect( self.displayfilename_func) self.mediaPlayer.stateChanged.connect(self.mediaStateChanged) self.mediaPlayer.durationChanged.connect(self.durationChanged) self.mediaPlayer.positionChanged.connect(self.positionChanged) self.playlist.currentMediaChanged.connect(self.filenameChange) self.tableWidget.cellDoubleClicked.connect(self.getcurrentrow) #self.mediaPlayer.mutedChanged.connect(self.muteChange) self.items = 0 self.repeated = True self.shuffled = True #p = self.palette() #p.setColor(self.backgroundRole(), Qt.lightGray) #self.setPalette(p) #self.dumpObjectInfo() @Slot() def addtoqueue(self): filename, _ = QFileDialog.getOpenFileName( self, "Select song") # QDir.homePath()) if filename != '': self.playlist.addMedia(QMediaContent(QUrl.fromLocalFile(filename))) self.addtoview(filename) def addtoview(self, filename): self.tableWidget.insertRow(self.items) table_filename = QTableWidgetItem(filename.split("/")[-1]) inputdialog = InputDialog() inputdialog.exec_() if len(inputdialog.accept_func()[0]) > 0 or len( inputdialog.accept_func()[1]) > 0: table_artist = QTableWidgetItem(inputdialog.accept_func()[0]) table_artist.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled) table_song = QTableWidgetItem(inputdialog.accept_func()[1]) table_song.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled) else: table_artist = QTableWidgetItem("..artist..") table_artist.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled) table_song = QTableWidgetItem("..song..") table_song.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled) self.tableWidget.setItem(self.items, 0, table_artist) self.tableWidget.setItem(self.items, 1, table_song) for i in range(self.playlist.mediaCount()): print(self.playlist.media(i).canonicalUrl().fileName()) self.PlayButton.setEnabled(True) self.items += 1 @Slot() def play(self): if self.playlist.mediaCount() == 0: alert = selectfileAlert() alert.exec_() elif self.mediaPlayer.state() == QMediaPlayer.PlayingState: self.mediaPlayer.pause() else: self.mediaPlayer.play() @Slot() def set_volume(self): volume = self.Volumedial.value() self.mediaPlayer.setVolume(volume) @Slot(QMediaPlayer.State) def mediaStateChanged(self, state): if self.mediaPlayer.state() == QMediaPlayer.PlayingState: self.PlayButton.setIcon(self.style().standardIcon( QStyle.SP_MediaPause)) icon = self.style().standardIcon(QStyle.SP_DialogYesButton) self.PlayingIcon.setPixmap(icon.pixmap(QSize(15, 15))) elif self.mediaPlayer.state() == QMediaPlayer.StoppedState: filename = "........Select song to enjoy the moment........" self.displayfilename_thread.filename = filename self.PlayButton.setIcon(self.style().standardIcon( QStyle.SP_MediaPlay)) icon = self.style().standardIcon(QStyle.SP_DialogNoButton) self.PlayingIcon.setPixmap(icon.pixmap(QSize(15, 15))) else: self.PlayButton.setIcon(self.style().standardIcon( QStyle.SP_MediaPlay)) icon = self.style().standardIcon(QStyle.SP_DialogYesButton) self.PlayingIcon.setPixmap(icon.pixmap(QSize(15, 15))) @Slot() def repeat(self): if self.repeated: self.playlist.setPlaybackMode(QMediaPlaylist.CurrentItemInLoop) self.repeatButton.setFlat(True) self.repeated = False self.statusbar.showMessage("Repeat function is on") else: self.playlist.setPlaybackMode(QMediaPlaylist.CurrentItemOnce) self.repeatButton.setFlat(False) self.repeated = True self.statusbar.showMessage("Repeat function is off") @Slot() def shuffle(self): if self.shuffled: self.playlist.setPlaybackMode(QMediaPlaylist.Random) self.shuffleButton.setFlat(True) self.shuffled = False self.displayfilename_thread.exit() self.statusbar.clearMessage() self.statusbar.showMessage("Shuffle function is on", 100000) self.statusbar.clearMessage() self.displayfilename_thread.start() else: self.playlist.setPlaybackMode(QMediaPlaylist.Sequential) self.shuffleButton.setFlat(False) self.shuffled = True self.displayfilename_thread.exit() self.statusbar.clearMessage() self.statusbar.showMessage("Shuffle function is off", 100000) self.statusbar.clearMessage() self.displayfilename_thread.start() @Slot('qint64', name="positionChanged") def positionChanged(self, position): self.Timeline.setValue(position) self.displaytime_func(position) @Slot() def set_position(self): self.mediaPlayer.setPosition( self.Timeline.value()) #self.mediaPlayer.setPosition(position) @Slot('qint64', name='durationChanged') def durationChanged(self, duration): self.Timeline.setRange(0, duration) def displaytime_func(self, position): sec = round(position / 1000.) sec_dt = datetime.timedelta(seconds=sec) sec_leftdt = datetime.timedelta( seconds=round(self.mediaPlayer.duration() / 1000.) - sec + 1) sec_format = datetime.datetime(2000, 1, 1) + sec_dt sec_formatdt = datetime.datetime(2000, 1, 1) + sec_leftdt self.Timepassed.setText(sec_format.strftime("%M min %S s")) self.Timeleft.setText(sec_formatdt.strftime("-%M min %S s")) @Slot('QString') def displayfilename_func(self, text): filetext = text.split(",")[0].replace("text: ", "") if text.split(",")[1] == " loop: 1": self.statusbar.showMessage(filetext) #self.displayfilename.setText(filetext) if text.split(",")[1] == " loop: 2": self.statusbar.showMessage(filetext) @Slot(QMediaContent) def filenameChange(self, media): if media: self.displayfilename_thread.quit() filename = media.canonicalUrl().fileName() filename = "Now playing ..... " + filename self.displayfilename_thread.filename = filename self.displayfilename_thread.start() @Slot() def mute(self): if self.volume_on: self.muteButton.setIcon(self.style().standardIcon( QStyle.SP_MediaVolumeMuted)) self.mediaPlayer.setMuted(True) self.volume_on = False else: self.muteButton.setIcon(self.style().standardIcon( QStyle.SP_MediaVolume)) self.mediaPlayer.setMuted(False) self.volume_on = True def fixLayout(self): _translate = QCoreApplication.translate self.PlayButton.setIcon(self.style().standardIcon(QStyle.SP_MediaPlay)) self.BackwardButton.setIcon(self.style().standardIcon( QStyle.SP_MediaSkipBackward)) self.ForwardButton.setIcon(self.style().standardIcon( QStyle.SP_MediaSkipForward)) self.StopButton.setIcon(self.style().standardIcon(QStyle.SP_MediaStop)) self.muteButton.setIcon(self.style().standardIcon( QStyle.SP_MediaVolume)) icon = self.style().standardIcon(QStyle.SP_DialogNoButton) self.PlayingIcon.setPixmap(icon.pixmap(QSize(15, 15))) self.addToQueue.setIcon(self.style().standardIcon( QStyle.SP_FileDialogNewFolder)) self.repeatButton.setIcon(self.style().standardIcon( QStyle.SP_BrowserReload)) self.shuffleButton.setIcon(self.style().standardIcon( QStyle.SP_DialogOkButton)) self.shuffleButton.setText(_translate("MainWindow", "")) self.repeatButton.setText(_translate("MainWindow", "")) self.PlayingIcon.setText(_translate("MainWindow", "")) self.PlayButton.setText(_translate("MainWindow", "")) self.BackwardButton.setText(_translate("MainWindow", "")) self.ForwardButton.setText(_translate("MainWindow", "")) self.StopButton.setText(_translate("MainWindow", "")) self.Timepassed.setText(_translate("MainWindow", "00 min 00 s")) self.muteButton.setText(_translate("MainWindow", "")) self.addToQueue.setText(_translate("MainWindow", "")) #self.displayfilename.setText(_translate("MainWindow", "")) #self.displayfilename2.setText(_translate("MainWindow", "")) self.Timeleft.setText(_translate("MainWindow", "-00 min 00 s")) self.tableWidget.setHorizontalHeaderLabels(["Artist", "Song"]) def erroralert(self): print(self.mediaPlayer.error()) #print("type of args: " + type(args), " args: ",args) def getcurrentrow(self, row, col): self.playlist.setCurrentIndex(row + 1) self.mediaPlayer.play()
class MusicApp(QMainWindow): def __init__(self): super().__init__() self.player = QMediaPlayer() self.playlist = QMediaPlaylist() self.title = 'GestureTunes - player' self.left = 300 self.top = 300 self.width = 600 self.height = 340 self.volume_initial_value = 60 self.model = QStandardItemModel(0, 1) self.song_list = QtWidgets.QTableView() self.play_btn = QPushButton('播放') self.media_controls = QHBoxLayout() # 共享变量 self.image_to_show = None self.rec_res = { "set": False, "used": True, "direction": None, "fingers": 0 } # 摄像头窗口 self.widget = QWidget() self.widget.move(1000, 200) self.widget.resize(300, 200) self.widget.setWindowTitle("GestureTunes - gesture panel") # 窗口标题 self.videoFrame = QLabel('正在打开摄像头,请稍等...') video_area = QVBoxLayout() self.widget.setLayout(video_area) video_area.addWidget(self.videoFrame) self.widget.show() # self.widget.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint) # 定时器 self.timer = QTimer() self.timer.setInterval(20) self.timer.start() self.timer.timeout.connect(self.show_image) self.have_song = False self.volume_slider = QSlider(Qt.Horizontal, self) self.volume_slider.setTracking(True) # 播放模式 self.play_style = '列表循环' self.single_img = QIcon('pics/single.png') self.loop_img = QIcon('pics/loop.png') self.play_style_btn = QPushButton() self.play_style_btn.setIcon(self.loop_img) self.play_tip = '' self.playlist.setPlaybackMode(QMediaPlaylist.Loop) # 初始化界面 self.init_ui() def init_ui(self): # Add file menu menubar = self.menuBar() file_menu = menubar.addMenu('文件') fileAct = QAction('打开文件', self) folderAct = QAction('打开文件夹', self) file_menu.addAction(fileAct) file_menu.addAction(folderAct) fileAct.triggered.connect(self.open_file) folderAct.triggered.connect(self.add_files) self.add_listener() self.setWindowTitle(self.title) self.setGeometry(self.left, self.top, self.width, self.height) self.show() def add_listener(self): wid = QWidget(self) self.setCentralWidget(wid) # 播放控件 self.volume_slider = QSlider(Qt.Horizontal, self) self.volume_slider.setFocusPolicy(Qt.NoFocus) self.volume_slider.valueChanged[int].connect(self.change_volume) self.volume_slider.setValue(self.volume_initial_value) open_btn = QPushButton('打开...') prev_btn = QPushButton('上一首') next_btn = QPushButton('下一首') # 显示播放列表 self.set_play_list() self.song_list.setSelectionBehavior( QtWidgets.QAbstractItemView.SelectRows) self.song_list.setShowGrid(False) self.song_list.setTextElideMode(QtCore.Qt.ElideLeft) # 按钮的layout control_area = QVBoxLayout() # centralWidget self.media_controls = QHBoxLayout() # 将播放控件添加到layout self.media_controls.addWidget(open_btn) self.media_controls.addWidget(prev_btn) self.media_controls.addWidget(self.play_btn) self.media_controls.addWidget(next_btn) self.media_controls.addWidget(self.play_style_btn) self.media_controls.addWidget(self.volume_slider) # 将layout添加到界面中 control_area.addWidget(self.song_list) control_area.addLayout(self.media_controls) wid.setLayout(control_area) # 设置监听 self.play_btn.clicked.connect(self.start_or_stop) open_btn.clicked.connect(self.add_files) prev_btn.clicked.connect(self.prev) next_btn.clicked.connect(self.next) self.song_list.doubleClicked.connect(self.change_song) self.play_style_btn.clicked.connect(self.change_play_style) self.statusBar() self.playlist.currentMediaChanged.connect(self.song_changed) def set_play_list(self): self.model.clear() self.song_list.horizontalHeader().hide() # self.song_list.verticalHeader().hide() # QHeaderView() self.song_list.setModel(self.model) # 把数据添加至QtableView中 self.song_list.horizontalHeader().setSectionResizeMode( QHeaderView.Stretch) self.song_list.setEditTriggers( QAbstractItemView.NoEditTriggers) # 设置不可编辑单元格 if self.playlist.mediaCount(): # 添加数据 for index in range(self.playlist.mediaCount()): self.model.appendRow([ # 添加一行数据 QStandardItem( self.playlist.media(index).canonicalUrl().fileName()) ]) self.song_list.selectRow(0) def open_file(self): song = QFileDialog.getOpenFileName(self, "打开文件", "~", "音频文件 (*.mp3 *.ogg *.wav *.m4a)") if song[0] != '': url = QUrl.fromLocalFile(song[0]) if self.playlist.mediaCount() == 0: self.playlist.addMedia(QMediaContent(url)) self.player.setPlaylist(self.playlist) self.player.play() else: self.playlist.addMedia(QMediaContent(url)) self.set_play_list() self.have_song = True def add_files(self): if self.playlist.mediaCount() != 0: self.playlist.clear() self.folder_iterator() self.player.setPlaylist(self.playlist) self.player.playlist().setCurrentIndex(0) self.song_list.selectRow(0) self.set_play_list() self.player.pause() self.play_tip = "暂停:" + self.playlist.currentMedia().canonicalUrl( ).fileName() self.statusBar().showMessage(self.play_tip + " - " + self.play_style) self.have_song = True def folder_iterator(self): folder_chosen = QFileDialog.getExistingDirectory(self, '打开文件夹', '~') if folder_chosen is not None: it = QDirIterator(folder_chosen) it.next() while it.hasNext(): if (not it.fileInfo().isDir()) and it.filePath() != '.': f_info = it.fileInfo() if f_info.suffix() in ('mp3', 'ogg', 'wav', 'm4a'): self.playlist.addMedia( QMediaContent(QUrl.fromLocalFile(it.filePath()))) it.next() if (not it.fileInfo().isDir()) and it.filePath() != '.': f_info = it.fileInfo() if f_info.suffix() in ('mp3', 'ogg', 'wav', 'm4a'): self.playlist.addMedia( QMediaContent(QUrl.fromLocalFile(it.filePath()))) def start_or_stop(self): print(self.player.state()) if self.playlist.mediaCount() == 0: self.open_file() else: if self.player.state() == QMediaPlayer.PlayingState or \ (self.player.state() == QMediaPlayer.PlayingState and self.play_btn.text() == '暂停'): self.player.pause() self.play_btn.setText('播放') self.play_tip = "暂停:" + self.playlist.currentMedia( ).canonicalUrl().fileName() elif self.player.state() == QMediaPlayer.PausedState or \ (self.player.state() == QMediaPlayer.PlayingState and self.play_btn.text() == '播放'): self.player.play() self.play_btn.setText('暂停') self.play_tip = "正在播放:" + self.playlist.currentMedia( ).canonicalUrl().fileName() self.statusBar().showMessage(self.play_tip + " - " + self.play_style) def change_volume(self, value): self.player.setVolume(value) def volume_up(self): volume = self.player.volume( ) + 10 if self.player.volume() + 10 <= 100 else 100 self.player.setVolume(volume) self.volume_slider.setTracking(True) self.volume_slider.setValue(volume) def volume_down(self): volume = self.player.volume( ) - 10 if self.player.volume() - 10 >= 0 else 0 self.player.setVolume(volume) self.volume_slider.setTracking(True) self.volume_slider.setValue(volume) def prev(self): if self.playlist.mediaCount() == 0: self.open_file() elif self.playlist.mediaCount() != 0: self.player.playlist().previous() def shuffle(self): self.playlist.shuffle() def next(self): if self.playlist.mediaCount() == 0: self.open_file() elif self.playlist.mediaCount() != 0: self.player.playlist().next() def song_changed(self, media): if not media.isNull(): url = media.canonicalUrl() self.play_tip = "正在播放:" + url.fileName() self.statusBar().showMessage(self.play_tip + " - " + self.play_style) self.song_list.selectRow(self.playlist.currentIndex()) def change_song(self): index = self.song_list.currentIndex().row() # 获取双击所在行 self.playlist.setCurrentIndex(index) def change_play_style(self): if self.playlist.playbackMode() == QMediaPlaylist.Loop: self.playlist.setPlaybackMode(QMediaPlaylist.CurrentItemInLoop) self.play_style = "单曲循环" self.play_style_btn.setIcon(self.single_img) elif self.playlist.playbackMode() == QMediaPlaylist.CurrentItemInLoop: self.playlist.setPlaybackMode(QMediaPlaylist.Loop) self.play_style = "列表循环" self.play_style_btn.setIcon(self.loop_img) self.statusBar().showMessage(self.play_tip + " - " + self.play_style) def convert_image(self, frame): if len(frame.shape) == 2: # 若是灰度图则转为三通道 frame = cv2.cvtColor(frame, cv2.COLOR_GRAY2BGR) rgb_image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) # 将BGR转为RGB rgb_image = np.asanyarray(rgb_image) label_image = QImage(rgb_image.data, rgb_image.shape[1], rgb_image.shape[0], QImage.Format_RGB888) # 转化为QImage self.image_to_show = QPixmap(label_image) def show_image(self): if self.image_to_show is not None: self.videoFrame.setPixmap(self.image_to_show) if self.rec_res['set'] and not self.rec_res['used']: final_direction = self.rec_res['direction'] final_fingers = self.rec_res['fingers'] if final_fingers == 3: self.start_or_stop() self.rec_res['used'] = True return if final_fingers == 5: self.change_play_style() self.rec_res['used'] = True return if final_direction is not None and final_direction != 'NOT_FOUND': if final_direction == "RIGHT": self.next() self.rec_res['used'] = True if final_direction == "LEFT": self.prev() self.rec_res['used'] = True if final_direction == "UP": self.volume_up() self.rec_res['used'] = True if final_direction == "DOWN": self.volume_down() self.rec_res['used'] = True def set_rec_res(self, res): self.rec_res = res
class MediaPlayer(QWidget): # 初始化 def __init__(self): super(MediaPlayer, self).__init__() self.setWindowTitle('播放器') # 先注册一个媒体播放器 self.player = QMediaPlayer(self) # 注册其他控件 self.volume_slider = QSlider(self) self.volume_save = 0 self.player.setVolume(36) # 进度条 self.progress_slider = QSlider(self) self.time_stamp = QLabel(self) # 按钮(PushButton)有,播放(暂停) 上一首,下一首 ,播放模式 self.preview_btn = QPushButton(self) # 播放按钮要切换播放与暂停 self.play_pause_btn = QPushButton(self) self.next_btn = QPushButton(self) self.play_mode = QPushButton() self.get_music = QPushButton('云音乐') self.sound_btn = QPushButton(self) # 播放列表 self.mediaList = QMediaPlaylist(self) self.play_list = QListWidget(self) self.songs_list = [] self.songs_formats = ['mp3', 'm4a', 'flac', 'wav', 'ogg'] # 布局注册 self.h1_layout = QHBoxLayout() self.h2_layout = QHBoxLayout() self.all_v_layout = QVBoxLayout() self.widget_init() self.layout_init() self.signal_init() self.center() '''控件初始化''' def widget_init(self): self.time_stamp.setText('--/--') self.volume_slider.setRange(0, 100) self.volume_slider.setValue(36) self.volume_slider.setOrientation(Qt.Horizontal) self.progress_slider.setEnabled(False) self.progress_slider.setOrientation(Qt.Horizontal) self.sound_btn.setIcon(QIcon('../icon_font/sound_on.png')) self.next_btn.setIcon(QIcon(r'..\icon\next.png')) self.play_pause_btn.setIcon(QIcon(r'..\icon\play.png')) self.preview_btn.setIcon(QIcon(r'..\icon\prev.png')) self.play_mode.setIcon(QIcon('../icon_font/list_down.png')) self.sound_btn.setIcon(QIcon(r'..\icon_font\sound_on.png')) self.player.setPlaylist(self.mediaList) self.mediaList.setPlaybackMode(QMediaPlaylist.Sequential) # 布局初始化 def layout_init(self): self.h1_layout.addWidget(self.progress_slider) # 5 self.h1_layout.addWidget(self.time_stamp) self.h2_layout.addWidget(self.preview_btn) self.h2_layout.addWidget(self.play_pause_btn) self.h2_layout.addWidget(self.next_btn) self.h2_layout.addWidget(self.sound_btn) self.h2_layout.addWidget(self.volume_slider) self.h2_layout.addWidget(self.play_mode) self.h2_layout.addWidget(self.get_music) self.all_v_layout.addLayout(self.h1_layout) self.all_v_layout.addWidget(self.play_list) self.all_v_layout.addLayout(self.h2_layout) self.all_v_layout.setSizeConstraint(QVBoxLayout.SetFixedSize) # 6 self.setLayout(self.all_v_layout) # 创建信号函数,连接槽函数 def signal_init(self): # self.preview_btn.clicked.connect(lambda: self.slot_func(self.preview_btn)) self.play_pause_btn.clicked.connect(lambda: self.slot_func(self.play_pause_btn)) self.next_btn.clicked.connect(lambda: self.slot_func(self.next_btn)) self.play_mode.clicked.connect(lambda: self.slot_func(self.play_mode)) self.sound_btn.clicked.connect(lambda: self.slot_func(self.sound_btn)) # 获取云音乐函数 self.get_music.clicked.connect(self.get_music_func) # 播放器音量控制 self.volume_slider.valueChanged.connect(self.volume_slider_func) self.play_list.doubleClicked.connect(self.play_list_func) self.player.durationChanged.connect(self.get_progress_func) self.player.positionChanged.connect(self.get_position_func) self.progress_slider.sliderMoved.connect(self.update_position_func) # 建立槽函数 def slot_func(self, btn): if btn == self.play_pause_btn: if self.player.state() == 1: self.player.pause() self.play_pause_btn.setIcon(QIcon(r'G:\python3\PyQt_Study\icon\play.png')) else: self.player.play() self.play_pause_btn.setIcon(QIcon(r'G:\python3\PyQt_Study\icon\pause.png')) #下一首 elif btn == self.next_btn: # 如果点击下一个按钮时为最后一首歌 if self.mediaList.currentIndex() == self.mediaList.mediaCount() - 1: self.mediaList.setCurrentIndex(0) self.play_list.setCurrentRow(0) else: # print(self.player.state()) if self.player.state() == 1: self.play_list.setCurrentRow(self.mediaList.currentIndex() + 1) self.mediaList.next() self.play_pause_btn.setIcon(QIcon(r'G:\python3\PyQt_Study\icon\pause.png')) # 上一首 elif btn == self.preview_btn: if self.mediaList.currentIndex() == 0: self.mediaList.setCurrentIndex(self.mediaList.mediaCount() -1) else: if self.player.state() == 1: self.mediaList.previous() self.play_pause_btn.setIcon(QIcon(r'G:\python3\PyQt_Study\icon\pause.png')) # 播放模式 elif btn == self.play_mode: # 根据播放模式切换 if self.mediaList.playbackMode() == 2: self.mediaList.setPlaybackMode(QMediaPlaylist.Loop) self.play_mode.setIcon(QIcon(r'G:\python3\PyQt_Study\icon_font\loop.png')) elif self.mediaList.playbackMode() == 3: self.mediaList.setPlaybackMode(QMediaPlaylist.Random) self.play_mode.setIcon(QIcon(r'..\icon_font\random.png')) elif self.mediaList.playbackMode() == 4: self.mediaList.setPlaybackMode(QMediaPlaylist.Sequential) self.play_mode.setIcon(QIcon(r'..\icon_font\list_loop.png')) # 声音按钮控制 elif btn == self.sound_btn: if self.player.isMuted(): # self.sound_btn.setIcon('') print("1", self.player.isMuted()) self.volume_slider.setValue(self.volume_save) self.player.setMuted(False) self.sound_btn.setIcon(QIcon(r'G:\python3\PyQt_Study\icon_font\sound_on.png')) else: print("2", self.player.isMuted()) self.player.setMuted(True) self.volume_save = self.volume_slider.value() self.volume_slider.setValue(0) self.sound_btn.setIcon(QIcon(r'G:\python3\PyQt_Study\icon_font\sound_off.png')) # 窗口居中 def center(self): qr = self.frameGeometry() # 获得主窗口的一个矩形特定几何图形。这包含了窗口的框架。 cp = QDesktopWidget().availableGeometry().center() # 算出相对于显示器的绝对值。 # 并且从这个绝对值中,我们获得了屏幕中心点。 qr.moveCenter(cp) # 矩形已经设置好了它的宽和高。现在我们把矩形的中心设置到屏幕的中间去。 # 矩形的大小并不会改变。 self.move(qr.topLeft()) # 移动了应用窗口的左上方的点到qr矩形的左上方的点,因此居中显示在我们的屏幕上。 # 获取音乐槽函数 def get_music_func(self): self.mediaList.clear() url = 'http://47.108.93.231:8000/cloudMusic' response = request.urlopen(url).read() # print(json.loads(response), type(json.loads(response))) # 读取数据库信息 # print(json.loads(response)) msg = json.loads(response)['msg'] for song in msg: if song['url'].split('.')[-1] in self.songs_formats: # self.songs_list.append([song, song['url']]) self.mediaList.addMedia(QMediaContent(QUrl(song['url']))) self.play_list.addItem(song['name']) self.play_list.setCurrentRow(0) # print(self.mediaList.mediaCount()) if self.songs_list: self.cur_playing_song = self.songs_list[self.play_list.currentRow()][-1] def volume_slider_func(self, val): self.player.setVolume(val) # if val == 0: # self.sound_btn.setICon(QIcon(r'G:\python3\PyQt_Study\icon\sound.png')) # else: # self.sound_btn.setIcon(QIcon(r'G:\python3\PyQt_Study\icon\sound.png')) # 播放列表双击函数 def play_list_func(self): self.mediaList.setCurrentIndex(self.play_list.currentRow()) self.player.play() self.play_pause_btn.setIcon(QIcon(r'G:\python3\PyQt_Study\icon\pause.png')) def get_progress_func(self, d): # 连接的函数,传入参数的d为毫秒。所以做一个时间变换 self.progress_slider.setRange(0, d) self.progress_slider.setEnabled(True) self.get_time_func(d) # 时间控制函数,每次调用改变滑条位置,以及时间减少 def get_time_func(self, d): seconds = int(d / 1000) minutes = int(seconds / 60) seconds -= minutes * 60 # print("%s分" %minutes, "%s秒" %seconds) if minutes == 0 and seconds == 0: self.time_stamp.setText("--/--") self.play_pause_btn.setIcon(QIcon(r'G:\python3\PyQt_Study\icon\play.png')) else: self.time_stamp.setText("{}:{}".format(minutes, seconds)) def get_position_func(self, p): self.progress_slider.setValue(p) self.get_time_func(self.progress_slider.maximum() - self.progress_slider.value()) # 当且仅当用户手动改变滑条位置时 def update_position_func(self, v): self.player.setPosition(v) d = self.progress_slider.maximum() - v self.get_time_func(d)
class MainWindow(QMainWindow, Ui_MainWindow): def __init__(self, *args, **kwargs): super(MainWindow, self).__init__(*args, **kwargs) self.setupUi(self) self.player = QMediaPlayer() self.player.error.connect(self.erroralert) self.player.play() self.player.stateChanged.connect(self.Set_Icons) # self.player.volumeChanged.connect(self.prev_volume) # Connect control buttons/slides for media player. self.playButton.pressed.connect(self.play_pause) self.stopButton.pressed.connect(self.player.stop) self.showButton.pressed.connect(self.playlist_toggle) self.equalizerButton.pressed.connect(self.ShowEqualizer) self.volumeButton.pressed.connect(self.mute) self.volumeSlider.valueChanged.connect(self.volumeChanged) self.currentVolume = self.volumeSlider.value() self.timeSlider.valueChanged.connect(self.player.setPosition) # Setup the playlist. self.playlist = QMediaPlaylist() self.player.setPlaylist(self.playlist) self.model = PlaylistModel(self.playlist) self.listWidget.setModel(self.model) self.playlist.currentIndexChanged.connect( self.playlist_position_changed) selection_model = self.listWidget.selectionModel() selection_model.selectionChanged.connect( self.playlist_selection_changed) self.player.durationChanged.connect(self.update_duration) self.player.positionChanged.connect(self.update_position) self.actionOpen_File.triggered.connect(self.open_file) self.files = [] self.setAcceptDrops(True) self.graphWidget.setBackground((60, 60, 60)) self.graphWidget.GetViewBox().setMenuEnabled(False) self.graphWidget.GetViewBox().setMouseEnabled(x=False, y=False) self.graphWidget.setPlotEQ((42, 130, 218)) self.Visualizer = FFTAnalyser(self.player) self.Visualizer.calculated_visual.connect(self.draw) self.Visualizer.start() self.Equalizer = QWidget() self.visdata = np.array([]) self.show() def draw(self, visdata): self.visdata = np.array(visdata) self.graphWidget.UpdatePlotEQ(self.visdata) def dragEnterEvent(self, e): if e.mimeData().hasUrls(): e.acceptProposedAction() def play_pause(self): if self.player.state() == QMediaPlayer.PlayingState: self.player.pause() else: self.player.play() def Set_Icons(self, state): if state == QMediaPlayer.StoppedState: self.stopButton.setIcon( qta.icon('fa5s.stop-circle', active='fa5s.stop-circle', color='white', color_active='grey')) self.playButton.setIcon(qta.icon('fa5s.play-circle', color='white')) self.playButton.setToolTip('Play') elif state == QMediaPlayer.PlayingState: self.playButton.setIcon( qta.icon('fa5s.pause-circle', color='white')) self.playButton.setToolTip('Pause') elif state == QMediaPlayer.PausedState: self.playButton.setIcon(qta.icon('fa5s.play-circle', color='white')) self.playButton.setToolTip('Play') def playlist_toggle(self): if self.listWidget.isHidden(): self.listWidget.setHidden(False) self.listWidget.setProperty("showDropIndicator", True) self.showButton.setIcon(qta.icon('fa5s.eye-slash', color='white')) self.showButton.setToolTip('Hide Playlist') else: self.listWidget.setHidden(True) self.listWidget.setProperty("showDropIndicator", False) self.showButton.setIcon(qta.icon('fa5s.list-ul', color='white')) self.showButton.setToolTip('Show Playlist') def dropEvent(self, e): for url in e.mimeData().urls(): self.playlist.addMedia(QMediaContent(url)) self.model.layoutChanged.emit() # If not playing, seeking to first of newly added + play. if self.player.state() != QMediaPlayer.PlayingState: i = self.playlist.mediaCount() - len(e.mimeData().urls()) self.playlist.setCurrentIndex(i) self.player.play() def open_file(self): options = QFileDialog.Options() options |= QFileDialog.DontUseNativeDialog path, _ = QFileDialog.getOpenFileName(self, "Open file", "", "Music Files (*.mp3 *.wav)", options=options) if path: self.playlist.addMedia(QMediaContent(QUrl.fromLocalFile(path))) self.files.append(QMediaContent(QUrl.fromLocalFile(path))) self.model.layoutChanged.emit() # def contextMenuEvent(self, event): # contextMenu = QMenu(self) # graph = contextMenu.addAction("Show Graph") def update_duration(self, duration): print("!", duration) print("?", self.player.duration()) self.timeSlider.setMaximum(duration) if duration >= 0: self.totalTimeLabel.setText(hhmmss(duration)) def update_position(self, position): if position >= 0: self.currentTimeLabel.setText(hhmmss(position)) self.timeSlider.blockSignals(True) self.timeSlider.setValue(position) self.timeSlider.blockSignals(False) def playlist_selection_changed(self, ix): i = ix.indexes()[0].row() self.playlist.setCurrentIndex(i) def playlist_position_changed(self, i): if i > -1: ix = self.model.index(i) self.listWidget.setCurrentIndex(ix) def erroralert(self, *args): print(args) def ShowEqualizer(self): if self.player.currentMedia().canonicalUrl().path() != "": self.Equalizer = WindowingWidget( self.player.currentMedia().canonicalUrl().path()) self.Equalizer.SendPath.connect(self.AddPathToPlaylist) self.Equalizer.show() def AddPathToPlaylist(self, path): if path: self.playlist.addMedia(QMediaContent(QUrl.fromLocalFile(path))) self.files.append(path) self.model.layoutChanged.emit() def volumeChanged(self): self.currentVolume = self.volumeSlider.value() self.player.setVolume(self.volumeSlider.value()) def mute(self): if self.player.isMuted(): self.player.setMuted(False) self.volumeButton.setIcon(qta.icon('fa5s.volume-up', color='white')) self.volumeButton.setToolTip('Mute') self.volumeSlider.setEnabled(True) else: self.player.setMuted(True) self.volumeButton.setIcon( qta.icon('fa5s.volume-mute', color='white')) self.volumeButton.setToolTip('Unmute') self.volumeSlider.setEnabled(False) def closeEvent(self, event): self.Visualizer.terminate() if self.Equalizer.isVisible(): self.Equalizer.close()
class MainWindow(QWidget): def __init__(self, parent=None): super(MainWindow, self).__init__(parent) self.playlistView = QListView() self.switch_status = 2 self.video_widget = QVideoWidget() self.playlist = QMediaPlaylist() self.model = PlaylistModel(self.playlist) self.titleBar = TitleBar(self) self.currentTimeLabel = QLabel() self.timeSlider = QSlider() self.totalTimeLabel = QLabel() self.mediaPlayer = QMediaPlayer() self.open_btn = QPushButton('Open File') self.play_btn = QPushButton() self.prev_btn = QPushButton() self.stop_btn = QPushButton() self.next_btn = QPushButton() self.switch_media_widgets_btn = QPushButton() self.pseudo_label = QLabel() self.vol_label = QLabel() self.volume_slider = Slider(Qt.Horizontal) self.gui() self.set_children_focus_policy(Qt.NoFocus) def gui(self): self.currentTimeLabel.setMinimumSize(QSize(80, 0)) self.currentTimeLabel.setAlignment(Qt.AlignRight | Qt.AlignTrailing | Qt.AlignVCenter) self.timeSlider.setOrientation(Qt.Horizontal) self.totalTimeLabel.setMinimumSize(QSize(80, 0)) self.totalTimeLabel.setAlignment(Qt.AlignLeading | Qt.AlignLeft | Qt.AlignVCenter) self.playlistView.setAcceptDrops(True) self.playlistView.setProperty("showDropIndicator", True) self.playlistView.setDragDropMode(QAbstractItemView.DropOnly) self.playlistView.setAlternatingRowColors(True) self.playlistView.setUniformItemSizes(True) self.setWindowFlags(Qt.FramelessWindowHint) self.setWindowTitle('Media Player') self.titleBar.label.setText('Media Player') self.setWindowIcon(QIcon('icon_png/media_player.png')) self.setGeometry(600, 200, 850, 600) self.timeSlider.setRange(0, 0) self.play_btn.setIcon(self.style().standardIcon(QStyle.SP_MediaPlay)) self.prev_btn.setIcon(self.style().standardIcon(QStyle.SP_MediaSkipBackward)) self.next_btn.setIcon(self.style().standardIcon(QStyle.SP_MediaSkipForward)) self.stop_btn.setIcon(self.style().standardIcon(QStyle.SP_MediaStop)) self.switch_media_widgets_btn.setIcon(self.style().standardIcon(QStyle.SP_FileDialogDetailedView)) self.vol_label.setText("") self.vol_label.setPixmap(QPixmap("icon_png/speaker-volume.png")) self.currentTimeLabel.setText("00:00") self.totalTimeLabel.setText("00:00") self.volume_slider.setValue(self.mediaPlayer.volume()) self.mediaPlayer.setVideoOutput(self.video_widget) self.mediaPlayer.setPlaylist(self.playlist) self.playlistView.setModel(self.model) self.video_widget.hide() sizegrip = QSizeGrip(self) self.setAcceptDrops(True) inner_h_box = QHBoxLayout() inner_h_box.addWidget(self.prev_btn) inner_h_box.addWidget(self.stop_btn) inner_h_box.addWidget(self.next_btn) vol_h_box = QHBoxLayout() vol_h_box.addWidget(self.vol_label, 0) vol_h_box.addWidget(self.volume_slider, 1) h_box = QHBoxLayout() h_box.addWidget(self.open_btn) h_box.addWidget(self.play_btn, 0) h_box.addLayout(inner_h_box, 0) h_box.addWidget(self.switch_media_widgets_btn, 0) h_box.addWidget(self.pseudo_label, 1) h_box.addLayout(vol_h_box, 0) h_box.addWidget(sizegrip, 0, Qt.AlignBottom | Qt.AlignRight) video_slider_h_box = QHBoxLayout() video_slider_h_box.addWidget(self.currentTimeLabel) video_slider_h_box.addWidget(self.timeSlider) video_slider_h_box.addWidget(self.totalTimeLabel) v_box = QVBoxLayout() v_box.addWidget(self.titleBar, 0) v_box.addWidget(self.video_widget, 1) v_box.addWidget(self.playlistView, 1) v_box.addLayout(video_slider_h_box, 0) v_box.addLayout(h_box, 0) inner_h_box.setContentsMargins(20, 0, 10, 0) vol_h_box.setContentsMargins(0, 0, 20, 0) h_box.setContentsMargins(20, 0, 0, 0) v_box.setContentsMargins(0, 0, 0, 0) video_slider_h_box.setSpacing(10) h_box.setSpacing(0) v_box.setSpacing(0) self.setLayout(v_box) self.enabler() # connections self.open_btn.clicked.connect(self.open_file) self.play_btn.clicked.connect(self.play_media) self.stop_btn.clicked.connect(self.stop_media) self.prev_btn.pressed.connect(self.play_prev) self.next_btn.pressed.connect(self.play_next) self.switch_media_widgets_btn.pressed.connect(self.switch_media) self.playlist.currentIndexChanged.connect(self.playlist_position_changed) selection_model = self.playlistView.selectionModel() selection_model.selectionChanged.connect(self.playlist_selection_changed) self.mediaPlayer.durationChanged.connect(self.update_duration) self.mediaPlayer.positionChanged.connect(self.update_position) self.timeSlider.valueChanged.connect(self.mediaPlayer.setPosition) self.mediaPlayer.stateChanged.connect(self.media_state) self.mediaPlayer.volumeChanged.connect(self.volume_changed) self.volume_slider.valueChanged.connect(self.set_volume) def set_children_focus_policy(self, policy): def recursive_set_child_focus_policy(parent_q_widget): for childQWidget in parent_q_widget.findChildren(QWidget): childQWidget.setFocusPolicy(policy) recursive_set_child_focus_policy(childQWidget) recursive_set_child_focus_policy(self) def enabler(self, state=False): if state is False: self.play_btn.setEnabled(False) self.prev_btn.setEnabled(False) self.stop_btn.setEnabled(False) self.next_btn.setEnabled(False) else: self.play_btn.setEnabled(True) self.stop_btn.setEnabled(True) self.prev_btn.setEnabled(True) self.next_btn.setEnabled(True) def switch_media(self): if self.switch_status == 0: self.video_widget.hide() self.playlistView.show() self.switch_status = 1 self.switch_media_widgets_btn.setEnabled(True) elif self.switch_status == 1: self.video_widget.show() self.playlistView.hide() self.switch_status = 0 self.switch_media_widgets_btn.setEnabled(True) else: self.video_widget.hide() self.playlistView.show() self.switch_media_widgets_btn.setEnabled(False) def play_media(self): if self.mediaPlayer.state() == QMediaPlayer.PlayingState: self.mediaPlayer.pause() else: self.mediaPlayer.play() self.ui_handler() def ui_handler(self): if not self.playlist.isEmpty(): self.enabler(True) file_path = QFileInfo(self.mediaPlayer.currentMedia().canonicalUrl().toString()).fileName() ext = os.path.splitext(file_path)[-1].lower() audio_ext = ['.flac', '.mp3'] video_ext = ['.mp4', '.m4a', '.mov', '.flv', 'avi', '3gp', '.mkv', '.wmv'] if ext in audio_ext: self.switch_status = 2 self.switch_media() if self.isFullScreen(): self.fullscreen() elif ext in video_ext: self.switch_status = 1 self.switch_media() self.setWindowTitle(file_path + ' - Media Player') self.titleBar.label.setText(file_path + ' - Media Player') def stop_media(self): if self.mediaPlayer.state() != QMediaPlayer.StoppedState: self.mediaPlayer.stop() self.setWindowTitle('Media Player') self.titleBar.label.setText('Media Player') def fullscreen(self): if self.switch_status == 2 or self.isFullScreen(): self.titleBar.show() self.timeSlider.show() self.currentTimeLabel.show() self.totalTimeLabel.show() self.volume_slider.show() self.open_btn.show() self.play_btn.show() self.prev_btn.show() self.stop_btn.show() self.next_btn.show() self.switch_media_widgets_btn.show() self.pseudo_label.show() self.vol_label.show() self.showNormal() else: self.titleBar.hide() self.timeSlider.hide() self.currentTimeLabel.hide() self.totalTimeLabel.hide() self.volume_slider.hide() self.open_btn.hide() self.play_btn.hide() self.prev_btn.hide() self.stop_btn.hide() self.next_btn.hide() self.switch_media_widgets_btn.hide() self.pseudo_label.hide() self.vol_label.hide() self.showFullScreen() def mouseDoubleClickEvent(self, event: QMouseEvent): event.accept() if event.button() == Qt.LeftButton: self.fullscreen() def media_state(self): os_sleep = WindowsInhibitor() if self.mediaPlayer.state() == QMediaPlayer.PlayingState: self.play_btn.setIcon(self.style().standardIcon(QStyle.SP_MediaPause)) if os.name == 'nt': os_sleep = WindowsInhibitor() os_sleep.inhibit() else: self.play_btn.setIcon(self.style().standardIcon(QStyle.SP_MediaPlay)) if os_sleep: os_sleep.uninhibit() def play_next(self): self.playlist.next() def media_seek(self, seek): if not self.playlist.isEmpty(): player = self.mediaPlayer if (player.duration() - seek) > player.position(): player.setPosition(player.position() + seek) def play_prev(self): self.playlist.previous() def dragEnterEvent(self, e): if e.mimeData().hasUrls(): e.acceptProposedAction() def dropEvent(self, e): for url in e.mimeData().urls(): ext = os.path.splitext(url.fileName())[-1].lower() allowed_ext = ['.flac', '.mp3', '.mp4', '.m4a', '.mov', '.flv', 'avi', '3gp', '.mkv', '.wmv'] if ext in allowed_ext: self.playlist.addMedia( QMediaContent(url) ) self.model.layoutChanged.emit() if self.mediaPlayer.state() != QMediaPlayer.PlayingState: i = self.playlist.mediaCount() - len(e.mimeData().urls()) self.playlist.setCurrentIndex(i) if not self.playlist.isEmpty(): self.play_media() def open_file(self): filter_files = "Media (*.mp3 *.mp4 *.mkv);; Videos files (*.mp4 *.mkv);; Music Files(*.mp3)" paths, _ = QFileDialog.getOpenFileNames(self, "Open file", "", filter_files) if paths: self.mediaPlayer.pause() for path in paths: self.playlist.addMedia( QMediaContent( QUrl.fromLocalFile(path) ) ) i = self.playlist.mediaCount() - len(paths) self.playlist.setCurrentIndex(i) self.play_media() self.model.layoutChanged.emit() def update_duration(self, duration): self.mediaPlayer.duration() self.timeSlider.setMaximum(duration) if duration >= 0: self.totalTimeLabel.setText(hhmmss(duration)) def update_position(self, position): if position >= 0: self.currentTimeLabel.setText(hhmmss(position)) self.timeSlider.blockSignals(True) self.timeSlider.setValue(position) self.timeSlider.blockSignals(False) def playlist_selection_changed(self, ix): i = ix.indexes()[0].row() self.playlist.setCurrentIndex(i) self.ui_handler() def playlist_position_changed(self, i): if i > -1: ix = self.model.index(i) self.playlistView.setCurrentIndex(ix) def set_volume(self, value): self.mediaPlayer.setVolume(value) def volume_changed(self, value): self.volume_slider.setValue(value) def keyPressEvent(self, event): key = event.key() modifiers = int(event.modifiers()) if (modifiers and modifiers & MOD_MASK == modifiers and key > 0 and key != Qt.Key_Shift and key != Qt.Key_Alt and key != Qt.Key_Control and key != Qt.Key_Meta): key_name = QKeySequence(modifiers + key).toString() if key_name == 'Ctrl+Right': self.media_seek(30000) elif key_name == 'Ctrl+Left': self.media_seek(-30000) elif key_name == 'Ctrl+Up': self.mediaPlayer.setVolume(self.mediaPlayer.volume() + 5) elif key_name == 'Ctrl+Down': self.mediaPlayer.setVolume(self.mediaPlayer.volume() - 5) elif key_name == 'Ctrl+O': self.open_file() else: if event.key() == Qt.Key_Space: self.play_media() elif event.key() == Qt.Key_MediaPlay: self.play_media() elif event.key() == Qt.Key_MediaNext: self.play_next() elif event.key() == Qt.Key_MediaPrevious: self.play_prev() elif event.key() == Qt.Key_Escape: self.close() elif event.key() == Qt.Key_F: self.fullscreen() elif event.key() == Qt.Key_Right: self.media_seek(5000) elif event.key() == Qt.Key_Left: self.media_seek(-5000)
class Control: """A class that handles the logic behind the program by manipulating the GUI classes and calling their methods in response to received signals.""" MAGIC = b"\x01\xff" def __init__(self, screens: list) -> None: self.player = QMediaPlayer() self.player.setAudioRole(QAudio.MusicRole) self.playlist = QMediaPlaylist() self.player.setPlaylist(self.playlist) self.mainWindow = MainWindow(self, screens) self.mainArea = self.mainWindow.centralWidget().upperBox.mainArea self.songList = self.mainWindow.centralWidget().upperBox.songList self.mediaControlArea = self.mainWindow.centralWidget( ).mediaControlArea self.mainWindow.show() self.library = None self.currentSong = None self.playing = False self.random = False self.repeat = 0 self.volume = 50 self.volumeChange(self.volume) self.mediaControlArea.volumeControl.setValue(self.volume) self.mainTimer = QTimer() self.mainTimer.setInterval(100) self.mainTimer.timeout.connect(self.updateSongProgress) self.mainTimer.start() self.libraryUpdateTimer = QTimer() self.libraryUpdateTimer.setInterval(15_000) self.libraryUpdateTimer.timeout.connect(self.updateLibrary) self.libraryUpdateTimer.start() self._connection = None self.connections() self.displayedType = None self.displayedName = None self.types = None self.songListWidth = None values = self.load() if values: self.mainWindow.setGeometry(*values) self.volumeChange(self.volume) self.mediaControlArea.volumeControl.setValue(self.volume) self.setUpTimer = QTimer() self.setUpTimer.setInterval(20) self.setUpTimer.setSingleShot(True) self.setUpTimer.timeout.connect(self.setAreas) self.setUpTimer.start() def connections(self): self.player.currentMediaChanged.connect(self.updateCurrentSong) self.player.durationChanged.connect(self.updateSongProgressRange) self.player.stateChanged.connect(self.playerStatusChanged) self.mediaControlArea.previousButton.click.connect( self.playlist.previous) self.mediaControlArea.repeatButton.click.connect( self.repeatButtonClick) self.mediaControlArea.stopButton.click.connect(self.stopButtonClick) self.mediaControlArea.playButton.click.connect(self.playButtonClick) self.mediaControlArea.randomButton.click.connect( self.randomButtonClick) self.mediaControlArea.nextButton.click.connect(self.playlist.next) self.mediaControlArea.muteButton.click.connect(self.mute) self.mediaControlArea.songProgress.sliderMoved.connect( self.songProgressMove) self.mediaControlArea.volumeControl.sliderMoved.connect( self.volumeChange) def setAreas(self) -> None: """Called after the GUI is created to provide user with a feedback that the program is running in case a larger amount of songs will be added when the Library class is initialized.""" # TODO add a tooltip that will notify the user larger amount of songs is being loaded # (freezes the program as the execution moves to the Library class.) self.library = library.Library() self.types = { "artist": self.library.getSongsForArtist, "album": self.library.getSongsForAlbum, "playlist": self.library.getSongsForPlaylist } self.mainArea.setAreas(self.library) self.setUpTimer.deleteLater() self.setUpTimer = None self.getSongs(self.displayedType, self.displayedName) if self.songListWidth is not None: songListGeometry = self.songList.geometry() self.songList.preferredWidth = songListGeometry.width( ) - self.songListWidth self.mainWindow.centralWidget().upperBox.line.resizeWidgets( songListGeometry.width() - self.songListWidth) def updateLibrary(self) -> None: self.library.update() self.mainArea.updateView(self.library) def updateCurrentSong(self) -> None: """Update all areas that may display information about the currently playing song - SongList, Now Playing tab, BottomBox""" media = self.player.currentMedia() self.currentSong = media.request().url().toLocalFile().replace( "/", "\\") if self.currentSong in self.library.library: self.songList.updateActiveSong(self.currentSong) self.mainArea.updateActiveSong(self.playlist.currentIndex()) songEntry = self.library.library[self.currentSong] self.mediaControlArea.updateSongInfo( f"{songEntry[ARTIST]} - {songEntry[NAME]}") def updateSongProgressRange(self) -> None: """Updates the range of the slider that represents the song position.""" self.mediaControlArea.updateSongProgressRange(self.player.duration()) def playerStatusChanged(self) -> None: """Used to properly update the player look after the current playlist has finished.""" index = self.playlist.currentIndex() if index == -1: self.stopButtonClick() def getSongs(self, isType: str, name: str) -> None: """Retrieves the songs for a given artist, album or playlist based on type and passes the resulting list to the SongList class.""" if isType is None: isType = self.displayedType if name is None: name = self.displayedName orderBy = self.songList.buttonOrderBy.text() reverse = True if self.songList.buttonOrderReverse.text() == chr( 0x25bc) else False listForType = self.types[isType](name, orderBy, reverse) playlist = None if isType == "playlist": playlist = name if len(listForType) == 0: artists = self.library.artists if len(artists): listForType = self.library.getSongsForArtist(artists[0]) self.songList.updateSongList(listForType, self.library.library, self.currentSong, playlist, isType) self.displayedType = isType self.displayedName = name def playSongList(self, song: str = None) -> None: """Called when user double-clicks on an artist/album/playlist widget or a song in right-hand side panel.""" self.playlist.clear() index = 0 loopIndex = 0 for songPath in self.songList.garbageProtector: if song == songPath: index = loopIndex self.playlist.addMedia(QMediaContent(QUrl.fromLocalFile(songPath))) loopIndex += 1 if self.playlist.isEmpty(): return self.player.play() if index > 0: self.playlist.setCurrentIndex(index) self.playing = True self.mediaControlArea.playButton.updatePictures( bottom.pausePixmap, bottom.pauseHoverPixmap, False) self.mainArea.setNowPlayingArea(self.library) self.mainArea.updateActiveSong(self.playlist.currentIndex()) def playSongWidget(self, songPath: str, afterCurrent: bool = False) -> None: if afterCurrent: index = self.playlist.currentIndex() + 1 self.playlist.insertMedia( index, QMediaContent(QUrl.fromLocalFile(songPath))) else: self.playlist.addMedia(QMediaContent(QUrl.fromLocalFile(songPath))) self.mainArea.setNowPlayingArea(self.library) self.mainArea.updateActiveSong(self.playlist.currentIndex()) self.playing = True def removeFromNowPlaying(self, widget) -> None: if self.playlist.mediaCount() > 1: for row in range(self.mainArea.nowPlayingLayout.rowCount()): for column in range( self.mainArea.nowPlayingLayout.columnCount()): if self.mainArea.nowPlayingLayout.itemAtPosition( row, column).widget() is widget: self.playlist.removeMedia(row - 1) break else: continue break else: self.stopButtonClick() self.playlist.clear() self.mainArea.setNowPlayingArea(self.library) if self.playing: self.mainArea.updateActiveSong(self.playlist.currentIndex()) def playMediaWidget(self, isType: str, target: str, startOver: bool, afterCurrent: bool) -> None: """Called from MediaWidget - plays all songs for MediaWidget's type and name.""" if startOver: self.playlist.clear() if afterCurrent: index = self.playlist.currentIndex() + 1 for songPath in self.types[isType](target): self.playlist.insertMedia( index, QMediaContent(QUrl.fromLocalFile(songPath))) index += 1 else: for songPath in self.types[isType](target): self.playlist.addMedia( QMediaContent(QUrl.fromLocalFile(songPath))) if startOver: self.player.play() self.playing = True self.mediaControlArea.playButton.updatePictures( bottom.pausePixmap, bottom.pauseHoverPixmap, False) self.mainArea.setNowPlayingArea(self.library) self.mainArea.updateActiveSong(self.playlist.currentIndex()) def playFromNowPlaying(self, song: str) -> None: """Called when user double-clicks on a song in the Now Playing tab.""" for n in range(self.playlist.mediaCount()): media = self.playlist.media(n) if song == media.request().url().toLocalFile().replace("/", "\\"): self.playlist.setCurrentIndex(n) if not self.playing: self.player.play() self.playing = True return def createPlaylist(self, playlistName: str) -> None: self.library.createPlaylist(playlistName) self.mainArea.setMainAreaPlaylists(self.library) def addToExistingPlaylist(self, playlist: str, songOrWidget: str, isType: str) -> None: if isType in self.types: for song in self.types[isType](songOrWidget): self.library.addToPlaylist(playlist, song) else: self.library.addToPlaylist(playlist, songOrWidget) self.library.update() def removeFromPlaylist(self, playlist: str, song: str) -> None: self.library.deleteFromPlaylist(playlist, song) self.mainArea.setMainAreaPlaylists(self.library) self.library.update() self.getSongs("playlist", playlist) def renamePlaylist(self, playlistName: str, newPlaylistName: str) -> None: self.library.renamePlaylist(playlistName, newPlaylistName) self.mainArea.setMainAreaPlaylists(self.library) self.library.update() def deletePlaylist(self, playlistName: str) -> None: self.library.deletePlaylist(playlistName) self.mainArea.setMainAreaPlaylists(self.library) self.library.update() def addWatchedFolder(self, folder: str) -> None: """Adds a folder to the Library class. all mp3 files within the folder and its sub-folders will be added to the library and accessible to the player.""" self.library.addFolder(folder.replace("/", "\\")) self.mainArea.updateView(self.library) def removeWatchedFolder(self, folder: str) -> None: """Removes folder from the library, updates view and stops playback if the current song was in the now-removed folder.""" self.library.deleteFolder(folder) self.mainArea.updateView(self.library) if self.currentSong not in self.library.library: self.songList.updateSongList([], [], "", "") self.player.stop() self.playlist.clear() self.mediaControlArea.updateSongInfo("") self.songList.nowPlayingSong = None self.mainArea.nowPlayingSong = None self.playing = False self.mediaControlArea.updatePlayButton(self.playing, False) def playButtonClick(self, passMove: bool = True) -> None: if not self.playing: if self.playlist.isEmpty(): self.playSongList() return self.playing = True self.player.play() self.mainArea.updateActiveSong(self.playlist.currentIndex()) self.songList.updateActiveSong(self.currentSong) else: self.playing = False self.player.pause() self.mediaControlArea.updatePlayButton(self.playing, passMove) def repeatButtonClick(self) -> None: if self.repeat == 0: self.repeat = 1 self.playlist.setPlaybackMode(QMediaPlaylist.Loop) elif self.repeat == 1: self.repeat = 2 self.playlist.setPlaybackMode(QMediaPlaylist.CurrentItemInLoop) elif self.repeat == 2: self.repeat = 0 self.playlist.setPlaybackMode(QMediaPlaylist.Sequential) self.mediaControlArea.updateRepeatButton(self.repeat) def randomButtonClick(self) -> None: if not self.random: self.random = True self.playlist.setPlaybackMode(QMediaPlaylist.Random) else: self.random = False self.playlist.setPlaybackMode(QMediaPlaylist.Sequential) self.mediaControlArea.updateRandomButton(self.random) def stopButtonClick(self) -> None: self.playing = False self.player.stop() if self.songList.nowPlayingSong is not None: self.songList.nowPlayingSong.clear() if self.mainArea.nowPlayingSong is not None: self.mainArea.nowPlayingSong.clear() self.mediaControlArea.updatePlayButton(self.playing, False) def mute(self) -> None: if not self.player.isMuted(): self.player.setMuted(True) self.mediaControlArea.showMute() else: self.player.setMuted(False) self.volumeChange(self.volume) def volumeChange(self, volume: int) -> None: logVolume = QAudio.convertVolume(volume / 100, QAudio.LogarithmicVolumeScale, QAudio.LinearVolumeScale) * 100 self.player.setVolume(logVolume) self.volume = volume self.mediaControlArea.updateVolumeBar(volume) def songProgressMove(self, position: int) -> None: self.player.setPosition(position) def updateSongProgress(self) -> None: position = self.player.position() if 0 <= position < 2_000_000_000: if self.player.state() > 0: self.mediaControlArea.updateSongProgress(position) if self.playing: self.songList.activeSongPixmap() self.mainArea.activeSongPixmap() else: self.mediaControlArea.updateSongProgress(0) def disconnect(self): self.player.currentMediaChanged.disconnect() self.player.durationChanged.disconnect() self.player.stateChanged.disconnect() self.mediaControlArea.previousButton.click.disconnect() self.mediaControlArea.repeatButton.click.disconnect() self.mediaControlArea.stopButton.click.disconnect() self.mediaControlArea.playButton.click.disconnect() self.mediaControlArea.randomButton.click.disconnect() self.mediaControlArea.nextButton.click.disconnect() self.mediaControlArea.muteButton.click.disconnect() self.mediaControlArea.songProgress.sliderMoved.disconnect() self.mediaControlArea.volumeControl.sliderMoved.disconnect() def close(self) -> None: self.disconnect() self.player.stop() self.mainTimer.stop() self.save() def save(self) -> None: """Called on exit, saves current view, geometry and volume.""" with gzip.open(r"musicplayer\mpdata", "wb") as fh: fh.write(self.MAGIC) toBeWritten = struct.pack(f"<h{len(self.displayedType.encode())}s", len(self.displayedType.encode()), self.displayedType.encode()) fh.write(toBeWritten) fh.write(self.MAGIC) toBeWritten = struct.pack(f"<h{len(self.displayedName.encode())}s", len(self.displayedName.encode()), self.displayedName.encode()) fh.write(toBeWritten) fh.write(self.MAGIC) geo = self.mainWindow.geometry() toBeWritten = struct.pack("<4h", geo.x(), geo.y(), geo.width(), geo.height()) fh.write(toBeWritten) toBeWritten = struct.pack("<h", self.volume) fh.write(toBeWritten) toBeWritten = struct.pack("<h", self.songList.width()) fh.write(toBeWritten) def load(self) -> [bool, tuple]: """Called on startup, loads view, geometry and volume saved on previous run.""" try: with gzip.open(r"musicplayer\mpdata", "rb") as fh: if not fh.read(2) == self.MAGIC: return False length = fh.read(2) length = struct.unpack("<h", length)[0] displayedType = fh.read(length) displayedType = struct.unpack(f"<{length}s", displayedType)[0].decode("utf8") if displayedType in ["artist", "album", "playlist"]: self.displayedType = displayedType if not fh.read(2) == self.MAGIC: return False length = fh.read(2) length = struct.unpack("<h", length)[0] displayedName = fh.read(length) displayedName = struct.unpack(f"<{length}s", displayedName)[0].decode("utf8") if not fh.read(2) == self.MAGIC: return False self.displayedName = displayedName variables = [] for n in range(6): var = fh.read(2) var = struct.unpack("<h", var)[0] variables.append(var) x, y, width, height, volume, songListWidth = variables self.volume = volume self.songListWidth = songListWidth return x, y, width, height except Exception: return False
class DPlayerCore(QWidget): def __init__(self): """Initialize player and load playlist if any.""" super().__init__() self.player = QMediaPlayer() self.playlist = QMediaPlaylist() self.player.setPlaylist(self.playlist) self.shuffling = False self.repeatingPlaylist = False self.repeatingSong = False self.musicOrder = [] self.loadPlaylist(QUrl( 'file://{}/lastListened.m3u'.format(os.getcwd()))) self.lyricsApi = 'http://api.musixmatch.com/ws/1.1/' self.lyricsApiKey = '4b364f0652e471aa50813a22cdf830ea' self.lastFMapi = 'http://ws.audioscrobbler.com/2.0/' self.lastFMapikey = '052c43a00a4fc294bb3c9e0c38bdf710' self.lastFMsecret = '14c66392fa9c6c142a41ccc2b0674e19' self.username = None self.password = None self.network = None self.error = 'Something went wrong! Try again later.' def play(self): """Start the player.""" self.player.play() def pause(self): """Pause the player.""" self.player.pause() def stop(self): """Stop the player.""" self.player.stop() def previous(self): """Play previous song.""" self.playlist.previous() def next(self): """Play next song.""" self.playlist.next() def mute(self): """Mute the player.""" self.player.setMuted(True) def unmute(self): """Unmute the player.""" self.player.setMuted(False) def setVolume(self, value): """Set player's volume to value.""" self.player.setVolume(value) def add(self, fileNames): """Add fileNames to the playlist.""" for name in fileNames: url = QUrl.fromLocalFile(QFileInfo(name).absoluteFilePath()) self.playlist.addMedia(QMediaContent(url)) self.musicOrder.append([name]) self.added(len(fileNames)) def added(self, added): """Saves music info in musicOrder.""" for name, index in zip( self.musicOrder[self.playlist.mediaCount() - added:], range(self.playlist.mediaCount() - added, len(self.musicOrder))): name = name[0] artist = self.getArtist(name)[0] title = self.getTitle(name)[0] album = self.getAlbum(name)[0] seconds = self.getDuration(name) duration = QTime(0, seconds // 60, seconds % 60) duration = duration.toString('mm:ss') self.musicOrder[index].extend( [artist, title, album, duration]) def remove(self, songIndexes): """Remove songIndexes from the playlist.""" for index in songIndexes: self.songChanged = True del self.musicOrder[index] self.playlist.removeMedia(index) self.songChanged = False def savePlaylist(self, path): """Save playlist to path.""" if path.toString()[len(path.toString()) - 4:] != '.m3u': path = QUrl('{}.m3u'.format(path.toString())) self.playlist.save(path, 'm3u') def loadPlaylist(self, path): """Load playlist form path.""" count = self.playlist.mediaCount() self.playlist.load(path) for index in range(count, self.playlist.mediaCount()): self.musicOrder.append( [self.playlist.media(index).canonicalUrl().path()]) self.added(self.playlist.mediaCount() - count) def clearPlaylist(self): """Delete all songs in the playlist.""" self.playlist.clear() self.musicOrder = [] def shuffle(self, value): """Shuffle playlist if value = True.""" self.shuffling = value if self.repeatingSong: return if self.shuffling: self.playlist.setPlaybackMode(QMediaPlaylist.Random) elif self.repeatingPlaylist: self.playlist.setPlaybackMode(QMediaPlaylist.Loop) else: self.playlist.setPlaybackMode(QMediaPlaylist.Sequential) def repeatPlaylist(self, value): """Repeat playlist after the last song is finished if value = True.""" self.repeatingPlaylist = value if self.repeatingSong or self.shuffling: return if self.repeatingPlaylist: self.playlist.setPlaybackMode(QMediaPlaylist.Loop) else: self.playlist.setPlaybackMode(QMediaPlaylist.Sequential) def repeatSong(self, value): """Repeat current song if value = True.""" self.repeatingSong = value if self.repeatingSong: self.playlist.setPlaybackMode(QMediaPlaylist.CurrentItemInLoop) elif self.shuffling: self.playlist.setPlaybackMode(QMediaPlaylist.Random) elif self.repeatingPlaylist: self.playlist.setPlaybackMode(QMediaPlaylist.Loop) else: self.playlist.setPlaybackMode(QMediaPlaylist.Sequential) def sort(self, column, order): """Sort playlist by column in order.""" ordered = sorted(self.musicOrder, key=itemgetter(column + 1), reverse=order) self.clearPlaylist() for song in ordered: url = QUrl.fromLocalFile(QFileInfo(song[0]).absoluteFilePath()) self.playlist.addMedia(QMediaContent(url)) self.musicOrder = ordered def findLyrics(self, index): """Returns lyrics for song at index.""" if self.musicOrder[index][2] == 'Unknown': return 'Unknown song.' searchSong = '{}track.search?q_track={}'.format( self.lyricsApi, self.musicOrder[index][2].replace(' ', '%20')) if self.musicOrder[index][1] != 'Unknown': searchSong = '{}&q_artist={}'.format( searchSong, self.musicOrder[index][1].replace(' ', '%20')) searchSong = '{}&f_has_lyrics=1&apikey={}'.format( searchSong, self.lyricsApiKey) try: requestSong = requests.get(searchSong) except requests.ConnectionError: return self.error songJson = requestSong.json() if requestSong.status_code != 200 or \ songJson['message']['header']['available'] == 0: return self.error songId = songJson[ 'message']['body']['track_list'][0]["track"]["track_id"] searchLyrics = '{}track.lyrics.get?track_id={}&apikey={}'.format( self.lyricsApi, songId, self.lyricsApiKey) try: requestLyrics = requests.get(searchLyrics) except requests.ConnectionError: return self.error if requestLyrics.status_code != 200 or \ songJson['message']['header']['available'] == 0: return self.error return requestLyrics.json()[ 'message']['body']['lyrics']['lyrics_body'][:-58] # spam and bacon def findInfo(self, index): """Returns info about artist and album for index if any.""" info = [] if self.musicOrder[index][1] != 'Unknown': artist = self.artistInfo(self.musicOrder[index][1]) if artist != self.error: info += artist if self.musicOrder[index][1] != 'Unknown' and \ self.musicOrder[index][3] != 'Unknown': album = self.albumInfo(self.musicOrder[index][1], self.musicOrder[index][3]) if album != self.error: info += album if info: return info else: return ['Unknown artist and song!'] def artistInfo(self, artist): """Returns info about artist if any.""" try: response = requests.get( ('{}/?method=artist.getinfo&artist={}&api_key={}&' 'format=json&autocorrect=1').format( self.lastFMapi, artist, self.lastFMapikey)) except Exception: return self.error if response.status_code != 200: return self.error artist = 'Artist: {}'.format(response.json()['artist']['name']) bio = 'Bio: {}'.format( response.json()['artist']['bio']['summary'].replace('.', '.\n')) spam = bio.find('<a') bio = bio[:spam] return [artist, bio] def albumInfo(self, artist, album): """Returns info about album if any.""" try: response = requests.get( ('{}/?method=album.getinfo&artist={}&album={}&api_key={}&' 'format=json&autocorrect=1').format( self.lastFMapi, artist, album, self.lastFMapikey)) except Exception: return self.error if response.status_code != 200 or \ 'album' not in response.json().keys(): return self.error album = 'Album: {}'.format(response.json()['album']['name']) tracks = ['Tracks: '] t = response.json()['album']['tracks']['track'] for track, index in zip(t, range(len(t))): tracks.append('{}. {}'.format(index + 1, track['name'])) info = [album, '\n'.join(tracks)] if 'wiki' in response.json()['album'].keys(): wiki = response.json()['album']['wiki'] if 'published' in wiki.keys(): info.append('Published: {}'.format(wiki['published'])) if 'summary' in wiki.keys(): summary = wiki['summary'].replace('.', '.\n') spam = summary.find('<a') info.append('Summary: {}'.format(summary[:spam])) if 'Musical style' in wiki.keys(): info.append('Musical style: {}'.format(wiki['Musical style'])) return info def login(self, username, password): """Creates lastFM network.""" self.username = username self.password = pylast.md5(password) try: self.network = pylast.LastFMNetwork(api_key=self.lastFMapikey, api_secret=self.lastFMsecret, username=self.username, password_hash=self.password) except Exception: self.username = None self.password = None self.network = None return False return True def logout(self): """Destoys lastFM network and current user info.""" self.username = None self.password = None self.network = None def loveTrack(self, index): """Love track at index in lastFM.""" if self.network is None: return False track = self.network.get_track(self.musicOrder[index][1], self.musicOrder[index][2]) try: track.love() except Exception: return False return True def unloveTrack(self, index): """Unlove track at index in lastFM.""" if self.network is None: return False track = self.network.get_track(self.musicOrder[index][1], self.musicOrder[index][2]) try: track.unlove() except Exception: return False return True def isMuted(self): """Returns True if player is muted.""" return self.player.isMuted() def getArtist(self, song): """Returns the artist of song.""" if song[-4:] == '.mp3': obj = EasyID3(song) if 'artist' in obj.keys(): return obj['artist'] elif 'TAG' in mediainfo(song).keys(): obj = mediainfo(song)['TAG'] if 'artist' in obj.keys(): return [obj['artist']] elif 'ARTIST' in obj.keys(): return [obj['ARTIST']] else: return ['Unknown'] else: return ['Unknown'] def getTitle(self, song): """Returns the title of song.""" if song[-4:] == '.mp3': obj = EasyID3(song) if 'title' in obj.keys(): return obj['title'] elif 'TAG' in mediainfo(song).keys(): obj = mediainfo(song)['TAG'] if 'title' in obj.keys(): return [obj['title']] elif 'TITLE' in obj.keys(): return [obj['TITLE']] else: return ['Unknown'] else: return ['Unknown'] def getAlbum(self, song): """Returns the album of song.""" if song[-4:] == '.mp3': obj = EasyID3(song) if 'album' in obj.keys(): return obj['album'] elif 'TAG' in mediainfo(song).keys(): obj = mediainfo(song)['TAG'] if 'album' in obj.keys(): return [obj['album']] elif 'ALBUM' in obj.keys(): return [obj['ALBUM']] else: return ['Unknown'] else: return ['Unknown'] def getDuration(self, song): """Returns the duration of song.""" if song[-4:] == '.mp3': return MP3(song).info.length return int(float(mediainfo(song)['duration']))
class App(QMainWindow): def __init__(self): super().__init__() self.player = QMediaPlayer() self.playlist = QMediaPlaylist() self.title = 'Personal audio player v2.3' self.left = 300 self.top = 300 self.width = 300 self.height = 150 self.color = 0 # 0- toggle to dark 1- toggle to light self.userAction = -1 # 0- stopped, 1- playing 2-paused self.duration = 0 #self.getRecommendedPlaylist() self.initUI() def initUI(self): # Add file menu menubar = self.menuBar() filemenu = menubar.addMenu('File') windowmenu = menubar.addMenu('Window') connectTo = menubar.addMenu('Connect to...') fileAct = QAction('Open File', self) folderAct = QAction('Open Folder', self) themeAct = QAction('Toggle light/dark theme', self) yandexMusic = QAction('Yandex Music', self) deezer = QAction('Deezer', self) spotify = QAction('Spotify', self) pandora = QAction('Pandora', self) mixcloud = QAction('Pandora', self) fileAct.setShortcut('Ctrl+O') folderAct.setShortcut('Ctrl+D') themeAct.setShortcut('Ctrl+T') filemenu.addAction(fileAct) filemenu.addAction(folderAct) windowmenu.addAction(themeAct) connectTo.addAction(yandexMusic) connectTo.addAction(deezer) connectTo.addAction(pandora) connectTo.addAction(mixcloud) connectTo.addAction(spotify) fileAct.triggered.connect(self.openFile) folderAct.triggered.connect(self.addFiles) themeAct.triggered.connect(self.toggleColors) yandexMusic.triggered.connect(self.connectYandexMusic) deezer.triggered.connect(self.connectYandexMusic) pandora.triggered.connect(self.connectYandexMusic) mixcloud.triggered.connect(self.connectYandexMusic) spotify.triggered.connect(self.connectYandexMusic) self.addControls() self.setWindowTitle(self.title) self.setGeometry(self.left, self.top, self.width, self.height) self.toggleColors() self.show() def downloadTrack(self, name): page = requests.get("http://zaycev.net/search.html?query_search=" + name).text parser = fromstring(page) fileJSON = parser.xpath('//div/@data-url') resp = requests.get("http://zaycev.net" + fileJSON[1]) data = resp.json() with open("track.json", 'w') as outfile: json.dump(data, outfile) connection_file = open("track.json", 'r') conn_string = json.load(connection_file) track = requests.get(conn_string['url'].rsplit('?')[0]) try: os.makedirs('./tracks') except OSError: pass out = open("./tracks/" + name.strip() + ".mp3", 'wb') out.write(track.content) out.close() QMessageBox.about(self, "Track added!", name.strip() + " was added") def playRecommend(self): try: similarTracks = open("playlist.txt", 'r') i = 0 for line in similarTracks.readlines(): if i == 5: break else: i = i+1 print(line) self.downloadTrack(line) self.openFileRec("./tracks/" + line.strip() + ".mp3") except: QMessageBox.about(self, "Play error!", "Unknown error!") def getRecommendedPlaylist(self, song): try: audio = ID3(song[0]) title = ''.join(audio['TIT2'].text[0].rsplit('(')[0]) if title[-1] == ' ': title = title[0:-1] artist = audio['TPE1'].text[0] if artist[-1] == ' ': artist = artist[0:-1] print(artist + " " + title) resp = requests.get("http://ws.audioscrobbler.com/2.0/?method=track.getsimilar&artist=" + artist + "&track=" + title + "&api_key=API_key&format=json") data = resp.json() with open("playlist" + artist + "-" + title + ".json", 'w') as outfile: json.dump(data, outfile) connection_file = open("playlist" + artist + "-" + title + ".json", 'r') conn_string = json.load(connection_file) with open("playlist.txt", 'a') as playlistFile: for track in conn_string['similartracks']['track']: playlistFile.write(track['artist']['name'] + " " + track['name'] + "\n") playlistFile.close() except: QMessageBox.about(self, "Error!", "Some trouble with export title or artist name, try other track") def getRecommendedPlaylistFolder(self, song): audio = ID3(song) title = ''.join(audio['TIT2'].text[0].rsplit('(')[0]) if title[-1] == ' ': title = title[0:-1] artist = audio['TPE1'].text[0] if artist[-1] == ' ': artist = artist[0:-1] print(artist + " " + title) resp = requests.get("http://ws.audioscrobbler.com/2.0/?method=track.getsimilar&artist=" + artist + "&track=" + title + "&api_key=API_key&format=json") data = resp.json() with open("playlist" + artist + "-" + title + ".json", 'w') as outfile: json.dump(data, outfile) connection_file = open("playlist" + artist + "-" + title + ".json", 'r') conn_string = json.load(connection_file) with open("playlist.txt", 'a') as playlistFile: for track in conn_string['similartracks']['track']: playlistFile.write(track['artist']['name'] + " " + track['name'] + "\n") playlistFile.close() def addControls(self): wid = QWidget(self) self.setCentralWidget(wid) # Add song controls volumeslider = QSlider(Qt.Horizontal, self) volumeslider.setFocusPolicy(Qt.NoFocus) volumeslider.valueChanged[int].connect(self.changeVolume) volumeslider.setValue(50) sldPosition = QSlider(Qt.Horizontal, self) sldPosition.setMinimum(0) sldPosition.setFocusPolicy(Qt.NoFocus) #sldPosition.valueChanged.connect(self.player.setPosition) self.player.positionChanged.connect(sldPosition.setValue) sldPosition.setMaximum(180000) playBtn = QPushButton('Play') # play button pauseBtn = QPushButton('Pause') # pause button stopBtn = QPushButton('Stop') # stop button # Add playlist controls prevBtn = QPushButton('Prev') shuffleBtn = QPushButton('Shuffle') nextBtn = QPushButton('Next') playRecommendedBtn = QPushButton('Play Recommended Playlist') like = QPushButton('Like') dislike = QPushButton('DisLike') # Add button layouts controlArea = QVBoxLayout() # centralWidget controls = QHBoxLayout() playlistCtrlLayout = QHBoxLayout() playRec = QHBoxLayout() ld = QHBoxLayout() # Add buttons to song controls layout controls.addWidget(playBtn) controls.addWidget(pauseBtn) controls.addWidget(stopBtn) # Add buttons to playlist controls layout playlistCtrlLayout.addWidget(prevBtn) playlistCtrlLayout.addWidget(shuffleBtn) playlistCtrlLayout.addWidget(nextBtn) playRec.addWidget(playRecommendedBtn) ld.addWidget(like) ld.addWidget(dislike) # Add to vertical layout controlArea.addWidget(sldPosition) controlArea.addWidget(volumeslider) controlArea.addLayout(controls) controlArea.addLayout(playlistCtrlLayout) controlArea.addLayout(playRec) controlArea.addLayout(ld) wid.setLayout(controlArea) # Connect each signal to their appropriate function playBtn.clicked.connect(self.playhandler) pauseBtn.clicked.connect(self.pausehandler) stopBtn.clicked.connect(self.stophandler) prevBtn.clicked.connect(self.prevSong) shuffleBtn.clicked.connect(self.shufflelist) nextBtn.clicked.connect(self.nextSong) playRecommendedBtn.clicked.connect(self.playRecommend) like.clicked.connect(self.like) dislike.clicked.connect(self.dislike) self.statusBar() self.playlist.currentMediaChanged.connect(self.songChanged) def like(self, text): QMessageBox.about(self, "Attention!", "You have Liked track!") def dislike(self, text): QMessageBox.about(self, "Attention!", "You have DisLiked track!") def connectYandexMusic(self): apple = LoginWindow() apple.exec_() def openFile(self): print("File button clicked!") song = QFileDialog.getOpenFileName(self, "Open Song", "~", "Sound Files (*.mp3 *.ogg *.wav *.m4a)") print(song[0]) if song[0] != '': url = QUrl.fromLocalFile(song[0]) if self.playlist.mediaCount() == 0: self.playlist.addMedia(QMediaContent(url)) self.player.setPlaylist(self.playlist) self.player.play() self.userAction = 1 print(self.playlist.mediaCount()) else: self.playlist.addMedia(QMediaContent(url)) print(self.playlist.mediaCount()) self.getRecommendedPlaylist(song) def openFileRec(self, path): song = path if song != '': url = QUrl.fromLocalFile(song) if self.playlist.mediaCount() == 0: self.playlist.addMedia(QMediaContent(url)) self.player.setPlaylist(self.playlist) self.player.play() self.userAction = 1 print(self.playlist.mediaCount()) else: self.playlist.addMedia(QMediaContent(url)) print(self.playlist.mediaCount()) def addFiles(self): print("Folder button clicked!") if self.playlist.mediaCount() != 0: self.folderIterator() print(self.playlist.mediaCount()) else: self.folderIterator() self.player.setPlaylist(self.playlist) self.player.playlist().setCurrentIndex(0) self.player.play() print(self.playlist.mediaCount()) self.userAction = 1 def folderIterator(self): folderChosen = QFileDialog.getExistingDirectory(self, 'Open Music Folder', '~') if folderChosen != None: it = QDirIterator(folderChosen) it.next() while it.hasNext(): if it.fileInfo().isDir() == False and it.filePath() != '.': fInfo = it.fileInfo() print(it.filePath(), fInfo.suffix()) if fInfo.suffix() in ('mp3', 'ogg', 'wav', 'm4a'): print('added file', fInfo.fileName()) self.getRecommendedPlaylistFolder(it.filePath()) self.playlist.addMedia(QMediaContent(QUrl.fromLocalFile(it.filePath()))) it.next() if it.fileInfo().isDir() == False and it.filePath() != '.': fInfo = it.fileInfo() print(it.filePath(), fInfo.suffix()) if fInfo.suffix() in ('mp3', 'ogg', 'wav', 'm4a'): print('added file', fInfo.fileName()) self.getRecommendedPlaylistFolder(it.filePath()) self.playlist.addMedia(QMediaContent(QUrl.fromLocalFile(it.filePath()))) def playhandler(self): if self.playlist.mediaCount() == 0: self.openFile() elif self.playlist.mediaCount() != 0: self.player.play() print(self.playlist.mediaCount()) self.userAction = 1 def pausehandler(self): self.userAction = 2 self.player.pause() def stophandler(self): self.userAction = 0 self.player.stop() self.playlist.clear() print("Playlist cleared!") self.statusBar().showMessage("Stopped and cleared playlist") def changeVolume(self, value): self.player.setVolume(value) def changePosition(self, value): self.player.setPosition(value) def prevSong(self): if self.playlist.mediaCount() == 0: self.openFile() elif self.playlist.mediaCount() != 0: self.player.playlist().previous() def shufflelist(self): self.playlist.shuffle() print("Shuffled playlist!") def nextSong(self): if self.playlist.mediaCount() == 0: self.openFile() elif self.playlist.mediaCount() != 0: self.player.playlist().next() def songChanged(self, media): if not media.isNull(): url = media.canonicalUrl() self.statusBar().showMessage(url.fileName()) def toggleColors(self): app.setStyle("Fusion") palette = QPalette() if self.color == 0: palette.setColor(QPalette.Window, QColor(53, 53, 53)) palette.setColor(QPalette.WindowText, Qt.white) palette.setColor(QPalette.Base, QColor(25, 25, 25)) palette.setColor(QPalette.AlternateBase, QColor(53, 53, 53)) palette.setColor(QPalette.ToolTipBase, Qt.white) palette.setColor(QPalette.ToolTipText, Qt.white) palette.setColor(QPalette.Text, Qt.white) palette.setColor(QPalette.Button, QColor(53, 53, 53)) palette.setColor(QPalette.ButtonText, Qt.white) palette.setColor(QPalette.BrightText, Qt.red) palette.setColor(QPalette.Link, QColor(235, 101, 54)) palette.setColor(QPalette.Highlight, QColor(235, 101, 54)) palette.setColor(QPalette.HighlightedText, Qt.black) app.setPalette(palette) self.color = 1 elif self.color == 1: palette.setColor(QPalette.Window, Qt.white) palette.setColor(QPalette.WindowText, Qt.black) palette.setColor(QPalette.Base, QColor(240, 240, 240)) palette.setColor(QPalette.AlternateBase, Qt.white) palette.setColor(QPalette.ToolTipBase, Qt.white) palette.setColor(QPalette.ToolTipText, Qt.white) palette.setColor(QPalette.Text, Qt.black) palette.setColor(QPalette.Button, Qt.white) palette.setColor(QPalette.ButtonText, Qt.black) palette.setColor(QPalette.BrightText, Qt.red) palette.setColor(QPalette.Link, QColor(66, 155, 248)) palette.setColor(QPalette.Highlight, QColor(66, 155, 248)) palette.setColor(QPalette.HighlightedText, Qt.black) app.setPalette(palette) self.color = 0
class Player(QFrame): def __init__(self, parent=None): """头部区域,包括图标/搜索/设置/登陆/最大/小化/关闭。""" super().__init__() self.setObjectName('Header') self.parent = parent self.player = QMediaPlayer() self.playlist = QMediaPlaylist() self.player.setPlaylist(self.playlist) self.playlist.setPlaybackMode(QMediaPlaylist.Loop) self.mainLayout = QHBoxLayout(self) #self.mainLayout.setSpacing(0) self.preButton = QPushButton() #self.preButton.setIcon(QIcon("../icons/music_pre.png")) self.preButton.setStyleSheet( "QPushButton{border-image: url(icons/music_pre.png)}") #self.preButton.setMaximumSize(48,48) self.preButton.setMinimumSize(36, 36) self.preButton.setObjectName("preButton") self.mainLayout.addWidget(self.preButton) self.playButton = QPushButton() self.playButton.setStyleSheet( "QPushButton{border-image: url(icons/music_on.png)}") #self.playButton.setMaximumSize(48, 48) self.playButton.setMinimumSize(36, 36) self.playButton.setObjectName("playButton") self.mainLayout.addWidget(self.playButton) self.nextButton = QPushButton() self.nextButton.setStyleSheet( "QPushButton{border-image: url(icons/music_next.png)}") #self.nextButton.setMaximumSize(48, 48) self.nextButton.setMinimumSize(36, 36) self.nextButton.setObjectName("nextButton") self.mainLayout.addWidget(self.nextButton) self.currentTimeLabel = QLabel("00:00") self.currentTimeLabel.setMinimumSize(QSize(60, 0)) self.currentTimeLabel.setAlignment(Qt.AlignRight | Qt.AlignTrailing | Qt.AlignVCenter) self.currentTimeLabel.setObjectName("currentTimeLabel") self.mainLayout.addWidget(self.currentTimeLabel) self.timeSlider = QSlider() self.timeSlider.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.timeSlider.setOrientation(Qt.Horizontal) self.timeSlider.setObjectName("timeSlider") self.mainLayout.addWidget(self.timeSlider) self.totalTimeLabel = QLabel("00:00") self.totalTimeLabel.setMinimumSize(QSize(60, 0)) self.totalTimeLabel.setAlignment(Qt.AlignLeading | Qt.AlignLeft | Qt.AlignVCenter) self.totalTimeLabel.setObjectName("totalTimeLabel") self.mainLayout.addWidget(self.totalTimeLabel) self.volumeButton = QPushButton() self.volumeButton.setObjectName("volumeButton") self.mainLayout.addWidget(self.volumeButton) self.volumeSlider = QSlider() self.volumeSlider.setMinimumWidth(100) self.volumeSlider.setMaximumWidth(150) self.volumeSlider.setMaximum(100) self.volumeSlider.setProperty("value", 5) self.volumeSlider.setOrientation(Qt.Horizontal) self.volumeSlider.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Expanding) self.volumeSlider.setObjectName("volumeSlider") self.player.setVolume(self.volumeSlider.value()) self.mainLayout.addWidget(self.volumeSlider) self.musicCycleButton = QPushButton() self.musicCycleButton.setObjectName("musicCycle") self.musicCycleButton.setToolTip("音乐循环") self.mainLayout.addWidget(self.musicCycleButton) self.musicCycleButton = QPushButton("词") self.musicCycleButton.setObjectName("musicCycle") self.musicCycleButton.setToolTip("歌词") self.mainLayout.addWidget(self.musicCycleButton) self.playlistButton = QPushButton("列") self.playlistButton.setObjectName("playlistButton") self.mainLayout.addWidget(self.playlistButton) #self.mainLayout.addStretch(1) self.registerSignalConnect() def registerSignalConnect(self): self.player.error.connect(self.erroralert) self.preButton.pressed.connect(self.playlist.previous) self.playButton.pressed.connect(self.playButtonClicked) self.nextButton.pressed.connect(self.playlist.next) self.player.durationChanged.connect(self.update_duration) self.player.positionChanged.connect(self.update_position) self.volumeSlider.valueChanged.connect(self.player.setVolume) #self.timeSlider.valueChanged.connect(self.player.setPosition) #self.timeSlider.sliderMoved.connect(self.player.setPosition) self.timeSlider.sliderReleased.connect(self.timeSliderReleased) self.timeSlider.sliderPressed.connect(self.timeSliderPressed) self.timeSlider.sliderMoved.connect(self.timeSliderMoved) def playMusic(self, qurl): print(qurl) content = QMediaContent(qurl) self.playlist.addMedia(content) self.playlist.setCurrentIndex(self.playlist.mediaCount() - 1) if self.player.state() != QMediaPlayer.PlayingState: self.player.play() #self.player.setMedia(content) #self.player.play() def playButtonClicked(self): if self.player.state() == QMediaPlayer.PlayingState: self.player.pause() else: self.player.play() def timeSliderPressed(self): """按下准备拖动时。""" # 将进度条自动更新取消。 self.player.durationChanged.disconnect() self.player.positionChanged.disconnect() # 添加进度条移动事件。 #self.slider.sliderMoved.connect(self.sliderMovedEvent) def timeSliderReleased(self): """拖动进度条的事件,用于快进快退。""" value = self.timeSlider.value() self.player.setPosition(value) self.player.durationChanged.connect(self.update_duration) self.player.positionChanged.connect(self.update_position) def timeSliderMoved(self): self.currentTimeLabel.setText(hhmmss(self.timeSlider.value())) def update_duration(self, mc): self.timeSlider.setMaximum(self.player.duration()) duration = self.player.duration() if duration >= 0: self.totalTimeLabel.setText(hhmmss(duration)) def update_position(self, *args): position = self.player.position() if position >= 0: self.currentTimeLabel.setText(hhmmss(position)) # Disable the events to prevent updating triggering a setPosition event (can cause stuttering). #self.timeSlider.blockSignals(True) self.timeSlider.setValue(position) #self.timeSlider.blockSignals(False) def erroralert(self, *args): print(args)
class MainWindow(QMainWindow, Ui_MainWindow): def __init__(self, *args, **kwargs): super(MainWindow, self).__init__(*args, **kwargs) self.setupUi(self) self.threadpool = QThreadPool() print('Multithreading with maximum %d threads' % self.threadpool.maxThreadCount()) self.player = QMediaPlayer() # Setup the playlist. self.playlist = QMediaPlaylist() self.player.setPlaylist(self.playlist) # Setup the player self.player.durationChanged.connect(self.update_duration) self.player.positionChanged.connect(self.update_position) self.playlist.currentIndexChanged.connect( self.playlist_position_changed) self.player.error.connect(self.erroralert) self.videoWidget = QVideoWidget() self.player.setVideoOutput(self.videoWidget) self.model = PlaylistModel(self.playlist) self.playListView.setModel(self.model) selection_model = self.playListView.selectionModel() selection_model.selectionChanged.connect( self.playlist_selection_changed) self.currentFrame = QVideoFrame() # Connect control buttons/slides for media player. self.playButton.pressed.connect(self.player.play) self.pauseButton.pressed.connect(self.player.pause) self.stopButton.pressed.connect(self.player.stop) layout = QVBoxLayout() layout.addWidget(self.videoWidget) self.viewWidget.setLayout(layout) self.probe = QVideoProbe() self.probe.videoFrameProbed.connect(self.on_videoFrameProbed) self.probe.setSource(self.player) self.timeSlider.valueChanged.connect(self.player.setPosition) self.open_file_action.triggered.connect(self.open_file) # button for save current frame self.saveButton.pressed.connect(self.save_frame) self.setAcceptDrops(True) self.show() def on_videoFrameProbed(self, frame): self.frame = frame def save_frame(self): worker = Worker(self.frame) self.threadpool.start(worker) def dragEnterEvent(self, e): if e.mimeData().hasUrls(): e.acceptProposedAction() def dropEvent(self, e): for url in e.mimeData().urls(): self.playlist.addMedia(QMediaContent(url)) self.model.layoutChanged.emit() # If not playing, seeking to first of newly added + play. if self.player.state() != QMediaPlayer.PlayingState: i = self.playlist.mediaCount() - len(e.mimeData().urls()) self.playlist.setCurrentIndex(i) self.player.play() def open_file(self): path, _ = QFileDialog.getOpenFileName( self, "Open file", "", "mp3 Audio (*.mp3);mp4 Video (*.mp4);Movie files (*.mov);All files (*.*)" ) if path: self.playlist.addMedia(QMediaContent(QUrl.fromLocalFile(path))) self.model.layoutChanged.emit() def update_duration(self, duration): print("!", duration) print("?", self.player.duration()) self.timeSlider.setMaximum(duration) if duration >= 0: self.totalTimeLabel.setText(hhmmss(duration)) def update_position(self, position): if position >= 0: self.currentTimeLabel.setText(hhmmss(position)) # Disable the events to prevent updating triggering a setPosition event (can cause stuttering). self.timeSlider.blockSignals(True) self.timeSlider.setValue(position) self.timeSlider.blockSignals(False) def playlist_selection_changed(self, ix): # We receive a QItemSelection from selectionChanged. i = ix.indexes()[0].row() self.playlist.setCurrentIndex(i) def playlist_position_changed(self, i): if i > -1: ix = self.model.index(i) self.playListView.setCurrentIndex(ix) # self.playlistView.setCurrentIndex(ix) def erroralert(self, *args): print(args)
class MusicPlayer(QMainWindow): """MusicPlayer houses all of elements that directly interact with the main window.""" def __init__(self, parent=None): """Initialize the QMainWindow widget. The window title, window icon, and window size are initialized here as well as the following widgets: QMediaPlayer, QMediaPlaylist, QMediaContent, QMenuBar, QToolBar, QLabel, QPixmap, QSlider, QDockWidget, QListWidget, QWidget, and QVBoxLayout. The connect signals for relavant widgets are also initialized. """ super(MusicPlayer, self).__init__(parent) self.setWindowTitle('Mosaic') window_icon = utilities.resource_filename('mosaic.images', 'icon.png') self.setWindowIcon(QIcon(window_icon)) self.resize(defaults.Settings().window_size, defaults.Settings().window_size + 63) # Initiates Qt objects to be used by MusicPlayer self.player = QMediaPlayer() self.playlist = QMediaPlaylist() self.playlist_location = defaults.Settings().playlist_path self.content = QMediaContent() self.menu = self.menuBar() self.toolbar = QToolBar() self.art = QLabel() self.pixmap = QPixmap() self.slider = QSlider(Qt.Horizontal) self.duration_label = QLabel() self.playlist_dock = QDockWidget('Playlist', self) self.library_dock = QDockWidget('Media Library', self) self.playlist_view = QListWidget() self.library_view = library.MediaLibraryView() self.library_model = library.MediaLibraryModel() self.preferences = configuration.PreferencesDialog() self.widget = QWidget() self.layout = QVBoxLayout(self.widget) self.duration = 0 self.playlist_dock_state = None self.library_dock_state = None # Sets QWidget() as the central widget of the main window self.setCentralWidget(self.widget) self.layout.setContentsMargins(0, 0, 0, 0) self.art.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored) # Initiates the playlist dock widget and the library dock widget self.addDockWidget(defaults.Settings().dock_position, self.playlist_dock) self.playlist_dock.setWidget(self.playlist_view) self.playlist_dock.setVisible(defaults.Settings().playlist_on_start) self.playlist_dock.setFeatures(QDockWidget.DockWidgetClosable) self.addDockWidget(defaults.Settings().dock_position, self.library_dock) self.library_dock.setWidget(self.library_view) self.library_dock.setVisible(defaults.Settings().media_library_on_start) self.library_dock.setFeatures(QDockWidget.DockWidgetClosable) self.tabifyDockWidget(self.playlist_dock, self.library_dock) # Sets the range of the playback slider and sets the playback mode as looping self.slider.setRange(0, self.player.duration() / 1000) self.playlist.setPlaybackMode(QMediaPlaylist.Sequential) # OSX system menu bar causes conflicts with PyQt5 menu bar if sys.platform == 'darwin': self.menu.setNativeMenuBar(False) # Initiates Settings in the defaults module to give access to settings.toml defaults.Settings() # Signals that connect to other methods when they're called self.player.metaDataChanged.connect(self.display_meta_data) self.slider.sliderMoved.connect(self.seek) self.player.durationChanged.connect(self.song_duration) self.player.positionChanged.connect(self.song_position) self.player.stateChanged.connect(self.set_state) self.playlist_view.itemActivated.connect(self.activate_playlist_item) self.library_view.activated.connect(self.open_media_library) self.playlist.currentIndexChanged.connect(self.change_index) self.playlist.mediaInserted.connect(self.initialize_playlist) self.playlist_dock.visibilityChanged.connect(self.dock_visiblity_change) self.library_dock.visibilityChanged.connect(self.dock_visiblity_change) self.preferences.dialog_media_library.media_library_line.textChanged.connect(self.change_media_library_path) self.preferences.dialog_view_options.dropdown_box.currentIndexChanged.connect(self.change_window_size) self.art.mousePressEvent = self.press_playback # Creating the menu controls, media controls, and window size of the music player self.menu_controls() self.media_controls() self.load_saved_playlist() def menu_controls(self): """Initiate the menu bar and add it to the QMainWindow widget.""" self.file = self.menu.addMenu('File') self.edit = self.menu.addMenu('Edit') self.playback = self.menu.addMenu('Playback') self.view = self.menu.addMenu('View') self.help_ = self.menu.addMenu('Help') self.file_menu() self.edit_menu() self.playback_menu() self.view_menu() self.help_menu() def media_controls(self): """Create the bottom toolbar and controls used for media playback.""" self.addToolBar(Qt.BottomToolBarArea, self.toolbar) self.toolbar.setMovable(False) play_icon = utilities.resource_filename('mosaic.images', 'md_play.png') self.play_action = QAction(QIcon(play_icon), 'Play', self) self.play_action.triggered.connect(self.player.play) stop_icon = utilities.resource_filename('mosaic.images', 'md_stop.png') self.stop_action = QAction(QIcon(stop_icon), 'Stop', self) self.stop_action.triggered.connect(self.player.stop) previous_icon = utilities.resource_filename('mosaic.images', 'md_previous.png') self.previous_action = QAction(QIcon(previous_icon), 'Previous', self) self.previous_action.triggered.connect(self.previous) next_icon = utilities.resource_filename('mosaic.images', 'md_next.png') self.next_action = QAction(QIcon(next_icon), 'Next', self) self.next_action.triggered.connect(self.playlist.next) repeat_icon = utilities.resource_filename('mosaic.images', 'md_repeat_none.png') self.repeat_action = QAction(QIcon(repeat_icon), 'Repeat', self) self.repeat_action.setShortcut('R') self.repeat_action.triggered.connect(self.repeat_song) self.toolbar.addAction(self.play_action) self.toolbar.addAction(self.stop_action) self.toolbar.addAction(self.previous_action) self.toolbar.addAction(self.next_action) self.toolbar.addAction(self.repeat_action) self.toolbar.addWidget(self.slider) self.toolbar.addWidget(self.duration_label) def file_menu(self): """Add a file menu to the menu bar. The file menu houses the Open File, Open Multiple Files, Open Playlist, Open Directory, and Exit Application menu items. """ self.open_action = QAction('Open File', self) self.open_action.setShortcut('O') self.open_action.triggered.connect(self.open_file) self.open_multiple_files_action = QAction('Open Multiple Files', self) self.open_multiple_files_action.setShortcut('M') self.open_multiple_files_action.triggered.connect(self.open_multiple_files) self.open_playlist_action = QAction('Open Playlist', self) self.open_playlist_action.setShortcut('CTRL+P') self.open_playlist_action.triggered.connect(self.open_playlist) self.open_directory_action = QAction('Open Directory', self) self.open_directory_action.setShortcut('D') self.open_directory_action.triggered.connect(self.open_directory) self.save_playlist_action = QAction('Save Playlist', self) self.save_playlist_action.setShortcut('CTRL+S') self.save_playlist_action.triggered.connect(self.save_playlist) self.exit_action = QAction('Quit', self) self.exit_action.setShortcut('CTRL+Q') self.exit_action.triggered.connect(self.closeEvent) self.file.addAction(self.open_action) self.file.addAction(self.open_multiple_files_action) self.file.addAction(self.open_playlist_action) self.file.addAction(self.open_directory_action) self.file.addSeparator() self.file.addAction(self.save_playlist_action) self.file.addSeparator() self.file.addAction(self.exit_action) def edit_menu(self): """Add an edit menu to the menu bar. The edit menu houses the preferences item that opens a preferences dialog that allows the user to customize features of the music player. """ self.preferences_action = QAction('Preferences', self) self.preferences_action.setShortcut('CTRL+SHIFT+P') self.preferences_action.triggered.connect(lambda: self.preferences.exec_()) self.edit.addAction(self.preferences_action) def playback_menu(self): """Add a playback menu to the menu bar. The playback menu houses """ self.play_playback_action = QAction('Play', self) self.play_playback_action.setShortcut('P') self.play_playback_action.triggered.connect(self.player.play) self.stop_playback_action = QAction('Stop', self) self.stop_playback_action.setShortcut('S') self.stop_playback_action.triggered.connect(self.player.stop) self.previous_playback_action = QAction('Previous', self) self.previous_playback_action.setShortcut('B') self.previous_playback_action.triggered.connect(self.previous) self.next_playback_action = QAction('Next', self) self.next_playback_action.setShortcut('N') self.next_playback_action.triggered.connect(self.playlist.next) self.playback.addAction(self.play_playback_action) self.playback.addAction(self.stop_playback_action) self.playback.addAction(self.previous_playback_action) self.playback.addAction(self.next_playback_action) def view_menu(self): """Add a view menu to the menu bar. The view menu houses the Playlist, Media Library, Minimalist View, and Media Information menu items. The Playlist item toggles the playlist dock into and out of view. The Media Library items toggles the media library dock into and out of view. The Minimalist View item resizes the window and shows only the menu bar and player controls. The Media Information item opens a dialog that shows information relevant to the currently playing song. """ self.dock_action = self.playlist_dock.toggleViewAction() self.dock_action.setShortcut('CTRL+ALT+P') self.library_dock_action = self.library_dock.toggleViewAction() self.library_dock_action.setShortcut('CTRL+ALT+L') self.minimalist_view_action = QAction('Minimalist View', self) self.minimalist_view_action.setShortcut('CTRL+ALT+M') self.minimalist_view_action.setCheckable(True) self.minimalist_view_action.triggered.connect(self.minimalist_view) self.view_media_info_action = QAction('Media Information', self) self.view_media_info_action.setShortcut('CTRL+SHIFT+M') self.view_media_info_action.triggered.connect(self.media_information_dialog) self.view.addAction(self.dock_action) self.view.addAction(self.library_dock_action) self.view.addSeparator() self.view.addAction(self.minimalist_view_action) self.view.addSeparator() self.view.addAction(self.view_media_info_action) def help_menu(self): """Add a help menu to the menu bar. The help menu houses the about dialog that shows the user information related to the application. """ self.about_action = QAction('About', self) self.about_action.setShortcut('H') self.about_action.triggered.connect(lambda: about.AboutDialog().exec_()) self.help_.addAction(self.about_action) def open_file(self): """Open the selected file and add it to a new playlist.""" filename, success = QFileDialog.getOpenFileName(self, 'Open File', '', 'Audio (*.mp3 *.flac)', '', QFileDialog.ReadOnly) if success: file_info = QFileInfo(filename).fileName() playlist_item = QListWidgetItem(file_info) self.playlist.clear() self.playlist_view.clear() self.playlist.addMedia(QMediaContent(QUrl().fromLocalFile(filename))) self.player.setPlaylist(self.playlist) playlist_item.setToolTip(file_info) self.playlist_view.addItem(playlist_item) self.playlist_view.setCurrentRow(0) self.player.play() def open_multiple_files(self): """Open the selected files and add them to a new playlist.""" filenames, success = QFileDialog.getOpenFileNames(self, 'Open Multiple Files', '', 'Audio (*.mp3 *.flac)', '', QFileDialog.ReadOnly) if success: self.playlist.clear() self.playlist_view.clear() for file in natsort.natsorted(filenames, alg=natsort.ns.PATH): file_info = QFileInfo(file).fileName() playlist_item = QListWidgetItem(file_info) self.playlist.addMedia(QMediaContent(QUrl().fromLocalFile(file))) self.player.setPlaylist(self.playlist) playlist_item.setToolTip(file_info) self.playlist_view.addItem(playlist_item) self.playlist_view.setCurrentRow(0) self.player.play() def open_playlist(self): """Load an M3U or PLS file into a new playlist.""" playlist, success = QFileDialog.getOpenFileName(self, 'Open Playlist', '', 'Playlist (*.m3u *.pls)', '', QFileDialog.ReadOnly) if success: playlist = QUrl.fromLocalFile(playlist) self.playlist.clear() self.playlist_view.clear() self.playlist.load(playlist) self.player.setPlaylist(self.playlist) for song_index in range(self.playlist.mediaCount()): file_info = self.playlist.media(song_index).canonicalUrl().fileName() playlist_item = QListWidgetItem(file_info) playlist_item.setToolTip(file_info) self.playlist_view.addItem(playlist_item) self.playlist_view.setCurrentRow(0) self.player.play() def save_playlist(self): """Save the media in the playlist dock as a new M3U playlist.""" playlist, success = QFileDialog.getSaveFileName(self, 'Save Playlist', '', 'Playlist (*.m3u)', '') if success: saved_playlist = "{}.m3u" .format(playlist) self.playlist.save(QUrl().fromLocalFile(saved_playlist), "m3u") def load_saved_playlist(self): """Load the saved playlist if user setting permits.""" saved_playlist = "{}/.m3u" .format(self.playlist_location) if os.path.exists(saved_playlist): playlist = QUrl().fromLocalFile(saved_playlist) self.playlist.load(playlist) self.player.setPlaylist(self.playlist) for song_index in range(self.playlist.mediaCount()): file_info = self.playlist.media(song_index).canonicalUrl().fileName() playlist_item = QListWidgetItem(file_info) playlist_item.setToolTip(file_info) self.playlist_view.addItem(playlist_item) self.playlist_view.setCurrentRow(0) def open_directory(self): """Open the selected directory and add the files within to an empty playlist.""" directory = QFileDialog.getExistingDirectory(self, 'Open Directory', '', QFileDialog.ReadOnly) if directory: self.playlist.clear() self.playlist_view.clear() for dirpath, __, files in os.walk(directory): for filename in natsort.natsorted(files, alg=natsort.ns.PATH): file = os.path.join(dirpath, filename) if filename.endswith(('mp3', 'flac')): self.playlist.addMedia(QMediaContent(QUrl().fromLocalFile(file))) playlist_item = QListWidgetItem(filename) playlist_item.setToolTip(filename) self.playlist_view.addItem(playlist_item) self.player.setPlaylist(self.playlist) self.playlist_view.setCurrentRow(0) self.player.play() def open_media_library(self, index): """Open a directory or file from the media library into an empty playlist.""" self.playlist.clear() self.playlist_view.clear() if self.library_model.fileName(index).endswith(('mp3', 'flac')): self.playlist.addMedia(QMediaContent(QUrl().fromLocalFile(self.library_model.filePath(index)))) self.playlist_view.addItem(self.library_model.fileName(index)) elif self.library_model.isDir(index): directory = self.library_model.filePath(index) for dirpath, __, files in os.walk(directory): for filename in natsort.natsorted(files, alg=natsort.ns.PATH): file = os.path.join(dirpath, filename) if filename.endswith(('mp3', 'flac')): self.playlist.addMedia(QMediaContent(QUrl().fromLocalFile(file))) playlist_item = QListWidgetItem(filename) playlist_item.setToolTip(filename) self.playlist_view.addItem(playlist_item) self.player.setPlaylist(self.playlist) self.player.play() def display_meta_data(self): """Display the current song's metadata in the main window. If the current song contains metadata, its cover art is extracted and shown in the main window while the track number, artist, album, and track title are shown in the window title. """ if self.player.isMetaDataAvailable(): file_path = self.player.currentMedia().canonicalUrl().toLocalFile() (album, artist, title, track_number, *__, artwork) = metadata.metadata(file_path) try: self.pixmap.loadFromData(artwork) except TypeError: self.pixmap = QPixmap(artwork) meta_data = '{} - {} - {} - {}' .format(track_number, artist, album, title) self.setWindowTitle(meta_data) self.art.setScaledContents(True) self.art.setPixmap(self.pixmap) self.layout.addWidget(self.art) def initialize_playlist(self, start): """Display playlist and reset playback mode when media inserted into playlist.""" if start == 0: if self.library_dock.isVisible(): self.playlist_dock.setVisible(True) self.playlist_dock.show() self.playlist_dock.raise_() if self.playlist.playbackMode() != QMediaPlaylist.Sequential: self.playlist.setPlaybackMode(QMediaPlaylist.Sequential) repeat_icon = utilities.resource_filename('mosaic.images', 'md_repeat_none.png') self.repeat_action.setIcon(QIcon(repeat_icon)) def press_playback(self, event): """Change the playback of the player on cover art mouse event. When the cover art is clicked, the player will play the media if the player is either paused or stopped. If the media is playing, the media is set to pause. """ if event.button() == 1 and configuration.Playback().cover_art_playback.isChecked(): if (self.player.state() == QMediaPlayer.StoppedState or self.player.state() == QMediaPlayer.PausedState): self.player.play() elif self.player.state() == QMediaPlayer.PlayingState: self.player.pause() def seek(self, seconds): """Set the position of the song to the position dragged to by the user.""" self.player.setPosition(seconds * 1000) def song_duration(self, duration): """Set the slider to the duration of the currently played media.""" duration /= 1000 self.duration = duration self.slider.setMaximum(duration) def song_position(self, progress): """Move the horizontal slider in sync with the duration of the song. The progress is relayed to update_duration() in order to display the time label next to the slider. """ progress /= 1000 if not self.slider.isSliderDown(): self.slider.setValue(progress) self.update_duration(progress) def update_duration(self, current_duration): """Calculate the time played and the length of the song. Both of these times are sent to duration_label() in order to display the times on the toolbar. """ duration = self.duration if current_duration or duration: time_played = QTime((current_duration / 3600) % 60, (current_duration / 60) % 60, (current_duration % 60), (current_duration * 1000) % 1000) song_length = QTime((duration / 3600) % 60, (duration / 60) % 60, (duration % 60), (duration * 1000) % 1000) if duration > 3600: time_format = "hh:mm:ss" else: time_format = "mm:ss" time_display = "{} / {}" .format(time_played.toString(time_format), song_length.toString(time_format)) else: time_display = "" self.duration_label.setText(time_display) def set_state(self, state): """Change the icon in the toolbar in relation to the state of the player. The play icon changes to the pause icon when a song is playing and the pause icon changes back to the play icon when either paused or stopped. """ if self.player.state() == QMediaPlayer.PlayingState: pause_icon = utilities.resource_filename('mosaic.images', 'md_pause.png') self.play_action.setIcon(QIcon(pause_icon)) self.play_action.triggered.connect(self.player.pause) elif (self.player.state() == QMediaPlayer.PausedState or self.player.state() == QMediaPlayer.StoppedState): self.play_action.triggered.connect(self.player.play) play_icon = utilities.resource_filename('mosaic.images', 'md_play.png') self.play_action.setIcon(QIcon(play_icon)) def previous(self): """Move to the previous song in the playlist. Moves to the previous song in the playlist if the current song is less than five seconds in. Otherwise, restarts the current song. """ if self.player.position() <= 5000: self.playlist.previous() else: self.player.setPosition(0) def repeat_song(self): """Set the current media to repeat and change the repeat icon accordingly. There are four playback modes: repeat none, repeat all, repeat once, and shuffle. Clicking the repeat button cycles through each playback mode. """ if self.playlist.playbackMode() == QMediaPlaylist.Sequential: self.playlist.setPlaybackMode(QMediaPlaylist.Loop) repeat_on_icon = utilities.resource_filename('mosaic.images', 'md_repeat_all.png') self.repeat_action.setIcon(QIcon(repeat_on_icon)) elif self.playlist.playbackMode() == QMediaPlaylist.Loop: self.playlist.setPlaybackMode(QMediaPlaylist.CurrentItemInLoop) repeat_on_icon = utilities.resource_filename('mosaic.images', 'md_repeat_once.png') self.repeat_action.setIcon(QIcon(repeat_on_icon)) elif self.playlist.playbackMode() == QMediaPlaylist.CurrentItemInLoop: self.playlist.setPlaybackMode(QMediaPlaylist.Random) repeat_icon = utilities.resource_filename('mosaic.images', 'md_shuffle.png') self.repeat_action.setIcon(QIcon(repeat_icon)) elif self.playlist.playbackMode() == QMediaPlaylist.Random: self.playlist.setPlaybackMode(QMediaPlaylist.Sequential) repeat_icon = utilities.resource_filename('mosaic.images', 'md_repeat_none.png') self.repeat_action.setIcon(QIcon(repeat_icon)) def activate_playlist_item(self, item): """Set the active media to the playlist item dobule-clicked on by the user.""" current_index = self.playlist_view.row(item) if self.playlist.currentIndex() != current_index: self.playlist.setCurrentIndex(current_index) if self.player.state() != QMediaPlayer.PlayingState: self.player.play() def change_index(self, row): """Highlight the row in the playlist of the active media.""" self.playlist_view.setCurrentRow(row) def minimalist_view(self): """Resize the window to only show the menu bar and audio controls.""" if self.minimalist_view_action.isChecked(): if self.playlist_dock.isVisible(): self.playlist_dock_state = True if self.library_dock.isVisible(): self.library_dock_state = True self.library_dock.close() self.playlist_dock.close() QTimer.singleShot(10, lambda: self.resize(500, 0)) else: self.resize(defaults.Settings().window_size, defaults.Settings().window_size + 63) if self.library_dock_state: self.library_dock.setVisible(True) if self.playlist_dock_state: self.playlist_dock.setVisible(True) def dock_visiblity_change(self, visible): """Change the size of the main window when the docks are toggled.""" if visible and self.playlist_dock.isVisible() and not self.library_dock.isVisible(): self.resize(defaults.Settings().window_size + self.playlist_dock.width() + 6, self.height()) elif visible and not self.playlist_dock.isVisible() and self.library_dock.isVisible(): self.resize(defaults.Settings().window_size + self.library_dock.width() + 6, self.height()) elif visible and self.playlist_dock.isVisible() and self.library_dock.isVisible(): self.resize(defaults.Settings().window_size + self.library_dock.width() + 6, self.height()) elif (not visible and not self.playlist_dock.isVisible() and not self.library_dock.isVisible()): self.resize(defaults.Settings().window_size, defaults.Settings().window_size + 63) def media_information_dialog(self): """Show a dialog of the current song's metadata.""" if self.player.isMetaDataAvailable(): file_path = self.player.currentMedia().canonicalUrl().toLocalFile() else: file_path = None dialog = information.InformationDialog(file_path) dialog.exec_() def change_window_size(self): """Change the window size of the music player.""" self.playlist_dock.close() self.library_dock.close() self.resize(defaults.Settings().window_size, defaults.Settings().window_size + 63) def change_media_library_path(self, path): """Change the media library path to the new path selected in the preferences dialog.""" self.library_model.setRootPath(path) self.library_view.setModel(self.library_model) self.library_view.setRootIndex(self.library_model.index(path)) def closeEvent(self, event): """Override the PyQt close event in order to handle save playlist on close.""" playlist = "{}/.m3u" .format(self.playlist_location) if defaults.Settings().save_playlist_on_close: self.playlist.save(QUrl().fromLocalFile(playlist), "m3u") else: if os.path.exists(playlist): os.remove(playlist) QApplication.quit()
class Ui_Dialog(QMainWindow): def __init__(self): super().__init__() self.player = QMediaPlayer() self.playlist = QMediaPlaylist() self.palette = QPalette() self.VerticalLayout = QVBoxLayout() self.userAction = -1 # 0- stopped, 1- playing 2-paused self.width = 550 self.height = 700 self.PlayerIsActive = 0 # 0 - not active, 1 - active self.nameFont = 'Plug_2.png' self.msgBox = QMessageBox() def setupUi(self, Dialog): Dialog.resize(self.width, self.height) Dialog.setWindowTitle('MyPlayer') Dialog.setWindowIcon(QIcon(self.nameFont)) # Add MainMenu self.mainMenu = QMenuBar(Dialog) self.fileMenu = self.mainMenu.addMenu('File') self.ChangeThemeMenu = self.mainMenu.addMenu('Change') self.DataBaseSongs = self.mainMenu.addMenu('Recommend') self.helpMenu = self.mainMenu.addMenu('About') # Add In Menu Actions self.openAction = QAction('Open File') self.openAction.setShortcut('Ctrl+O') self.openActions = QAction('Open Directory') self.exitAction = QAction('Exit') self.exitAction.setShortcut('Ctrl+Q') self.loadBackgroung = QAction('Load my background') self.changeBlack = QAction('Change Black') self.changeWhite = QAction('Change white') self.RecommendedDataBase = QAction('Recommended data base of songs') self.Developer = QAction('About Developer') self.Program = QAction('About Program') self.Help_ = QAction('Help') # Add to Menus Actions self.fileMenu.addAction(self.openAction) self.fileMenu.addAction(self.openActions) self.fileMenu.addAction(self.exitAction) self.ChangeThemeMenu.addAction(self.changeBlack) self.ChangeThemeMenu.addAction(self.changeWhite) self.ChangeThemeMenu.addAction(self.loadBackgroung) self.DataBaseSongs.addAction(self.RecommendedDataBase) self.helpMenu.addAction(self.Developer) self.helpMenu.addAction(self.Program) self.helpMenu.addAction(self.Help_) # Create Slider Volume self.volumeslider = QSlider(Qt.Horizontal, Dialog) self.volumeslider.setFocusPolicy(Qt.NoFocus) self.volumeslider.setGeometry(420, 650, 120, 30) self.volumeslider.setValue(100) self.volumeslider.setTickInterval(20) self.volumeslider.setTickPosition(QSlider.TicksBelow) self.volumeslider.valueChanged[int].connect(self.changeVolume) #Create Slider Status of song self.songSlider = QSlider(Qt.Horizontal, Dialog) self.songSlider.setGeometry(17, 602, 521, 15) self.songSlider.setRange(0, 0) self.songSlider.sliderMoved.connect(self.set_position) # Add labels self.timeInStart = QLabel(Dialog) self.timeInStart.setGeometry(10, 612, 70, 25) self.AllTimeSong = QLabel(Dialog) self.AllTimeSong.setGeometry(500, 610, 70, 25) self.PictureAlbum = QLabel(Dialog) self.PictureAlbum.setGeometry(20, 170, 300, 300) self.showInfoToMusic_1 = QLabel(Dialog) self.showInfoToMusic_2 = QLabel(Dialog) self.showInfoToMusic_3 = QLabel(Dialog) self.showInfoToMusic_4 = QLabel(Dialog) # Add labals in Vertical layout widget self.verticalLayoutWidget = QtWidgets.QWidget(Dialog) self.verticalLayoutWidget.setGeometry(QtCore.QRect(350, 230, 200, 200)) self.verticalLayout = QtWidgets.QVBoxLayout(self.verticalLayoutWidget) self.verticalLayout.setContentsMargins(0, 0, 0, 0) self.showInfoToMusic_1 = QtWidgets.QLabel(self.verticalLayoutWidget) self.verticalLayout.addWidget(self.showInfoToMusic_1) self.showInfoToMusic_1.setFont(QtGui.QFont("Times", 12, QtGui.QFont.Bold)) self.showInfoToMusic_2 = QtWidgets.QLabel(self.verticalLayoutWidget) self.verticalLayout.addWidget(self.showInfoToMusic_2) self.showInfoToMusic_2.setFont(QtGui.QFont("Times", 12, QtGui.QFont.Bold)) self.showInfoToMusic_3 = QtWidgets.QLabel(self.verticalLayoutWidget) self.verticalLayout.addWidget(self.showInfoToMusic_3) self.showInfoToMusic_3.setFont(QtGui.QFont("Times", 12, QtGui.QFont.Bold)) self.showInfoToMusic_4 = QtWidgets.QLabel(self.verticalLayoutWidget) self.verticalLayout.addWidget(self.showInfoToMusic_4) self.showInfoToMusic_4.setFont(QtGui.QFont("Times", 12, QtGui.QFont.Bold)) #Create buttons #initialization self.PlayBtn = QPushButton(Dialog) self.StopBtn = QPushButton(Dialog) self.BreakBtn = QPushButton(Dialog) self.NextBtn = QPushButton(Dialog) self.PrevBtn = QPushButton(Dialog) self.shuffleBtn = QPushButton(Dialog) #Show buttons self.PlayBtn.setGeometry(QtCore.QRect(245, 630, 60, 60)) self.PlayBtn.setStyleSheet('''background: transparent; border-image: url(Play.png) ''') self.StopBtn.setGeometry(QtCore.QRect(190, 635, 55, 55)) self.StopBtn.setStyleSheet('''background: transparent; border-image: url(Stop.png)''') self.BreakBtn.setGeometry(QtCore.QRect(305, 635, 55, 55)) self.BreakBtn.setStyleSheet('''background: transparent; border-image: url(Break.png) ''') self.NextBtn.setGeometry(QtCore.QRect(355, 635, 55, 55)) self.NextBtn.setStyleSheet('''background: transparent; border-image: url(Next_song.png) ''') self.PrevBtn.setGeometry(QtCore.QRect(140, 635, 55, 55)) self.PrevBtn.setStyleSheet('''background: transparent; border-image: url(Prev_song.png) ''') self.shuffleBtn.setGeometry(QtCore.QRect(90, 635, 55, 55)) self.shuffleBtn.setStyleSheet('''background: transparent; border-image: url(Shuffle_.png) ''') # Events self.player.positionChanged.connect(self.position_changed) self.player.durationChanged.connect(self.duration_changed) self.RecommendedDataBase.triggered.connect(self.connect_list) self.exitAction.triggered.connect(self.quit_trigger) self.openAction.triggered.connect(self.file_open) self.openActions.triggered.connect(self.addFiles) self.PlayBtn.clicked.connect(self.playMusic) self.BreakBtn.clicked.connect(self.stopMusic) self.StopBtn.clicked.connect(self.pauseMusic) self.PrevBtn.clicked.connect(self.prevSong) self.shuffleBtn.clicked.connect(self.shufflelist) self.NextBtn.clicked.connect(self.nextSong) self.loadBackgroung.triggered.connect(self.LoadYourBackground) self.changeBlack.triggered.connect(self.ChangeBlackTheme) self.changeWhite.triggered.connect(self.ChangeWhiteTheme) self.Developer.triggered.connect(self.AboutDeveloper) self.Program.triggered.connect(self.AboutProgram) self.Help_.triggered.connect(self.Help) self.ChangeBlackTheme() QtCore.QMetaObject.connectSlotsByName(Dialog) # Triggered function def AboutDeveloper(self): textOnDev = open('Developer.txt').read() self.msgBox.setStyleSheet('QMessageBox {background-image: url(BlackFont.png)}') self.msgBox.setText('About Developer') self.msgBox.setInformativeText(textOnDev) self.msgBox.setStandardButtons(QMessageBox.Close) self.msgBox.show() def AboutProgram(self): TextOnDev = open('Program.txt').read() self.msgBox.setStyleSheet('QMessageBox {background-image: url(BlackFont.png)}') self.msgBox.setText('About Program') self.msgBox.setInformativeText(TextOnDev) self.msgBox.setStandardButtons(QMessageBox.Close) self.msgBox.show() def Help(self): TextOnDev = open('Help.txt').read() self.msgBox.setStyleSheet('QMessageBox {background-image: url(BlackFont.png)}') self.msgBox.setText('Help') self.msgBox.setInformativeText(TextOnDev) self.msgBox.setStandardButtons(QMessageBox.Close) self.msgBox.show() def connect_list(self): self.w2 = Window2() dic_music = MusicParser.parse() x = 550 y = 50 for i, j in dic_music.items(): #h = QLabel(self.w2) #h.setGeometry(10,0, x,y) verticalLayoutWidget = QtWidgets.QWidget(self.w2) verticalLayoutWidget.setGeometry(QtCore.QRect(10, 0, x, y)) verticalLayout = QtWidgets.QVBoxLayout(verticalLayoutWidget) verticalLayout.setContentsMargins(0, 0, 0, 0) left_label = QLabel(verticalLayoutWidget) verticalLayout.addWidget(left_label) left_label.setFont(QtGui.QFont("Times", 9, QtGui.QFont.Bold)) left_label.setText(i + ' - ' + j) y += 60 self.w2.show() def position_changed(self, position): self.songSlider.setValue(position) self.timeInStart.setText(str(time.strftime("%M:%S", time.gmtime(position / 1000)))) def duration_changed(self, duration): self.songSlider.setRange(0, duration) def set_position(self, position): self.player.setPosition(position) def quit_trigger(self): qApp.quit() def file_open(self): self.song = QFileDialog.getOpenFileName(self, "Open Song", "", "Sound Files (*.mp3 *.ogg *.wav *.m4a)") if self.song[0] != '': self.PlayerIsActive = 1 url = QUrl.fromLocalFile(self.song[0]) if self.playlist.mediaCount() == 0: self.playlist.addMedia(QMediaContent(url)) self.player.setPlaylist(self.playlist) self.player.play() self.userAction = 1 else: self.playlist.addMedia(QMediaContent(url)) if self.PlayerIsActive == 1: self.showImage() self.ShowInfoAboutSong() self.showSlider() def showSlider(self): self.songSlider.setMaximum(self.long) def showImage(self): self.audioFile = mutagen.File(self.song[0]) pixmap_1 = QPixmap('Plug_2.png').scaled(300, 300) try: photo = self.audioFile.tags.getall('APIC')[0].data pixmap = QPixmap() pixmap.loadFromData(photo) pixmap.scaled(300, 300) self.PictureAlbum.setPixmap(pixmap) except IndexError: self.PictureAlbum.setPixmap(pixmap_1) def ShowInfoAboutSong(self): #Open song in mutagen audioFile = mutagen.File(self.song[0]) #Create time label (all time song) self.long = audioFile.info.length #Open all info about song and editing singer = audioFile.tags.getall('TPE1') song_title = audioFile.tags.getall('TIT2') YearOfSong = audioFile.tags.getall('TDRC') Bitrate = (audioFile.info.bitrate) // 1000 singer = str(singer[0]) song_title = str(song_title[0]) #show all info on labels try: self.AllTimeSong.setText(str(time.strftime("%M:%S", time.gmtime(round(self.long))))) self.showInfoToMusic_1.setToolTip(singer.encode('latin1').decode('cp1251')) self.showInfoToMusic_2.setToolTip(song_title.encode('latin1').decode('cp1251')) self.showInfoToMusic_1.setText(singer.encode('latin1').decode('cp1251')) self.showInfoToMusic_2.setWordWrap(True) self.showInfoToMusic_2.setText(song_title.encode('latin1').decode('cp1251')) self.showInfoToMusic_2.setWordWrap(True) self.showInfoToMusic_3.setText(str(YearOfSong[0])) self.showInfoToMusic_4.setText(str(Bitrate) + ' kbps') except IndexError: self.showInfoToMusic.setText('') except UnicodeEncodeError: self.showInfoToMusic_2.setText(song_title) self.showInfoToMusic_1.setText(singer) def show_more_music(self): try: self.audioFile = mutagen.File(self.sp_songs[self.playlist.currentIndex()]) pixmap_1 = QPixmap('Plug_2.png').scaled(300, 300) try: photo = self.audioFile.tags.getall('APIC')[0].data pixmap = QPixmap() pixmap.loadFromData(photo) pixmap.scaled(300, 300) self.PictureAlbum.setPixmap(pixmap) except IndexError: self.PictureAlbum.setPixmap(pixmap_1) except IndexError: pass try: self.index = 0 audioFile = mutagen.File(self.sp_songs[self.playlist.currentIndex()]) self.long = audioFile.info.length singer = audioFile.tags.getall('TPE1') song_title = audioFile.tags.getall('TIT2') YearOfSong = audioFile.tags.getall('TDRC') Bitrate = (audioFile.info.bitrate) // 1000 singer = str(singer[0]) song_title = str(song_title[0]) try: self.AllTimeSong.setText(str(time.strftime("%M:%S", time.gmtime(round(self.long))))) self.showInfoToMusic_1.setToolTip(singer.encode('latin1').decode('cp1251')) self.showInfoToMusic_2.setToolTip(song_title.encode('latin1').decode('cp1251')) self.showInfoToMusic_1.setText(singer.encode('latin1').decode('cp1251')) self.showInfoToMusic_2.setWordWrap(True) self.showInfoToMusic_2.setText(song_title.encode('latin1').decode('cp1251')) self.showInfoToMusic_2.setWordWrap(True) self.showInfoToMusic_3.setText(str(YearOfSong[0])) self.showInfoToMusic_4.setText(str(Bitrate) + ' kbps') except IndexError: self.showInfoToMusic.setText('') except UnicodeEncodeError: self.showInfoToMusic_2.setText(song_title) self.showInfoToMusic_1.setText(singer) except IndexError: pass def addFiles(self): if self.playlist.mediaCount() != 0: self.folderIterator() else: self.folderIterator() self.player.setPlaylist(self.playlist) self.player.playlist().setCurrentIndex(0) self.player.play() self.userAction = 1 self.PlayerIsActive = 1 self.show_more_music() self.playlist.currentIndexChanged.connect(self.show_more_music) def folderIterator(self): folderChosen = QFileDialog.getExistingDirectory(self, 'Open Music Folder', '') if folderChosen != None: it = QDirIterator(folderChosen) it.next() self.sp_songs = [] while it.hasNext(): if it.fileInfo().isDir() == False and it.filePath() != '.': fInfo = it.fileInfo() if fInfo.suffix() in ('mp3', 'ogg', 'wav', 'm4a'): self.sp_songs.append(it.filePath()) self.playlist.addMedia(QMediaContent(QUrl.fromLocalFile(it.filePath()))) it.next() if it.fileInfo().isDir() == False and it.filePath() != '.': self.sp_songs.append(it.filePath()) fInfo = it.fileInfo() if fInfo.suffix() in ('mp3', 'ogg', 'wav', 'm4a'): self.playlist.addMedia(QMediaContent(QUrl.fromLocalFile(it.filePath()))) def ChangeBlackTheme(self): oImage = QImage("BlackFont.png") sImage = oImage.scaled(QtCore.QSize(self.width, self.height)) self.palette.setBrush(QPalette.Window, QBrush(sImage)) self.palette.setColor(QPalette.Text, Qt.white) self.palette.setColor(QPalette.WindowText, Qt.white) self.palette.setColor(QPalette.ToolTipText, Qt.white) QApplication.setPalette(self.palette) def ChangeWhiteTheme(self): oImage = QImage("WhiteFont_.jpg") sImage = oImage.scaled(QtCore.QSize(self.width, self.height)) self.palette.setBrush(QPalette.Window, QBrush(sImage)) self.palette.setColor(QPalette.Text, Qt.black) self.palette.setColor(QPalette.WindowText, Qt.black) self.palette.setColor(QPalette.ToolTipText, Qt.black) QApplication.setPalette(self.palette) def LoadYourBackground(self): font = QFileDialog.getOpenFileName(self, "Open Image", "", "Image Files (*.png *.jpg *.JPEG )") oImage = QImage(font[0]) sImage = oImage.scaled(QtCore.QSize(self.width, self.height)) self.palette.setBrush(QPalette.Window, QBrush(sImage)) self.palette.setColor(QPalette.Text, Qt.white) self.palette.setColor(QPalette.WindowText, Qt.white) self.palette.setColor(QPalette.ToolTipText, Qt.white) QApplication.setPalette(self.palette) def playMusic(self): if self.playlist.mediaCount() == 0: self.file_open() elif self.playlist.mediaCount() != 0: self.player.play() self.userAction = 1 self.PlayerIsActive = 1 def pauseMusic(self): self.userAction = 2 self.player.pause() self.PlayerIsActive = 0 def stopMusic(self): self.PlayerIsActive = 0 self.userAction = 0 self.player.stop() self.playlist.clear() def changeVolume(self, value): self.player.setVolume(value) def prevSong(self): if self.playlist.mediaCount() == 0: self.file_open() elif self.playlist.mediaCount() != 0: self.player.playlist().previous() try: self.show_more_music() except AttributeError: pass def shufflelist(self): if self.playlist.mediaCount() == 0: self.file_open() elif self.playlist.mediaCount() != 0: try: self.show_more_music() self.playlist.shuffle() except AttributeError: pass def nextSong(self): if self.playlist.mediaCount() == 0: self.file_open() elif self.playlist.mediaCount() != 0: try: self.player.playlist().next() self.show_more_music() except AttributeError: pass
class App(QMainWindow): def __init__(self): super().__init__() self.player = QMediaPlayer() self.playlist = QMediaPlaylist() self.title = 'PyTunes' self.left = 300 self.top = 300 self.width = 300 self.height = 150 self.color = 0 # 0- toggle to dark 1- toggle to light self.userAction = -1 # 0- stopped, 1- playing 2-paused self.initUI() def initUI(self): # Add file menu menubar = self.menuBar() filemenu = menubar.addMenu('File') windowmenu = menubar.addMenu('Window') fileAct = QAction('Open File', self) folderAct = QAction('Open Folder', self) themeAct = QAction('Toggle light/dark theme', self) fileAct.setShortcut('Ctrl+O') folderAct.setShortcut('Ctrl+D') themeAct.setShortcut('Ctrl+T') filemenu.addAction(fileAct) filemenu.addAction(folderAct) windowmenu.addAction(themeAct) fileAct.triggered.connect(self.openFile) folderAct.triggered.connect(self.addFiles) themeAct.triggered.connect(self.toggleColors) self.addControls() self.setWindowTitle(self.title) self.setGeometry(self.left, self.top, self.width, self.height) self.toggleColors() self.show() def addControls(self): wid = QWidget(self) self.setCentralWidget(wid) # Add song controls volumeslider = QSlider(Qt.Horizontal, self) volumeslider.setFocusPolicy(Qt.NoFocus) volumeslider.valueChanged[int].connect(self.changeVolume) volumeslider.setValue(100) playBtn = QPushButton('Play') # play button pauseBtn = QPushButton('Pause') # pause button stopBtn = QPushButton('Stop') # stop button # Add playlist controls prevBtn = QPushButton('Prev') shuffleBtn = QPushButton('Shuffle') nextBtn = QPushButton('Next') # Add button layouts controlArea = QVBoxLayout() # centralWidget controls = QHBoxLayout() playlistCtrlLayout = QHBoxLayout() # Add buttons to song controls layout controls.addWidget(playBtn) controls.addWidget(pauseBtn) controls.addWidget(stopBtn) # Add buttons to playlist controls layout playlistCtrlLayout.addWidget(prevBtn) playlistCtrlLayout.addWidget(shuffleBtn) playlistCtrlLayout.addWidget(nextBtn) # Add to vertical layout controlArea.addWidget(volumeslider) controlArea.addLayout(controls) controlArea.addLayout(playlistCtrlLayout) wid.setLayout(controlArea) # Connect each signal to their appropriate function playBtn.clicked.connect(self.playhandler) pauseBtn.clicked.connect(self.pausehandler) stopBtn.clicked.connect(self.stophandler) prevBtn.clicked.connect(self.prevSong) shuffleBtn.clicked.connect(self.shufflelist) nextBtn.clicked.connect(self.nextSong) self.statusBar() self.playlist.currentMediaChanged.connect(self.songChanged) def openFile(self): song = QFileDialog.getOpenFileName( self, "Open Song", "~", "Sound Files (*.mp3 *.ogg *.wav *.m4a)") if song[0] != '': url = QUrl.fromLocalFile(song[0]) if self.playlist.mediaCount() == 0: self.playlist.addMedia(QMediaContent(url)) self.player.setPlaylist(self.playlist) self.player.play() self.userAction = 1 else: self.playlist.addMedia(QMediaContent(url)) def addFiles(self): if self.playlist.mediaCount() != 0: self.folderIterator() else: self.folderIterator() self.player.setPlaylist(self.playlist) self.player.playlist().setCurrentIndex(0) self.player.play() self.userAction = 1 def folderIterator(self): folderChosen = QFileDialog.getExistingDirectory( self, 'Open Music Folder', '~') if folderChosen != None: it = QDirIterator(folderChosen) it.next() while it.hasNext(): if it.fileInfo().isDir() == False and it.filePath() != '.': fInfo = it.fileInfo() if fInfo.suffix() in ('mp3', 'ogg', 'wav', 'm4a'): self.playlist.addMedia( QMediaContent(QUrl.fromLocalFile(it.filePath()))) it.next() if it.fileInfo().isDir() == False and it.filePath() != '.': fInfo = it.fileInfo() if fInfo.suffix() in ('mp3', 'ogg', 'wav', 'm4a'): self.playlist.addMedia( QMediaContent(QUrl.fromLocalFile(it.filePath()))) def playhandler(self): if self.playlist.mediaCount() == 0: self.openFile() elif self.playlist.mediaCount() != 0: self.player.play() self.userAction = 1 def pausehandler(self): self.userAction = 2 self.player.pause() def stophandler(self): self.userAction = 0 self.player.stop() self.playlist.clear() self.statusBar().showMessage("Stopped and cleared playlist") def changeVolume(self, value): self.player.setVolume(value) def prevSong(self): if self.playlist.mediaCount() == 0: self.openFile() elif self.playlist.mediaCount() != 0: self.player.playlist().previous() def shufflelist(self): self.playlist.shuffle() def nextSong(self): if self.playlist.mediaCount() == 0: self.openFile() elif self.playlist.mediaCount() != 0: self.player.playlist().next() def songChanged(self, media): if not media.isNull(): url = media.canonicalUrl() self.statusBar().showMessage(url.fileName()) def toggleColors(self): app.setStyle("Fusion") palette = QPalette() if self.color == 0: palette.setColor(QPalette.Window, QColor(53, 53, 53)) palette.setColor(QPalette.WindowText, Qt.white) palette.setColor(QPalette.Base, QColor(25, 25, 25)) palette.setColor(QPalette.AlternateBase, QColor(53, 53, 53)) palette.setColor(QPalette.ToolTipBase, Qt.white) palette.setColor(QPalette.ToolTipText, Qt.white) palette.setColor(QPalette.Text, Qt.white) palette.setColor(QPalette.Button, QColor(53, 53, 53)) palette.setColor(QPalette.ButtonText, Qt.white) palette.setColor(QPalette.BrightText, Qt.red) palette.setColor(QPalette.Link, QColor(235, 101, 54)) palette.setColor(QPalette.Highlight, QColor(235, 101, 54)) palette.setColor(QPalette.HighlightedText, Qt.black) app.setPalette(palette) self.color = 1 elif self.color == 1: palette.setColor(QPalette.Window, Qt.white) palette.setColor(QPalette.WindowText, Qt.black) palette.setColor(QPalette.Base, QColor(240, 240, 240)) palette.setColor(QPalette.AlternateBase, Qt.white) palette.setColor(QPalette.ToolTipBase, Qt.white) palette.setColor(QPalette.ToolTipText, Qt.white) palette.setColor(QPalette.Text, Qt.black) palette.setColor(QPalette.Button, Qt.white) palette.setColor(QPalette.ButtonText, Qt.black) palette.setColor(QPalette.BrightText, Qt.red) palette.setColor(QPalette.Link, QColor(66, 155, 248)) palette.setColor(QPalette.Highlight, QColor(66, 155, 248)) palette.setColor(QPalette.HighlightedText, Qt.black) app.setPalette(palette) self.color = 0
class Demo(QWidget): def __init__(self): super(Demo, self).__init__() self.time_label = QLabel(self) self.volume_slider = QSlider(self) self.progress_slider = QSlider(self) self.sound_btn = QPushButton(self) self.previous_btn = QPushButton(self) self.play_pause_btn = QPushButton(self) self.next_btn = QPushButton(self) self.mode_btn = QPushButton(self) self.list_btn = QPushButton(self) self.list_widget = QListWidget(self) self.h1_layout = QHBoxLayout() self.h2_layout = QHBoxLayout() self.all_v_layout = QVBoxLayout() self.playlist = QMediaPlaylist(self) self.player = QMediaPlayer(self) self.widget_init() self.layout_init() self.signal_init() def widget_init(self): self.time_label.setText('--/--') self.volume_slider.setRange(0, 100) self.volume_slider.setValue(100) self.volume_slider.setOrientation(Qt.Horizontal) self.progress_slider.setEnabled(False) self.progress_slider.setOrientation(Qt.Horizontal) self.sound_btn.setIcon(QIcon('images/sound_on.png')) self.previous_btn.setIcon(QIcon('images/previous.png')) self.play_pause_btn.setIcon(QIcon('images/play.png')) self.next_btn.setIcon(QIcon('images/next.png')) self.mode_btn.setIcon(QIcon('images/list_loop.png')) self.list_btn.setIcon(QIcon('images/show.png')) self.player.setPlaylist(self.playlist) self.media_list = ['/Users/louis/Downloads/music1.mp3', '/Users/louis/Downloads/music2.mp4', '/Users/louis/Downloads/music3.mp3'] for m in self.media_list: self.playlist.addMedia(QMediaContent(QUrl.fromLocalFile(m))) self.playlist.setPlaybackMode(QMediaPlaylist.Sequential) self.list_widget.addItems([m.split('/')[-1] for m in self.media_list]) def layout_init(self): self.h1_layout.addWidget(self.progress_slider) self.h1_layout.addWidget(self.time_label) self.h2_layout.addWidget(self.volume_slider) self.h2_layout.addWidget(self.sound_btn) self.h2_layout.addWidget(self.previous_btn) self.h2_layout.addWidget(self.play_pause_btn) self.h2_layout.addWidget(self.next_btn) self.h2_layout.addWidget(self.mode_btn) self.h2_layout.addWidget(self.list_btn) self.all_v_layout.addLayout(self.h1_layout) self.all_v_layout.addLayout(self.h2_layout) self.all_v_layout.addWidget(self.list_widget) self.all_v_layout.setSizeConstraint(QVBoxLayout.SetFixedSize) self.setLayout(self.all_v_layout) def signal_init(self): self.sound_btn.clicked.connect(lambda: self.btn_func(self.sound_btn)) self.previous_btn.clicked.connect(lambda: self.btn_func(self.previous_btn)) self.play_pause_btn.clicked.connect(lambda: self.btn_func(self.play_pause_btn)) self.next_btn.clicked.connect(lambda: self.btn_func(self.next_btn)) self.mode_btn.clicked.connect(lambda: self.btn_func(self.mode_btn)) self.list_btn.clicked.connect(lambda: self.btn_func(self.list_btn)) self.volume_slider.valueChanged.connect(self.volume_slider_func) self.list_widget.doubleClicked.connect(self.list_play_func) self.player.durationChanged.connect(self.get_duration_func) self.player.positionChanged.connect(self.get_position_func) self.progress_slider.sliderMoved.connect(self.update_position_func) def btn_func(self, btn): if btn == self.sound_btn: if self.player.isMuted(): self.player.setMuted(False) self.sound_btn.setIcon(QIcon('images/sound_on')) else: self.player.setMuted(True) self.sound_btn.setIcon(QIcon('images/sound_off')) elif btn == self.previous_btn: if self.playlist.currentIndex() == 0: self.playlist.setCurrentIndex(self.playlist.mediaCount() - 1) else: self.playlist.previous() elif btn == self.play_pause_btn: if self.player.state() == 1: self.player.pause() self.play_pause_btn.setIcon(QIcon('images/play.png')) else: self.player.play() self.play_pause_btn.setIcon(QIcon('images/pause.png')) elif btn == self.next_btn: if self.playlist.currentIndex() == self.playlist.mediaCount() - 1: self.playlist.setCurrentIndex(0) else: self.playlist.next() elif btn == self.mode_btn: if self.playlist.playbackMode() == 2: self.playlist.setPlaybackMode(QMediaPlaylist.Loop) self.mode_btn.setIcon(QIcon('images/item_loop.png')) elif self.playlist.playbackMode() == 3: self.playlist.setPlaybackMode(QMediaPlaylist.Random) self.mode_btn.setIcon(QIcon('images/random.png')) elif self.playlist.playbackMode() == 4: self.playlist.setPlaybackMode(QMediaPlaylist.Sequential) self.mode_btn.setIcon(QIcon('images/list_loop.png')) elif btn == self.list_btn: if self.list_widget.isHidden(): self.list_widget.show() self.list_btn.setIcon(QIcon('images/show.png')) else: self.list_widget.hide() self.list_btn.setIcon(QIcon('images/hide.png')) def volume_slider_func(self, value): self.player.setVolume(value) if value == 0: self.sound_btn.setIcon(QIcon('images/sound_off.png')) else: self.sound_btn.setIcon(QIcon('images/sound_on.png')) def list_play_func(self): self.playlist.setCurrentIndex(self.list_widget.currentRow()) self.player.play() self.play_pause_btn.setIcon(QIcon('images/pause.png')) def get_duration_func(self, d): self.progress_slider.setRange(0, d) self.progress_slider.setEnabled(True) self.get_time_func(d) def get_time_func(self, d): seconds = int(d / 1000) minutes = int(seconds / 60) seconds -= minutes * 60 if minutes == 0 and seconds == 0: self.time_label.setText('--/--') self.play_pause_btn.setIcon(QIcon('images/play.png')) else: self.time_label.setText('{}:{}'.format(minutes, seconds)) def get_position_func(self, p): self.progress_slider.setValue(p) def update_position_func(self, v): self.player.setPosition(v) d = self.progress_slider.maximum() - v self.get_time_func(d)
class MainWindowMusicPlayer(QMainWindow): stopState: bool def __init__(self): super().__init__() self.currentPlaylist = QMediaPlaylist() self.player = QMediaPlayer() self.userAction = -1 # 0 - stopped, 1 - playing 2 - paused self.player.mediaStatusChanged.connect(self.qmp_media_status_changed) self.player.stateChanged.connect(self.qmp_state_changed) self.player.positionChanged.connect(self.qmp_position_changed) self.player.volumeChanged.connect(self.qmp_volume_changed) self.player.setVolume(60) # Status bar self.statusBar().showMessage('No Media' ' :: %d' % self.player.volume()) self.home_screen() def home_screen(self): self.setWindowTitle('Music Player') self.create_menubar() self.create_toolbar() controlBar = self.add_controls() # need to add both information screen # and control bar to the central widget. centralWidget = QWidget() centralWidget.setLayout(controlBar) self.setCentralWidget(centralWidget) # Set size of the MainWindow self.resize(200, 100) self.show() def create_menubar(self): menubar = self.menuBar() file_menu = menubar.addMenu('File') file_menu.addAction(self.file_open()) file_menu.addAction(self.song_info()) file_menu.addAction(self.folder_open()) file_menu.addAction(self.exit_action()) def create_toolbar(self): pass def add_controls(self): controlArea = QVBoxLayout() seekSliderLayout = QHBoxLayout() controls = QHBoxLayout() playlistCtrlLayout = QHBoxLayout() # creating buttons playBtn = QPushButton('Play') # play button pauseBtn = QPushButton('Pause') # pause button stopBtn = QPushButton('Stop') # stop button volumeDescBtn = QPushButton('V (-)') # Decrease Volume volumeIncBtn = QPushButton('V (+)') # Increase Volume # creating playlist controls prevBtn = QPushButton('Prev Song') nextBtn = QPushButton('Next Song') # creating seek slider seekSlider = QSlider() seekSlider.setMinimum(0) seekSlider.setMaximum(100) seekSlider.setOrientation(Qt.Horizontal) seekSlider.setTracking(False) seekSlider.sliderMoved.connect(self.seek_position) # seekSlider.valueChanged.connect(self.seekPosition) seekSliderLabel1 = QLabel('0.00') seekSliderLabel2 = QLabel('0.00') seekSliderLayout.addWidget(seekSliderLabel1) seekSliderLayout.addWidget(seekSlider) seekSliderLayout.addWidget(seekSliderLabel2) # Add handler for each button. Not using the default slots. playBtn.clicked.connect(self.play_handler) pauseBtn.clicked.connect(self.pause_handler) stopBtn.clicked.connect(self.stop_handler) volumeDescBtn.clicked.connect(self.decrease_volume) volumeIncBtn.clicked.connect(self.increase_volume) # Adding to the horizontal layout controls.addWidget(volumeDescBtn) controls.addWidget(playBtn) controls.addWidget(pauseBtn) controls.addWidget(stopBtn) controls.addWidget(volumeIncBtn) # playlist control button handlers prevBtn.clicked.connect(self.prev_item_playlist) nextBtn.clicked.connect(self.next_item_playlist) playlistCtrlLayout.addWidget(prevBtn) playlistCtrlLayout.addWidget(nextBtn) # Adding to the vertical layout controlArea.addLayout(seekSliderLayout) controlArea.addLayout(controls) controlArea.addLayout(playlistCtrlLayout) return controlArea # Music playback function def play_handler(self): self.userAction = 1 self.statusBar().showMessage('Playing at Volume %d' % self.player.volume()) if self.player.state() == QMediaPlayer.StoppedState: if self.player.mediaStatus() == QMediaPlayer.NoMedia: print(self.currentPlaylist.mediaCount()) if self.currentPlaylist.mediaCount() == 0: self.open_file() if self.currentPlaylist.mediaCount() != 0: self.player.setPlaylist(self.currentPlaylist) elif self.player.mediaStatus() ==\ QMediaPlayer.LoadedMedia: self.player.play() elif self.player.mediaStatus() ==\ QMediaPlayer.BufferedMedia: self.player.play() elif self.player.state() == QMediaPlayer.PlayingState: pass elif self.player.state() == QMediaPlayer.PausedState: self.player.play() # Music pause function def pause_handler(self): self.userAction = 2 self.statusBar().showMessage( 'Paused %s at position' ' %s at Volume %d' % (self.player.metaData(QMediaMetaData.Title), self.centralWidget().layout().itemAt(0).layout().itemAt( 0).widget().text(), self.player.volume())) self.player.pause() # Music stop function def stop_handler(self): self.userAction = 0 self.statusBar().showMessage('Stopped at Volume %d' % (self.player.volume())) if self.player.state() == QMediaPlayer.PlayingState: self.stopState = True self.player.stop() elif self.player.state() == QMediaPlayer.PausedState: self.player.stop() elif self.player.state() == QMediaPlayer.StoppedState: pass # Music status change function def qmp_media_status_changed(self): if self.player.mediaStatus() == QMediaPlayer.LoadedMedia \ and self.userAction == 1: durationT = self.player.duration() self.centralWidget().layout().itemAt(0).layout() \ .itemAt(1).widget().setRange(0, durationT) self.centralWidget().layout().itemAt(0).layout() \ .itemAt(2).widget().setText( '%d:%02d' % (int(durationT / 60000), int((durationT / 1000) % 60))) self.player.play() # Music playing change function def qmp_state_changed(self): if self.player.state() == QMediaPlayer.StoppedState: self.player.stop() # Music time change function def qmp_position_changed(self, position, senderType=False): sliderLayout = self.centralWidget().layout().itemAt(0)\ .layout() if not senderType: sliderLayout.itemAt(1).widget().setValue(position) # update the text label sliderLayout.itemAt(0).widget()\ .setText('%d:%02d' % (int(position / 60000), int((position / 1000) % 60))) def seek_position(self, position): sender = self.sender() if isinstance(sender, QSlider): if self.player.isSeekable(): self.player.setPosition(position) # Music volume change function def qmp_volume_changed(self): msg = self.statusBar().currentMessage() msg = msg[:-2] + str(self.player.volume()) self.statusBar().showMessage(msg) # Music volume + change function def increase_volume(self): vol = self.player.volume() vol = min(vol + 5, 100) self.player.setVolume(vol) # Music volume - change function def decrease_volume(self): vol = self.player.volume() vol = max(vol - 5, 0) self.player.setVolume(vol) # File open function def file_open(self): fileAc = QAction(QIcon('icons\\open.png'), 'Open File', self) fileAc.setShortcut('Ctrl+O') fileAc.setStatusTip('Open File') fileAc.triggered.connect(self.open_file) return fileAc # File opening function def open_file(self): file_Chosen = QFileDialog.getOpenFileUrl(self, 'Open Music File', expanduser('~'), 'Audio (*.mp3 *.ogg *.wav)', '*.mp3 *.ogg *.wav') if file_Chosen is not None: self.currentPlaylist.addMedia(QMediaContent(file_Chosen[0])) # Folder open function def folder_open(self): folderAc = QAction(QIcon('icons\\open_fld.png'), 'Open Folder', self) folderAc.setShortcut('Ctrl+D') folderAc.setStatusTip('Open Folder ' '(Will add all the files in' ' the folder)') folderAc.triggered.connect(self.add_files) return folderAc # Folder opening function def add_files(self): folder_Chosen = QFileDialog\ .getExistingDirectory(self, 'Open Music Folder', expanduser('~')) if folder_Chosen is not None: it = QDirIterator(folder_Chosen) it.next() while it.hasNext(): if it.fileInfo().isDir() == False\ and it.filePath() != '.': fInfo = it.fileInfo() print(it.filePath(), fInfo.suffix()) if fInfo.suffix() in ('mp3', 'ogg', 'wav'): print('added file ', fInfo.fileName()) self.currentPlaylist. \ addMedia(QMediaContent( QUrl.fromLocalFile(it.filePath()))) it.next() # Song information function def song_info(self): infoAc = QAction(QIcon('icons\\info.png'), 'Info', self) infoAc.setShortcut('Ctrl+I') infoAc.setStatusTip('Displays Current Song Information') infoAc.triggered.connect(self.display_song_info) return infoAc # Show song information def display_song_info(self): metaDataKeyList = self.player.availableMetaData() fullText = '<table class="tftable" border="0">' for key in metaDataKeyList: value = self.player.metaData(key) fullText = \ fullText + '<tr><td>' + key + '</td><td>' + \ str(value) + '</td></tr>' fullText = fullText + '</table>' infoBox = QMessageBox(self) infoBox.setWindowTitle('Detailed Song Information') infoBox.setTextFormat(Qt.RichText) infoBox.setText(fullText) infoBox.addButton('OK', QMessageBox.AcceptRole) infoBox.show() # Switch to previous song def prev_item_playlist(self): self.player.playlist().previous() # Switch to next song def next_item_playlist(self): self.player.playlist().next() # exit function def exit_action(self): exitAc = QAction(QIcon('icons\\exit.png'), '&Exit', self) exitAc.setShortcut('Ctrl+Q') exitAc.setStatusTip('Exit App') exitAc.triggered.connect(self.close) return exitAc # exiting function @staticmethod def exit(): sys.exit(app.exec_())
class Player(QMediaPlayer): def __init__(self, parent=None): super(Player, self).__init__(parent) self.parent = parent self.player = QMediaPlayer() self.queueList = QMediaPlaylist() self.player.setPlaylist(self.queueList) self.queueData = [] self.position = 0 self.volume = 100 self.player.mediaStatusChanged.connect(self.qmp_mediaStatusChanged) self.player.positionChanged.connect(self.qmp_positionChanged) self.player.durationChanged.connect(self.durationChanged) self.queueList.currentIndexChanged.connect(self.playlistPosChanged) def add(self, data): """Add track to the queue""" queueData = { 'pc_title': data['pc_title'], 'title': data['title'], 'url': data['url'], 'date': data['date_format'], 'description': data['description'] } self.queueData.append(queueData) self.queueList.addMedia(QMediaContent(QUrl(data['url']))) def playPause(self): icon = QIcon.fromTheme("media-playback-pause") if self.player.state() == QMediaPlayer.StoppedState: if self.player.mediaStatus() == QMediaPlayer.NoMedia: if self.queueList.mediaCount() != 0: self.player.play() elif self.player.mediaStatus() == QMediaPlayer.LoadedMedia: self.queueList.setCurrentIndex(self.position) self.player.play() elif self.player.mediaStatus() == QMediaPlayer.BufferedMedia: self.player.play() elif self.player.state() == QMediaPlayer.PlayingState: icon = QIcon.fromTheme("media-playback-start") self.player.pause() elif self.player.state() == QMediaPlayer.PausedState: self.player.play() self.parent.playBtn.setIcon(icon) def startPlay(self): data = self.queueData[0] self.queueList.setCurrentIndex(0) self.parent.curPCLabel.setText(data['pc_title']) self.parent.curTrackName.setText(data['title']) self.player.play() icon = QIcon.fromTheme("media-playback-pause") self.parent.playBtn.setIcon(icon) def stop(self): self.player.stop() icon = QIcon.fromTheme("media-playback-start") self.parent.playBtn.setIcon(icon) def setPosition(self, pos): self.player.setPosition(pos) def durationChanged(self, duration): total_time = '0:00:00' duration = self.player.duration() total_time = ms_to_time(duration) self.parent.timeSlider.setMaximum(duration) self.currentTrackDuration = duration self.parent.totalTimeLabel.setText(total_time) def qmp_mediaStatusChanged(self, status): icon = QIcon.fromTheme("media-playback-pause") if self.player.state() == QMediaPlayer.StoppedState: icon = QIcon.fromTheme("media-playback-start") elif self.player.state() == QMediaPlayer.PausedState: icon = QIcon.fromTheme("media-playback-start") self.parent.playBtn.setIcon(icon) def qmp_positionChanged(self, position, senderType=False): self.currentTime = position current_time = '0:00:00' if position != -1: current_time = ms_to_time(position) self.parent.timeLabel.setText(current_time) self.parent.timeSlider.blockSignals(True) self.parent.timeSlider.setValue(position) self.parent.timeSlider.blockSignals(False) def playlistPosChanged(self): pos = self.queueList.currentIndex() data = self.queueData[pos] self.parent.curPCLabel.setText(data['pc_title']) self.parent.curTrackName.setText(data['title']) windowTitle = '{0} - {1}'.format(data['pc_title'], data['title']) self.parent.setWindowTitle(windowTitle) if self.queueList.mediaCount() > 1: if pos < self.queueList.mediaCount() - 1: self.parent.queueNextBtn.setEnabled(True) else: self.parent.queueNextBtn.setEnabled(False) if pos > 0: self.parent.queuePrevBtn.setEnabled(True) else: self.parent.queuePrevBtn.setEnabled(False) if pos < self.queueList.mediaCount(): prevPos = 0 if self.position < pos: prevPos = pos - 1 else: prevPos = pos + 1 prevItem = self.parent.queueList.item(prevPos) prevWidget = self.parent.queueList.itemWidget(prevItem) if prevItem: prevWidget.statusIcon.setPixmap(QPixmap()) self.position = pos item = self.parent.queueList.item(pos) widget = self.parent.queueList.itemWidget(item) if widget: icon = QIcon.fromTheme("media-playback-start") widget.statusIcon.setPixmap(icon.pixmap(16, 16)) def setVolume(self, volume): self.player.setVolume(volume) def rev10Secs(self): position = self.player.position() new_pos = position - 10000 self.player.setPosition(new_pos) def for10Secs(self): position = self.player.position() new_pos = position + 10000 self.player.setPosition(new_pos) def delete(self, position): """ Delete the track and her data from position""" self.queueData.pop(position) self.queueList.removeMedia(position) if (position == self.position): self.playlistPosChanged()
class YtdlMusic(QMainWindow, Ui_MainWindow): def __init__(self, *args, **kwargs): QMainWindow.__init__(self, *args, **kwargs) self.setupUi(self) self.setWindowIcon(QIcon(LOCAL_DIR + '/ytdl_music.svg')) self.player = QMediaPlayer() self.player.isSeekable() self.playList = QMediaPlaylist() self.playListData = [] self.player.setPlaylist(self.playList) self.currentPos = 0 self.currentTrackDuration = '0:00:00' self.player.positionChanged.connect(self.positionChanged) self.player.durationChanged.connect(self.durationChanged) self.playList.currentIndexChanged.connect(self.playlistPosChanged) self.playlistTable.itemDoubleClicked.connect(self.changeTrack) self.playlistTable.itemSelectionChanged.connect(self.selectedTracks) # self.timeSlider.valueChanged.connect(self.setPosition) self.addBtn.clicked.connect(self.addDialog) self.removeBtn.clicked.connect(self.delTracks) self.playBtn.clicked.connect(self.playPause) self.stopBtn.clicked.connect(self.stop) self.prevBtn.clicked.connect(self.playList.previous) self.nextBtn.clicked.connect(self.playList.next) self.playlistTable.setHorizontalHeaderLabels([ '', _translate('MainWindow', 'Channel'), _translate('MainWindow', 'Title') ]) header = self.playlistTable.horizontalHeader() header.setSectionResizeMode(1, QHeaderView.Stretch) header.setSectionResizeMode(2, QHeaderView.Stretch) def setPosition(self, pos): self.player.setPosition(pos) def durationChanged(self, duration): total_time = '0:00:00' duration = self.player.duration() total_time = ms_to_time(duration) # self.timeSlider.setMaximum(duration) self.currentTrackDuration = duration # self.totalTimeLabel.setText(total_time) self.currentTrackDuration = total_time def mediaStatusChanged(self, status): icon = QIcon.fromTheme("media-playback-pause") if self.player.state() == QMediaPlayer.StoppedState: icon = QIcon.fromTheme("media-playback-start") elif self.player.state() == QMediaPlayer.PausedState: icon = QIcon.fromTheme("media-playback-start") self.playBtn.setIcon(icon) def positionChanged(self, position, senderType=False): self.currentTime = position current_time = '0:00:00' if position != -1: current_time = ms_to_time(position) self.timeLabel.setText('{0} / {1}'.format( current_time, self.currentTrackDuration)) '''self.timeSlider.blockSignals(True) self.timeSlider.setValue(position) self.timeSlider.blockSignals(False)''' def playlistPosChanged(self): pos = self.playList.currentIndex() data = self.playListData[pos] self.setWindowTitle('YouTube-dl Music: ' + data['title']) duration = ms_to_time(data['duration'] * 1000) self.timeLabel.setText('0:00:00 / {0}'.format(duration)) # self.totalTimeLabel.setText(ms_to_time(data['duration'] * 1000)) if self.playList.mediaCount() > 1: if pos < self.playList.mediaCount() - 1: self.nextBtn.setEnabled(True) else: self.nextBtn.setEnabled(False) if pos > 0: self.prevBtn.setEnabled(True) else: self.prevBtn.setEnabled(False) prevPos = 0 if pos < self.playList.mediaCount(): if self.currentPos < pos: prevPos = pos - 1 else: prevPos = pos + 1 statusItem = QLabel() icon = QIcon.fromTheme("media-playback-start") statusItem.setPixmap(icon.pixmap(16, 16)) statusItem.setAlignment(Qt.AlignCenter) self.playlistTable.setCellWidget(pos, 0, statusItem) if prevPos > -1: self.playlistTable.setCellWidget(prevPos, 0, QLabel()) else: self.playlistTable.setItem(pos, 0, QTableWidgetItem('')) self.currentPos = pos else: statusItem = QLabel() icon = QIcon.fromTheme("media-playback-start") statusItem.setPixmap(icon.pixmap(16, 16)) statusItem.setAlignment(Qt.AlignCenter) self.playlistTable.setCellWidget(pos, 0, statusItem) def playPause(self): icon = QIcon.fromTheme("media-playback-pause") if self.player.state() == QMediaPlayer.StoppedState: if self.player.mediaStatus() == QMediaPlayer.NoMedia: if self.playList.mediaCount() != 0: self.player.play() elif self.player.mediaStatus() == QMediaPlayer.LoadedMedia: self.playList.setCurrentIndex(self.currentPos) self.player.play() elif self.player.mediaStatus() == QMediaPlayer.BufferedMedia: self.player.play() elif self.player.state() == QMediaPlayer.PlayingState: icon = QIcon.fromTheme("media-playback-start") self.player.pause() elif self.player.state() == QMediaPlayer.PausedState: self.player.play() self.playBtn.setIcon(icon) def stop(self): self.player.stop() icon = QIcon.fromTheme("media-playback-start") self.playBtn.setIcon(icon) def insertTrack(self, data): if data: self.playListData.append(data) totalTracks = self.playList.mediaCount() pos = totalTracks self.playlistTable.insertRow(totalTracks) self.playlistTable.setRowCount(totalTracks + 1) self.playlistTable.setItem(pos, 0, QTableWidgetItem('')) self.playlistTable.setItem(pos, 1, QTableWidgetItem(data['channel'])) self.playlistTable.setItem(pos, 2, QTableWidgetItem(data['title'])) media = QMediaContent(QUrl(data['url'])) self.playList.addMedia(media) if totalTracks > 1: self.nextBtn.setEnabled(True) else: self.statusBar().showMessage('Total pistas: {0}'.format( self.playList.mediaCount())) def addDialog(self): url, ok = QInputDialog.getText( self, _translate('MainWindow', 'Add video/playlist'), 'URL:') if ok and url != '': self.addPCThread = addVideos(self, url) self.addPCThread.video.connect(self.insertTrack) self.addPCThread.start() def changeTrack(self, item): pos = item.row() self.playlistTable.setCellWidget(self.currentPos, 0, QLabel()) self.playList.setCurrentIndex(pos) if self.player.state() == QMediaPlayer.StoppedState: icon = QIcon.fromTheme("media-playback-pause") self.playBtn.setIcon(icon) self.player.play() def delTracks(self): indexes = self.playlistTable.selectionModel().selectedRows() del_first = True for index in sorted(indexes): pos = index.row() if not del_first: pos -= 1 else: del_first = False self.playlistTable.removeRow(pos) self.playListData.pop(pos) self.playList.removeMedia(pos) self.playlistPosChanged() def selectedTracks(self): totalSelected = len(self.playlistTable.selectedItems()) if totalSelected > 0: self.removeBtn.setEnabled(True) else: self.removeBtn.setEnabled(False)
class App(QMainWindow): def __init__(self, song): super().__init__() self.song_path = song self.player = QMediaPlayer() self.playlist = QMediaPlaylist() self.title = 'PyTunes' self.left = 300 self.top = 300 self.width = 300 self.height = 150 self.color = 0 # 0- toggle to dark 1- toggle to light self.userAction = -1 # 0- stopped, 1- playing 2-paused self.initUI() self.song = song def initUI(self): # Add file menu self.addControls() self.setWindowTitle(self.title) self.setGeometry(self.left, self.top, self.width, self.height) self.toggleColors() self.show() def addControls(self): wid = QWidget(self) self.setCentralWidget(wid) # Add song controls volumeslider = QSlider(Qt.Horizontal, self) volumeslider.setFocusPolicy(Qt.NoFocus) volumeslider.valueChanged[int].connect(self.changeVolume) volumeslider.setValue(100) playBtn = QPushButton('Play') # play button pauseBtn = QPushButton('Pause') # pause button # Add button layouts controlArea = QVBoxLayout() # centralWidget controls = QHBoxLayout() playlistCtrlLayout = QHBoxLayout() # Add buttons to song controls layout controls.addWidget(playBtn) controls.addWidget(pauseBtn) # Add buttons to playlist controls layout # Add to vertical layout controlArea.addWidget(volumeslider) controlArea.addLayout(controls) controlArea.addLayout(playlistCtrlLayout) wid.setLayout(controlArea) # Connect each signal to their appropriate function playBtn.clicked.connect(self.playhandler) pauseBtn.clicked.connect(self.pausehandler) self.statusBar() self.playlist.currentMediaChanged.connect(self.songChanged) def openFile(self): url = QUrl.fromLocalFile(self.song_path) self.playlist.addMedia(QMediaContent(url)) self.player.setPlaylist(self.playlist) self.player.play() self.userAction = 1 def addFiles(self): if self.playlist.mediaCount() != 0: self.folderIterator() else: self.folderIterator() self.player.setPlaylist(self.playlist) self.player.playlist().setCurrentIndex(0) self.player.play() self.userAction = 1 def folderIterator(self): folderChosen = QFileDialog.getExistingDirectory( self, 'Open Music Folder', '~') if folderChosen != None: it = QDirIterator(folderChosen) it.next() while it.hasNext(): if it.fileInfo().isDir() == False and it.filePath() != '.': fInfo = it.fileInfo() if fInfo.suffix() in ('mp3', 'ogg', 'wav', 'm4a'): self.playlist.addMedia( QMediaContent(QUrl.fromLocalFile(it.filePath()))) it.next() if it.fileInfo().isDir() == False and it.filePath() != '.': fInfo = it.fileInfo() if fInfo.suffix() in ('mp3', 'ogg', 'wav', 'm4a'): self.playlist.addMedia( QMediaContent(QUrl.fromLocalFile(it.filePath()))) def playhandler(self): if self.playlist.mediaCount() == 0: self.openFile() elif self.playlist.mediaCount() != 0: self.player.play() self.userAction = 1 def pausehandler(self): self.userAction = 2 self.player.pause() def changeVolume(self, value): self.player.setVolume(value) def songChanged(self, media): if not media.isNull(): url = media.canonicalUrl() self.statusBar().showMessage(url.fileName()) def toggleColors(self): """ Fusion dark palette from https://gist.github.com/QuantumCD/6245215. Modified by me and J.J. """ app.setStyle("Fusion") palette = QPalette() if self.color == 0: palette.setColor(QPalette.Window, QColor(53, 53, 53)) palette.setColor(QPalette.WindowText, Qt.white) palette.setColor(QPalette.Base, QColor(25, 25, 25)) palette.setColor(QPalette.AlternateBase, QColor(53, 53, 53)) palette.setColor(QPalette.ToolTipBase, Qt.white) palette.setColor(QPalette.ToolTipText, Qt.white) palette.setColor(QPalette.Text, Qt.white) palette.setColor(QPalette.Button, QColor(53, 53, 53)) palette.setColor(QPalette.ButtonText, Qt.white) palette.setColor(QPalette.BrightText, Qt.red) palette.setColor(QPalette.Link, QColor(235, 101, 54)) palette.setColor(QPalette.Highlight, QColor(235, 101, 54)) palette.setColor(QPalette.HighlightedText, Qt.black) app.setPalette(palette) self.color = 1 elif self.color == 1: palette.setColor(QPalette.Window, Qt.white) palette.setColor(QPalette.WindowText, Qt.black) palette.setColor(QPalette.Base, QColor(240, 240, 240)) palette.setColor(QPalette.AlternateBase, Qt.white) palette.setColor(QPalette.ToolTipBase, Qt.white) palette.setColor(QPalette.ToolTipText, Qt.white) palette.setColor(QPalette.Text, Qt.black) palette.setColor(QPalette.Button, Qt.white) palette.setColor(QPalette.ButtonText, Qt.black) palette.setColor(QPalette.BrightText, Qt.red) palette.setColor(QPalette.Link, QColor(66, 155, 248)) palette.setColor(QPalette.Highlight, QColor(66, 155, 248)) palette.setColor(QPalette.HighlightedText, Qt.black) app.setPalette(palette) self.color = 0
class Win(QWidget): """ 重写 QWidget 类,自定义创建一个播放器窗体 """ def __init__(self): """ 构造方法:类的初始化 """ # 初始化父类 super().__init__() # 定义类属性 self.moveMode = False # 是否开启窗口移动模式 self.windowPoint = QPoint(0, 0) # 记录窗口移动时位置 self.startPoint = QPoint(0, 0) # 记录窗口移动前位置 # 设定窗体基本属性(标题、尺寸、图标等) self.setWindowTitle("简易MP3音乐播放器") # 窗口标题 self.setObjectName("app") # 对象名称 self.resize(260, 348) # 窗口尺寸 self.setFixedSize(self.width(), self.height()) # 禁止拉伸窗口大小 self.setWindowFlags(Qt.FramelessWindowHint) # 窗口无边框 self.setAutoFillBackground(True) # 允许背景填充 palette = QPalette() palette.setBrush(QPalette.Background, QBrush(QPixmap('./img/bg.jpg'))) self.setPalette(palette) # 填充背景图片 # 定义窗体组件(定义按钮、标签、列表框、滑块条等)及其基本属性 self.create_widgets() # 创建播放器和播放列表 self.media = QMediaPlayer() self.list = QMediaPlaylist() self.media.setPlaylist(self.list) # 设置窗体组件的样式和布局(颜色、图片、背景、位置、字体等) self.set_style() # 绑定事件(信号槽) self.bind_slots() # 其他初始化操作 self.list.setPlaybackMode(QMediaPlaylist.Loop) # 循环播放 self.media.setVolume(100) # 设置默认音量 self.slider_volume.setRange(0, 100) self.slider_volume.setValue(100) self.slider_pos.setRange(0, 0) # 设置默认进度 self.slider_pos.setValue(0) self.label_title.setText("简易MP3音乐播放器") # 设置默认标题 # 加载已保存的播放列表 self.load_playlist() def create_widgets(self): """ 定义窗体组件(定义按钮、标签、列表框、滑块条等)及其基本属性 :return: """ self.frame_player = QFrame(self) # 播放器布局框 self.frame_player.setObjectName("frame_player") self.frame_player.setGeometry(0, 0, 260, 120) self.frame_list = QFrame(self) # 播放列表布局框 self.frame_list.setObjectName("frame_list") self.frame_list.setGeometry(0, 120, 260, 228) self.btn_play = QPushButton(self.frame_player) # 播放按钮 self.btn_play.setObjectName("btn_play") self.btn_play.setGeometry(8, 78, 32, 32) self.btn_pause = QPushButton(self.frame_player) # 暂停按钮 self.btn_pause.setObjectName("btn_pause") self.btn_pause.setGeometry(8, 78, 32, 32) self.btn_pause.setVisible(False) self.btn_stop = QPushButton(self.frame_player) # 停止按钮 self.btn_stop.setObjectName("btn_stop") self.btn_stop.setGeometry(40, 78, 32, 32) self.btn_previous = QPushButton(self.frame_player) # 上一曲按钮 self.btn_previous.setObjectName("btn_previous") self.btn_previous.setGeometry(72, 78, 32, 32) self.btn_next = QPushButton(self.frame_player) # 下一曲按钮 self.btn_next.setObjectName("btn_next") self.btn_next.setGeometry(104, 78, 32, 32) self.btn_mute = QPushButton(self.frame_player) # 静音按钮 self.btn_mute.setObjectName("btn_mute") self.btn_mute.setGeometry(144, 90, 20, 20) self.label_title = QLabel(self.frame_player) # 歌曲标题标签 self.label_title.setObjectName("label_title") self.label_title.setGeometry(8, 38, 244, 32) self.label_time = QLabel("00:00", self.frame_player) # 时间标签 self.label_time.setObjectName("label_time") self.label_time.setGeometry(220, 74, 30, 20) self.slider_volume = QSlider(self.frame_player) # 音量滑块条 self.slider_volume.setObjectName("slider_volume") self.slider_volume.setGeometry(172, 90, 80, 20) self.slider_volume.setOrientation(Qt.Horizontal) self.slider_pos = QSlider(self.frame_player) # 播放进度条 self.slider_pos.setObjectName("slider_pos") self.slider_pos.setGeometry(0, 114, 260, 6) self.slider_pos.setOrientation(Qt.Horizontal) self.btn_add = QPushButton(self.frame_list) # 添加文件按钮 self.btn_add.setObjectName("btn_add") self.btn_add.setGeometry(8, 196, 24, 24) self.btn_remove = QPushButton(self.frame_list) # 移除文件按钮 self.btn_remove.setObjectName("btn_remove") self.btn_remove.setGeometry(40, 196, 24, 24) self.play_list = QListWidget(self.frame_list) # 播放列表 self.play_list.setObjectName("play_list") self.play_list.setGeometry(8, 8, 244, 180) self.label_version = QLabel(self.frame_list) # 软件版本说明标签 self.label_version.setObjectName("label_version") self.label_version.setGeometry(72, 196, 172, 24) self.label_version.setText("Version: 1.0 | By: yowfung") self.btn_close = QPushButton(self) # 关闭窗口按钮 self.btn_close.setObjectName("btn_close") self.btn_close.setGeometry(236, 0, 24, 24) def set_style(self): """ 设置窗体组件的样式和布局(颜色、图片、背景、位置、字体等) :return: """ # 通过各种选择器的方式,并结合qss样式表对组件进行样式美化和布局 self.setStyleSheet( # 窗体全局样式 "QWidget{font-family:'微软雅黑'}" # 播放器框和播放列表框的样式 "QFrame#frame_player{background-color: rgba(0,0,0,0)}" "QFrame#frame_list{background-color:rgba(200, 240, 255, 0.3)}" # 歌曲标题标签、时间标签、版本说明标签的样式 "QLabel#label_title{font-size:18px; color: white}" "QLabel#label_time{color: #bbb; font-size:11px; }" "QLabel#label_version{color:#444;font-size:12px}" # 音量滑块条、进度条样式 "QSlider#slider_volume::groove:horizontal{height:4px;background:rgb(167,176,255)}" "QSlider#slider_volume::handle:horizontal{width:4px;background:rgb(180, 220, 255);margin:-2px 0px;}" "QSlider#slider_pos::groove:horizontal{height:2px;background:rgb(50,80,120)}" "QSlider#slider_pos::handle:horizontal{width:6px;background:rgb(240, 50, 60);margin:-2px 0;border-radius:1px}" # 按钮样式 "QPushButton{border: none;}" "QPushButton:pressed{margin: 1px}" "QPushButton#btn_play:pressed, QPushButton#btn_pause:pressed, QPushButton#btn_stop:pressed{margin:2px}" "QPushButton#btn_play{image: url('./img/icons/play.png')}" "QPushButton#btn_pause{image: url('./img/icons/pause.png')}" "QPushButton#btn_stop{image: url('./img/icons/stop.png')}" "QPushButton#btn_previous{image: url('./img/icons/previous.png')}" "QPushButton#btn_next{image: url('./img/icons/next.png')}" "QPushButton#btn_mute{image: url('./img/icons/volume-normal.png')}" "QPushButton#btn_add{image: url('./img/icons/add.png')}" "QPushButton#btn_remove{image: url('./img/icons/remove.png')}" "QPushButton#btn_close{image: url('./img/icons/close.png')}" # 播放列表框样式 "QListWidget{border:1px solid #ccc; font-size:12px;color:#444;background-color:rgba(255,255,255,0.6)}" "QListWidget::Item:hover{background-color:rgba(120,120,120,0.15);color:black}" "QListWidget::Item:selected{background-color:rgba(120,120,150,0.3);color:#208}" # 垂直滚动条样式 "QScrollBar:vertical{background-color:#eee;width:6px}" "QScrollBar::handle:vertical{background-color:#ccc;min-height:16px}" "QScrollBar::handle::vertical:hover {background-color:#bbb;}" "QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical {height: 0}" # 水平滚动条样式 "QScrollBar:horizontal{background-color: #eee;height: 6px;}" "QScrollBar::handle:horizontal {background-color:#ccc;min-width:16px;}" "QScrollBar::handle::horizontal:hover {background-color:#bbb;}" "QScrollBar::add-line:horizontal, QScrollBar::sub-line:horizontal {width:0;}" ) def bind_slots(self): """ 绑定信号槽 :return: """ self.btn_close.clicked.connect(self.close) # 关闭按钮:被单击 self.btn_play.clicked.connect(self.con_btn_clicked_event) # 播放按钮:被单击 self.btn_pause.clicked.connect(self.con_btn_clicked_event) # 暂停按钮:被单击 self.btn_stop.clicked.connect(self.con_btn_clicked_event) # 停止按钮:被单击 self.btn_next.clicked.connect(self.con_btn_clicked_event) # 下一曲按钮:被单击 self.btn_previous.clicked.connect( self.con_btn_clicked_event) # 上一曲按钮:被单击 self.btn_mute.clicked.connect(self.con_btn_clicked_event) # 静音按钮:被单击 self.btn_add.clicked.connect(self.con_btn_clicked_event) # 添加文件按钮:被单击 self.btn_remove.clicked.connect( self.con_btn_clicked_event) # 移除项目按钮:被单击 self.slider_volume.valueChanged.connect( self.vol_changed_event) # 音量滑块条:值被改变 self.slider_pos.sliderReleased.connect( self.progress_changed_event) # 进度滑块条:值被改变 self.play_list.doubleClicked.connect( self.list_double_clicked_event) # 播放列表:被双击 self.media.positionChanged.connect( self.pos_changed_event) # 媒体播放器:播放位置被改变 self.media.durationChanged.connect( self.duration_changed_event) # 媒体播放器:时间区间被改变 self.media.stateChanged.connect( self.state_changed_event) # 媒体播放器:播放状态被改变 self.list.currentIndexChanged.connect( self.cur_changed_event) # 媒体播放列表:当前播放项被改变 def closeEvent(self, e): """ 重写 closeEvent:关闭程序前停止播放并保存记录 :param e: :return: """ self.save_playlist() self.media.stop() def mousePressEvent(self, e): """ 重写 mousePressEvent:鼠标在顶部按下时进入“移动窗口模式” :param e: :return: """ if e.button() == Qt.LeftButton \ and 2 <= e.pos().x() <= self.width() - 2 \ and 2 <= e.pos().y() <= 28: self.moveMode = True self.startPoint = e.globalPos() self.windowPoint = self.geometry().topLeft() def mouseMoveEvent(self, e): """ 重写 mouseMoveEvent:在“移动窗口模式”中进行窗口位置移动 :param e: :return: """ if e.buttons() & Qt.LeftButton and self.moveMode: re_point = e.globalPos() - self.startPoint self.move(self.windowPoint + re_point) def mouseReleaseEvent(self, e): """ 重写 mouseReleaseEvent:鼠标释放时离开“移动窗口模式” :param e: :return: """ self.moveMode = False def con_btn_clicked_event(self): """ 播放/暂停/上一曲/下一曲/停止/静音/添加文件/移除文件等控制按钮鼠标被单击事件 :return: """ # 取出事件对象(信号发送者) sender = self.sender() # 播放 if sender.objectName() == "btn_play": self.media.play() # 暂停 if sender.objectName() == "btn_pause": self.media.pause() # 停止 if sender.objectName() == "btn_stop": self.media.stop() # 上一曲 if sender.objectName() == "btn_previous": self.previous() # 下一曲 if sender.objectName() == "btn_next": self.next() # 静音 if sender.objectName() == "btn_mute": # 如果音量为0,或者原先未静音,则设置成静音 status = self.slider_volume.value() is 0 or not self.media.isMuted( ) self.media.setMuted(status) img = "./img/icons/volume-mute.png" if status else "./img/icons/volume-normal.png" self.btn_mute.setStyleSheet("QPushButton#btn_mute{image:url('" + img + "')}") # 静音按钮的图标样式 # 添加文件到列表 if sender.objectName() == "btn_add": self.add_to_list() # 从列表移除 if sender.objectName() == "btn_remove": self.remove_from_list() def list_double_clicked_event(self): """ 播放列表框中的项目被双击事件 :return: """ # 播放选中的歌曲 index = self.play_list.currentRow() if 0 <= index < self.play_list.count(): self.list.setCurrentIndex(index) self.media.play() def vol_changed_event(self, val): """ 音量滑块条值被改变事件:调节音量 :return: """ self.media.setVolume(val) self.media.setMuted(val is 0) # 音量为0时设置为静音 img = "./img/icons/volume-mute.png" if val is 0 else "./img/icons/volume-normal.png" self.btn_mute.setStyleSheet("QPushButton#btn_mute{image:url('" + img + "')}") # 静音按钮图标样式 def state_changed_event(self, state): """ 播放状态被改变事件:变化播放按钮和暂停按钮的可视状态 :param state: :return: """ self.btn_play.setVisible( True if state != 1 else False) # 只要不是“正在播放”,就显示播放按钮 self.btn_pause.setVisible( True if state == 1 else False) # 只要“正在播放”,就显示暂停按钮 def progress_changed_event(self): """ 播放进度条值被改变事件:定位播放 :return: """ # 将播放位置设置为进度条调节的位置 self.media.setPosition(self.slider_pos.value()) def pos_changed_event(self, position): """ 播放位置被改变事件:更新播放时间和进度条位置 :return: """ time = self.calc_time(position) # 计算播放时间 self.label_time.setText(time) # 显示播放时间 self.slider_pos.setValue(position) # 设置播放进度 def duration_changed_event(self, duration): """ 时间区间被改变事件:更新进度条区间 :param duration: :return: """ self.slider_pos.setRange(0, duration) def cur_changed_event(self, index): """ 当前播放索引被改变事件:更新播放列表索引 :param index: :return: """ self.play_list.setCurrentRow(index) # 在列表中选中已显示正在播放的项 self.label_title.setText(self.play_list.currentItem().text()) # 显示歌曲标题 self.label_time.setText("00:00") # 设置为默认时间 self.save_playlist() # 保存播放列表记录 def next(self): """ 下一曲 :return: """ # 判断播放列表是否为空 if self.list.mediaCount() < 0: return index = self.list.currentIndex() index = 0 if index + 1 >= self.list.mediaCount( ) else index + 1 # 如果当前是最后一首,则下一首就为第一首 self.list.setCurrentIndex(index) self.media.play() def previous(self): """ 上一曲 :return: """ # 判断播放列表是否为空 if self.list.mediaCount() < 0: return index = self.list.currentIndex() index = self.list.mediaCount( ) - 1 if index - 1 < 0 else index - 1 # 如果当前为第一首,则上一首就为最后一首 self.list.setCurrentIndex(index) self.media.play() def add_to_list(self): """ 添加项目到播放列表中 """ # 弹出多文件选择对话框,默认目录为程序当前目录,仅支持MP3格式 files = QFileDialog.getOpenFileNames(self, "打开MP3文件", QDir.currentPath(), "MP3文件(*.mp3)") for filename in files[0]: # 判断是否为MP3文件 if os.path.exists(filename) and str.lower(filename[-4:]) == ".mp3": tmp = filename.replace('\\', '/') # 路径转义 name = tmp.split('/')[-1][:-4] # 提取出简单的歌曲文件名 index = self.list.mediaCount() - 1 song = QMediaContent(QUrl.fromLocalFile(filename)) if self.list.insertMedia(index, song): self.play_list.insertItem(index, name) # 保存当前播放列表 self.save_playlist() def remove_from_list(self): """ 从播放列表中移除选中的项目 :return: bool 是否移除成功 """ index = self.play_list.currentRow() # 记录将要删除的索引 tmp_index = -1 # 记录删除后继续播放的索引 tmp_position = 0 # 记录删除后继续播放的位置 exec_play = self.media.state() == 1 # 删除后是否要执行播放操作 if index < 0: # 索引无效,不作处理 return elif index == self.list.currentIndex(): # 如果要删除的歌曲为正在播放的那首歌曲,则先停止播放,删除后播放下一曲 tmp_index = index if index + 1 < self.list.mediaCount() else 0 self.media.stop() elif index <= self.list.currentIndex(): # 如果要删除的歌曲是当前播放歌曲之前的歌曲,则改变索引继续播放 tmp_index = self.list.currentIndex() - 1 tmp_position = self.media.position() self.media.stop() # 删除操作 if self.list.removeMedia(index): self.play_list.takeItem(index) # 设置新的播放 self.label_title.setText("简易MP3音乐播放器") if 0 <= tmp_index <= self.list.mediaCount() - 1: self.list.setCurrentIndex(tmp_index) if exec_play: self.media.setPosition(tmp_position) self.media.play() self.label_title.setText(self.play_list.item(tmp_index).text()) # 保存当前播放列表 self.save_playlist() def load_playlist(self): """ 加载播放列表 :return: """ if not os.path.exists("./default.playlist"): return try: with open("./default.playlist", 'rb') as f: data = eval(f.read()) self.list.clear() self.play_list.clear() for item in data["list"]: # 判断是否为MP3文件 if os.path.exists(item["path"]) and str.lower( item["path"][-4:]) == ".mp3": index = self.list.mediaCount() song = QMediaContent(QUrl.fromLocalFile(item["path"])) if self.list.insertMedia(index, song): self.play_list.insertItem(index, item["name"]) if -1 <= data["currentItem"] < self.list.mediaCount(): self.list.setCurrentIndex(data["currentItem"]) self.play_list.setCurrentRow(data["currentItem"]) self.label_title.setText( self.play_list.currentItem().text()) self.slider_pos.setRange(0, data["duration"]) self.slider_pos.setValue(data["currentPosition"]) self.media.setPosition(data["currentPosition"]) self.media.setVolume(data["volume"]) self.slider_volume.setValue(data["volume"]) self.media.setMuted(data["muted"]) img = "./img/icons/volume-mute.png" if data[ "muted"] else "./img/icons/volume-normal.png" self.btn_mute.setStyleSheet( "QPushButton#btn_mute{image:url('" + img + "')}") # 静音按钮图标样式 except: pass def save_playlist(self): """ 保存播放列表 :return: """ data = {"list": [], "current": -1} for i in range(self.list.mediaCount()): path = self.list.media(i).canonicalUrl().path() name = self.list.media(i).canonicalUrl().fileName()[:-4] data["list"].append({"path": path[1:], "name": name}) data["currentItem"] = self.list.currentIndex() data["currentPosition"] = self.media.position() data["duration"] = self.media.duration() data["volume"] = self.media.volume() data["muted"] = self.media.isMuted() with open("./default.playlist", 'wb') as f: f.write(str(data).encode('utf-8')) @staticmethod def calc_time(millisecond): """ 计算时间(毫秒数格式转“分:秒”格式) :param millisecond: int 毫秒数 :return: string min:sec """ second = millisecond / 1000 minute = int(second / 60) second = int(second % 60) # 如果是个位数的话,就在前面加个“0” minute = "0" + str(minute) if minute < 10 else str(minute) second = "0" + str(second) if second < 10 else str(second) return str(minute) + ":" + str(second)