示例#1
0
class DebugDevice(QDialog):
    def __init__(self, gui, parent=None):
        QDialog.__init__(self, parent)
        self.gui = gui
        self._layout = QVBoxLayout(self)
        self.setLayout(self._layout)
        self.log = QPlainTextEdit(self)
        self._layout.addWidget(self.log)
        self.log.setPlainText(
            _('Getting debug information, please wait') + '...')
        self.copy = QPushButton(_('Copy to &clipboard'))
        self.copy.setDefault(True)
        self.setWindowTitle(_('Debug device detection'))
        self.setWindowIcon(QIcon(I('debug.png')))
        self.copy.clicked.connect(self.copy_to_clipboard)
        self.ok = QPushButton('&OK')
        self.ok.setAutoDefault(False)
        self.ok.clicked.connect(self.accept)
        self.bbox = QDialogButtonBox(self)
        self.bbox.addButton(self.copy, QDialogButtonBox.ActionRole)
        self.bbox.addButton(self.ok, QDialogButtonBox.AcceptRole)
        self._layout.addWidget(self.bbox)
        self.resize(750, 500)
        self.bbox.setEnabled(False)
        QTimer.singleShot(1000, self.debug)

    def debug(self):
        if self.gui.device_manager.is_device_connected:
            error_dialog(
                self,
                _('Device already detected'),
                _('A device (%s) is already detected by calibre.'
                  ' If you wish to debug the detection of another device'
                  ', first disconnect this device.') %
                self.gui.device_manager.connected_device.get_gui_name(),
                show=True)
            self.bbox.setEnabled(True)
            return
        self.gui.debug_detection(self)

    def __call__(self, job):
        if not self.isVisible():
            return
        self.bbox.setEnabled(True)
        if job.failed:
            return error_dialog(
                self,
                _('Debugging failed'),
                _('Running debug device detection failed. Click Show '
                  'Details for more information.'),
                det_msg=job.details,
                show=True)
        self.log.setPlainText(job.result)

    def copy_to_clipboard(self):
        QApplication.clipboard().setText(self.log.toPlainText())
示例#2
0
class Button(StylableWidget, Shortcutable):
    def __init__(self, label):
        super().__init__()
        self.widget = QPushButton(label)
        self.widget.setAutoDefault(False)
        qDlgStackTop().addChild(self.widget)

    def onClick(self, callback):
        self.widget.clicked.connect(callback)
        return self
示例#3
0
class DebugDevice(QDialog):

    def __init__(self, gui, parent=None):
        QDialog.__init__(self, parent)
        self.gui = gui
        self._layout = QVBoxLayout(self)
        self.setLayout(self._layout)
        self.log = QPlainTextEdit(self)
        self._layout.addWidget(self.log)
        self.log.setPlainText(_('Getting debug information, please wait')+'...')
        self.copy = QPushButton(_('Copy to &clipboard'))
        self.copy.setDefault(True)
        self.setWindowTitle(_('Debug device detection'))
        self.setWindowIcon(QIcon(I('debug.png')))
        self.copy.clicked.connect(self.copy_to_clipboard)
        self.ok = QPushButton('&OK')
        self.ok.setAutoDefault(False)
        self.ok.clicked.connect(self.accept)
        self.bbox = QDialogButtonBox(self)
        self.bbox.addButton(self.copy, QDialogButtonBox.ActionRole)
        self.bbox.addButton(self.ok, QDialogButtonBox.AcceptRole)
        self._layout.addWidget(self.bbox)
        self.resize(750, 500)
        self.bbox.setEnabled(False)
        QTimer.singleShot(1000, self.debug)

    def debug(self):
        if self.gui.device_manager.is_device_connected:
            error_dialog(self, _('Device already detected'),
                    _('A device (%s) is already detected by calibre.'
                        ' If you wish to debug the detection of another device'
                        ', first disconnect this device.')%
                    self.gui.device_manager.connected_device.get_gui_name(),
                    show=True)
            self.bbox.setEnabled(True)
            return
        self.gui.debug_detection(self)

    def __call__(self, job):
        if not self.isVisible():
            return
        self.bbox.setEnabled(True)
        if job.failed:
            return error_dialog(self, _('Debugging failed'),
                    _('Running debug device detection failed. Click Show '
                        'Details for more information.'), det_msg=job.details,
                    show=True)
        self.log.setPlainText(job.result)

    def copy_to_clipboard(self):
        QApplication.clipboard().setText(self.log.toPlainText())
