Exemple #1
0
class TeamChooserWidget(QWidget):
    def __init__(self, parent, on_next, league=None):
        super().__init__(parent)
        self.layout = QVBoxLayout()
        self.list_view = QListView()
        self.league = league
        self.model = TeamListModel(league)
        self.list_view.setModel(self.model)
        self.list_view.setSelectionMode(QAbstractItemView.MultiSelection)
        self.list_view.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
        self.list_view.customContextMenuRequested.connect(self.list_context_menu)
        self.layout.addWidget(self.list_view)
        self.team_add = AddItemWidget(self, "New Team Name: ", self.add_team, unavailable_options=self.model.all_team_names)
        self.layout.addWidget(self.team_add)

        self.button_h_layout = QHBoxLayout()
        self.button_cancel = QPushButton("Cancel")
        self.button_cancel.clicked.connect(self.on_cancel)
        self.button_next = QPushButton("Next")
        self.button_next.clicked.connect(on_next)
        self.button_h_layout.addWidget(self.button_cancel)
        self.button_h_layout.addWidget(self.button_next)
        self.layout.addLayout(self.button_h_layout)
        self.setLayout(self.layout)
        self.new_teams = []

    def add_team(self, name):
        self.new_teams.append(name)
        team = Team.create(name=name)
        self.model.add_team(team)

    def list_context_menu(self, pos):
        self.listMenu = QMenu()
        current_index = self.list_view.currentIndex()
        select = self.listMenu.addAction("Select")
        select.triggered.connect(
            lambda: self.list_view.selectionModel().select(current_index, QtCore.QItemSelectionModel.Select)
        )

        deselect = self.listMenu.addAction("Deselect")
        deselect.triggered.connect(
            lambda: self.list_view.selectionModel().select(current_index, QtCore.QItemSelectionModel.Deselect)
        )
        delete = self.listMenu.addAction("Delete")
        delete.setDisabled(current_index.data() not in self.new_teams)
        delete.triggered.connect(lambda: self.model.delete_team(current_index.row()))

        parentPosition = self.list_view.mapToGlobal(QtCore.QPoint(0, 0))
        self.listMenu.move(parentPosition + pos)
        self.listMenu.show()

    def on_cancel(self):
        self.model.delete_added_teams()
        self.window().close()

    def get_selected_teams(self):
        teams = []
        for i in self.list_view.selectedIndexes():
            teams.append(self.model.get_team(i.row()))
        return teams
Exemple #2
0
class AbstractSubItemListView(QFrame):
    """
    THIS IS AN ABSTRACT CLASS, DO NOT INSTANTIATE
    """

    def __init__(self, parent: QObject, image_cache: ImageCache):
        super().__init__(parent)

        self._image_cache = image_cache
        self._model = AbstractItemListModel(self, image_cache, True)

        self._item_delegate = ItemDelegate(self)

        self._list_view = QListView(self)
        self._list_view.setItemDelegate(self._item_delegate)
        self._list_view.setContextMenuPolicy(Qt.CustomContextMenu)

        self._layout_manager = QVBoxLayout(self)
        self._layout_ui()

        # Connect signals to slots
        self._list_view.customContextMenuRequested.connect(self._show_context_menu)

    def _layout_ui(self):
        self._list_view.setSpacing(22)
        self._layout_manager.addWidget(self._list_view)

    def model(self) -> AbstractItemListModel:
        return self._model

    def _add_item_to_playlist(self, index: QModelIndex):
        """
        Given an index in the list, allows the user to add the selected item to a playlist of their choice
        """
        selected_item = self._list_view.model().at(index.row())
        add_dialog = AddToPlaylistDialog(self, selected_item)

        if add_dialog.exec() == QDialog.Accepted:
            # Insertion playlist has been accepted
            selected_playlist_id: int = add_dialog.get_selection()

            if type(selected_item) is Episode:
                add_episode_to_playlist(cursor, connection, selected_item.media_id, selected_item.episode_number,
                                        selected_item.name, selected_playlist_id)
            elif type(selected_item) is Song:
                add_song_to_playlist(cursor, connection, selected_item.media_id, selected_item.name,
                                     selected_playlist_id)
            elif type(selected_item) is ComedySpecial:
                add_comedy_special_to_playlist(cursor, connection, selected_item.media_id, selected_playlist_id)
            else:
                print("Attempting to add unexpected type to playlist")
                exit(1)

    @QtCore.pyqtSlot(QPoint)
    def _show_context_menu(self, pos: QPoint):
        """
        Displays a context menu of user choices on a right-click

        :param pos: Location where user clicked on the screen
        """

        index = self._list_view.indexAt(pos)

        if index.row() != -1:  # Must be a valid index
            global_pos = self._list_view.mapToGlobal(pos)

            context_menu = QMenu(self)

            # User should have the ability to add to playlist
            add_action = QAction("Add to playlist")
            add_action.triggered.connect(lambda: self._add_item_to_playlist(index))

            context_menu.addAction(add_action)

            context_menu.exec(global_pos)
            del context_menu
