Exemplo n.º 1
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)
Exemplo n.º 2
0
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)
Exemplo n.º 3
0
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 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)
Exemplo n.º 5
0
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()
Exemplo n.º 6
0
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)
Exemplo n.º 7
0
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()
Exemplo n.º 8
0
class Ui_MainWindow(object):

    # for stylizing progress bar
    progress_styleSheet = """
            QSlider::groove:horizontal {
                background: dark;
                height: 40px;
            }

            QSlider::sub-page:horizontal {
                background: qlineargradient(x1: 0, y1: 0,    x2: 0, y2: 1,
                    stop: 0 #07406a, stop: 1 #696969);
                background: qlineargradient(x1: 0, y1: 0.2, x2: 1, y2: 1,
                    stop: 0 #696969, stop: 1 #07406a);
                height: 40px;
            }

            QSlider::add-page:horizontal {
                background: #696969;
                height: 40px;
            }

            QSlider::handle:horizontal {
                background: #aaa;
                border: 0px;
                width: 5px;
                margin-top: 0px;
                margin-bottom: 0px;
                border-radius: 0px;
            }
        """

    def setupUi(self, MainWindow):
        MainWindow.setObjectName("Music Player")
        MainWindow.resize(640, 480)
        MainWindow.setAcceptDrops(True)

        self.playlist = QMediaPlaylist()
        self.player = QMediaPlayer()
        self.player.setPlaylist(self.playlist)
        self.player.setVolume(20)
        self.player.setPlaybackRate(jdata['playback'])
        self.playlist.setPlaybackMode(jdata['curr_playstyle'])

        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.gridLayout = QtWidgets.QGridLayout(self.centralwidget)
        self.gridLayout.setObjectName("gridLayout")
        self.gridLayout_2 = QtWidgets.QGridLayout()
        self.gridLayout_2.setObjectName("gridLayout_2")
        self.progress = QtWidgets.QSlider(self.centralwidget)
        self.progress.setOrientation(QtCore.Qt.Horizontal)
        self.progress.setStyleSheet(self.progress_styleSheet)
        self.progress.setObjectName("progress")
        self.progress.setSingleStep(1)
        self.progress.setPageStep(1)
        self.gridLayout_2.addWidget(self.progress, 1, 0, 1, 3)
        self.stop_but = QtWidgets.QPushButton(self.centralwidget)
        self.stop_but.setObjectName("stop_but")
        self.gridLayout_2.addWidget(self.stop_but, 0, 2, 1, 1)
        self.backward_b = QtWidgets.QPushButton(self.centralwidget)
        self.backward_b.setObjectName("backward")
        self.gridLayout_2.addWidget(self.backward_b, 0, 0, 1, 1)
        self.cont_pau = QtWidgets.QPushButton(self.centralwidget)
        self.cont_pau.setObjectName("cont_pau")
        self.gridLayout_2.addWidget(self.cont_pau, 0, 1, 1, 1)
        self.playstyle = QtWidgets.QPushButton(self.centralwidget)
        self.playstyle.setObjectName("playstyle")
        self.playstyle.setText("Loop")
        self.gridLayout_2.addWidget(self.playstyle, 0, 4, 1, 1)
        self.forward_b = QtWidgets.QPushButton(self.centralwidget)
        self.forward_b.setObjectName("forward")
        self.gridLayout_2.addWidget(self.forward_b, 0, 3, 1, 1)
        self.time_remain = QtWidgets.QLabel(self.centralwidget)
        self.time_remain.setObjectName("time_remain")
        self.gridLayout_2.addWidget(self.time_remain, 1, 3, 1, 1)
        self.verticalLayout_2 = QtWidgets.QVBoxLayout()
        self.verticalLayout_2.setObjectName("verticalLayout_2")
        self.volume = QtWidgets.QLabel(self.centralwidget)
        self.volume.setAlignment(QtCore.Qt.AlignCenter)
        self.volume.setObjectName("volume")
        self.verticalLayout_2.addWidget(self.volume)
        self.volume_ctrl = QtWidgets.QSlider(self.centralwidget)
        self.volume_ctrl.setOrientation(QtCore.Qt.Horizontal)
        self.volume_ctrl.setObjectName("volume_ctrl")
        self.volume_ctrl.setValue(jdata["volume"])
        self.volume_ctrl.setRange(-1, 101)
        self.volume_ctrl.setSingleStep(1)
        self.volume_ctrl.setPageStep(1)
        self.verticalLayout_2.addWidget(self.volume_ctrl)
        self.gridLayout_2.addLayout(self.verticalLayout_2, 1, 4, 1, 1)
        self.gridLayout.addLayout(self.gridLayout_2, 1, 0, 1, 1)
        self.verticalLayout = QtWidgets.QVBoxLayout()
        self.verticalLayout.setObjectName("verticalLayout")
        self.playing = QtWidgets.QLabel(self.centralwidget)
        font = QtGui.QFont()
        font.setFamily("Arial")
        font.setPointSize(12)
        self.playing.setFont(font)
        self.playing.setAlignment(QtCore.Qt.AlignCenter)
        self.playing.setObjectName("playing")
        self.playing.setScaledContents(True)
        self.playing.setWordWrap(True)
        self.verticalLayout.addWidget(self.playing)
        self.gridLayout.addLayout(self.verticalLayout, 0, 0, 1, 1)
        self.verticalLayout_3 = QtWidgets.QVBoxLayout()
        self.verticalLayout_3.setObjectName("verticalLayout_3")
        self.song_list = ListWidget(self.centralwidget)
        self.song_list.setMinimumSize(QtCore.QSize(183, 0))
        self.song_list.setAcceptDrops(True)
        self.song_list.setDragDropMode(
            QtWidgets.QAbstractItemView.InternalMove)
        self.song_list.setObjectName("song_list")
        self.song_list.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
        self.song_list.setWordWrap(True)
        self.verticalLayout_3.addWidget(self.song_list)
        self.gridLayout.addLayout(self.verticalLayout_3, 0, 1, 2, 1)
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 632, 25))
        self.menubar.setObjectName("menubar")
        self.menuFile = QtWidgets.QMenu(self.menubar)
        self.menuFile.setObjectName("menuFile")
        self.menuAbout = QtWidgets.QMenu(self.menubar)
        self.menuAbout.setObjectName("menuAbout")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)
        self.actionOpen = QtWidgets.QAction(MainWindow)
        self.actionOpen.setObjectName("actionOpen")
        self.actionLisence_Information = QtWidgets.QAction(MainWindow)
        self.actionLisence_Information.setObjectName(
            "actionLisence_Information")
        self.menuFile.addAction(self.actionOpen)
        self.menuAbout.addAction(self.actionLisence_Information)
        self.menubar.addAction(self.menuFile.menuAction())
        self.menubar.addAction(self.menuAbout.menuAction())
        spacerItem = QtWidgets.QSpacerItem(40, 20,
                                           QtWidgets.QSizePolicy.Expanding,
                                           QtWidgets.QSizePolicy.Minimum)
        self.gridLayout_2.addItem(spacerItem, 0, 5, 1, 1)
        self.gridLayout.addLayout(self.gridLayout_2, 1, 0, 1, 1)

        self.info = QMessageBox()
        self.info.setWindowTitle("License Information")
        self.info.setText("MIT License\n\nCopyright (c) 2021 Marganotvke")

        self.err = QMessageBox()
        self.err.setWindowTitle("Error Loading File")
        self.err.setText("Error Loading File!\n\nPlease check file type!")

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

        self.actionOpen.triggered.connect(lambda: self.open_trig())
        self.actionLisence_Information.triggered.connect(
            lambda: self.info.exec_())
        self.cont_pau.clicked.connect(lambda: self.cur_playing())
        self.volume_ctrl.sliderMoved.connect(lambda: self.change_vol())
        self.stop_but.clicked.connect(lambda: self.stop_playing())
        self.forward_b.clicked.connect(lambda: self.forward())
        self.backward_b.clicked.connect(lambda: self.backward())
        self.playstyle.clicked.connect(lambda: self.playlist_style())
        self.song_list.dropped.connect(lambda e: self.start_play(e))
        self.song_list.itemDoubleClicked.connect(
            lambda: self.jump_start(self.song_list.currentRow()))
        self.song_list.model().rowsAboutToBeMoved.connect(
            lambda e, f, g, h, i: self.re_arr(f, i))
        self.song_list.dele.connect(lambda e: self.delete_q(e))
        self.player.metaDataAvailableChanged.connect(lambda: self.set_meta())
        self.player.stateChanged.connect(lambda e: self.set_state(e))
        self.player.positionChanged.connect(lambda e: self.set_pos(e))
        self.progress.sliderMoved.connect(lambda e: self.skip_to(e))

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "Music Player"))
        self.stop_but.setText(_translate("MainWindow", "Stop"))
        self.backward_b.setText(_translate("MainWindow", "Backward"))
        self.cont_pau.setText(_translate("MainWindow", "Load Song"))
        self.playstyle.setText(
            _translate("MainWindow", f"{playstyles[jdata['curr_playstyle']]}"))
        self.forward_b.setText(_translate("MainWindow", "Forward"))
        self.time_remain.setText(_translate("MainWindow", "--/--"))
        self.volume.setText(
            _translate("MainWindow", f"Volume: {jdata['volume']}%"))
        self.playing.setText(
            _translate("MainWindow", "Currently playing: None"))
        self.menuFile.setTitle(_translate("MainWindow", "File"))
        self.menuAbout.setTitle(_translate("MainWindow", "About"))
        self.actionOpen.setText(_translate("MainWindow", "Open"))
        self.actionLisence_Information.setText(
            _translate("MainWindow", "License Information"))
        self.actionOpen.setShortcut(_translate("MainWindow", "Ctrl+O"))

    def re_arr(self, e=None, f=None):
        f -= 1 if self.song_list.count() == f else 0
        media = self.playlist.media(e)
        self.playlist.removeMedia(e)
        self.playlist.insertMedia(f, media)

    def jump_start(self, index=None):
        if self.playlist.currentIndex() != index:
            self.playlist.setCurrentIndex(index)
            self.player.stop()
            self.start_play()

    def start_play(self, url=None):
        if url:
            print("Emitted from drop event:", url)
            threading.Thread(target=self.open_file(url)).start()
        self.player.play()

    def stop_playing(self):
        self.player.stop()

    def cur_playing(self):
        if self.playlist.isEmpty():
            self.open_trig()
        else:
            if self.player.state() == 1:
                self.player.pause()
                self.cont_pau.setText("Play")
            else:
                self.player.play()
                self.cont_pau.setText("Pause")

    def forward(self):
        self.playlist.next()

    def backward(self):
        if round(self.player.position() / 1000) >= 5:
            self.player.setPosition(0)
            if not self.player.state():
                self.start_play()
        else:
            self.playlist.previous()

    def open_trig(self):
        filename = QFileDialog.getOpenFileName(
            None,
            'Open File',
            filter=
            "MPEG-2 (*.mp3);;WAVE Audio (*.wav,*.aif,*.aiff);;MPEG-1/DD/ACC (*.aac);;MIDI (*.mid,*.midi);;Windows Media Audio (*.wma);;Xiph.Org OGG Vorbis (*.ogg);;NeXT SouND (*.snd);;FLAC (*.flac);;All files(*)"
        )
        threading.Thread(target=self.open_file(filename[0])).start()

    def open_file(self, file_url):
        url = QUrl.fromLocalFile(file_url)
        avai = ('.mp3', '.wav', 'ogg', '.aac', '.aif', '.aiff', '.mid',
                '.midi', '.wma', '.snd', '.flac')
        if url.url() != '' and not url.fileName().lower().endswith(avai):
            self.err.exec_()
        elif url.fileName().lower().endswith(avai):
            song_name = url.fileName().split(".")[0]
            self.song_list.addItem(song_name)
            if self.playlist.isEmpty():
                self.playlist.addMedia(QMediaContent(url))
                self.cont_pau.setText("Pause")
                self.start_play()
            else:
                self.playlist.addMedia(QMediaContent(url))

    def delete_q(self, i):
        self.playlist.removeMedia(i)
        self.song_list.takeItem(i)

    def playlist_style(self):
        i = self.playlist.playbackMode()
        i += 1
        i *= (i < 5)
        self.playlist.setPlaybackMode(i)
        self.playstyle.setText(f"{playstyles[self.playlist.playbackMode()]}")
        jdata["curr_playstyle"] = self.playlist.playbackMode()
        print(playstyles[self.playlist.playbackMode()])

    def change_vol(self):
        vol = self.volume_ctrl.value()
        self.player.setVolume(vol)
        self.volume.setText(f"Volume: {vol}%")
        jdata["volume"] = vol

    def set_pos(self, e):
        if self.player.isMetaDataAvailable():
            t = self.player.duration()
            dt = datetime.datetime.fromtimestamp(
                round(self.player.duration() / 1000)).strftime('%M:%S')
            dt_ct = datetime.datetime.fromtimestamp(round(
                e / 1000)).strftime('%M:%S')
            self.thread = threading.Thread(
                target=self.progress.setValue(round((e / t) * 100)))
            self.thread.start()
            self.time_remain.setText(f"{dt_ct}/{dt}")

    def set_meta(self):
        if self.player.isMetaDataAvailable():
            self.duraiton = self.player.duration()
            self.progress.setRange(0, 100)
            self.playing.setText(
                f"Currently playing: {self.player.currentMedia().canonicalUrl().fileName().split('.')[0]}"
            )
        else:
            if self.playlist.isEmpty():
                self.cont_pau.setText("Load Song")
                self.time_remain.setText("--/--")

    def set_state(self, e):
        if not e:
            self.playing.setText("Currently playing: None")
            if self.playlist.isEmpty():
                self.cont_pau.setText("Load Song")
            else:
                self.cont_pau.setText("Play")
        else:
            if self.player.isMetaDataAvailable():
                self.playing.setText(
                    f"Currently playing: {self.player.currentMedia().canonicalUrl().fileName().split('.')[0]}"
                )

    def skip_to(self, e):
        self.player.setPosition(round((e / 100) * self.player.duration()))
Exemplo n.º 9
0
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