示例#4
0
class UserDefinedDevice(QDialog):
    def __init__(self, parent=None):
        QDialog.__init__(self, parent)
        self._layout = QVBoxLayout(self)
        self.setLayout(self._layout)
        self.log = QPlainTextEdit(self)
        self._layout.addWidget(self.log)
        self.log.setPlainText(_('Getting device information') + '...')
        self.copy = QPushButton(_('Copy to &clipboard'))
        self.copy.setDefault(True)
        self.setWindowTitle(_('User-defined device information'))
        self.setWindowIcon(QIcon(I('debug.png')))
        self.copy.clicked.connect(self.copy_to_clipboard)
        self.ok = QPushButton('&OK')
        self.ok.setAutoDefault(False)
        self.ok.clicked.connect(self.accept)
        self.bbox = QDialogButtonBox(self)
        self.bbox.addButton(self.copy, QDialogButtonBox.ButtonRole.ActionRole)
        self.bbox.addButton(self.ok, QDialogButtonBox.ButtonRole.AcceptRole)
        self._layout.addWidget(self.bbox)
        self.resize(750, 500)
        self.bbox.setEnabled(False)
        QTimer.singleShot(1000, self.device_info)

    def device_info(self):
        try:
            from calibre.devices import device_info
            r = step_dialog(
                self.parent(), _('Device Detection'),
                _('Ensure your device is disconnected, then press OK'))
            if r:
                self.close()
                return
            before = device_info()
            r = step_dialog(
                self.parent(), _('Device Detection'),
                _('Ensure your device is connected, then press OK'))
            if r:
                self.close()
                return
            after = device_info()
            new_devices = after['device_set'] - before['device_set']
            res = ''
            if len(new_devices) == 1:

                def fmtid(x):
                    x = x or 0
                    if isinstance(x, numbers.Integral):
                        x = hex(x)
                    if not x.startswith('0x'):
                        x = '0x' + x
                    return x

                for d in new_devices:
                    res =  _('USB Vendor ID (in hex)') + ': ' + \
                            fmtid(after['device_details'][d][0]) + '\n'
                    res += _('USB Product ID (in hex)') + ': ' + \
                            fmtid(after['device_details'][d][1]) + '\n'
                    res += _('USB Revision ID (in hex)') + ': ' + \
                            fmtid(after['device_details'][d][2]) + '\n'
            trailer = _(
                'Copy these values to the clipboard, paste them into an '
                'editor, then enter them into the USER_DEVICE by '
                'customizing the device plugin in Preferences->Advanced->Plugins. '
                'Remember to also enter the folders where you want the books to '
                'be put. You must restart calibre for your changes '
                'to take effect.\n')
            self.log.setPlainText(res + '\n\n' + trailer)
        finally:
            self.bbox.setEnabled(True)

    def copy_to_clipboard(self):
        QApplication.clipboard().setText(self.log.toPlainText())