Exemple #3
0
class MusicPlayer(QWidget):
    def __init__(self, parent=None):
        QWidget.__init__(self, parent)
        self.options = self.parent().options

        self.ffmpeg = self.options['paths']['ffmpeg_bin']
        self.thumbnails = self.options['paths']['thumbnails']
        self.thumb_width = self.options['thumbnail']['width']
        self.thumb_height = self.options['thumbnail']['height']

        self.jumping = False
        self.blocked = False
        self.durations = {}
        self.playback_value = 0

        self.text = ["None", "Repeat", "Random"]
        self.playlist_list = []
        self.values = [
            QMediaPlaylist.Loop, QMediaPlaylist.CurrentItemInLoop,
            QMediaPlaylist.Random
        ]

        # Thumbnail widget
        self.image_label = QLabel()

        # Control widgets
        self.playButton = QToolButton(clicked=self.play)
        self.playButton.setIcon(self.style().standardIcon(QStyle.SP_MediaPlay))

        self.stopButton = QToolButton(clicked=self.stop)
        self.stopButton.setIcon(self.style().standardIcon(QStyle.SP_MediaStop))
        self.stopButton.setEnabled(False)

        self.playbackButton = QToolButton(clicked=self.playback_mode)
        self.playbackButton.setText(self.text[0])

        self.nextButton = QToolButton(clicked=self.next_song)
        self.nextButton.setIcon(self.style().standardIcon(
            QStyle.SP_MediaSkipForward))

        self.previousButton = QToolButton(clicked=self.previous_song)
        self.previousButton.setIcon(self.style().standardIcon(
            QStyle.SP_MediaSkipBackward))

        self.muteButton = QToolButton(clicked=self.mute_clicked)
        self.muteButton.setIcon(self.style().standardIcon(
            QStyle.SP_MediaVolume))

        self.volumeSlider = QSlider(Qt.Horizontal,
                                    sliderMoved=self.change_volume)
        self.volumeSlider.setRange(0, 100)
        self.volumeSlider.setPageStep(1)
        self.volumeSlider.setValue(50)

        # Player and playlist setup

        self.player = QMediaPlayer()
        self.player.setVolume(50)
        self.player.stateChanged.connect(self.waveform)

        self.playlist = QMediaPlaylist()
        self.playlist.setPlaybackMode(self.values[0])
        self.playlist.setCurrentIndex(1)

        self.player.setPlaylist(self.playlist)

        self.playlistModel = PlaylistModel()
        self.playlistModel.setPlaylist(self.playlist)

        self.playlistView = QListView()
        self.playlistView.setModel(self.playlistModel)
        self.playlistView.setCurrentIndex(
            self.playlistModel.index(self.playlist.currentIndex(), 0))
        self.playlistView.activated.connect(self.jump)
        self.playlistView.setContextMenuPolicy(Qt.CustomContextMenu)
        self.playlistView.customContextMenuRequested.connect(
            self.list_view_menu)
        self.playlist.currentIndexChanged.connect(
            lambda position: self.change_thumbnail(position))

        song_search = QLineEdit()
        song_search.textChanged.connect(self.search)
        song_search.setClearButtonEnabled(True)

        # Playlist
        self.playlist_name = QComboBox()
        self.playlist_name.currentTextChanged.connect(self.switch_playlist)
        self.refresh_lists()

        self.up_button = QToolButton(clicked=self.move_up)
        self.up_button.setIcon(self.style().standardIcon(QStyle.SP_ArrowUp))
        self.down_button = QToolButton(clicked=self.move_down)
        self.down_button.setIcon(self.style().standardIcon(
            QStyle.SP_ArrowDown))

        # Sound wave widget

        self.wave_graphic = WaveGraphic(self)
        #self.wave_graphic.hide()

        # Testing slider again
        self.slider = QSlider(Qt.Horizontal)
        self.slider.setRange(0, self.player.duration() / 1000)
        self.slider.sliderMoved.connect(self.seek)
        self.player.durationChanged.connect(self.durationChanged)
        self.player.positionChanged.connect(self.positionChanged)

        # Shortcuts
        QShortcut(QKeySequence(Qt.CTRL + Qt.Key_F), self, song_search.setFocus)

        # Layouts setup

        playlist_layout = QHBoxLayout()
        playlist_layout.addWidget(self.playlist_name)
        playlist_layout.addWidget(self.up_button)
        playlist_layout.addWidget(self.down_button)

        control_layout = QHBoxLayout()
        control_layout.setContentsMargins(0, 0, 0, 0)
        control_layout.addWidget(self.stopButton)
        control_layout.addWidget(self.previousButton)
        control_layout.addWidget(self.playButton)
        control_layout.addWidget(self.nextButton)
        control_layout.addWidget(self.muteButton)
        control_layout.addWidget(self.volumeSlider)
        control_layout.addWidget(self.playbackButton)

        display_layout = QVBoxLayout()
        display_layout.addWidget(song_search)
        display_layout.addWidget(self.playlistView)
        display_layout.addLayout(playlist_layout)

        music_layout = QVBoxLayout()
        music_layout.addWidget(self.image_label)
        music_layout.addWidget(self.slider)
        music_layout.addLayout(control_layout)

        main_layout = QHBoxLayout()
        main_layout.addLayout(music_layout)
        main_layout.addLayout(display_layout)

        main_2_layout = QVBoxLayout()
        main_2_layout.addLayout(main_layout)
        main_2_layout.addWidget(self.wave_graphic)

        self.setLayout(main_2_layout)

    def waveform(self, status):
        if status == QMediaPlayer.PlayingState:
            self.wave_graphic.start()
        elif status == QMediaPlayer.PausedState:
            self.wave_graphic.pause()
        else:
            self.wave_graphic.stop()

    def list_view_menu(self, point):
        menu = QMenu("Menu", self)
        recommend_action = QAction('&Recommend Songs', self)
        menu.addAction(recommend_action)

        # TODO: [FEATURE] add rename song
        recommend_action.triggered.connect(
            lambda: self.parent().call_download_manager(self.get_links()))

        rename_action = QAction('&Rename', self)
        menu.addAction(rename_action)

        # TODO: [FEATURE] add rename song
        rename_action.triggered.connect(lambda: print("rename"))

        # Show the context menu.
        menu.exec_(self.playlistView.mapToGlobal(point))

    def get_links(self):
        title = self.playlistView.selectedIndexes()[0].data()
        link = getYoutubeURLFromSearch(title)
        if len(link) > 1:
            return youtube_recommendations(link)

    def move_up(self):
        index = self.playlistView.currentIndex().row()
        if index - 1 >= 0:
            item, above = self.playlist.media(index), self.playlist.media(
                index - 1)
            self.playlist.removeMedia(index)
            self.playlist.removeMedia(index - 1)
            self.playlist.insertMedia(index - 1, item)
            self.playlist.insertMedia(index, above)
            self.blocked = True
            self.playlistView.setCurrentIndex(
                self.playlistModel.index(index - 1, 0))
            self.blocked = False
            self.stop()

    def move_down(self):
        index = self.playlistView.currentIndex().row()
        if index + 1 <= self.playlistModel.rowCount():
            item, below = self.playlist.media(index), self.playlist.media(
                index + 1)
            self.playlist.removeMedia(index + 1)
            self.playlist.removeMedia(index)
            self.playlist.insertMedia(index, below)
            self.playlist.insertMedia(index + 1, item)
            self.blocked = True
            self.playlistView.setCurrentIndex(
                self.playlistModel.index(index + 1, 0))
            self.blocked = False
            self.stop()

    def search(self, part_of_song):
        for index in range(self.playlistModel.rowCount()):
            item = self.playlistModel.data(self.playlistModel.index(
                index, 0)).lower()
            self.playlistView.setRowHidden(index,
                                           part_of_song.lower() not in item)

    def change_thumbnail(self, position=0):

        self.playlistView.setCurrentIndex(self.playlistModel.index(
            position, 0))

        song = self.playlistView.selectedIndexes()[0].data()
        if self.wave_graphic.is_song_cached(song):
            self.wave_graphic.load_waves(song)
        else:
            wc_ = WaveConverter(song, self.wave_graphic.set_wav)
            wc_.convert()

        if self.playlistView.currentIndex().data() is None or self.blocked:
            return

        max_ratio = 0
        img = None

        for item in listdir(self.thumbnails):
            if item.endswith('.jpg'):
                ratio = similar(
                    item,
                    self.playlistView.currentIndex().data().replace(
                        '.mp3', '.jpg'))
                if ratio > max_ratio:
                    max_ratio = ratio
                    img = item

        if img:
            p = QPixmap(self.thumbnails + img)
            self.image_label.setPixmap(
                p.scaled(self.thumb_width, self.thumb_height,
                         Qt.KeepAspectRatio))

    def switch_playlist(self, current_text):
        self.playlist.clear()
        if current_text == "No Playlist":
            self.refresh()
        else:
            if read_playlist(current_text):
                songs = read_playlist(current_text).split('\n')
                for song in songs:
                    self.playlist.addMedia(
                        QMediaContent(QUrl.fromLocalFile(song)))

    def refresh(self):
        # Change it so it will go to same song.
        if self.playlist_name.currentText() != "No Playlist":
            return

        paths = fetch_options()['paths']['music_path'].split(';')

        current_songs = [
            self.playlistModel.data(self.playlistModel.index(row, 0))
            for row in range(self.playlistModel.rowCount())
        ]

        for path in paths:
            if not path:
                continue
            for item in listdir(path):
                if isfile(join(path, item)) and item.endswith(".mp3") and (
                        item not in current_songs):
                    self.playlist.addMedia(
                        QMediaContent(QUrl.fromLocalFile(join(path, item))))

    def refresh_lists(self):
        path = fetch_options()['paths']['playlist']
        self.playlist_name.clear()
        self.playlist_list = ["No Playlist"]
        self.playlist_name.addItem("No Playlist")
        for item in listdir(path):
            if isfile(join(path, item)) and item.endswith(".lst"):
                self.playlist_list.append(item.split('.')[0])
                self.playlist_name.addItem(item.split('.')[0])

    def playback_mode(self):
        # Normal -> Loop -> Random
        def up_value(value, max_value=3):
            if value + 1 == max_value:
                return 0
            return value + 1

        self.playback_value = up_value(self.playback_value)
        self.playlist.setPlaybackMode(self.values[self.playback_value])
        self.playbackButton.setText(self.text[self.playback_value])

    def jump(self, index):
        if index.isValid() and not self.blocked:
            self.playlist.setCurrentIndex(index.row())
            self.jumping = True
            self.play()

    def play(self):
        if self.blocked:
            return
        if self.player.state() != QMediaPlayer.PlayingState or self.jumping:
            self.player.play()
            self.playButton.setIcon(self.style().standardIcon(
                QStyle.SP_MediaPause))
            self.jumping = False
        else:
            self.player.pause()
            self.playButton.setIcon(self.style().standardIcon(
                QStyle.SP_MediaPlay))
        self.stopButton.setEnabled(True)

    def change_volume(self, value):
        self.player.setVolume(value)

    def stop(self):
        if self.player.state() != QMediaPlayer.StoppedState:
            self.player.stop()
        self.playButton.setIcon(self.style().standardIcon(QStyle.SP_MediaPlay))
        self.stopButton.setEnabled(False)

    def next_song(self):
        self.playlist.next()
        self.playlistView.setCurrentIndex(
            self.playlistModel.index(self.playlist.currentIndex(), 0))

    def previous_song(self):
        self.playlist.previous()
        self.playlistView.setCurrentIndex(
            self.playlistModel.index(self.playlist.currentIndex(), 0))

    def mute_clicked(self):
        self.player.setMuted(not self.player.isMuted())
        self.muteButton.setIcon(self.style().standardIcon(
            QStyle.SP_MediaVolume if not self.player.isMuted() else QStyle.
            SP_MediaVolumeMuted))

    def durationChanged(self, duration):
        duration /= 1000
        self.slider.setMaximum(duration)

    def positionChanged(self, progress):
        progress /= 1000

        if not self.slider.isSliderDown():
            self.slider.setValue(progress)

    def seek(self, seconds):
        self.player.setPosition(seconds * 1000)
