Example #1
0
class WindowSearch(QWidget):
    def __init__(self,
                 heading,
                 parent=None,
                 counter=0,
                 pastResults={},
                 grid=False):
        QWidget.__init__(self)
        self.setParent(parent)
        self.heading = heading
        self.counter = counter
        self.pastResults = pastResults

        # Set layout and spacings
        layout = QGridLayout()

        # The heading of the widget
        label = QLabel(heading)
        label.setAlignment(Qt.AlignCenter)
        label.setFixedHeight(40)
        # TODO: Change with global themes
        label.setStyleSheet("color: white;")
        # Font for the label
        font = QFont()
        font.setPixelSize(25)
        font.setBold(True)
        label.setFont(font)
        layout.addWidget(label, 0, 0)

        # Add back button
        if self.counter:
            backButton = QToolButton()
            backButton.setText("🡸")
            font = QFont()
            font.setPointSize(30)
            backButton.setFont(font)
            backButton.setFixedSize(QSize(40, 40))
            backButton.setAutoRaise(True)
            backButton.setStyleSheet(
                "QToolButton { color: white; background-color: transparent;} QToolButton:pressed {background-color: rgba(255, 255, 255, 0.1);}"
            )
            layout.addWidget(backButton, 0, 0)
            backButton.clicked.connect(self.goBack)

        # Sub heading if this is an indepth search
        sublabel = QLabel()
        sublabel.setFixedHeight(20)
        if self.pastResults:
            sublabel.setAlignment(Qt.AlignCenter)
            # TODO: Change with global themes
            sublabel.setStyleSheet("color: white;")
            if "artist_id" in self.pastResults:
                sublabel.setText("Artist: " + self.pastResults["artist_name"])
            elif "language_id" in self.pastResults:
                sublabel.setText("Language: " +
                                 self.pastResults["language_name"])
            elif "playlist_id" in self.pastResults:
                sublabel.setText("Playlist: " +
                                 self.pastResults["playlist_name"])
            font = QFont()
            font.setPixelSize(15)
            font.setItalic(True)
            sublabel.setFont(font)
        layout.addWidget(sublabel, 1, 0)

        # Set searchbar
        self.searchBar = QLineEdit(self)
        # TODO: change with resizeEvent
        self.searchBar.setMinimumWidth(800)
        font = QFont()
        font.setPointSize(20)
        self.searchBar.setFont(font)
        layout.addWidget(self.searchBar, 2, 0)
        layout.setAlignment(self.searchBar, Qt.AlignCenter)

        # Set results page
        if (grid):
            self.results = self.ResultsGrid(self)
            layout.addWidget(self.results, 3, 0)
        else:
            self.results = self.ResultsList(self)
            layout.addWidget(self.results, 3, 0)

        self.searchBar.textEdited.connect(self.updateResults)
        self.updateResults()

        self.setLayout(layout)

    def updateResults(self):
        # Depending on heading, update page
        if self.heading == "播放清单/Playlists":
            self.results.addResults(DB.getPlaylists(self.searchBar.text()))
        elif self.heading == "最喜欢的歌曲/Favourite Songs":
            self.results.addResults(DB.getFavouriteSongs(
                self.searchBar.text()))
        elif self.heading == "最喜欢的歌手/Favourite Artists":
            self.results.addResults(
                DB.getFavouriteArtists(self.searchBar.text()))
        elif self.heading == "搜索语言/Language Search":
            self.results.addResults(DB.getLanguages(self.searchBar.text()))
        elif self.heading == "搜索歌手/Artist Search":
            if self.pastResults:
                self.results.addResults(
                    DB.getSongArtists(self.searchBar.text(),
                                      self.pastResults["language_id"]))
            else:
                self.results.addResults(
                    DB.getSongArtists(self.searchBar.text()))
        elif self.heading == "搜索全部/Search":
            if self.pastResults:
                if self.pastResults["type"] == "artists":
                    self.results.addResults(
                        DB.getSongTitles(self.searchBar.text(),
                                         artist=self.pastResults["artist_id"]))
                elif self.pastResults["type"] == "playlists":
                    self.results.addResults(
                        DB.getSongTitles(
                            self.searchBar.text(),
                            playlist=self.pastResults["playlist_id"]))
            else:
                self.results.addResults(DB.getSongTitles(
                    self.searchBar.text()))

    def goBack(self):
        # Move back in the QStackedWidget index
        if self.counter:
            self.window().content.setCurrentIndex(self.counter - 1)
            self.window().content.removeWidget(self)

    class ResultsList(QScrollArea):
        def __init__(self, parent=None):
            # List View of results
            # Only Title Search and Playlist Search use this
            QScrollArea.__init__(self)
            self.setParent(parent)

            # Make QScrollArea transparent
            self.setStyleSheet(
                "QScrollArea { background-color: transparent } .QFrame { background-color: transparent }"
            )
            self.setWidgetResizable(True)
            self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)

            # Add touch screen control to QScrollArea
            QScroller.grabGesture(self, QScroller.LeftMouseButtonGesture)

            # Set layout settings of the QScrollArea
            self.layout = QVBoxLayout()
            self.layout.setContentsMargins(0, 0, 0, 0)
            self.layout.setSpacing(0)

            # A separate QWidget is needed to properly use QScrollArea
            frame = QFrame()
            frame.setLayout(self.layout)

            self.setWidget(frame)

        def clearResults(self):
            # Clear the results in the list
            while self.layout.count():
                item = self.layout.takeAt(0)
                if item.widget() is not None:
                    item.widget().deleteLater()

        def addResults(self, results):
            # Add the results to the list
            # results (list): list of python dict representing results details (playlist or song search)
            self.results = results
            self.clearResults()
            if results:
                for i in self.results:
                    item = self.ResultsListItem(i, self)
                    self.layout.addWidget(item)
                self.layout.addStretch(1)
            else:
                # If the results are empty, display no result
                label = QLabel("没有结果/No Result")
                label.setStyleSheet("color: white")
                label.setAlignment(Qt.AlignCenter)
                font = QFont()
                font.setPointSize(35)
                label.setFont(font)

                self.layout.addWidget(label)

        class ResultsListItem(QToolButton):
            def __init__(self, result, parent=None):
                # An item of ResultsList
                # result: (dict) represents details (playlist or song search)
                QToolButton.__init__(self)
                self.setParent(parent)
                self.result = result

                # Button formatting
                self.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
                self.setFixedHeight(70)
                self.setAutoRaise(True)
                # TODO: change with global themes
                self.setStyleSheet(
                    "QToolButton:pressed { background-color: rgba(255, 255, 255, 0.1)} QToolButton { background-color: rgba(255, 255, 255, 0.05); border: 1px solid white}"
                )

                # Set layout
                self.layout = QHBoxLayout()
                self.layout.setContentsMargins(0, 0, 0, 0)
                self.layout.setSpacing(0)

                # Depending on result type, add action when the item is clicked
                if result["type"] == "songs":
                    self.formatTitle()
                    self.clicked.connect(self.clickedSong)
                elif result["type"] == "playlists":
                    self.formatPlaylist()
                    self.clicked.connect(self.clickedPlaylist)
                self.setLayout(self.layout)

            def formatTitle(self):
                # Format the title for the ResultsList

                # Add space to the beginning of the ResultsListItem
                labelQueue = self.formattedLabel(QLabel("", self))
                labelQueue.setFixedWidth(70)
                labelQueue.setAlignment(Qt.AlignCenter)
                self.layout.addWidget(labelQueue)

                # Artist name
                labelArtist = self.formattedLabel(
                    QLabel(self.result["artist_name"]))
                labelArtist.setFixedWidth(300)
                self.layout.addWidget(labelArtist)

                # Song title
                labelTitle = self.formattedLabel(
                    QLabel(self.result["song_title"]))
                self.layout.addWidget(labelTitle)

                # Add buttons for favourites and playlists

                # Favourite button
                self.favouriteButton = QToolButton()

                # Toggle Favourite Icon depending on DB
                if self.result["favourited"] == 0:
                    self.favouriteButton.isFavourited = False
                    self.favouriteButton.setIcon(QIcon("icons/star.svg"))
                else:
                    self.favouriteButton.isFavourited = True
                    self.favouriteButton.setIcon(
                        QIcon("icons/star-yellow.svg"))

                self.favouriteButton.setIconSize(QSize(30, 30))
                self.favouriteButton.setFixedSize(70, 70)
                self.favouriteButton.clicked.connect(self.clickedFavourite)
                self.layout.addWidget(self.favouriteButton)

                # Playlist button
                playlistButton = QToolButton()
                playlistButton.setIcon(QIcon("icons/music-player-2.svg"))
                playlistButton.setIconSize(QSize(30, 30))
                playlistButton.setFixedSize(70, 70)
                playlistButton.clicked.connect(self.addToPlaylist)
                self.layout.addWidget(playlistButton)

            def formatPlaylist(self):
                # Add space to beginning of LisItem
                labelQueue = self.formattedLabel(QLabel("", self))
                labelQueue.setFixedWidth(70)
                labelQueue.setAlignment(Qt.AlignCenter)
                self.layout.addWidget(labelQueue)

                # Playlist name
                labelPlaylist = self.formattedLabel(
                    QLabel(self.result["playlist_name"]))
                self.layout.addWidget(labelPlaylist)

                # Remove playlist button
                btnRemove = QToolButton()
                btnRemove.setText("X")
                btnRemove.setFixedSize(70, 70)
                btnRemove.setStyleSheet("color: white")
                btnRemove.clicked.connect(self.removePlaylist)
                self.layout.addWidget(btnRemove)

            def formattedLabel(self, label):
                # Format a label
                font = QFont()
                font.setPixelSize(25)
                # TODO: change with global themes
                label.setStyleSheet("color: white")
                label.setSizePolicy(QSizePolicy.Expanding,
                                    QSizePolicy.Expanding)
                label.setFont(font)
                return label

            def clickedSong(self):
                # Called when a song is clicked on

                # Add to queue
                self.window().songQueue.addSong(self.result)

                if len(self.window().songQueue.getQueue()) < 2:
                    if not self.window().mediaPlayer.currentSong:
                        # If this is the first song to queue, signal media player to start it
                        self.window().mediaPlayer.skipSong()
                    # Update scrolling text marquee
                    self.window().mediaPlayer.updateMarquee()

            def clickedPlaylist(self):
                # Called when a playlist is clicked on
                self.window().content.addWidget(
                    WindowSearch(
                        "搜索全部/Search", self,
                        self.window().content.currentWidget().counter + 1,
                        self.result))
                self.window().content.setCurrentIndex(
                    self.window().content.currentWidget().counter + 1)

            def clickedFavourite(self):
                # Toggle Icon and DB state of song being favourited
                # For songs only
                if self.favouriteButton.isFavourited:
                    self.favouriteButton.isFavourited = False
                    self.favouriteButton.setIcon(QIcon("icons/star.svg"))
                else:
                    self.favouriteButton.isFavourited = True
                    self.favouriteButton.setIcon(
                        QIcon("icons/star-yellow.svg"))
                DB.setFavouriteSong(self.result["song_id"])

            def addToPlaylist(self):
                # Opens a popup for adding songs to playlists
                popup = PlaylistPopup(self.result, self.window())
                popup.resize(1366, 768)
                popup.SIGNALS.CLOSE.connect(lambda: popup.close())
                popup.show()

            def removePlaylist(self):
                # Remove a playlist
                DB.removePlaylist(self.result["playlist_id"])
                self.window().content.currentWidget().results.results.remove(
                    self.result)
                self.window().content.currentWidget().results.addResults(
                    self.window().content.currentWidget().results.results)

    class ResultsGrid(QScrollArea):
        def __init__(self, parent=None):
            QScrollArea.__init__(self)
            self.setParent(parent)

            # Make QScrollArea transparent
            self.setStyleSheet(
                "QScrollArea { background-color: transparent } .QFrame { background-color: transparent }"
            )
            self.setWidgetResizable(True)
            self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)

            # Add Touch Gestures to menu.
            QScroller.grabGesture(self, QScroller.LeftMouseButtonGesture)

            # Layout settings
            self.layout = QGridLayout()
            self.layout.setContentsMargins(0, 0, 0, 0)
            self.layout.setSpacing(0)

            # QScrollArea requires a separate QWidget to work properly
            frame = QFrame()
            frame.setLayout(self.layout)

            self.setWidget(frame)

        def clearResults(self):
            # Clear the results in the list
            while self.layout.count():
                item = self.layout.takeAt(0)
                if item.widget() is not None:
                    item.widget().deleteLater()

        def addResults(self, results):
            # Add the results to the list
            # results (list): list of python dict representing results details (playlist or song search)
            self.clearResults()
            for i in range(len(results)):
                item = self.ResultsGridItem(results[i], self)
                self.layout.addWidget(item, i // 6, i % 6)
            if results:
                self.layout.setRowStretch(self.layout.rowCount(), 1)
                self.layout.setColumnStretch(self.layout.columnCount(), 1)
            else:
                # If the results are empty, display no result
                label = QLabel("没有结果/No Result")
                label.setStyleSheet("color: white")
                label.setAlignment(Qt.AlignCenter)
                font = QFont()
                font.setPointSize(35)
                label.setFont(font)

                self.layout.addWidget(label)

        class ResultsGridItem(QToolButton):
            def __init__(self, result, parent=None):
                QToolButton.__init__(self)
                self.setParent(parent)
                self.result = result

                self.setContentsMargins(0, 0, 0, 0)
                # Button formatting
                self.setFixedSize(200, 240)
                self.setAutoRaise(True)
                # TODO: change with global themes
                self.setStyleSheet(
                    "QToolButton:pressed { background-color: rgba(255, 255, 255, 0.1)} QToolButton { background-color: rgba(255, 255, 255, 0.05); border: 1px solid white; color: white}"
                )

                # Set layout settings
                self.layout = QGridLayout()
                self.layout.setContentsMargins(0, 0, 0, 0)
                self.layout.setSpacing(0)

                if result["type"] == "artists":
                    # Artist image
                    self.formattedImage(self.window().artistPath +
                                        result["artist_path"])
                    self.formattedLabel(result["artist_name"])

                    # Favourite button
                    self.favouriteButton = QToolButton()
                    self.favouriteButton.setStyleSheet(
                        "QToolButton:pressed { background-color: rgb(31, 41, 75)} QToolButton { background-color: rgb(25, 33, 60);}"
                    )

                    # Toggle Favourite Icon depending on DB
                    if self.result["favourited"] == 0:
                        self.favouriteButton.isFavourited = False
                        self.favouriteButton.setIcon(QIcon("icons/star.svg"))
                    else:
                        self.favouriteButton.isFavourited = True
                        self.favouriteButton.setIcon(
                            QIcon("icons/star-yellow.svg"))
                    self.favouriteButton.setIconSize(QSize(30, 30))
                    self.favouriteButton.setFixedSize(70, 70)
                    self.favouriteButton.clicked.connect(self.clickedFavourite)
                    self.layout.addWidget(self.favouriteButton, 0, 0,
                                          Qt.AlignRight | Qt.AlignTop)

                    self.clicked.connect(self.clickedArtist)
                elif result["type"] == "languages":
                    # Language image
                    self.formattedImage(self.window().languagePath +
                                        result["language_path"])
                    self.formattedLabel(result["language_name"])
                    self.clicked.connect(self.clickedLanguage)

                self.setLayout(self.layout)

            def clickedFavourite(self):
                # Toggle Icon and DB state of song being favourited
                # For artists only
                if self.favouriteButton.isFavourited:
                    self.favouriteButton.isFavourited = False
                    self.favouriteButton.setIcon(QIcon("icons/star.svg"))
                else:
                    self.favouriteButton.isFavourited = True
                    self.favouriteButton.setIcon(
                        QIcon("icons/star-yellow.svg"))
                DB.setFavouriteArtist(self.result["artist_id"])

            def formattedImage(self, path):
                # Format an image given the path
                self.setIcon(QIcon(path))
                self.setIconSize(QSize(180, 180))

            def formattedLabel(self, text):
                # Format a label given text
                font = QFont()
                font.setPixelSize(18)
                self.setText(text)
                self.setFont(font)
                self.setToolButtonStyle(Qt.ToolButtonTextUnderIcon)

            def clickedArtist(self):
                # Called when an Artist is clicked on
                self.window().content.addWidget(
                    WindowSearch(
                        "搜索全部/Search", self,
                        self.window().content.currentWidget().counter + 1,
                        self.result))
                self.window().content.setCurrentIndex(
                    self.window().content.currentWidget().counter + 1)

            def clickedLanguage(self):
                # Called when a Language is clicked on
                self.window().content.addWidget(
                    WindowSearch(
                        "搜索歌手/Artist Search",
                        self,
                        self.window().content.currentWidget().counter + 1,
                        self.result,
                        grid=True))
                self.window().content.setCurrentIndex(
                    self.window().content.currentWidget().counter + 1)
Example #2
0
class ResourceRequirementEditor:
    def __init__(
        self,
        parent: QWidget,
        layout: QHBoxLayout,
        resource_database: ResourceDatabase,
        item: ResourceRequirement,
    ):
        self.parent = parent
        self.layout = layout
        self.resource_database = resource_database

        self.resource_type_combo = _create_resource_type_combo(
            item.resource.resource_type, parent, resource_database)
        self.resource_type_combo.setMinimumWidth(75)
        self.resource_type_combo.setMaximumWidth(75)

        self.resource_name_combo = _create_resource_name_combo(
            self.resource_database, item.resource.resource_type, item.resource,
            self.parent)

        self.negate_combo = ScrollProtectedComboBox(parent)
        self.negate_combo.addItem("≥", False)
        self.negate_combo.addItem("<", True)
        self.negate_combo.setCurrentIndex(int(item.negate))
        self.negate_combo.setMinimumWidth(40)
        self.negate_combo.setMaximumWidth(40)

        self.negate_check = QtWidgets.QCheckBox(parent)
        self.negate_check.setChecked(item.negate)

        self.amount_edit = QLineEdit(parent)
        self.amount_edit.setValidator(QIntValidator(1, 10000))
        self.amount_edit.setText(str(item.amount))
        self.amount_edit.setMinimumWidth(45)
        self.amount_edit.setMaximumWidth(45)

        self.amount_combo = ScrollProtectedComboBox(parent)
        for trick_level in iterate_enum(LayoutTrickLevel):
            self.amount_combo.addItem(trick_level.long_name,
                                      userData=trick_level.as_number)
        self.amount_combo.setCurrentIndex(
            self.amount_combo.findData(item.amount))

        for widget in self._all_widgets:
            self.layout.addWidget(widget)

        self.resource_type_combo.currentIndexChanged.connect(self._update_type)
        self._update_visible_elements_by_type()

    @property
    def resource_type(self) -> ResourceType:
        return self.resource_type_combo.currentData()

    def _update_visible_elements_by_type(self):
        resource_type = self.resource_type

        if resource_type == ResourceType.DAMAGE:
            self.negate_combo.setCurrentIndex(0)

        self.negate_check.setText("Before" if resource_type ==
                                  ResourceType.EVENT else "Not")
        self.negate_check.setVisible(
            resource_type in
            {ResourceType.EVENT, ResourceType.VERSION, ResourceType.MISC})
        self.negate_combo.setVisible(
            resource_type in {ResourceType.ITEM, ResourceType.DAMAGE})
        self.negate_combo.setEnabled(resource_type == ResourceType.ITEM)
        self.amount_edit.setVisible(
            resource_type in {ResourceType.ITEM, ResourceType.DAMAGE})
        self.amount_combo.setVisible(resource_type == ResourceType.TRICK)

    def _update_type(self):
        old_combo = self.resource_name_combo

        self.resource_name_combo = _create_resource_name_combo(
            self.resource_database, self.resource_type_combo.currentData(),
            None, self.parent)

        self.layout.replaceWidget(old_combo, self.resource_name_combo)
        old_combo.deleteLater()
        self._update_visible_elements_by_type()

    def deleteLater(self):
        for widget in self._all_widgets:
            widget.deleteLater()

    @property
    def _all_widgets(self) -> typing.Iterable[QWidget]:
        yield self.resource_type_combo
        yield self.negate_check
        yield self.resource_name_combo
        yield self.negate_combo
        yield self.amount_edit
        yield self.amount_combo

    @property
    def current_requirement(self) -> ResourceRequirement:
        resource_type = self.resource_type

        # Quantity
        if resource_type == ResourceType.TRICK:
            quantity: int = self.amount_combo.currentData()
        elif resource_type == ResourceType.EVENT:
            quantity = 1
        else:
            quantity = int(self.amount_edit.text())

        # Negate flag
        if resource_type == ResourceType.ITEM:
            negate: bool = self.negate_combo.currentData()
        elif resource_type == ResourceType.EVENT:
            negate = self.negate_check.isChecked()
        else:
            negate = False

        return ResourceRequirement(self.resource_name_combo.currentData(),
                                   quantity, negate)
Example #3
0
class ItemRow:
    def __init__(self, parent: QWidget, parent_layout: QVBoxLayout,
                 resource_database: ResourceDatabase,
                 item: ResourceRequirement, rows: List["ItemRow"]):
        self.parent = parent
        self.resource_database = resource_database
        self._rows = rows
        rows.append(self)

        self.layout = QHBoxLayout()
        self.layout.setObjectName(f"Box layout for {item.resource.long_name}")
        parent_layout.addLayout(self.layout)

        self.resource_type_combo = _create_resource_type_combo(
            item.resource.resource_type, parent)
        self.resource_type_combo.setMinimumWidth(75)
        self.resource_type_combo.setMaximumWidth(75)

        self.resource_name_combo = _create_resource_name_combo(
            self.resource_database, item.resource.resource_type, item.resource,
            self.parent)

        self.negate_combo = QComboBox(parent)
        self.negate_combo.addItem("≥", False)
        self.negate_combo.addItem("<", True)
        self.negate_combo.setCurrentIndex(int(item.negate))
        self.negate_combo.setMinimumWidth(40)
        self.negate_combo.setMaximumWidth(40)

        self.amount_edit = QLineEdit(parent)
        self.amount_edit.setValidator(QIntValidator(1, 10000))
        self.amount_edit.setText(str(item.amount))
        self.amount_edit.setMinimumWidth(45)
        self.amount_edit.setMaximumWidth(45)

        self.remove_button = QPushButton(parent)
        self.remove_button.setText("X")
        self.remove_button.setMaximumWidth(20)

        self.layout.addWidget(self.resource_type_combo)
        self.layout.addWidget(self.resource_name_combo)
        self.layout.addWidget(self.negate_combo)
        self.layout.addWidget(self.amount_edit)
        self.layout.addWidget(self.remove_button)

        self.resource_type_combo.currentIndexChanged.connect(self._update_type)
        self.remove_button.clicked.connect(self._delete_row)

    def _update_type(self):
        old_combo = self.resource_name_combo
        self.resource_name_combo = _create_resource_name_combo(
            self.resource_database, self.resource_type_combo.currentData(),
            None, self.parent)

        self.layout.replaceWidget(old_combo, self.resource_name_combo)
        old_combo.deleteLater()

    def _delete_row(self):
        self.resource_type_combo.deleteLater()
        self.resource_name_combo.deleteLater()
        self.negate_combo.deleteLater()
        self.amount_edit.deleteLater()
        self.remove_button.deleteLater()
        self.layout.deleteLater()
        self._rows.remove(self)

    @property
    def current_individual(self) -> ResourceRequirement:
        return ResourceRequirement(self.resource_name_combo.currentData(),
                                   int(self.amount_edit.text()),
                                   self.negate_combo.currentData())