コード例 #1
0
class OrderableListItem(QWidget, Subscribable):
    """Available subscription types"""
    DELETED = 1
    UPDATED = 2
    """Registered subscription callbacks"""
    _subscriptions: dict[int, list[callable]]
    """The widgets layout"""
    _layout: QLayout
    """The items id"""
    id = 0

    def __init__(self):
        # Init QWidget
        super().__init__()
        # Init Subscribable
        Subscribable.__init__(
            self, (OrderableListItem.DELETED, OrderableListItem.UPDATED))
        self._layout = QVBoxLayout()
        self.setLayout(self._layout)
        # Set and increase id
        self.id = OrderableListItem.id
        OrderableListItem.id += 1

    def delete(self):
        """Delete this item"""
        # Perform deletion subscriptions
        self._trigger_subscriptions(OrderableListItem.DELETED, item=self)
        # Delete all children
        while self._layout.count():
            child: QLayoutItem = self._layout.takeAt(0)
            child_widget: QWidget = child.widget()
            child_widget.deleteLater()
        # Delete the item itself
        self.deleteLater()

    @abstractmethod
    def get_order_string(self):
        """Get the string that this item can be ordered by
        :returns str: The string to order this item by"""
        return ""
コード例 #2
0
ファイル: WindowSearch.py プロジェクト: stkjoe/KTVHome
    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)
コード例 #3
0
class MainWidget(TritonWidget):
    def __init__(self, base):
        TritonWidget.__init__(self, base)
        self.addOTP = None
        self.closeEvent = self.widgetDeleted

        self.setWindowTitle('TritonAuth')
        self.setBackgroundColor(self, Qt.white)

        self.menu = QMenuBar()
        self.addMenu = self.menu.addMenu('Add')
        self.authAction = QAction('Authenticator', self)
        self.authAction.triggered.connect(self.openAddOTP)
        self.steamAction = QAction('Steam', self)
        self.steamAction.triggered.connect(self.openAddSteam)

        self.addMenu.addAction(self.authAction)
        self.addMenu.addAction(self.steamAction)

        self.sortMenu = self.menu.addMenu('Sort')
        self.nameAction = QAction('Sort by name', self)
        self.nameAction.triggered.connect(self.sortByName)

        self.sortMenu.addAction(self.nameAction)

        self.exportMenu = self.menu.addMenu('Export')
        self.andOTPAction = QAction('Export to andOTP', self)
        self.andOTPAction.triggered.connect(self.exportToAndOTP)

        self.exportMenu.addAction(self.andOTPAction)

        self.widget = QWidget()
        self.widget.setContentsMargins(10, 10, 10, 10)

        self.scrollArea = QScrollArea()
        self.scrollArea.setFixedSize(400, 495)
        self.scrollArea.setWidgetResizable(True)
        self.scrollWidget = QWidget()
        self.scrollLayout = QVBoxLayout(self.scrollWidget)
        self.scrollLayout.setAlignment(Qt.AlignTop)

        self.createAccounts()

        self.scrollArea.setWidget(self.scrollWidget)

        self.widgetLayout = QVBoxLayout(self.widget)
        self.widgetLayout.addWidget(self.scrollArea)

        self.boxLayout = QVBoxLayout(self)
        self.boxLayout.setContentsMargins(0, 5, 0, 0)
        self.boxLayout.addWidget(self.menu)
        self.boxLayout.addWidget(self.widget)

        self.setFixedSize(self.sizeHint())
        self.center()
        self.show()

    def keyPressEvent(self, event):
        if type(event) != QKeyEvent:
            return

        letter = event.text().strip().lower()

        for i in range(self.scrollLayout.count()):
            widget = self.scrollLayout.itemAt(i).widget()

            if widget is not None and widget.name[0].lower() == letter:
                self.scrollArea.verticalScrollBar().setValue(
                    widget.geometry().top())
                return

    def widgetDeleted(self, arg):
        self.closeAddOTP()

    def closeAddOTP(self):
        if self.addOTP:
            self.addOTP.close()
            self.addOTP = None

    def addAccount(self, account):
        entry = EntryWidget(self.base, account)
        self.scrollLayout.addWidget(entry)

    def deleteAccount(self, account):
        for i in range(self.scrollLayout.count()):
            widget = self.scrollLayout.itemAt(i).widget()

            if widget.account == account:
                widget.close()

    def clearAccounts(self):
        for i in range(self.scrollLayout.count()):
            self.scrollLayout.itemAt(i).widget().close()

    def createAccounts(self):
        for account in self.base.getAccounts():
            self.addAccount(account)

    def openAddOTP(self):
        self.closeAddOTP()
        self.addOTP = AddOTPWidget(self.base)

    def openAddSteam(self):
        self.closeAddOTP()
        self.addOTP = AddSteamWidget(self.base)

    def sortByName(self):
        self.base.sortAccountsByName()
        self.clearAccounts()
        self.createAccounts()

    def exportToAndOTP(self):
        accounts = []

        for account in self.base.getAccounts():
            type = account['type']

            if type == Globals.OTPAuth:
                accounts.append({
                    'secret': account['key'],
                    'digits': 6,
                    'period': 30,
                    'label': account['name'],
                    'type': 'TOTP',
                    'algorithm': 'SHA1',
                    'thumbnail': 'Default',
                    'last_used': 0,
                    'tags': []
                })
            elif type == Globals.SteamAuth:
                accounts.append({
                    'secret':
                    base64.b32encode(base64.b64decode(
                        account['sharedSecret'])).decode('utf-8'),
                    'digits':
                    5,
                    'period':
                    30,
                    'label':
                    account['name'],
                    'type':
                    'STEAM',
                    'algorithm':
                    'SHA1',
                    'thumbnail':
                    'Default',
                    'last_used':
                    0,
                    'tags': []
                })

        accounts = json.dumps(accounts)
        filename, _ = QFileDialog.getSaveFileName(
            self, 'Export to andOTP JSON file', '', 'All Files (*)')

        if filename:
            with open(filename, 'w') as file:
                file.write(accounts)
コード例 #4
0
class OrderableListWidget(QScrollArea):
    """All available items in this list"""
    _item_list: list[OrderableListItem]
    """This lists actual widget"""
    _widget: QWidget
    """The widgets layout"""
    _layout: QLayout
    """Decides which way this list is ordered; 1 for ascending, -1 for descending"""
    _order_factor: int

    def __init__(self, order_asc=True, orientation_horizontal=False):
        """Init gui
        :type order_asc: bool
        :param order_asc: Whether to order the list ascending
        :type orientation_horizontal: bool
        :param orientation_horizontal: Should the list orientation be horizontal?
        """
        super().__init__()
        if order_asc:
            self._order_factor = 1
        else:
            self._order_factor = -1
        self._widget = QWidget()
        self.setWidget(self._widget)
        self.setWidgetResizable(True)
        # Set layout
        if orientation_horizontal:
            self._layout = QHBoxLayout()
        else:
            self._layout = QVBoxLayout()
        self._widget.setLayout(self._layout)
        self._layout.setAlignment(Qt.AlignTop)
        self._item_list = []

    def _get_order(self, list_item_a, list_item_b):
        """Defines this lists widget order
        :type list_item_a: OrderableListItem
        :param list_item_a: The first item to compare
        :type list_item_b: OrderableListItem
        :param list_item_b: The second item to compare
        :returns -1|0|1: list_item_a is: before, same, after list_item_b"""
        str_a: str = list_item_a.get_order_string()
        str_b: str = list_item_b.get_order_string()

        if str_a == str_b:
            return 0
        elif str_a < str_b:
            return -1 * self._order_factor
        else:
            return 1 * self._order_factor

    def add(self, list_item):
        """Add a new item to the list
        :type list_item: OrderableListItem
        :param list_item: The item to add
        """
        # Subscribe to changes
        list_item.subscribe(OrderableListItem.DELETED, self._item_deleted)
        list_item.subscribe(OrderableListItem.UPDATED, self._item_updated)
        # Make sure to add the item only once
        if list_item not in self._item_list:
            list_item_inserted = False
            self._item_list.append(list_item)

            # Walk all existing items
            for i in range(self._layout.count()):
                existing_item: OrderableListItem = self._layout.itemAt(i).widget()

                if 1 == self._get_order(existing_item, list_item):
                    self._layout.insertWidget(i, list_item)
                    list_item_inserted = True
                    break
            if not list_item_inserted:
                self._layout.addWidget(list_item)

    def _item_deleted(self, item):
        """Delete an item from the list
        :type item: OrderableListItem
        :param item: The item to delete
        """
        # See if the item exists in this list
        try:
            i: int = self._item_list.index(item)
        except ValueError:
            return
        # Delete the item
        self._item_list.pop(i)

    def _item_updated(self, item):
        """Update the list with the items new information
        :type item: OrderableListItem
        :param item: The item that was updated
        """
        pass
コード例 #5
0
    class QueueList(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)

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

            # A singular widget frame is needed to set the scroll area properly.
            frame = QFrame()
            frame.setLayout(self.layout)

            self.setWidget(frame)

            self.updateQueue()

        class QueueListItem(QToolButton):
            def __init__(self, song, parent=None):
                QToolButton.__init__(self)
                self.setParent(parent)
                self.song = song

                # 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)

                self.createItem()
                self.setLayout(self.layout)

            def createItem(self):
                # Create a song item

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

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

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

                # Set font (icon) size
                font = QFont()
                font.setPointSize(48)

                # Add buttons for queue-specific actions

                # Move the song up the queue
                btnMoveUp = QToolButton()
                btnMoveUp.setText("▲")
                btnMoveUp.setFixedSize(70, 70)
                btnMoveUp.setStyleSheet("color: white")
                btnMoveUp.setFont(font)
                btnMoveUp.clicked.connect(self.moveUp)
                self.layout.addWidget(btnMoveUp)

                # Move the song down the queue
                btnMoveDown = QToolButton()
                btnMoveDown.setText("▼")
                btnMoveDown.setFixedSize(70, 70)
                btnMoveDown.setStyleSheet("color: white")
                btnMoveDown.setFont(font)
                btnMoveDown.clicked.connect(self.moveDown)
                self.layout.addWidget(btnMoveDown)

                # Move the song to the top of the queue
                btnMoveTop = QToolButton()
                btnMoveTop.setText("⍏")
                btnMoveTop.setFixedSize(70, 70)
                btnMoveTop.setStyleSheet("color: white")
                btnMoveTop.setFont(font)
                btnMoveTop.clicked.connect(self.moveTop)
                self.layout.addWidget(btnMoveTop)

                # Play the song immediately
                btnPlay = QToolButton()
                btnPlay.setText("➤")
                btnPlay.setFixedSize(70, 70)
                btnPlay.setStyleSheet("color: white")
                btnPlay.setFont(font)
                btnPlay.clicked.connect(self.playSong)
                self.layout.addWidget(btnPlay)

                # Remove the song from the queue
                btnRemove = QToolButton()
                btnRemove.setText("X")
                btnRemove.setFixedSize(70, 70)
                btnRemove.setStyleSheet("color: white")
                btnRemove.setFont(font)
                btnRemove.clicked.connect(self.removeSong)
                self.layout.addWidget(btnRemove)

            def formattedLabel(self, 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 moveUp(self):
                # Move the song up the queue
                self.window().songQueue.moveUp(self.song)
                self.window().content.queueList.updateQueue()

            def moveDown(self):
                # Move the song down the queue
                self.window().songQueue.moveDown(self.song)
                self.window().content.queueList.updateQueue()

            def moveTop(self):
                # Move the song to the top of the queue
                self.window().songQueue.moveTop(self.song)
                self.window().content.queueList.updateQueue()

            def playSong(self):
                # Play the song immediately
                self.window().songQueue.moveTop(self.song)
                self.window().overlayBottom.buttonAction("next")
                self.window().content.queueList.updateQueue()

            def removeSong(self):
                # Remove the song from the queue
                self.window().songQueue.removeSong(self.song)
                self.window().content.queueList.updateQueue()
                if len(self.window().mediaPlayer.songQueue.getQueue()) == 0:
                    self.window().mediaPlayer.updateMarquee()

        def updateQueue(self):
            # "Refresh" the queue visually
            while self.layout.count():
                item = self.layout.takeAt(0)
                if item.widget() is not None:
                    item.widget().deleteLater()
            for i in self.window().songQueue.getQueue():
                item = self.QueueListItem(i, self)
                self.layout.addWidget(item)
            if self.window().songQueue.getQueue():
                self.layout.addStretch(1)
            else:
                label = QLabel("没有结果/No Result")
                label.setStyleSheet("color: white")
                label.setAlignment(Qt.AlignCenter)
                font = QFont()
                font.setPointSize(35)
                label.setFont(font)

                self.layout.addWidget(label)