class PlaylistView(QFrame):
    """
    Horizontal Scroll Area containing playlists that can be selected
    """

    # Signals
    should_display_playlist = pyqtSignal(
        int)  # Display playlist given playlist_id

    def __init__(self, parent: QObject, image_cache: ImageCache):
        super().__init__(parent)

        self.__item_delegate = PlaylistDelegate(self)
        self.__model = PlaylistModel(self, image_cache)

        self.__horizontal_list = QListView(self)
        self.__horizontal_list.setModel(self.__model)
        self.__horizontal_list.setItemDelegate(self.__item_delegate)
        self.__horizontal_list.verticalScrollBar().setEnabled(False)
        self.__horizontal_list.setContextMenuPolicy(Qt.CustomContextMenu)

        self.__layout_manager = QVBoxLayout(self)
        self.__layout_ui()

        # Connect signals to slots
        self.__horizontal_list.doubleClicked.connect(
            self.__playlist_double_clicked)
        self.__horizontal_list.customContextMenuRequested.connect(
            self.__show_context_menu)

    def __layout_ui(self):
        # Set up horizontal list
        self.__horizontal_list.setFlow(QListView.LeftToRight)
        self.__horizontal_list.setMinimumHeight(235)
        self.__horizontal_list.setSpacing(20)
        self.__layout_manager.addWidget(self.__horizontal_list)

    def model(self) -> PlaylistModel:
        return self.__model

    @QtCore.pyqtSlot(QModelIndex)
    def __playlist_double_clicked(self, index: QModelIndex):
        """
        Slot that connects to the doubleClicked signal

        Should either display contents of the playlist or allow for playlist creation (index dependent)
        :index: Index that was clicked
        """
        should_create_playlist = (index.row() != self.__model.rowCount() - 1)
        if should_create_playlist:
            # Didn't click last index (create playlist), should display contents
            self.should_display_playlist.emit(
                self.__model.at(index.row()).playlist_id)
        else:
            self.__model.create_new_playlist()

    @QtCore.pyqtSlot(QModelIndex)
    def __handle_playlist_rename(self, index: QModelIndex):
        """
        Allows the user to rename the playlist at index
        """
        playlist = self.__model.at(index.row())
        rename_dialog = RenamePlaylistDialog(self, playlist)
        if rename_dialog.exec() == QDialog.Accepted:
            # Rename successfully requested
            new_playlist_name = rename_dialog.get_new_name()
            rename_playlist(cursor, connection, playlist.playlist_id,
                            new_playlist_name)
            self.relayout()

    @QtCore.pyqtSlot(QPoint)
    def __show_context_menu(self, pos: QPoint):
        """
        Displays a context menu of user choices on a right-click

        :param pos: Location where user clicked on the screen
        """

        index = self.__horizontal_list.indexAt(pos)

        if index.row() != -1 and index.row() != self.__model.rowCount(
        ) - 1:  # Must be a valid index and not create
            global_pos = self.__horizontal_list.mapToGlobal(pos)

            context_menu = QMenu(self)

            show_action = QAction("Show Playlist")
            show_action.triggered.connect(
                lambda: self.__playlist_double_clicked(index))

            rename_action = QAction("Rename Playlist")
            rename_action.triggered.connect(
                lambda: self.__handle_playlist_rename(index))

            del_action = QAction("Delete Playlist")
            del_action.triggered.connect(
                lambda: self.__horizontal_list.model().delete_playlist(index))

            context_menu.addAction(show_action)
            context_menu.addSeparator()
            context_menu.addAction(rename_action)
            context_menu.addAction(del_action)

            context_menu.exec(global_pos)
            del context_menu

    def relayout(self):
        """
        Refreshes the UI to reflect the state of the DB
        """
        playlists = get_all_user_playlists(cursor)
        self.__model.update_playlist(playlists)