示例#5
0
class OpdsDialog(QDialog):
    def __init__(self, gui, icon, do_user_config):
        QDialog.__init__(self, gui)
        self.gui = gui
        self.do_user_config = do_user_config

        self.db = gui.current_db.new_api

        # The model for the book list
        self.model = OpdsBooksModel(None, self.dummy_books(), self.db)
        self.searchproxymodel = QSortFilterProxyModel(self)
        self.searchproxymodel.setFilterCaseSensitivity(Qt.CaseInsensitive)
        self.searchproxymodel.setFilterKeyColumn(-1)
        self.searchproxymodel.setSourceModel(self.model)

        self.layout = QGridLayout()
        self.setLayout(self.layout)

        self.setWindowTitle("OPDS Client")
        self.setWindowIcon(icon)

        labelColumnWidths = []

        self.opdsUrlLabel = QLabel("OPDS URL: ")
        self.layout.addWidget(self.opdsUrlLabel, 0, 0)
        labelColumnWidths.append(self.layout.itemAtPosition(0, 0).sizeHint().width())

        config.convertSingleStringOpdsUrlPreferenceToListOfStringsPreference()
        self.opdsUrlEditor = QComboBox(self)
        self.opdsUrlEditor.activated.connect(self.opdsUrlEditorActivated)
        self.opdsUrlEditor.addItems(prefs["opds_url"])
        self.opdsUrlEditor.setEditable(True)
        self.opdsUrlEditor.setInsertPolicy(QComboBox.InsertAtTop)
        self.layout.addWidget(self.opdsUrlEditor, 0, 1, 1, 3)
        self.opdsUrlLabel.setBuddy(self.opdsUrlEditor)

        buttonColumnNumber = 7
        buttonColumnWidths = []
        self.about_button = QPushButton("About", self)
        self.about_button.setAutoDefault(False)
        self.about_button.clicked.connect(self.about)
        self.layout.addWidget(self.about_button, 0, buttonColumnNumber)
        buttonColumnWidths.append(
            self.layout.itemAtPosition(0, buttonColumnNumber).sizeHint().width()
        )

        # Initially download the catalogs found in the root catalog of the URL
        # selected at startup.  Fail quietly on failing to open the URL
        catalogsTuple = self.model.downloadOpdsRootCatalog(
            self.gui, self.opdsUrlEditor.currentText(), False
        )
        print(catalogsTuple)
        firstCatalogTitle = catalogsTuple[0]
        self.currentOpdsCatalogs = catalogsTuple[1]  # A dictionary of title->feedURL

        self.opdsCatalogSelectorLabel = QLabel("OPDS Catalog:")
        self.layout.addWidget(self.opdsCatalogSelectorLabel, 1, 0)
        labelColumnWidths.append(self.layout.itemAtPosition(1, 0).sizeHint().width())

        self.opdsCatalogSelector = QComboBox(self)
        self.opdsCatalogSelector.setEditable(False)
        self.opdsCatalogSelectorModel = QStringListModel(self.currentOpdsCatalogs.keys())
        self.opdsCatalogSelector.setModel(self.opdsCatalogSelectorModel)
        self.opdsCatalogSelector.setCurrentText(firstCatalogTitle)
        self.layout.addWidget(self.opdsCatalogSelector, 1, 1, 1, 3)

        self.download_opds_button = QPushButton("Download OPDS", self)
        self.download_opds_button.setAutoDefault(False)
        self.download_opds_button.clicked.connect(self.download_opds)
        self.layout.addWidget(self.download_opds_button, 1, buttonColumnNumber)
        buttonColumnWidths.append(
            self.layout.itemAtPosition(1, buttonColumnNumber).sizeHint().width()
        )

        # Search GUI
        self.searchEditor = QLineEdit(self)
        self.searchEditor.returnPressed.connect(self.searchBookList)
        self.layout.addWidget(self.searchEditor, 2, buttonColumnNumber - 2, 1, 2)

        self.searchButton = QPushButton("Search", self)
        self.searchButton.setAutoDefault(False)
        self.searchButton.clicked.connect(self.searchBookList)
        self.layout.addWidget(self.searchButton, 2, buttonColumnNumber)
        buttonColumnWidths.append(
            self.layout.itemAtPosition(2, buttonColumnNumber).sizeHint().width()
        )

        # The main book list
        self.library_view = QTableView(self)
        self.library_view.setAlternatingRowColors(True)
        self.library_view.setModel(self.searchproxymodel)
        self.library_view.horizontalHeader().setSectionResizeMode(0, QHeaderView.Stretch)
        self.library_view.horizontalHeader().setSectionResizeMode(1, QHeaderView.Stretch)
        self.library_view.horizontalHeader().setSectionResizeMode(2, QHeaderView.Stretch)
        self.library_view.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.resizeAllLibraryViewLinesToHeaderHeight()
        self.library_view.resizeColumnsToContents()
        self.layout.addWidget(self.library_view, 3, 0, 3, buttonColumnNumber + 1)

        self.hideNewsCheckbox = QCheckBox("Hide Newspapers", self)
        self.hideNewsCheckbox.clicked.connect(self.setHideNewspapers)
        self.hideNewsCheckbox.setChecked(prefs["hideNewspapers"])
        self.layout.addWidget(self.hideNewsCheckbox, 6, 0, 1, 3)

        self.hideBooksAlreadyInLibraryCheckbox = QCheckBox("Hide books already in library", self)
        self.hideBooksAlreadyInLibraryCheckbox.clicked.connect(self.setHideBooksAlreadyInLibrary)
        self.hideBooksAlreadyInLibraryCheckbox.setChecked(prefs["hideBooksAlreadyInLibrary"])
        self.layout.addWidget(self.hideBooksAlreadyInLibraryCheckbox, 7, 0, 1, 3)

        # Let the checkbox initial state control the filtering
        self.model.setFilterBooksThatAreNewspapers(self.hideNewsCheckbox.isChecked())
        self.model.setFilterBooksThatAreAlreadyInLibrary(
            self.hideBooksAlreadyInLibraryCheckbox.isChecked()
        )

        self.downloadButton = QPushButton("Download selected books", self)
        self.downloadButton.setAutoDefault(False)
        self.downloadButton.clicked.connect(self.downloadSelectedBooks)
        self.layout.addWidget(self.downloadButton, 6, buttonColumnNumber)
        buttonColumnWidths.append(
            self.layout.itemAtPosition(6, buttonColumnNumber).sizeHint().width()
        )

        self.fixTimestampButton = QPushButton("Fix timestamps of selection", self)
        self.fixTimestampButton.setAutoDefault(False)
        self.fixTimestampButton.clicked.connect(self.fixBookTimestamps)
        self.layout.addWidget(self.fixTimestampButton, 7, buttonColumnNumber)
        buttonColumnWidths.append(
            self.layout.itemAtPosition(7, buttonColumnNumber).sizeHint().width()
        )

        # Make all columns of the grid layout the same width as the button column
        buttonColumnWidth = max(buttonColumnWidths)
        for columnNumber in range(0, buttonColumnNumber):
            self.layout.setColumnMinimumWidth(columnNumber, buttonColumnWidth)

        # Make sure the first column isn't wider than the labels it holds
        labelColumnWidth = max(labelColumnWidths)
        self.layout.setColumnMinimumWidth(0, labelColumnWidth)

        self.resize(self.sizeHint())

    def opdsUrlEditorActivated(self, text):
        prefs["opds_url"] = config.saveOpdsUrlCombobox(self.opdsUrlEditor)
        catalogsTuple = self.model.downloadOpdsRootCatalog(
            self.gui, self.opdsUrlEditor.currentText(), True
        )
        firstCatalogTitle = catalogsTuple[0]
        self.currentOpdsCatalogs = catalogsTuple[1]  # A dictionary of title->feedURL
        self.opdsCatalogSelectorModel.setStringList(self.currentOpdsCatalogs.keys())
        self.opdsCatalogSelector.setCurrentText(firstCatalogTitle)

    def setHideNewspapers(self, checked):
        prefs["hideNewspapers"] = checked
        self.model.setFilterBooksThatAreNewspapers(checked)
        self.resizeAllLibraryViewLinesToHeaderHeight()

    def setHideBooksAlreadyInLibrary(self, checked):
        prefs["hideBooksAlreadyInLibrary"] = checked
        self.model.setFilterBooksThatAreAlreadyInLibrary(checked)
        self.resizeAllLibraryViewLinesToHeaderHeight()

    def searchBookList(self):
        searchString = self.searchEditor.text()
        print("starting book list search for: %s" % searchString)
        self.searchproxymodel.setFilterFixedString(searchString)

    def about(self):
        text = get_resources("about.txt")
        QMessageBox.about(self, "About the OPDS Client plugin", text.decode("utf-8"))

    def download_opds(self):
        opdsCatalogUrl = self.currentOpdsCatalogs.get(self.opdsCatalogSelector.currentText(), None)
        if opdsCatalogUrl is None:
            # Just give up quietly
            return
        self.model.downloadOpdsCatalog(self.gui, opdsCatalogUrl)
        if self.model.isCalibreOpdsServer():
            self.model.downloadMetadataUsingCalibreRestApi(self.opdsUrlEditor.currentText())
        self.library_view.horizontalHeader().setSectionResizeMode(0, QHeaderView.Stretch)
        self.library_view.horizontalHeader().setSectionResizeMode(1, QHeaderView.Stretch)
        self.library_view.horizontalHeader().setSectionResizeMode(2, QHeaderView.Stretch)
        self.resizeAllLibraryViewLinesToHeaderHeight()
        self.resize(self.sizeHint())

    def config(self):
        self.do_user_config(parent=self)

    def downloadSelectedBooks(self):
        selectionmodel = self.library_view.selectionModel()
        if selectionmodel.hasSelection():
            rows = selectionmodel.selectedRows()
            for row in reversed(rows):
                book = row.data(Qt.UserRole)
                self.downloadBook(book)

    def downloadBook(self, book):
        if len(book.links) > 0:
            self.gui.download_ebook(book.links[0])

    def fixBookTimestamps(self):
        selectionmodel = self.library_view.selectionModel()
        if selectionmodel.hasSelection():
            rows = selectionmodel.selectedRows()
            for row in reversed(rows):
                book = row.data(Qt.UserRole)
                self.fixBookTimestamp(book)

    def fixBookTimestamp(self, book):
        bookTimestamp = book.timestamp
        identicalBookIds = self.findIdenticalBooksForBooksWithMultipleAuthors(book)
        bookIdToValMap = {}
        for identicalBookId in identicalBookIds:
            bookIdToValMap[identicalBookId] = bookTimestamp
        if len(bookIdToValMap) < 1:
            print("Failed to set timestamp of book: %s" % book)
        self.db.set_field("timestamp", bookIdToValMap)

    def findIdenticalBooksForBooksWithMultipleAuthors(self, book):
        authorsList = book.authors
        if len(authorsList) < 2:
            return self.db.find_identical_books(book)
        # Try matching the authors one by one
        identicalBookIds = set()
        for author in authorsList:
            singleAuthorBook = Metadata(book.title, [author])
            singleAuthorIdenticalBookIds = self.db.find_identical_books(singleAuthorBook)
            identicalBookIds = identicalBookIds.union(singleAuthorIdenticalBookIds)
        return identicalBookIds

    def dummy_books(self):
        dummy_author = " " * 40
        dummy_title = " " * 60
        books_list = []
        for line in range(1, 10):
            book = DynamicBook()
            book.author = dummy_author
            book.title = dummy_title
            book.updated = datetime.datetime.now().strftime("%Y-%m-%dT%H:%M:%S+00:00")
            book.id = ""
            books_list.append(book)
        return books_list

    def resizeAllLibraryViewLinesToHeaderHeight(self):
        rowHeight = self.library_view.horizontalHeader().height()
        for rowNumber in range(0, self.library_view.model().rowCount()):
            self.library_view.setRowHeight(rowNumber, rowHeight)
示例#6
0
class UserDefinedDevice(QDialog):

    def __init__(self, parent=None):
        QDialog.__init__(self, parent)
        self._layout = QVBoxLayout(self)
        self.setLayout(self._layout)
        self.log = QPlainTextEdit(self)
        self._layout.addWidget(self.log)
        self.log.setPlainText(_('Getting device information')+'...')
        self.copy = QPushButton(_('Copy to &clipboard'))
        self.copy.setDefault(True)
        self.setWindowTitle(_('User-defined device information'))
        self.setWindowIcon(QIcon(I('debug.png')))
        self.copy.clicked.connect(self.copy_to_clipboard)
        self.ok = QPushButton('&OK')
        self.ok.setAutoDefault(False)
        self.ok.clicked.connect(self.accept)
        self.bbox = QDialogButtonBox(self)
        self.bbox.addButton(self.copy, QDialogButtonBox.ActionRole)
        self.bbox.addButton(self.ok, QDialogButtonBox.AcceptRole)
        self._layout.addWidget(self.bbox)
        self.resize(750, 500)
        self.bbox.setEnabled(False)
        QTimer.singleShot(1000, self.device_info)

    def device_info(self):
        try:
            from calibre.devices import device_info
            r = step_dialog(self.parent(), _('Device Detection'),
                        _('Ensure your device is disconnected, then press OK'))
            if r:
                self.close()
                return
            before = device_info()
            r = step_dialog(self.parent(), _('Device Detection'),
                        _('Ensure your device is connected, then press OK'))
            if r:
                self.close()
                return
            after = device_info()
            new_devices = after['device_set'] - before['device_set']
            res = ''
            if len(new_devices) == 1:
                def fmtid(x):
                    if isinstance(x, (int, long)):
                        x = hex(x)
                    if not x.startswith('0x'):
                        x = '0x' + x
                    return x

                for d in new_devices:
                    res =  _('USB Vendor ID (in hex)') + ': ' + \
                            fmtid(after['device_details'][d][0]) + '\n'
                    res += _('USB Product ID (in hex)') + ': ' + \
                            fmtid(after['device_details'][d][1]) + '\n'
                    res += _('USB Revision ID (in hex)') + ': ' + \
                            fmtid(after['device_details'][d][2]) + '\n'
            trailer = _(
                    'Copy these values to the clipboard, paste them into an '
                    'editor, then enter them into the USER_DEVICE by '
                    'customizing the device plugin in Preferences->Plugins. '
                    'Remember to also enter the folders where you want the books to '
                    'be put. You must restart calibre for your changes '
                    'to take effect.\n')
            self.log.setPlainText(res + '\n\n' + trailer)
        finally:
            self.bbox.setEnabled(True)

    def copy_to_clipboard(self):
        QApplication.clipboard().setText(self.log.toPlainText())
示例#7
0
class AboutDialog(QDialog):
    def __init__(self, parent=None, name=None, modal=0, fl=None):
        if fl is None:
            fl = Qt.Dialog | Qt.WindowTitleHint
        QDialog.__init__(self, parent, Qt.Dialog | Qt.WindowTitleHint)
        self.setModal(modal)

        image0 = pixmaps.tigger_logo.pm()

        # self.setSizeGripEnabled(0)
        LayoutWidget = QWidget(self)
        LayoutWidget.setSizePolicy(QSizePolicy.MinimumExpanding,
                                   QSizePolicy.MinimumExpanding)

        lo_top = QVBoxLayout(LayoutWidget)

        lo_title = QHBoxLayout(None)

        self.title_icon = QLabel(LayoutWidget)
        self.title_icon.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        self.title_icon.setPixmap(image0)
        self.title_icon.setAlignment(Qt.AlignCenter)
        lo_title.addWidget(self.title_icon)

        self.title_label = QLabel(LayoutWidget)
        self.title_label.setWordWrap(True)
        lo_title.addWidget(self.title_label)
        lo_top.addLayout(lo_title)

        lo_logos = QHBoxLayout(None)
        lo_top.addLayout(lo_logos)
        # for logo in ("astron",):
        #   icon = QLabel(LayoutWidget)
        #   icon.setSizePolicy(QSizePolicy.Fixed,QSizePolicy.Fixed)
        #   icon.setPixmap(getattr(pixmaps,logo+"_logo").pm())
        #   icon.setAlignment(Qt.AlignCenter)
        #   lo_logos.addWidget(icon)

        lo_mainbtn = QHBoxLayout(None)
        lo_mainbtn.addItem(
            QSpacerItem(20, 20, QSizePolicy.Expanding, QSizePolicy.Minimum))
        lo_top.addLayout(lo_mainbtn)

        self.btn_ok = QPushButton(LayoutWidget)
        self.btn_ok.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        self.btn_ok.setMinimumSize(QSize(60, 0))
        self.btn_ok.setAutoDefault(1)
        self.btn_ok.setDefault(1)
        lo_mainbtn.addWidget(self.btn_ok)
        lo_mainbtn.addItem(
            QSpacerItem(20, 20, QSizePolicy.Expanding, QSizePolicy.Minimum))

        self.languageChange()

        LayoutWidget.adjustSize()

        # LayoutWidget.resize(QSize(489,330).expandedTo(LayoutWidget.minimumSizeHint()))
        # self.resize(QSize(489,330).expandedTo(self.minimumSizeHint()))
        # self.clearWState(Qt.WState_Polished)

        self.btn_ok.clicked.connect(self.accept)

    def languageChange(self):
        self.setWindowTitle(self.__tr("About Tigger"))
        self.title_label.setText(self.__tr( \
            """<h3>Tigger %s</h3>
            <p>\u00a92010-2021 Oleg Smirnov & Rhodes University & SKA SA<br>
            <br>Please direct feedback and bug reports at https://github.com/ska-sa/tigger</p>
            """ % (release_string) \
            ))

        self.btn_ok.setText(self.__tr("&OK"))

    def __tr(self, s, c=None):
        return QApplication.translate("About", s, c)
示例#8
0
class OpdsDialog(QDialog):

    def __init__(self, gui, icon, do_user_config):
        QDialog.__init__(self, gui)
        self.gui = gui
        self.do_user_config = do_user_config

        self.db = gui.current_db.new_api

        # The model for the book list
        self.model = OpdsBooksModel(None, self.dummy_books(), self.db)
        self.searchproxymodel = QSortFilterProxyModel(self)
        self.searchproxymodel.setFilterCaseSensitivity(Qt.CaseInsensitive)
        self.searchproxymodel.setFilterKeyColumn(-1)
        self.searchproxymodel.setSourceModel(self.model)

        self.layout = QGridLayout()
        self.setLayout(self.layout)

        self.setWindowTitle('OPDS Client')
        self.setWindowIcon(icon)

        labelColumnWidths = []

        self.opdsUrlLabel = QLabel('OPDS URL: ')
        self.layout.addWidget(self.opdsUrlLabel, 0, 0)
        labelColumnWidths.append(self.layout.itemAtPosition(0, 0).sizeHint().width())

        config.convertSingleStringOpdsUrlPreferenceToListOfStringsPreference()
        self.opdsUrlEditor = QComboBox(self)
        self.opdsUrlEditor.activated.connect(self.opdsUrlEditorActivated)
        self.opdsUrlEditor.addItems(prefs['opds_url'])
        self.opdsUrlEditor.setEditable(True)
        self.opdsUrlEditor.setInsertPolicy(QComboBox.InsertAtTop)
        self.layout.addWidget(self.opdsUrlEditor, 0, 1, 1, 3)
        self.opdsUrlLabel.setBuddy(self.opdsUrlEditor)

        buttonColumnNumber = 7
        buttonColumnWidths = []
        self.about_button = QPushButton('About', self)
        self.about_button.setAutoDefault(False)
        self.about_button.clicked.connect(self.about)
        self.layout.addWidget(self.about_button, 0, buttonColumnNumber)
        buttonColumnWidths.append(self.layout.itemAtPosition(0, buttonColumnNumber).sizeHint().width()) 

        # Initially download the catalogs found in the root catalog of the URL
        # selected at startup.  Fail quietly on failing to open the URL
        catalogsTuple = self.model.downloadOpdsRootCatalog(self.gui, self.opdsUrlEditor.currentText(), False)
        print catalogsTuple
        firstCatalogTitle = catalogsTuple[0]
        self.currentOpdsCatalogs = catalogsTuple[1] # A dictionary of title->feedURL

        self.opdsCatalogSelectorLabel = QLabel('OPDS Catalog:')
        self.layout.addWidget(self.opdsCatalogSelectorLabel, 1, 0)
        labelColumnWidths.append(self.layout.itemAtPosition(1, 0).sizeHint().width())

        self.opdsCatalogSelector = QComboBox(self)
        self.opdsCatalogSelector.setEditable(False)
        self.opdsCatalogSelectorModel = QStringListModel(self.currentOpdsCatalogs.keys())
        self.opdsCatalogSelector.setModel(self.opdsCatalogSelectorModel)
        self.opdsCatalogSelector.setCurrentText(firstCatalogTitle)
        self.layout.addWidget(self.opdsCatalogSelector, 1, 1, 1, 3)

        self.download_opds_button = QPushButton('Download OPDS', self)
        self.download_opds_button.setAutoDefault(False)
        self.download_opds_button.clicked.connect(self.download_opds)
        self.layout.addWidget(self.download_opds_button, 1, buttonColumnNumber)
        buttonColumnWidths.append(self.layout.itemAtPosition(1, buttonColumnNumber).sizeHint().width()) 

        # Search GUI
        self.searchEditor = QLineEdit(self)
        self.searchEditor.returnPressed.connect(self.searchBookList)
        self.layout.addWidget(self.searchEditor, 2, buttonColumnNumber - 2, 1, 2)

        self.searchButton = QPushButton('Search', self)
        self.searchButton.setAutoDefault(False)
        self.searchButton.clicked.connect(self.searchBookList)
        self.layout.addWidget(self.searchButton, 2, buttonColumnNumber)
        buttonColumnWidths.append(self.layout.itemAtPosition(2, buttonColumnNumber).sizeHint().width())

        # The main book list
        self.library_view = QTableView(self)
        self.library_view.setAlternatingRowColors(True)
        self.library_view.setModel(self.searchproxymodel)
        self.library_view.horizontalHeader().setSectionResizeMode(0, QHeaderView.Stretch)
        self.library_view.horizontalHeader().setSectionResizeMode(1, QHeaderView.Stretch)
        self.library_view.horizontalHeader().setSectionResizeMode(2, QHeaderView.Stretch)
        self.library_view.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.resizeAllLibraryViewLinesToHeaderHeight()
        self.library_view.resizeColumnsToContents()
        self.layout.addWidget(self.library_view, 3, 0, 3, buttonColumnNumber + 1)

        self.hideNewsCheckbox = QCheckBox('Hide Newspapers', self)
        self.hideNewsCheckbox.clicked.connect(self.setHideNewspapers)
        self.hideNewsCheckbox.setChecked(prefs['hideNewspapers'])
        self.layout.addWidget(self.hideNewsCheckbox, 6, 0, 1, 3)

        self.hideBooksAlreadyInLibraryCheckbox = QCheckBox('Hide books already in library', self)
        self.hideBooksAlreadyInLibraryCheckbox.clicked.connect(self.setHideBooksAlreadyInLibrary)
        self.hideBooksAlreadyInLibraryCheckbox.setChecked(prefs['hideBooksAlreadyInLibrary'])
        self.layout.addWidget(self.hideBooksAlreadyInLibraryCheckbox, 7, 0, 1, 3)

        # Let the checkbox initial state control the filtering
        self.model.setFilterBooksThatAreNewspapers(self.hideNewsCheckbox.isChecked())
        self.model.setFilterBooksThatAreAlreadyInLibrary(self.hideBooksAlreadyInLibraryCheckbox.isChecked())

        self.downloadButton = QPushButton('Download selected books', self)
        self.downloadButton.setAutoDefault(False)
        self.downloadButton.clicked.connect(self.downloadSelectedBooks)
        self.layout.addWidget(self.downloadButton, 6, buttonColumnNumber)
        buttonColumnWidths.append(self.layout.itemAtPosition(6, buttonColumnNumber).sizeHint().width()) 

        self.fixTimestampButton = QPushButton('Fix timestamps of selection', self)
        self.fixTimestampButton.setAutoDefault(False)
        self.fixTimestampButton.clicked.connect(self.fixBookTimestamps)
        self.layout.addWidget(self.fixTimestampButton, 7, buttonColumnNumber)
        buttonColumnWidths.append(self.layout.itemAtPosition(7, buttonColumnNumber).sizeHint().width()) 

        # Make all columns of the grid layout the same width as the button column
        buttonColumnWidth = max(buttonColumnWidths)
        for columnNumber in range(0, buttonColumnNumber):
            self.layout.setColumnMinimumWidth(columnNumber, buttonColumnWidth)

        # Make sure the first column isn't wider than the labels it holds
        labelColumnWidth = max(labelColumnWidths)
        self.layout.setColumnMinimumWidth(0, labelColumnWidth)

        self.resize(self.sizeHint())

    def opdsUrlEditorActivated(self, text):
        prefs['opds_url'] = config.saveOpdsUrlCombobox(self.opdsUrlEditor)
        catalogsTuple = self.model.downloadOpdsRootCatalog(self.gui, self.opdsUrlEditor.currentText(), True)
        firstCatalogTitle = catalogsTuple[0]
        self.currentOpdsCatalogs = catalogsTuple[1] # A dictionary of title->feedURL
        self.opdsCatalogSelectorModel.setStringList(self.currentOpdsCatalogs.keys())
        self.opdsCatalogSelector.setCurrentText(firstCatalogTitle)

    def setHideNewspapers(self, checked):
        prefs['hideNewspapers'] = checked
        self.model.setFilterBooksThatAreNewspapers(checked)
        self.resizeAllLibraryViewLinesToHeaderHeight()

    def setHideBooksAlreadyInLibrary(self, checked):
        prefs['hideBooksAlreadyInLibrary'] = checked
        self.model.setFilterBooksThatAreAlreadyInLibrary(checked)
        self.resizeAllLibraryViewLinesToHeaderHeight()

    def searchBookList(self):
        searchString = self.searchEditor.text()
        print "starting book list search for: %s" % searchString
        self.searchproxymodel.setFilterFixedString(searchString)

    def about(self):
        text = get_resources('about.txt')
        QMessageBox.about(self, 'About the OPDS Client plugin', text.decode('utf-8'))

    def download_opds(self):
        opdsCatalogUrl = self.currentOpdsCatalogs.get(self.opdsCatalogSelector.currentText(), None)
        if opdsCatalogUrl is None:
            # Just give up quietly
            return
        self.model.downloadOpdsCatalog(self.gui, opdsCatalogUrl)
        if self.model.isCalibreOpdsServer():
            self.model.downloadMetadataUsingCalibreRestApi(self.opdsUrlEditor.currentText())
        self.library_view.horizontalHeader().setSectionResizeMode(0, QHeaderView.Stretch)
        self.library_view.horizontalHeader().setSectionResizeMode(1, QHeaderView.Stretch)
        self.library_view.horizontalHeader().setSectionResizeMode(2, QHeaderView.Stretch)
        self.resizeAllLibraryViewLinesToHeaderHeight()
        self.resize(self.sizeHint())

    def config(self):
        self.do_user_config(parent=self)

    def downloadSelectedBooks(self):
        selectionmodel = self.library_view.selectionModel()
        if selectionmodel.hasSelection():
            rows = selectionmodel.selectedRows()
            for row in reversed(rows):
                book = row.data(Qt.UserRole)
                self.downloadBook(book)

    def downloadBook(self, book):
        if len(book.links) > 0:
            self.gui.download_ebook(book.links[0])

    def fixBookTimestamps(self):
        selectionmodel = self.library_view.selectionModel()
        if selectionmodel.hasSelection():
            rows = selectionmodel.selectedRows()
            for row in reversed(rows):
                book = row.data(Qt.UserRole)
                self.fixBookTimestamp(book)

    def fixBookTimestamp(self, book):
        bookTimestamp = book.timestamp
        identicalBookIds = self.findIdenticalBooksForBooksWithMultipleAuthors(book)
        bookIdToValMap = {}
        for identicalBookId in identicalBookIds:
            bookIdToValMap[identicalBookId] = bookTimestamp
        if len(bookIdToValMap) < 1:
            print "Failed to set timestamp of book: %s" % book
        self.db.set_field('timestamp', bookIdToValMap)

    def findIdenticalBooksForBooksWithMultipleAuthors(self, book):
        authorsList = book.authors
        if len(authorsList) < 2:
            return self.db.find_identical_books(book)
        # Try matching the authors one by one
        identicalBookIds = set()
        for author in authorsList:
            singleAuthorBook = Metadata(book.title, [author])
            singleAuthorIdenticalBookIds = self.db.find_identical_books(singleAuthorBook)
            identicalBookIds = identicalBookIds.union(singleAuthorIdenticalBookIds)
        return identicalBookIds

    def dummy_books(self):
        dummy_author = ' ' * 40
        dummy_title = ' ' * 60
        books_list = []
        for line in range (1, 10):
            book = DynamicBook()
            book.author = dummy_author
            book.title = dummy_title
            book.updated = datetime.datetime.now().strftime('%Y-%m-%dT%H:%M:%S+00:00')
            book.id = ''
            books_list.append(book)
        return books_list

    def resizeAllLibraryViewLinesToHeaderHeight(self):
        rowHeight = self.library_view.horizontalHeader().height()
        for rowNumber in range (0, self.library_view.model().rowCount()):
            self.library_view.setRowHeight(rowNumber, rowHeight)