Exemplo n.º 1
0
class Home(QMainWindow, Ui_MainWindow):
    """The main screen

    Args:
        signals (signals.Signals)
        directory (str)

    Attributes:
        search_bar (QLineEdit)
        search_button (QPushButton): same function as pressing entet in search bar
        details_button (QPushButton)
        advanced_search_button (QPushButton): opens a new window with all searchable options
        metadata_button (QPushButton):
        sort_by (QComboBox): contains "Alphabetically", "Rating", and "Randomly"
        random_button (QPushButton): randomly selects from the list

        main_area (QHBoxLayout)
        bookshelf (QGridLayout): the layout where all the books will be displayed
        bookshelf_area (QScrollArea): the whole area where books will be displayed

        books ([QFrame]): holds a list of all book frames
        selected (QFrame): holds the currently selected book
        details_panel (DetailsPanel)
        search_panel (SearchPanel)
        metadata_panel (MetadataPanel)
    """
    def __init__(self, signals, directory: str):
        super().__init__()
        self.setupUi(self)

        # init attributes
        self.signals = signals
        self.directory = directory
        self.db = database.DBHandler()
        self.books = []
        self.selected = None

        self.details_panel = DetailsPanel(self.db, self.signals)
        self.search_panel = SearchPanel(self.db, self.signals)
        self.metadata_panel = MetadataPanel(self.db, self.signals)
        self.setup_panels()

        self.connect_events()
        self.populate_gallery()

    def setup_panels(self):
        """Adds all panels to the main layout and makes only the details panel visible
        """
        self.main_area.addWidget(self.details_panel)
        self.main_area.addWidget(self.search_panel)
        self.main_area.addWidget(self.metadata_panel)
        self.search_panel.setVisible(False)
        self.metadata_panel.setVisible(False)

    def connect_events(self):
        """Connects each signal to their respective functions
        """
        # dropdowns
        self.sort_by.textActivated.connect(self.sort_gallery)

        # buttons
        self.details_button.clicked.connect(lambda: [
            self.details_panel.setVisible(True),
            self.search_panel.setVisible(False),
            self.metadata_panel.setVisible(False)
        ])
        self.advanced_search_button.clicked.connect(lambda: [
            self.details_panel.setVisible(False),
            self.search_panel.setVisible(True),
            self.metadata_panel.setVisible(False)
        ])
        self.metadata_button.clicked.connect(lambda: [
            self.details_panel.setVisible(False),
            self.search_panel.setVisible(False),
            self.metadata_panel.setVisible(True)
        ])
        self.random_button.clicked.connect(self.random_select)

        # bookshelf
        self.bookshelf_area.contextMenuEvent = self.context_menu
        self.bookshelf_area.mousePressEvent = self.reset_selected

        # signals
        self.signals.update_spines.connect(self.update_gallery)
        self.signals.search_advanced.connect(self.populate_gallery)

    def random_select(self):
        """Randomly selects a book from the list
        """
        self.select(random.choice(self.books))

    def generate_books(self, filters, sort):
        """Creates objects for the books based on certain search parameters

        Args:
            filters (dict, list, optional): filters to filter by (either a dict from search panel or a list from sort_gallery)
            sort (int): obtained from self.sort_by.currentIndex()
        """
        self.books = []
        for book in self.db.get_books(filters, sort):
            # set up frame
            spine = spines.BookSpine(book['id'], book['name'],
                                     book['directory'])

            # add the new frames to the list
            self.books.append(spine)

            # connect events
            spine.mousePressEvent = partial(self.select, self.books[-1])
            spine.mouseDoubleClickEvent = partial(self.open_book,
                                                  self.books[-1])
            spine.enterEvent = partial(self.highlight, self.books[-1])
            spine.leaveEvent = partial(self.unhighlight, self.books[-1])

    def populate_gallery(self, filters=None):
        """Populates the gallery with books that match a search filter

        Books should be generated by self.generate_books()

        Args:
            filters (dict, list, optional): filters to filter by (either a dict from search panel or a list from sort_gallery)
        """
        self.clear_gallery()
        self.generate_books(filters, self.sort_by.currentIndex())

        row_pos = 0
        col_pos = 0
        for book in self.books:
            self.bookshelf.addWidget(book, col_pos, row_pos)

            # calculate next position
            row_pos += 1
            if row_pos > 4:  # max 5 columns. aka 5 slots per row
                row_pos = 0
                col_pos += 1

        while col_pos <= 1:
            # if there's only a few books, place invisible frames to shove things into the top left corner
            # this avoids books showing up in the middle and messing up the look of the gallery
            blank = spines.BlankSpine()
            self.bookshelf.addWidget(blank, col_pos, row_pos)

            # calculate next position
            row_pos += 1
            if row_pos > 4:  # max 5 columns. aka 5 slots per row
                row_pos = 0
                col_pos += 1

        if (new_select := next((x for x in self.books if x == self.selected),
                               None)):
            self.select(new_select)
        else: