Ejemplo n.º 1
0
    def initialise (self, univers):
        model = QStandardItemModel (3,1)
    
        i = 0
        for faction in univers.faction_list :
            item = QStandardItem(faction.name)
            item.setFlags(QtCore.Qt.ItemIsUserCheckable | QtCore.Qt.ItemIsEnabled);
            item.setData(QtCore.Qt.Unchecked, QtCore.Qt.CheckStateRole);

            model.setItem(i, 0, item)
            i = i + 1

        combo = QComboBox()
        combo.setModel(model)

        list_v = QListView()
        list_v.setModel(model)

        table = QTableView()
        table.setModel(model)

        container = QWidget()
        containerLayout = QVBoxLayout()
        container.setLayout(containerLayout)
        containerLayout.addWidget(combo)
        containerLayout.addWidget(list_v)
        containerLayout.addWidget(table)
Ejemplo n.º 2
0
 def __init__(self, parent=None):
     QTableView.__init__(self, parent)
     self.rt_delegate = RichTextDelegate(self)
     self.setSelectionMode(self.SingleSelection)
     self.setAlternatingRowColors(True)
     self.setSelectionBehavior(self.SelectRows)
     self.setIconSize(QSize(24, 24))
     self.clicked.connect(self.show_details)
     self.doubleClicked.connect(self.select_index)
     self.setSortingEnabled(True)
Ejemplo n.º 3
0
 def __init__(self, model, parent=None):
     QTableView.__init__(self, parent)
     self.setSelectionBehavior(self.SelectRows), self.setSelectionMode(self.ExtendedSelection)
     self.setAlternatingRowColors(True)
     self.setSortingEnabled(True)
     self.proxy = p = ProxyModel(self)
     p.setSourceModel(model)
     self.setModel(p)
     self.doubleClicked.connect(self._double_clicked)
     self.setContextMenuPolicy(Qt.CustomContextMenu)
     self.customContextMenuRequested.connect(self.show_context_menu)
Ejemplo n.º 4
0
 def keyPressEvent(self, ev):
     if ev.key() in (Qt.Key_Enter, Qt.Key_Return) and self.state() != self.EditingState:
         ci = self.currentIndex()
         if ci.isValid() and ci.flags() & Qt.ItemIsEditable:
             if self.edit(ci, self.EditKeyPressed, ev):
                 ev.accept()
                 return
     return QTableView.keyPressEvent(self, ev)
Ejemplo n.º 5
0
 def viewportEvent(self, event):
     if (event.type() == event.ToolTip and not gprefs['book_list_tooltips']):
         return False
     try:
         ret = self.gesture_manager.handle_event(event)
     except AttributeError:
         ret = None
     if ret is not None:
         return ret
     return QTableView.viewportEvent(self, event)
Ejemplo n.º 6
0
 def keyPressEvent(self, ev):
     if ev.key() in (Qt.Key_Left, Qt.Key_Right):
         ac = self.MoveDown if ev.key() == Qt.Key_Right else self.MoveUp
         index = self.moveCursor(ac, ev.modifiers())
         if index.isValid() and index != self.currentIndex():
             m = self.selectionModel()
             m.select(index, m.Select|m.Current|m.Rows)
             self.setCurrentIndex(index)
             ev.accept()
             return
     return QTableView.keyPressEvent(self, ev)
Ejemplo n.º 7
0
 def moveCursor(self, action, modifiers):
     orig = self.currentIndex()
     index = QTableView.moveCursor(self, action, modifiers)
     if action == QTableView.MovePageDown:
         moved = index.row() - orig.row()
         try:
             rows = self.row_at_bottom() - self.row_at_top()
         except TypeError:
             rows = moved
         if moved > rows:
             index = self.model().index(orig.row() + rows, index.column())
     elif action == QTableView.MovePageUp:
         moved = orig.row() - index.row()
         try:
             rows = self.row_at_bottom() - self.row_at_top()
         except TypeError:
             rows = moved
         if moved > rows:
             index = self.model().index(orig.row() - rows, index.column())
     elif action == QTableView.MoveHome and modifiers & Qt.ControlModifier:
         return self.model().index(0, orig.column())
     elif action == QTableView.MoveEnd and modifiers & Qt.ControlModifier:
         return self.model().index(self.model().rowCount(QModelIndex()) - 1, orig.column())
     return index
Ejemplo n.º 8
0
    def __init__(self, parent, modelcls=BooksModel, use_edit_metadata_dialog=True):
        QTableView.__init__(self, parent)
        self.default_row_height = self.verticalHeader().defaultSectionSize()
        self.gui = parent
        self.setProperty('highlight_current_item', 150)
        self.row_sizing_done = False
        self.alternate_views = AlternateViews(self)

        if not tweaks['horizontal_scrolling_per_column']:
            self.setHorizontalScrollMode(self.ScrollPerPixel)

        self.setEditTriggers(self.EditKeyPressed)
        if tweaks['doubleclick_on_library_view'] == 'edit_cell':
            self.setEditTriggers(self.DoubleClicked|self.editTriggers())
        elif tweaks['doubleclick_on_library_view'] == 'open_viewer':
            self.setEditTriggers(self.SelectedClicked|self.editTriggers())
            self.doubleClicked.connect(parent.iactions['View'].view_triggered)
        elif tweaks['doubleclick_on_library_view'] == 'edit_metadata':
            # Must not enable single-click to edit, or the field will remain
            # open in edit mode underneath the edit metadata dialog
            if use_edit_metadata_dialog:
                self.doubleClicked.connect(
                        partial(parent.iactions['Edit Metadata'].edit_metadata,
                                checked=False))
            else:
                self.setEditTriggers(self.DoubleClicked|self.editTriggers())

        setup_dnd_interface(self)
        self.setAlternatingRowColors(True)
        self.setShowGrid(False)
        self.setWordWrap(False)

        self.rating_delegate = RatingDelegate(self)
        self.timestamp_delegate = DateDelegate(self)
        self.pubdate_delegate = PubDateDelegate(self)
        self.last_modified_delegate = DateDelegate(self,
                tweak_name='gui_last_modified_display_format')
        self.languages_delegate = LanguagesDelegate(self)
        self.tags_delegate = CompleteDelegate(self, ',', 'all_tag_names')
        self.authors_delegate = CompleteDelegate(self, '&', 'all_author_names', True)
        self.cc_names_delegate = CompleteDelegate(self, '&', 'all_custom', True)
        self.series_delegate = TextDelegate(self)
        self.publisher_delegate = TextDelegate(self)
        self.text_delegate = TextDelegate(self)
        self.cc_text_delegate = CcTextDelegate(self)
        self.cc_enum_delegate = CcEnumDelegate(self)
        self.cc_bool_delegate = CcBoolDelegate(self)
        self.cc_comments_delegate = CcCommentsDelegate(self)
        self.cc_template_delegate = CcTemplateDelegate(self)
        self.cc_number_delegate = CcNumberDelegate(self)
        self.display_parent = parent
        self._model = modelcls(self)
        self.setModel(self._model)
        self._model.count_changed_signal.connect(self.do_row_sizing,
                                                 type=Qt.QueuedConnection)
        self.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.setSortingEnabled(True)
        self.selectionModel().currentRowChanged.connect(self._model.current_changed)
        self.preserve_state = partial(PreserveViewState, self)
        self.marked_changed_listener = FunctionDispatcher(self.marked_changed)

        # {{{ Column Header setup
        self.can_add_columns = True
        self.was_restored = False
        self.column_header = HeaderView(Qt.Horizontal, self)
        self.setHorizontalHeader(self.column_header)
        self.column_header.sortIndicatorChanged.disconnect()
        self.column_header.sortIndicatorChanged.connect(self.user_sort_requested)
        self.column_header.setSectionsMovable(True)
        self.column_header.setSectionsClickable(True)
        self.column_header.sectionMoved.connect(self.save_state)
        self.column_header.setContextMenuPolicy(Qt.CustomContextMenu)
        self.column_header.customContextMenuRequested.connect(self.show_column_header_context_menu)
        self.column_header.sectionResized.connect(self.column_resized, Qt.QueuedConnection)
        self.row_header = HeaderView(Qt.Vertical, self)
        self.row_header.setSectionResizeMode(self.row_header.Fixed)
        self.setVerticalHeader(self.row_header)
        # }}}

        self._model.database_changed.connect(self.database_changed)
        hv = self.verticalHeader()
        hv.setSectionsClickable(True)
        hv.setCursor(Qt.PointingHandCursor)
        self.selected_ids = []
        self._model.about_to_be_sorted.connect(self.about_to_be_sorted)
        self._model.sorting_done.connect(self.sorting_done,
                type=Qt.QueuedConnection)
Ejemplo n.º 9
0
 def keyPressEvent(self, ev):
     if handle_enter_press(self, ev):
         return
     return QTableView.keyPressEvent(self, ev)
Ejemplo n.º 10
0
 def resizeColumnsToContents(self):
     QTableView.resizeColumnsToContents(self)
     self.columns_resized = True
Ejemplo n.º 11
0
 def __init__(self, books_view, parent=None):
     QTableView.__init__(self, parent)
     self.books_view = books_view
     self.verticalHeader().close()
     self.splitter = None
     self.disable_save_state = False
Ejemplo n.º 12
0
    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())
Ejemplo n.º 13
0
Archivo: views.py Proyecto: kba/calibre
 def scrollContentsBy(self, dx, dy):
     # Needed as Qt bug causes headerview to not always update when scrolling
     QTableView.scrollContentsBy(self, dx, dy)
     if dy != 0:
         self.column_header.update()
Ejemplo n.º 14
0
 def viewportEvent(self, event):
     if (event.type() == event.ToolTip
             and not gprefs['book_list_tooltips']):
         return False
     return QTableView.viewportEvent(self, event)
Ejemplo n.º 15
0
    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())
Ejemplo n.º 16
0
class PluginUpdaterDialog(SizePersistedDialog):

    initial_extra_size = QSize(350, 100)
    forum_label_text = _('Plugin homepage')

    def __init__(self, gui, initial_filter=FILTER_UPDATE_AVAILABLE):
        SizePersistedDialog.__init__(
            self, gui, 'Plugin Updater plugin:plugin updater dialog')
        self.gui = gui
        self.forum_link = None
        self.zip_url = None
        self.model = None
        self.do_restart = False
        self._initialize_controls()
        self._create_context_menu()

        try:
            display_plugins = read_available_plugins(raise_error=True)
        except Exception:
            display_plugins = []
            import traceback
            error_dialog(self.gui,
                         _('Update Check Failed'),
                         _('Unable to reach the plugin index page.'),
                         det_msg=traceback.format_exc(),
                         show=True)

        if display_plugins:
            self.model = DisplayPluginModel(display_plugins)
            self.proxy_model = DisplayPluginSortFilterModel(self)
            self.proxy_model.setSourceModel(self.model)
            self.plugin_view.setModel(self.proxy_model)
            self.plugin_view.resizeColumnsToContents()
            self.plugin_view.selectionModel().currentRowChanged.connect(
                self._plugin_current_changed)
            self.plugin_view.doubleClicked.connect(self.install_button.click)
            self.filter_combo.setCurrentIndex(initial_filter)
            self._select_and_focus_view()
        else:
            self.filter_combo.setEnabled(False)
        # Cause our dialog size to be restored from prefs or created on first usage
        self.resize_dialog()

    def _initialize_controls(self):
        self.setWindowTitle(_('User plugins'))
        self.setWindowIcon(QIcon(I('plugins/plugin_updater.png')))
        layout = QVBoxLayout(self)
        self.setLayout(layout)
        title_layout = ImageTitleLayout(self, 'plugins/plugin_updater.png',
                                        _('User plugins'))
        layout.addLayout(title_layout)

        header_layout = QHBoxLayout()
        layout.addLayout(header_layout)
        self.filter_combo = PluginFilterComboBox(self)
        self.filter_combo.setMinimumContentsLength(20)
        self.filter_combo.currentIndexChanged[int].connect(
            self._filter_combo_changed)
        la = QLabel(_('Filter list of &plugins') + ':', self)
        la.setBuddy(self.filter_combo)
        header_layout.addWidget(la)
        header_layout.addWidget(self.filter_combo)
        header_layout.addStretch(10)

        # filter plugins by name
        la = QLabel(_('Filter by &name') + ':', self)
        header_layout.addWidget(la)
        self.filter_by_name_lineedit = QLineEdit(self)
        la.setBuddy(self.filter_by_name_lineedit)
        self.filter_by_name_lineedit.setText("")
        self.filter_by_name_lineedit.textChanged.connect(
            self._filter_name_lineedit_changed)

        header_layout.addWidget(self.filter_by_name_lineedit)

        self.plugin_view = QTableView(self)
        self.plugin_view.horizontalHeader().setStretchLastSection(True)
        self.plugin_view.setSelectionBehavior(
            QAbstractItemView.SelectionBehavior.SelectRows)
        self.plugin_view.setSelectionMode(
            QAbstractItemView.SelectionMode.SingleSelection)
        self.plugin_view.setAlternatingRowColors(True)
        self.plugin_view.setSortingEnabled(True)
        self.plugin_view.setIconSize(QSize(28, 28))
        layout.addWidget(self.plugin_view)

        details_layout = QHBoxLayout()
        layout.addLayout(details_layout)
        forum_label = self.forum_label = QLabel('')
        forum_label.setTextInteractionFlags(
            Qt.TextInteractionFlag.LinksAccessibleByMouse
            | Qt.TextInteractionFlag.LinksAccessibleByKeyboard)
        forum_label.linkActivated.connect(self._forum_label_activated)
        details_layout.addWidget(QLabel(_('Description') + ':', self), 0,
                                 Qt.AlignmentFlag.AlignLeft)
        details_layout.addWidget(forum_label, 1, Qt.AlignmentFlag.AlignRight)

        self.description = QLabel(self)
        self.description.setFrameStyle(QFrame.Shape.Panel
                                       | QFrame.Shadow.Sunken)
        self.description.setAlignment(Qt.AlignmentFlag.AlignTop
                                      | Qt.AlignmentFlag.AlignLeft)
        self.description.setMinimumHeight(40)
        self.description.setWordWrap(True)
        layout.addWidget(self.description)

        self.button_box = QDialogButtonBox(
            QDialogButtonBox.StandardButton.Close)
        self.button_box.rejected.connect(self.reject)
        self.finished.connect(self._finished)
        self.install_button = self.button_box.addButton(
            _('&Install'), QDialogButtonBox.ButtonRole.AcceptRole)
        self.install_button.setToolTip(_('Install the selected plugin'))
        self.install_button.clicked.connect(self._install_clicked)
        self.install_button.setEnabled(False)
        self.configure_button = self.button_box.addButton(
            ' ' + _('&Customize plugin ') + ' ',
            QDialogButtonBox.ButtonRole.ResetRole)
        self.configure_button.setToolTip(
            _('Customize the options for this plugin'))
        self.configure_button.clicked.connect(self._configure_clicked)
        self.configure_button.setEnabled(False)
        layout.addWidget(self.button_box)

    def update_forum_label(self):
        txt = ''
        if self.forum_link:
            txt = '<a href="%s">%s</a>' % (self.forum_link,
                                           self.forum_label_text)
        self.forum_label.setText(txt)

    def _create_context_menu(self):
        self.plugin_view.setContextMenuPolicy(
            Qt.ContextMenuPolicy.ActionsContextMenu)
        self.install_action = QAction(
            QIcon(I('plugins/plugin_upgrade_ok.png')), _('&Install'), self)
        self.install_action.setToolTip(_('Install the selected plugin'))
        self.install_action.triggered.connect(self._install_clicked)
        self.install_action.setEnabled(False)
        self.plugin_view.addAction(self.install_action)
        self.forum_action = QAction(QIcon(I('plugins/mobileread.png')),
                                    _('Plugin &forum thread'), self)
        self.forum_action.triggered.connect(self._forum_label_activated)
        self.forum_action.setEnabled(False)
        self.plugin_view.addAction(self.forum_action)

        sep1 = QAction(self)
        sep1.setSeparator(True)
        self.plugin_view.addAction(sep1)

        self.toggle_enabled_action = QAction(_('Enable/&disable plugin'), self)
        self.toggle_enabled_action.setToolTip(
            _('Enable or disable this plugin'))
        self.toggle_enabled_action.triggered.connect(
            self._toggle_enabled_clicked)
        self.toggle_enabled_action.setEnabled(False)
        self.plugin_view.addAction(self.toggle_enabled_action)
        self.uninstall_action = QAction(_('&Remove plugin'), self)
        self.uninstall_action.setToolTip(_('Uninstall the selected plugin'))
        self.uninstall_action.triggered.connect(self._uninstall_clicked)
        self.uninstall_action.setEnabled(False)
        self.plugin_view.addAction(self.uninstall_action)

        sep2 = QAction(self)
        sep2.setSeparator(True)
        self.plugin_view.addAction(sep2)

        self.donate_enabled_action = QAction(QIcon(I('donate.png')),
                                             _('Donate to developer'), self)
        self.donate_enabled_action.setToolTip(
            _('Donate to the developer of this plugin'))
        self.donate_enabled_action.triggered.connect(self._donate_clicked)
        self.donate_enabled_action.setEnabled(False)
        self.plugin_view.addAction(self.donate_enabled_action)

        sep3 = QAction(self)
        sep3.setSeparator(True)
        self.plugin_view.addAction(sep3)

        self.configure_action = QAction(QIcon(I('config.png')),
                                        _('&Customize plugin'), self)
        self.configure_action.setToolTip(
            _('Customize the options for this plugin'))
        self.configure_action.triggered.connect(self._configure_clicked)
        self.configure_action.setEnabled(False)
        self.plugin_view.addAction(self.configure_action)

    def _finished(self, *args):
        if self.model:
            update_plugins = list(
                filter(filter_upgradeable_plugins, self.model.display_plugins))
            self.gui.recalc_update_label(len(update_plugins))

    def _plugin_current_changed(self, current, previous):
        if current.isValid():
            actual_idx = self.proxy_model.mapToSource(current)
            display_plugin = self.model.display_plugins[actual_idx.row()]
            self.description.setText(display_plugin.description)
            self.forum_link = display_plugin.forum_link
            self.zip_url = display_plugin.zip_url
            self.forum_action.setEnabled(bool(self.forum_link))
            self.install_button.setEnabled(
                display_plugin.is_valid_to_install())
            self.install_action.setEnabled(self.install_button.isEnabled())
            self.uninstall_action.setEnabled(display_plugin.is_installed())
            self.configure_button.setEnabled(display_plugin.is_installed())
            self.configure_action.setEnabled(self.configure_button.isEnabled())
            self.toggle_enabled_action.setEnabled(
                display_plugin.is_installed())
            self.donate_enabled_action.setEnabled(
                bool(display_plugin.donation_link))
        else:
            self.description.setText('')
            self.forum_link = None
            self.zip_url = None
            self.forum_action.setEnabled(False)
            self.install_button.setEnabled(False)
            self.install_action.setEnabled(False)
            self.uninstall_action.setEnabled(False)
            self.configure_button.setEnabled(False)
            self.configure_action.setEnabled(False)
            self.toggle_enabled_action.setEnabled(False)
            self.donate_enabled_action.setEnabled(False)
        self.update_forum_label()

    def _donate_clicked(self):
        plugin = self._selected_display_plugin()
        if plugin and plugin.donation_link:
            open_url(QUrl(plugin.donation_link))

    def _select_and_focus_view(self, change_selection=True):
        if change_selection and self.plugin_view.model().rowCount() > 0:
            self.plugin_view.selectRow(0)
        else:
            idx = self.plugin_view.selectionModel().currentIndex()
            self._plugin_current_changed(idx, 0)
        self.plugin_view.setFocus()

    def _filter_combo_changed(self, idx):
        self.filter_by_name_lineedit.setText(
            ""
        )  # clear the name filter text when a different group was selected
        self.proxy_model.set_filter_criteria(idx)
        if idx == FILTER_NOT_INSTALLED:
            self.plugin_view.sortByColumn(5, Qt.SortOrder.DescendingOrder)
        else:
            self.plugin_view.sortByColumn(0, Qt.SortOrder.AscendingOrder)
        self._select_and_focus_view()

    def _filter_name_lineedit_changed(self, text):
        self.proxy_model.set_filter_text(
            text)  # set the filter text for filterAcceptsRow

    def _forum_label_activated(self):
        if self.forum_link:
            open_url(QUrl(self.forum_link))

    def _selected_display_plugin(self):
        idx = self.plugin_view.selectionModel().currentIndex()
        actual_idx = self.proxy_model.mapToSource(idx)
        return self.model.display_plugins[actual_idx.row()]

    def _uninstall_plugin(self, name_to_remove):
        if DEBUG:
            prints('Removing plugin: ', name_to_remove)
        remove_plugin(name_to_remove)
        # Make sure that any other plugins that required this plugin
        # to be uninstalled first have the requirement removed
        for display_plugin in self.model.display_plugins:
            # Make sure we update the status and display of the
            # plugin we just uninstalled
            if name_to_remove in display_plugin.uninstall_plugins:
                if DEBUG:
                    prints('Removing uninstall dependency for: ',
                           display_plugin.name)
                display_plugin.uninstall_plugins.remove(name_to_remove)
            if display_plugin.qname == name_to_remove:
                if DEBUG:
                    prints('Resetting plugin to uninstalled status: ',
                           display_plugin.name)
                display_plugin.installed_version = None
                display_plugin.plugin = None
                display_plugin.uninstall_plugins = []
                if self.proxy_model.filter_criteria not in [
                        FILTER_INSTALLED, FILTER_UPDATE_AVAILABLE
                ]:
                    self.model.refresh_plugin(display_plugin)

    def _uninstall_clicked(self):
        display_plugin = self._selected_display_plugin()
        if not question_dialog(
                self,
                _('Are you sure?'),
                '<p>' +
                _('Are you sure you want to uninstall the <b>%s</b> plugin?') %
                display_plugin.name,
                show_copy_button=False):
            return
        self._uninstall_plugin(display_plugin.qname)
        if self.proxy_model.filter_criteria in [
                FILTER_INSTALLED, FILTER_UPDATE_AVAILABLE
        ]:
            self.model.beginResetModel(), self.model.endResetModel()
            self._select_and_focus_view()
        else:
            self._select_and_focus_view(change_selection=False)

    def _install_clicked(self):
        display_plugin = self._selected_display_plugin()
        if not question_dialog(
                self,
                _('Install %s') % display_plugin.name,
                '<p>' +
                _('Installing plugins is a <b>security risk</b>. '
                  'Plugins can contain a virus/malware. '
                  'Only install it if you got it from a trusted source.'
                  ' Are you sure you want to proceed?'),
                show_copy_button=False):
            return

        if display_plugin.uninstall_plugins:
            uninstall_names = list(display_plugin.uninstall_plugins)
            if DEBUG:
                prints('Uninstalling plugin: ', ', '.join(uninstall_names))
            for name_to_remove in uninstall_names:
                self._uninstall_plugin(name_to_remove)

        plugin_zip_url = display_plugin.zip_url
        if DEBUG:
            prints('Downloading plugin ZIP attachment: ', plugin_zip_url)
        self.gui.status_bar.showMessage(
            _('Downloading plugin ZIP attachment: %s') % plugin_zip_url)
        zip_path = self._download_zip(plugin_zip_url)

        if DEBUG:
            prints('Installing plugin: ', zip_path)
        self.gui.status_bar.showMessage(_('Installing plugin: %s') % zip_path)

        do_restart = False
        try:
            from calibre.customize.ui import config
            installed_plugins = frozenset(config['plugins'])
            try:
                plugin = add_plugin(zip_path)
            except NameConflict as e:
                return error_dialog(self.gui,
                                    _('Already exists'),
                                    unicode_type(e),
                                    show=True)
            # Check for any toolbars to add to.
            widget = ConfigWidget(self.gui)
            widget.gui = self.gui
            widget.check_for_add_to_toolbars(plugin,
                                             previously_installed=plugin.name
                                             in installed_plugins)
            self.gui.status_bar.showMessage(
                _('Plugin installed: %s') % display_plugin.name)
            d = info_dialog(
                self.gui,
                _('Success'),
                _('Plugin <b>{0}</b> successfully installed under <b>'
                  ' {1} plugins</b>. You may have to restart calibre '
                  'for the plugin to take effect.').format(
                      plugin.name, plugin.type),
                show_copy_button=False)
            b = d.bb.addButton(_('&Restart calibre now'),
                               QDialogButtonBox.ButtonRole.AcceptRole)
            b.setIcon(QIcon(I('lt.png')))
            d.do_restart = False

            def rf():
                d.do_restart = True

            b.clicked.connect(rf)
            d.set_details('')
            d.exec_()
            b.clicked.disconnect()
            do_restart = d.do_restart

            display_plugin.plugin = plugin
            # We cannot read the 'actual' version information as the plugin will not be loaded yet
            display_plugin.installed_version = display_plugin.available_version
        except:
            if DEBUG:
                prints('ERROR occurred while installing plugin: %s' %
                       display_plugin.name)
                traceback.print_exc()
            error_dialog(
                self.gui,
                _('Install plugin failed'),
                _('A problem occurred while installing this plugin.'
                  ' This plugin will now be uninstalled.'
                  ' Please post the error message in details below into'
                  ' the forum thread for this plugin and restart calibre.'),
                det_msg=traceback.format_exc(),
                show=True)
            if DEBUG:
                prints('Due to error now uninstalling plugin: %s' %
                       display_plugin.name)
            remove_plugin(display_plugin.name)
            display_plugin.plugin = None

        display_plugin.uninstall_plugins = []
        if self.proxy_model.filter_criteria in [
                FILTER_NOT_INSTALLED, FILTER_UPDATE_AVAILABLE
        ]:
            self.model.beginResetModel(), self.model.endResetModel()
            self._select_and_focus_view()
        else:
            self.model.refresh_plugin(display_plugin)
            self._select_and_focus_view(change_selection=False)
        if do_restart:
            self.do_restart = True
            self.accept()

    def _configure_clicked(self):
        display_plugin = self._selected_display_plugin()
        plugin = display_plugin.plugin
        if not plugin.is_customizable():
            return info_dialog(self,
                               _('Plugin not customizable'),
                               _('Plugin: %s does not need customization') %
                               plugin.name,
                               show=True)
        from calibre.customize import InterfaceActionBase
        if isinstance(plugin, InterfaceActionBase) and not getattr(
                plugin, 'actual_iaction_plugin_loaded', False):
            return error_dialog(self,
                                _('Must restart'),
                                _('You must restart calibre before you can'
                                  ' configure the <b>%s</b> plugin') %
                                plugin.name,
                                show=True)
        plugin.do_user_config(self.parent())

    def _toggle_enabled_clicked(self):
        display_plugin = self._selected_display_plugin()
        plugin = display_plugin.plugin
        if not plugin.can_be_disabled:
            return error_dialog(self,
                                _('Plugin cannot be disabled'),
                                _('The plugin: %s cannot be disabled') %
                                plugin.name,
                                show=True)
        if is_disabled(plugin):
            enable_plugin(plugin)
        else:
            disable_plugin(plugin)
        self.model.refresh_plugin(display_plugin)

    def _download_zip(self, plugin_zip_url):
        from calibre.ptempfile import PersistentTemporaryFile
        raw = get_https_resource_securely(
            plugin_zip_url,
            headers={'User-Agent': '%s %s' % (__appname__, __version__)})
        with PersistentTemporaryFile('.zip') as pt:
            pt.write(raw)
        return pt.name
Ejemplo n.º 17
0
    def __init__(self, parent, book_list, get_annotations_as_HTML, source):
        self.opts = parent.opts
        self.parent = parent
        self.get_annotations_as_HTML = get_annotations_as_HTML
        self.show_confidence_colors = self.opts.prefs.get(
            'annotated_books_dialog_show_confidence_as_bg_color', True)
        self.source = source

        #         QDialog.__init__(self, parent=self.opts.gui)
        SizePersistedDialog.__init__(
            self, self.opts.gui,
            'Annotations plugin:import annotations dialog')
        self.setWindowTitle(_('Import Annotations'))
        self.setWindowIcon(self.opts.icon)
        self.l = QVBoxLayout(self)
        self.setLayout(self.l)
        self.perfect_width = 0

        from calibre_plugins.annotations.appearance import default_timestamp
        friendly_timestamp_format = plugin_prefs.get(
            'appearance_timestamp_format', default_timestamp)

        # Are we collecting News clippings?
        collect_news_clippings = self.opts.prefs.get(
            'cfg_news_clippings_checkbox', False)
        news_clippings_destination = self.opts.prefs.get(
            'cfg_news_clippings_lineEdit', None)

        # Populate the table data
        self.tabledata = []
        for book_data in book_list:
            enabled = QCheckBox()
            enabled.setChecked(True)

            # last_annotation sorts by timestamp
            last_annotation = SortableTableWidgetItem(
                strftime(friendly_timestamp_format,
                         localtime(book_data['last_update'])),
                book_data['last_update'])

            # reader_app sorts case-insensitive
            reader_app = SortableTableWidgetItem(
                book_data['reader_app'], book_data['reader_app'].upper())

            # title, author sort by title_sort, author_sort
            if not book_data['title_sort']:
                book_data['title_sort'] = book_data['title']
            title = SortableTableWidgetItem(book_data['title'],
                                            book_data['title_sort'].upper())

            if not book_data['author_sort']:
                book_data['author_sort'] = book_data['author']
            author = SortableTableWidgetItem(book_data['author'],
                                             book_data['author_sort'].upper())

            genres = book_data['genre'].split(', ')
            if 'News' in genres and collect_news_clippings:
                cid = get_clippings_cid(self, news_clippings_destination)
                confidence = 5
            else:
                cid, confidence = parent.generate_confidence(book_data)

            # List order matches self.annotations_header
            this_book = [
                book_data['uuid'], book_data['book_id'], book_data['genre'],
                enabled, reader_app, title, author, last_annotation,
                book_data['annotations'], confidence
            ]
            self.tabledata.append(this_book)

        self.tv = QTableView(self)
        self.l.addWidget(self.tv)
        self.annotations_header = [
            'uuid', 'book_id', 'genre', '',
            _('Reader App'),
            _('Title'),
            _('Author'),
            _('Last Annotation'),
            _('Annotations'),
            _('Confidence')
        ]
        self.ENABLED_COL = 3
        self.READER_APP_COL = 4
        self.TITLE_COL = 5
        self.AUTHOR_COL = 6
        self.LAST_ANNOTATION_COL = 7
        self.CONFIDENCE_COL = 9
        columns_to_center = [8]
        self.tm = MarkupTableModel(self, columns_to_center=columns_to_center)
        self.tv.setModel(self.tm)
        self.tv.setShowGrid(False)
        self.tv.setFont(self.FONT)
        self.tvSelectionModel = self.tv.selectionModel()
        self.tv.setAlternatingRowColors(not self.show_confidence_colors)
        self.tv.setShowGrid(False)
        self.tv.setWordWrap(False)
        self.tv.setSelectionBehavior(self.tv.SelectRows)

        # Connect signals
        self.tv.doubleClicked.connect(self.getTableRowDoubleClick)
        self.tv.horizontalHeader().sectionClicked.connect(
            self.capture_sort_column)

        # Hide the vertical self.header
        self.tv.verticalHeader().setVisible(False)

        # Hide uuid, book_id, genre, confidence
        self.tv.hideColumn(self.annotations_header.index('uuid'))
        self.tv.hideColumn(self.annotations_header.index('book_id'))
        self.tv.hideColumn(self.annotations_header.index('genre'))
        #         self.tv.hideColumn(self.annotations_header.index(_('Confidence')))
        self.tv.hideColumn(self.CONFIDENCE_COL)

        # Set horizontal self.header props
        self.tv.horizontalHeader().setStretchLastSection(True)

        narrow_columns = [
            _('Last Annotation'),
            _('Reader App'),
            _('Annotations')
        ]
        extra_width = 10
        breathing_space = 20

        # Set column width to fit contents
        self.tv.resizeColumnsToContents()
        perfect_width = 10 + (len(narrow_columns) * extra_width)
        for i in range(3, 8):
            perfect_width += self.tv.columnWidth(i) + breathing_space
        self.tv.setMinimumSize(perfect_width, 100)
        self.perfect_width = perfect_width

        # Add some width to narrow columns
        for nc in narrow_columns:
            cw = self.tv.columnWidth(self.annotations_header.index(nc))
            self.tv.setColumnWidth(self.annotations_header.index(nc),
                                   cw + extra_width)

        # Set row height
        fm = QFontMetrics(self.FONT)
        nrows = len(self.tabledata)
        for row in xrange(nrows):
            self.tv.setRowHeight(row, fm.height() + 4)

        self.tv.setSortingEnabled(True)
        sort_column = self.opts.prefs.get('annotated_books_dialog_sort_column',
                                          self.CONFIDENCE_COL)
        sort_order = self.opts.prefs.get('annotated_books_dialog_sort_order',
                                         Qt.DescendingOrder)
        self.tv.sortByColumn(sort_column, sort_order)

        # ~~~~~~~~ Create the ButtonBox ~~~~~~~~
        self.dialogButtonBox = QDialogButtonBox(QDialogButtonBox.Cancel
                                                | QDialogButtonBox.Help)
        self.dialogButtonBox.setOrientation(Qt.Horizontal)
        self.import_button = self.dialogButtonBox.addButton(
            self.dialogButtonBox.Ok)
        self.import_button.setText(_('Import Annotations'))

        # Action buttons
        self.toggle_checkmarks_button = self.dialogButtonBox.addButton(
            _('Clear All'), QDialogButtonBox.ActionRole)
        self.toggle_checkmarks_button.setObjectName('toggle_checkmarks_button')

        scb_text = _('Show match status')
        if self.show_confidence_colors:
            scb_text = _("Hide match status")
        self.show_confidence_button = self.dialogButtonBox.addButton(
            scb_text, QDialogButtonBox.ActionRole)
        self.show_confidence_button.setObjectName('confidence_button')
        if self.show_confidence_colors:
            self.show_confidence_button.setIcon(
                get_icon('images/matches_hide.png'))
        else:
            self.show_confidence_button.setIcon(
                get_icon('images/matches_show.png'))

        self.preview_button = self.dialogButtonBox.addButton(
            _('Preview'), QDialogButtonBox.ActionRole)
        self.preview_button.setObjectName('preview_button')

        self.dialogButtonBox.clicked.connect(
            self.show_annotated_books_dialog_clicked)
        self.l.addWidget(self.dialogButtonBox)

        # Cause our dialog size to be restored from prefs or created on first usage
        self.resize_dialog()
Ejemplo n.º 18
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)
Ejemplo n.º 19
0
class AnnotatedBooksDialog(SizePersistedDialog):
    '''
    This dialog is shown when the user fetches or imports books
    self.fetch_single_annotations controls checkmark display, behavior of fetch button
    '''
    if isosx:
        FONT = QFont('Monaco', 11)
    elif iswindows:
        FONT = QFont('Lucida Console', 9)
    elif islinux:
        FONT = QFont('Monospace', 9)
        FONT.setStyleHint(QFont.TypeWriter)

    def __init__(self, parent, book_list, get_annotations_as_HTML, source):
        self.opts = parent.opts
        self.parent = parent
        self.get_annotations_as_HTML = get_annotations_as_HTML
        self.show_confidence_colors = self.opts.prefs.get(
            'annotated_books_dialog_show_confidence_as_bg_color', True)
        self.source = source

        #         QDialog.__init__(self, parent=self.opts.gui)
        SizePersistedDialog.__init__(
            self, self.opts.gui,
            'Annotations plugin:import annotations dialog')
        self.setWindowTitle(_('Import Annotations'))
        self.setWindowIcon(self.opts.icon)
        self.l = QVBoxLayout(self)
        self.setLayout(self.l)
        self.perfect_width = 0

        from calibre_plugins.annotations.appearance import default_timestamp
        friendly_timestamp_format = plugin_prefs.get(
            'appearance_timestamp_format', default_timestamp)

        # Are we collecting News clippings?
        collect_news_clippings = self.opts.prefs.get(
            'cfg_news_clippings_checkbox', False)
        news_clippings_destination = self.opts.prefs.get(
            'cfg_news_clippings_lineEdit', None)

        # Populate the table data
        self.tabledata = []
        for book_data in book_list:
            enabled = QCheckBox()
            enabled.setChecked(True)

            # last_annotation sorts by timestamp
            last_annotation = SortableTableWidgetItem(
                strftime(friendly_timestamp_format,
                         localtime(book_data['last_update'])),
                book_data['last_update'])

            # reader_app sorts case-insensitive
            reader_app = SortableTableWidgetItem(
                book_data['reader_app'], book_data['reader_app'].upper())

            # title, author sort by title_sort, author_sort
            if not book_data['title_sort']:
                book_data['title_sort'] = book_data['title']
            title = SortableTableWidgetItem(book_data['title'],
                                            book_data['title_sort'].upper())

            if not book_data['author_sort']:
                book_data['author_sort'] = book_data['author']
            author = SortableTableWidgetItem(book_data['author'],
                                             book_data['author_sort'].upper())

            genres = book_data['genre'].split(', ')
            if 'News' in genres and collect_news_clippings:
                cid = get_clippings_cid(self, news_clippings_destination)
                confidence = 5
            else:
                cid, confidence = parent.generate_confidence(book_data)

            # List order matches self.annotations_header
            this_book = [
                book_data['uuid'], book_data['book_id'], book_data['genre'],
                enabled, reader_app, title, author, last_annotation,
                book_data['annotations'], confidence
            ]
            self.tabledata.append(this_book)

        self.tv = QTableView(self)
        self.l.addWidget(self.tv)
        self.annotations_header = [
            'uuid', 'book_id', 'genre', '',
            _('Reader App'),
            _('Title'),
            _('Author'),
            _('Last Annotation'),
            _('Annotations'),
            _('Confidence')
        ]
        self.ENABLED_COL = 3
        self.READER_APP_COL = 4
        self.TITLE_COL = 5
        self.AUTHOR_COL = 6
        self.LAST_ANNOTATION_COL = 7
        self.CONFIDENCE_COL = 9
        columns_to_center = [8]
        self.tm = MarkupTableModel(self, columns_to_center=columns_to_center)
        self.tv.setModel(self.tm)
        self.tv.setShowGrid(False)
        self.tv.setFont(self.FONT)
        self.tvSelectionModel = self.tv.selectionModel()
        self.tv.setAlternatingRowColors(not self.show_confidence_colors)
        self.tv.setShowGrid(False)
        self.tv.setWordWrap(False)
        self.tv.setSelectionBehavior(self.tv.SelectRows)

        # Connect signals
        self.tv.doubleClicked.connect(self.getTableRowDoubleClick)
        self.tv.horizontalHeader().sectionClicked.connect(
            self.capture_sort_column)

        # Hide the vertical self.header
        self.tv.verticalHeader().setVisible(False)

        # Hide uuid, book_id, genre, confidence
        self.tv.hideColumn(self.annotations_header.index('uuid'))
        self.tv.hideColumn(self.annotations_header.index('book_id'))
        self.tv.hideColumn(self.annotations_header.index('genre'))
        #         self.tv.hideColumn(self.annotations_header.index(_('Confidence')))
        self.tv.hideColumn(self.CONFIDENCE_COL)

        # Set horizontal self.header props
        self.tv.horizontalHeader().setStretchLastSection(True)

        narrow_columns = [
            _('Last Annotation'),
            _('Reader App'),
            _('Annotations')
        ]
        extra_width = 10
        breathing_space = 20

        # Set column width to fit contents
        self.tv.resizeColumnsToContents()
        perfect_width = 10 + (len(narrow_columns) * extra_width)
        for i in range(3, 8):
            perfect_width += self.tv.columnWidth(i) + breathing_space
        self.tv.setMinimumSize(perfect_width, 100)
        self.perfect_width = perfect_width

        # Add some width to narrow columns
        for nc in narrow_columns:
            cw = self.tv.columnWidth(self.annotations_header.index(nc))
            self.tv.setColumnWidth(self.annotations_header.index(nc),
                                   cw + extra_width)

        # Set row height
        fm = QFontMetrics(self.FONT)
        nrows = len(self.tabledata)
        for row in xrange(nrows):
            self.tv.setRowHeight(row, fm.height() + 4)

        self.tv.setSortingEnabled(True)
        sort_column = self.opts.prefs.get('annotated_books_dialog_sort_column',
                                          self.CONFIDENCE_COL)
        sort_order = self.opts.prefs.get('annotated_books_dialog_sort_order',
                                         Qt.DescendingOrder)
        self.tv.sortByColumn(sort_column, sort_order)

        # ~~~~~~~~ Create the ButtonBox ~~~~~~~~
        self.dialogButtonBox = QDialogButtonBox(QDialogButtonBox.Cancel
                                                | QDialogButtonBox.Help)
        self.dialogButtonBox.setOrientation(Qt.Horizontal)
        self.import_button = self.dialogButtonBox.addButton(
            self.dialogButtonBox.Ok)
        self.import_button.setText(_('Import Annotations'))

        # Action buttons
        self.toggle_checkmarks_button = self.dialogButtonBox.addButton(
            _('Clear All'), QDialogButtonBox.ActionRole)
        self.toggle_checkmarks_button.setObjectName('toggle_checkmarks_button')

        scb_text = _('Show match status')
        if self.show_confidence_colors:
            scb_text = _("Hide match status")
        self.show_confidence_button = self.dialogButtonBox.addButton(
            scb_text, QDialogButtonBox.ActionRole)
        self.show_confidence_button.setObjectName('confidence_button')
        if self.show_confidence_colors:
            self.show_confidence_button.setIcon(
                get_icon('images/matches_hide.png'))
        else:
            self.show_confidence_button.setIcon(
                get_icon('images/matches_show.png'))

        self.preview_button = self.dialogButtonBox.addButton(
            _('Preview'), QDialogButtonBox.ActionRole)
        self.preview_button.setObjectName('preview_button')

        self.dialogButtonBox.clicked.connect(
            self.show_annotated_books_dialog_clicked)
        self.l.addWidget(self.dialogButtonBox)

        # Cause our dialog size to be restored from prefs or created on first usage
        self.resize_dialog()

    def capture_sort_column(self, sort_column):
        sort_order = self.tv.horizontalHeader().sortIndicatorOrder()
        self.opts.prefs.set('annotated_books_dialog_sort_column', sort_column)
        self.opts.prefs.set('annotated_books_dialog_sort_order', sort_order)

    def fetch_selected_annotations(self):
        '''
        Invoked by 'Import annotations' button in show_annotated_books_dialog()
        Populate a list of books by Reader App:
        { 'iBooks': [{'title':, 'author':, 'uuid'}, ...],
          'Marvin': [{'title':, 'author':, 'uuid'}, ...] }
        '''
        self.selected_books = {}

        for i in range(len(self.tabledata)):
            self.tv.selectRow(i)
            enabled = bool(self.tm.arraydata[i][self.ENABLED_COL].checkState())
            if not enabled:
                continue

            reader_app = str(self.tm.arraydata[i][self.READER_APP_COL].text())
            if not reader_app in self.selected_books:
                self.selected_books[reader_app] = []

            author = str(self.tm.arraydata[i][self.AUTHOR_COL].text())
            book_id = self.tm.arraydata[i][self.annotations_header.index(
                'book_id')]
            genre = self.tm.arraydata[i][self.annotations_header.index(
                'genre')]
            title = str(self.tm.arraydata[i][self.TITLE_COL].text())
            uuid = self.tm.arraydata[i][self.annotations_header.index('uuid')]

            book_mi = BookStruct()
            book_mi.author = author
            book_mi.book_id = book_id
            book_mi.genre = genre
            book_mi.reader_app = reader_app
            book_mi.title = title
            book_mi.uuid = uuid
            self.selected_books[reader_app].append(book_mi)

    def getTableRowDoubleClick(self, index):
        self.preview_annotations()

    def preview_annotations(self):
        """
        The listed annotations are in annotations.db.
        AnnotationsDB:annotations_to_HTML() needs title, book_id, reader_app
        """
        i = self.tvSelectionModel.currentIndex().row()
        reader_app = str(self.tm.arraydata[i][self.READER_APP_COL].text())
        title = str(self.tm.arraydata[i][self.TITLE_COL].text())

        book_mi = BookStruct()
        book_mi.book_id = self.tm.arraydata[i][self.annotations_header.index(
            'book_id')]
        book_mi.reader_app = reader_app
        book_mi.title = title

        # Render annotations from db
        annotations_db = ReaderApp.generate_annotations_db_name(
            reader_app, self.source)
        annotations = self.get_annotations_as_HTML(annotations_db, book_mi)

        PreviewDialog(book_mi, annotations, parent=self.opts.gui).exec_()

    def show_annotated_books_dialog_clicked(self, button):
        '''
        BUTTON_ROLES = ['AcceptRole', 'RejectRole', 'DestructiveRole', 'ActionRole',
                        'HelpRole', 'YesRole', 'NoRole', 'ApplyRole', 'ResetRole']
        '''
        if self.dialogButtonBox.buttonRole(
                button) == QDialogButtonBox.AcceptRole:
            self.fetch_selected_annotations()
            self.accept()
        elif self.dialogButtonBox.buttonRole(
                button) == QDialogButtonBox.ActionRole:
            if button.objectName() == 'confidence_button':
                self.toggle_confidence_colors()
            elif button.objectName() == 'preview_button':
                self.preview_annotations()
            elif button.objectName() == 'toggle_checkmarks_button':
                self.toggle_checkmarks()
        elif self.dialogButtonBox.buttonRole(
                button) == QDialogButtonBox.HelpRole:
            self.show_help()
        elif self.dialogButtonBox.buttonRole(
                button) == QDialogButtonBox.RejectRole:
            self.close()

    def show_help(self):
        '''
        Display help file
        '''
        hv = HelpView(self,
                      self.opts.icon,
                      self.opts.prefs,
                      html=get_resources('help/import_annotations.html'),
                      title=_("Import Annotations"))
        hv.show()

    def size_hint(self):
        return QtCore.QSize(self.perfect_width, self.height())

    def start_confidence_scan(self):
        self.annotated_books_scanner.start()

    def toggle_checkmarks(self):
        button_text = str(self.toggle_checkmarks_button.text())
        if button_text == _('Clear All'):
            for i in range(len(self.tabledata)):
                self.tm.arraydata[i][self.ENABLED_COL].setCheckState(False)
            self.toggle_checkmarks_button.setText(_('Set All'))
        else:
            for i in range(len(self.tabledata)):
                self.tm.arraydata[i][self.ENABLED_COL].setCheckState(True)
            self.toggle_checkmarks_button.setText(_('Clear All'))
        self.tm.refresh(self.show_confidence_colors)

    def toggle_confidence_colors(self):
        self.show_confidence_colors = not self.show_confidence_colors
        self.opts.prefs.set(
            'annotated_books_dialog_show_confidence_as_bg_color',
            self.show_confidence_colors)
        if self.show_confidence_colors:
            self.show_confidence_button.setText(_("Hide match status"))
            self.show_confidence_button.setIcon(
                get_icon('images/matches_hide.png'))
            self.tv.sortByColumn(self.CONFIDENCE_COL, Qt.DescendingOrder)
            self.capture_sort_column(self.CONFIDENCE_COL)
        else:
            self.show_confidence_button.setText(_("Show match status"))
            self.show_confidence_button.setIcon(
                get_icon('images/matches_show.png'))
        self.tv.setAlternatingRowColors(not self.show_confidence_colors)
        self.tm.refresh(self.show_confidence_colors)
Ejemplo n.º 20
0
 def keyPressEvent(self, ev):
     if self.DELETE_POSSIBLE and ev.key() == Qt.Key_Delete:
         self.delete_selected()
         ev.accept()
         return
     return QTableView.keyPressEvent(self, ev)
Ejemplo n.º 21
0
 def currentChanged(self, current, previous):
     QTableView.currentChanged(self, current, previous)
     self.current_changed.emit(*map(self.proxy.mapToSource, (current, previous)))
Ejemplo n.º 22
0
 def scrollContentsBy(self, dx, dy):
     # Needed as Qt bug causes headerview to not always update when scrolling
     QTableView.scrollContentsBy(self, dx, dy)
     if dy != 0:
         self.column_header.update()
Ejemplo n.º 23
0
    def _initialize_controls(self):
        self.setWindowTitle(_('User plugins'))
        self.setWindowIcon(QIcon(I('plugins/plugin_updater.png')))
        layout = QVBoxLayout(self)
        self.setLayout(layout)
        title_layout = ImageTitleLayout(self, 'plugins/plugin_updater.png',
                                        _('User plugins'))
        layout.addLayout(title_layout)

        header_layout = QHBoxLayout()
        layout.addLayout(header_layout)
        self.filter_combo = PluginFilterComboBox(self)
        self.filter_combo.setMinimumContentsLength(20)
        self.filter_combo.currentIndexChanged[int].connect(
            self._filter_combo_changed)
        la = QLabel(_('Filter list of &plugins') + ':', self)
        la.setBuddy(self.filter_combo)
        header_layout.addWidget(la)
        header_layout.addWidget(self.filter_combo)
        header_layout.addStretch(10)

        # filter plugins by name
        la = QLabel(_('Filter by &name') + ':', self)
        header_layout.addWidget(la)
        self.filter_by_name_lineedit = QLineEdit(self)
        la.setBuddy(self.filter_by_name_lineedit)
        self.filter_by_name_lineedit.setText("")
        self.filter_by_name_lineedit.textChanged.connect(
            self._filter_name_lineedit_changed)

        header_layout.addWidget(self.filter_by_name_lineedit)

        self.plugin_view = QTableView(self)
        self.plugin_view.horizontalHeader().setStretchLastSection(True)
        self.plugin_view.setSelectionBehavior(
            QAbstractItemView.SelectionBehavior.SelectRows)
        self.plugin_view.setSelectionMode(
            QAbstractItemView.SelectionMode.SingleSelection)
        self.plugin_view.setAlternatingRowColors(True)
        self.plugin_view.setSortingEnabled(True)
        self.plugin_view.setIconSize(QSize(28, 28))
        layout.addWidget(self.plugin_view)

        details_layout = QHBoxLayout()
        layout.addLayout(details_layout)
        forum_label = self.forum_label = QLabel('')
        forum_label.setTextInteractionFlags(
            Qt.TextInteractionFlag.LinksAccessibleByMouse
            | Qt.TextInteractionFlag.LinksAccessibleByKeyboard)
        forum_label.linkActivated.connect(self._forum_label_activated)
        details_layout.addWidget(QLabel(_('Description') + ':', self), 0,
                                 Qt.AlignmentFlag.AlignLeft)
        details_layout.addWidget(forum_label, 1, Qt.AlignmentFlag.AlignRight)

        self.description = QLabel(self)
        self.description.setFrameStyle(QFrame.Shape.Panel
                                       | QFrame.Shadow.Sunken)
        self.description.setAlignment(Qt.AlignmentFlag.AlignTop
                                      | Qt.AlignmentFlag.AlignLeft)
        self.description.setMinimumHeight(40)
        self.description.setWordWrap(True)
        layout.addWidget(self.description)

        self.button_box = QDialogButtonBox(
            QDialogButtonBox.StandardButton.Close)
        self.button_box.rejected.connect(self.reject)
        self.finished.connect(self._finished)
        self.install_button = self.button_box.addButton(
            _('&Install'), QDialogButtonBox.ButtonRole.AcceptRole)
        self.install_button.setToolTip(_('Install the selected plugin'))
        self.install_button.clicked.connect(self._install_clicked)
        self.install_button.setEnabled(False)
        self.configure_button = self.button_box.addButton(
            ' ' + _('&Customize plugin ') + ' ',
            QDialogButtonBox.ButtonRole.ResetRole)
        self.configure_button.setToolTip(
            _('Customize the options for this plugin'))
        self.configure_button.clicked.connect(self._configure_clicked)
        self.configure_button.setEnabled(False)
        layout.addWidget(self.button_box)
 def selectedItemChanged(self, selected, deselected):
     QTableView.selectionChanged(self.tableView, selected, deselected)
     self.selectedChanged.emit(self.getCurrentSelected())
Ejemplo n.º 25
0
 def currentChanged(self, current, previous):
     ret = QTableView.currentChanged(self, current, previous)
     self.show_details(current)
     return ret
Ejemplo n.º 26
0
 def resizeColumnsToContents(self):
     QTableView.resizeColumnsToContents(self)
     self.columns_resized = True
class AnnotatedBooksDialog(SizePersistedDialog):
    '''
    This dialog is shown when the user fetches or imports books
    self.fetch_single_annotations controls checkmark display, behavior of fetch button
    '''
    if isosx:
        FONT = QFont('Monaco', 11)
    elif iswindows:
        FONT = QFont('Lucida Console', 9)
    elif islinux:
        FONT = QFont('Monospace', 9)
        FONT.setStyleHint(QFont.TypeWriter)

    def __init__(self, parent, book_list, get_annotations_as_HTML, source):
        self.opts = parent.opts
        self.parent = parent
        self.get_annotations_as_HTML = get_annotations_as_HTML
        self.show_confidence_colors = self.opts.prefs.get('annotated_books_dialog_show_confidence_as_bg_color', True)
        self.source = source

#         QDialog.__init__(self, parent=self.opts.gui)
        SizePersistedDialog.__init__(self, self.opts.gui, 'Annotations plugin:import annotations dialog')
        self.setWindowTitle(u'Import Annotations')
        self.setWindowIcon(self.opts.icon)
        self.l = QVBoxLayout(self)
        self.setLayout(self.l)
        self.perfect_width = 0

        from calibre_plugins.annotations.appearance import default_timestamp
        friendly_timestamp_format = plugin_prefs.get('appearance_timestamp_format', default_timestamp)

        # Are we collecting News clippings?
        collect_news_clippings = self.opts.prefs.get('cfg_news_clippings_checkbox', False)
        news_clippings_destination = self.opts.prefs.get('cfg_news_clippings_lineEdit', None)

        # Populate the table data
        self.tabledata = []
        for book_data in book_list:
            enabled = QCheckBox()
            enabled.setChecked(True)

            # last_annotation sorts by timestamp
            last_annotation = SortableTableWidgetItem(
                strftime(friendly_timestamp_format,
                         localtime(book_data['last_update'])),
                book_data['last_update'])

            # reader_app sorts case-insensitive
            reader_app = SortableTableWidgetItem(
                book_data['reader_app'],
                book_data['reader_app'].upper())

            # title, author sort by title_sort, author_sort
            if not book_data['title_sort']:
                book_data['title_sort'] = book_data['title']
            title = SortableTableWidgetItem(
                book_data['title'],
                book_data['title_sort'].upper())

            if not book_data['author_sort']:
                book_data['author_sort'] = book_data['author']
            author = SortableTableWidgetItem(
                book_data['author'],
                book_data['author_sort'].upper())

            genres = book_data['genre'].split(', ')
            if 'News' in genres and collect_news_clippings:
                cid = get_clippings_cid(self, news_clippings_destination)
                confidence = 5
            else:
                cid, confidence = parent.generate_confidence(book_data)

            # List order matches self.annotations_header
            this_book = [
                book_data['uuid'],
                book_data['book_id'],
                book_data['genre'],
                enabled,
                reader_app,
                title,
                author,
                last_annotation,
                book_data['annotations'],
                confidence]
            self.tabledata.append(this_book)

        self.tv = QTableView(self)
        self.l.addWidget(self.tv)
        self.annotations_header = ['uuid', 'book_id', 'genre', '', 'Reader App', 'Title',
                                   'Author', 'Last Annotation', 'Annotations', 'Confidence']
        self.ENABLED_COL = 3
        self.READER_APP_COL = 4
        self.TITLE_COL = 5
        self.AUTHOR_COL = 6
        self.LAST_ANNOTATION_COL = 7
        self.CONFIDENCE_COL = 9
        columns_to_center = [8]
        self.tm = MarkupTableModel(self, columns_to_center=columns_to_center)
        self.tv.setModel(self.tm)
        self.tv.setShowGrid(False)
        self.tv.setFont(self.FONT)
        self.tvSelectionModel = self.tv.selectionModel()
        self.tv.setAlternatingRowColors(not self.show_confidence_colors)
        self.tv.setShowGrid(False)
        self.tv.setWordWrap(False)
        self.tv.setSelectionBehavior(self.tv.SelectRows)

        # Connect signals
        self.tv.doubleClicked.connect(self.getTableRowDoubleClick)
        self.tv.horizontalHeader().sectionClicked.connect(self.capture_sort_column)

        # Hide the vertical self.header
        self.tv.verticalHeader().setVisible(False)

        # Hide uuid, book_id, genre, confidence
        self.tv.hideColumn(self.annotations_header.index('uuid'))
        self.tv.hideColumn(self.annotations_header.index('book_id'))
        self.tv.hideColumn(self.annotations_header.index('genre'))
        self.tv.hideColumn(self.annotations_header.index('Confidence'))

        # Set horizontal self.header props
        self.tv.horizontalHeader().setStretchLastSection(True)

        narrow_columns = ['Last Annotation', 'Reader App', 'Annotations']
        extra_width = 10
        breathing_space = 20

        # Set column width to fit contents
        self.tv.resizeColumnsToContents()
        perfect_width = 10 + (len(narrow_columns) * extra_width)
        for i in range(3, 8):
            perfect_width += self.tv.columnWidth(i) + breathing_space
        self.tv.setMinimumSize(perfect_width, 100)
        self.perfect_width = perfect_width

        # Add some width to narrow columns
        for nc in narrow_columns:
            cw = self.tv.columnWidth(self.annotations_header.index(nc))
            self.tv.setColumnWidth(self.annotations_header.index(nc), cw + extra_width)

        # Set row height
        fm = QFontMetrics(self.FONT)
        nrows = len(self.tabledata)
        for row in xrange(nrows):
            self.tv.setRowHeight(row, fm.height() + 4)

        self.tv.setSortingEnabled(True)
        sort_column = self.opts.prefs.get('annotated_books_dialog_sort_column',
                                          self.annotations_header.index('Confidence'))
        sort_order = self.opts.prefs.get('annotated_books_dialog_sort_order',
                                         Qt.DescendingOrder)
        self.tv.sortByColumn(sort_column, sort_order)

        # ~~~~~~~~ Create the ButtonBox ~~~~~~~~
        self.dialogButtonBox = QDialogButtonBox(QDialogButtonBox.Cancel | QDialogButtonBox.Help)
        self.dialogButtonBox.setOrientation(Qt.Horizontal)
        self.import_button = self.dialogButtonBox.addButton(self.dialogButtonBox.Ok)
        self.import_button.setText('Import Annotations')

        # Action buttons
        self.toggle_checkmarks_button = self.dialogButtonBox.addButton('Clear All', QDialogButtonBox.ActionRole)
        self.toggle_checkmarks_button.setObjectName('toggle_checkmarks_button')

        scb_text = 'Show match status'
        if self.show_confidence_colors:
            scb_text = "Hide match status"
        self.show_confidence_button = self.dialogButtonBox.addButton(scb_text, QDialogButtonBox.ActionRole)
        self.show_confidence_button.setObjectName('confidence_button')
        if self.show_confidence_colors:
            self.show_confidence_button.setIcon(get_icon('images/matches_hide.png'))
        else:
            self.show_confidence_button.setIcon(get_icon('images/matches_show.png'))

        self.preview_button = self.dialogButtonBox.addButton('Preview', QDialogButtonBox.ActionRole)
        self.preview_button.setObjectName('preview_button')

        self.dialogButtonBox.clicked.connect(self.show_annotated_books_dialog_clicked)
        self.l.addWidget(self.dialogButtonBox)

        # Cause our dialog size to be restored from prefs or created on first usage
        self.resize_dialog()

    def capture_sort_column(self, sort_column):
        sort_order = self.tv.horizontalHeader().sortIndicatorOrder()
        self.opts.prefs.set('annotated_books_dialog_sort_column', sort_column)
        self.opts.prefs.set('annotated_books_dialog_sort_order', sort_order)

    def fetch_selected_annotations(self):
        '''
        Invoked by 'Import annotations' button in show_annotated_books_dialog()
        Populate a list of books by Reader App:
        { 'iBooks': [{'title':, 'author':, 'uuid'}, ...],
          'Marvin': [{'title':, 'author':, 'uuid'}, ...] }
        '''
        self.selected_books = {}

        for i in range(len(self.tabledata)):
            self.tv.selectRow(i)
            enabled = bool(self.tm.arraydata[i][self.ENABLED_COL].checkState())
            if not enabled:
                continue

            reader_app = str(self.tm.arraydata[i][self.annotations_header.index('Reader App')].text())
            if not reader_app in self.selected_books:
                self.selected_books[reader_app] = []

            author = str(self.tm.arraydata[i][self.annotations_header.index('Author')].text())
            book_id = self.tm.arraydata[i][self.annotations_header.index('book_id')]
            genre = self.tm.arraydata[i][self.annotations_header.index('genre')]
            title = str(self.tm.arraydata[i][self.annotations_header.index('Title')].text())
            uuid = self.tm.arraydata[i][self.annotations_header.index('uuid')]

            book_mi = BookStruct()
            book_mi.author = author
            book_mi.book_id = book_id
            book_mi.genre = genre
            book_mi.reader_app = reader_app
            book_mi.title = title
            book_mi.uuid = uuid
            self.selected_books[reader_app].append(book_mi)

    def getTableRowDoubleClick(self, index):
        self.preview_annotations()

    def preview_annotations(self):
        """
        The listed annotations are in annotations.db.
        AnnotationsDB:annotations_to_HTML() needs title, book_id, reader_app
        """
        i = self.tvSelectionModel.currentIndex().row()
        reader_app = str(self.tm.arraydata[i][self.annotations_header.index('Reader App')].text())
        title = str(self.tm.arraydata[i][self.annotations_header.index('Title')].text())

        book_mi = BookStruct()
        book_mi.book_id = self.tm.arraydata[i][self.annotations_header.index('book_id')]
        book_mi.reader_app = reader_app
        book_mi.title = title

        # Render annotations from db
        annotations_db = ReaderApp.generate_annotations_db_name(reader_app, self.source)
        annotations = self.get_annotations_as_HTML(annotations_db, book_mi)

        PreviewDialog(book_mi, annotations, parent=self.opts.gui).exec_()

    def show_annotated_books_dialog_clicked(self, button):
        '''
        BUTTON_ROLES = ['AcceptRole', 'RejectRole', 'DestructiveRole', 'ActionRole',
                        'HelpRole', 'YesRole', 'NoRole', 'ApplyRole', 'ResetRole']
        '''
        if self.dialogButtonBox.buttonRole(button) == QDialogButtonBox.AcceptRole:
            self.fetch_selected_annotations()
            self.accept()
        elif self.dialogButtonBox.buttonRole(button) == QDialogButtonBox.ActionRole:
            if button.objectName() == 'confidence_button':
                self.toggle_confidence_colors()
            elif button.objectName() == 'preview_button':
                self.preview_annotations()
            elif button.objectName() == 'toggle_checkmarks_button':
                self.toggle_checkmarks()
        elif self.dialogButtonBox.buttonRole(button) == QDialogButtonBox.HelpRole:
            self.show_help()
        elif self.dialogButtonBox.buttonRole(button) == QDialogButtonBox.RejectRole:
            self.close()

    def show_help(self):
        '''
        Display help file
        '''
        hv = HelpView(self, self.opts.icon, self.opts.prefs,
                      html=get_resources('help/import_annotations.html'), title="Import Annotations")
        hv.show()

    def size_hint(self):
        return QtCore.QSize(self.perfect_width, self.height())

    def start_confidence_scan(self):
        self.annotated_books_scanner.start()

    def toggle_checkmarks(self):
        button_text = str(self.toggle_checkmarks_button.text())
        if button_text == 'Clear All':
            for i in range(len(self.tabledata)):
                self.tm.arraydata[i][self.ENABLED_COL].setCheckState(False)
            self.toggle_checkmarks_button.setText(' Set All ')
        else:
            for i in range(len(self.tabledata)):
                self.tm.arraydata[i][self.ENABLED_COL].setCheckState(True)
            self.toggle_checkmarks_button.setText('Clear All')
        self.tm.refresh(self.show_confidence_colors)

    def toggle_confidence_colors(self):
        self.show_confidence_colors = not self.show_confidence_colors
        self.opts.prefs.set('annotated_books_dialog_show_confidence_as_bg_color', self.show_confidence_colors)
        if self.show_confidence_colors:
            self.show_confidence_button.setText("Hide match status")
            self.show_confidence_button.setIcon(get_icon('images/matches_hide.png'))
            self.tv.sortByColumn(self.annotations_header.index('Confidence'), Qt.DescendingOrder)
            self.capture_sort_column(self.annotations_header.index('Confidence'))
        else:
            self.show_confidence_button.setText("Show match status")
            self.show_confidence_button.setIcon(get_icon('images/matches_show.png'))
        self.tv.setAlternatingRowColors(not self.show_confidence_colors)
        self.tm.refresh(self.show_confidence_colors)
Ejemplo n.º 28
0
 def __init__(self, books_view, parent=None):
     QTableView.__init__(self, parent)
     self.books_view = books_view
     self.verticalHeader().close()
     self.splitter = None
Ejemplo n.º 29
0
 def currentChanged(self, current, previous):
     QTableView.currentChanged(self, current, previous)
     self.current_changed.emit(*map(self.proxy.mapToSource, (current, previous)))
Ejemplo n.º 30
0
 def currentChanged(self, current, previous):
     ret = QTableView.currentChanged(self, current, previous)
     self.show_details(current)
     return ret
Ejemplo n.º 31
0
class PluginUpdaterDialog(SizePersistedDialog):

    initial_extra_size = QSize(350, 100)
    forum_label_text = _('Plugin homepage')

    def __init__(self, gui, initial_filter=FILTER_UPDATE_AVAILABLE):
        SizePersistedDialog.__init__(self, gui, 'Plugin Updater plugin:plugin updater dialog')
        self.gui = gui
        self.forum_link = None
        self.zip_url = None
        self.model = None
        self.do_restart = False
        self._initialize_controls()
        self._create_context_menu()

        try:
            display_plugins = read_available_plugins(raise_error=True)
        except Exception:
            display_plugins = []
            import traceback
            error_dialog(self.gui, _('Update Check Failed'),
                        _('Unable to reach the plugin index page.'),
                        det_msg=traceback.format_exc(), show=True)

        if display_plugins:
            self.model = DisplayPluginModel(display_plugins)
            self.proxy_model = DisplayPluginSortFilterModel(self)
            self.proxy_model.setSourceModel(self.model)
            self.plugin_view.setModel(self.proxy_model)
            self.plugin_view.resizeColumnsToContents()
            self.plugin_view.selectionModel().currentRowChanged.connect(self._plugin_current_changed)
            self.plugin_view.doubleClicked.connect(self.install_button.click)
            self.filter_combo.setCurrentIndex(initial_filter)
            self._select_and_focus_view()
        else:
            self.filter_combo.setEnabled(False)
        # Cause our dialog size to be restored from prefs or created on first usage
        self.resize_dialog()

    def _initialize_controls(self):
        self.setWindowTitle(_('User plugins'))
        self.setWindowIcon(QIcon(I('plugins/plugin_updater.png')))
        layout = QVBoxLayout(self)
        self.setLayout(layout)
        title_layout = ImageTitleLayout(self, 'plugins/plugin_updater.png',
                _('User plugins'))
        layout.addLayout(title_layout)

        header_layout = QHBoxLayout()
        layout.addLayout(header_layout)
        self.filter_combo = PluginFilterComboBox(self)
        self.filter_combo.setMinimumContentsLength(20)
        self.filter_combo.currentIndexChanged[int].connect(self._filter_combo_changed)
        la = QLabel(_('Filter list of &plugins')+':', self)
        la.setBuddy(self.filter_combo)
        header_layout.addWidget(la)
        header_layout.addWidget(self.filter_combo)
        header_layout.addStretch(10)

        # filter plugins by name
        la = QLabel(_('Filter by &name')+':', self)
        header_layout.addWidget(la)
        self.filter_by_name_lineedit = QLineEdit(self)
        la.setBuddy(self.filter_by_name_lineedit)
        self.filter_by_name_lineedit.setText("")
        self.filter_by_name_lineedit.textChanged.connect(self._filter_name_lineedit_changed)

        header_layout.addWidget(self.filter_by_name_lineedit)

        self.plugin_view = QTableView(self)
        self.plugin_view.horizontalHeader().setStretchLastSection(True)
        self.plugin_view.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.plugin_view.setSelectionMode(QAbstractItemView.SingleSelection)
        self.plugin_view.setAlternatingRowColors(True)
        self.plugin_view.setSortingEnabled(True)
        self.plugin_view.setIconSize(QSize(28, 28))
        layout.addWidget(self.plugin_view)

        details_layout = QHBoxLayout()
        layout.addLayout(details_layout)
        forum_label = self.forum_label = QLabel('')
        forum_label.setTextInteractionFlags(Qt.LinksAccessibleByMouse | Qt.LinksAccessibleByKeyboard)
        forum_label.linkActivated.connect(self._forum_label_activated)
        details_layout.addWidget(QLabel(_('Description')+':', self), 0, Qt.AlignLeft)
        details_layout.addWidget(forum_label, 1, Qt.AlignRight)

        self.description = QLabel(self)
        self.description.setFrameStyle(QFrame.Panel | QFrame.Sunken)
        self.description.setAlignment(Qt.AlignTop | Qt.AlignLeft)
        self.description.setMinimumHeight(40)
        self.description.setWordWrap(True)
        layout.addWidget(self.description)

        self.button_box = QDialogButtonBox(QDialogButtonBox.Close)
        self.button_box.rejected.connect(self.reject)
        self.finished.connect(self._finished)
        self.install_button = self.button_box.addButton(_('&Install'), QDialogButtonBox.AcceptRole)
        self.install_button.setToolTip(_('Install the selected plugin'))
        self.install_button.clicked.connect(self._install_clicked)
        self.install_button.setEnabled(False)
        self.configure_button = self.button_box.addButton(' '+_('&Customize plugin ')+' ', QDialogButtonBox.ResetRole)
        self.configure_button.setToolTip(_('Customize the options for this plugin'))
        self.configure_button.clicked.connect(self._configure_clicked)
        self.configure_button.setEnabled(False)
        layout.addWidget(self.button_box)

    def update_forum_label(self):
        txt = ''
        if self.forum_link:
            txt = '<a href="%s">%s</a>' % (self.forum_link, self.forum_label_text)
        self.forum_label.setText(txt)

    def _create_context_menu(self):
        self.plugin_view.setContextMenuPolicy(Qt.ActionsContextMenu)
        self.install_action = QAction(QIcon(I('plugins/plugin_upgrade_ok.png')), _('&Install'), self)
        self.install_action.setToolTip(_('Install the selected plugin'))
        self.install_action.triggered.connect(self._install_clicked)
        self.install_action.setEnabled(False)
        self.plugin_view.addAction(self.install_action)
        self.history_action = QAction(QIcon(I('chapters.png')), _('Version &history'), self)
        self.history_action.setToolTip(_('Show history of changes to this plugin'))
        self.history_action.triggered.connect(self._history_clicked)
        self.history_action.setEnabled(False)
        self.plugin_view.addAction(self.history_action)
        self.forum_action = QAction(QIcon(I('plugins/mobileread.png')), _('Plugin &forum thread'), self)
        self.forum_action.triggered.connect(self._forum_label_activated)
        self.forum_action.setEnabled(False)
        self.plugin_view.addAction(self.forum_action)

        sep1 = QAction(self)
        sep1.setSeparator(True)
        self.plugin_view.addAction(sep1)

        self.toggle_enabled_action = QAction(_('Enable/&disable plugin'), self)
        self.toggle_enabled_action.setToolTip(_('Enable or disable this plugin'))
        self.toggle_enabled_action.triggered.connect(self._toggle_enabled_clicked)
        self.toggle_enabled_action.setEnabled(False)
        self.plugin_view.addAction(self.toggle_enabled_action)
        self.uninstall_action = QAction(_('&Remove plugin'), self)
        self.uninstall_action.setToolTip(_('Uninstall the selected plugin'))
        self.uninstall_action.triggered.connect(self._uninstall_clicked)
        self.uninstall_action.setEnabled(False)
        self.plugin_view.addAction(self.uninstall_action)

        sep2 = QAction(self)
        sep2.setSeparator(True)
        self.plugin_view.addAction(sep2)

        self.donate_enabled_action = QAction(QIcon(I('donate.png')), _('Donate to developer'), self)
        self.donate_enabled_action.setToolTip(_('Donate to the developer of this plugin'))
        self.donate_enabled_action.triggered.connect(self._donate_clicked)
        self.donate_enabled_action.setEnabled(False)
        self.plugin_view.addAction(self.donate_enabled_action)

        sep3 = QAction(self)
        sep3.setSeparator(True)
        self.plugin_view.addAction(sep3)

        self.configure_action = QAction(QIcon(I('config.png')), _('&Customize plugin'), self)
        self.configure_action.setToolTip(_('Customize the options for this plugin'))
        self.configure_action.triggered.connect(self._configure_clicked)
        self.configure_action.setEnabled(False)
        self.plugin_view.addAction(self.configure_action)

    def _finished(self, *args):
        if self.model:
            update_plugins = list(filter(filter_upgradeable_plugins, self.model.display_plugins))
            self.gui.recalc_update_label(len(update_plugins))

    def _plugin_current_changed(self, current, previous):
        if current.isValid():
            actual_idx = self.proxy_model.mapToSource(current)
            display_plugin = self.model.display_plugins[actual_idx.row()]
            self.description.setText(display_plugin.description)
            self.forum_link = display_plugin.forum_link
            self.zip_url = display_plugin.zip_url
            self.forum_action.setEnabled(bool(self.forum_link))
            self.install_button.setEnabled(display_plugin.is_valid_to_install())
            self.install_action.setEnabled(self.install_button.isEnabled())
            self.uninstall_action.setEnabled(display_plugin.is_installed())
            self.history_action.setEnabled(display_plugin.has_changelog)
            self.configure_button.setEnabled(display_plugin.is_installed())
            self.configure_action.setEnabled(self.configure_button.isEnabled())
            self.toggle_enabled_action.setEnabled(display_plugin.is_installed())
            self.donate_enabled_action.setEnabled(bool(display_plugin.donation_link))
        else:
            self.description.setText('')
            self.forum_link = None
            self.zip_url = None
            self.forum_action.setEnabled(False)
            self.install_button.setEnabled(False)
            self.install_action.setEnabled(False)
            self.uninstall_action.setEnabled(False)
            self.history_action.setEnabled(False)
            self.configure_button.setEnabled(False)
            self.configure_action.setEnabled(False)
            self.toggle_enabled_action.setEnabled(False)
            self.donate_enabled_action.setEnabled(False)
        self.update_forum_label()

    def _donate_clicked(self):
        plugin = self._selected_display_plugin()
        if plugin and plugin.donation_link:
            open_url(QUrl(plugin.donation_link))

    def _select_and_focus_view(self, change_selection=True):
        if change_selection and self.plugin_view.model().rowCount() > 0:
            self.plugin_view.selectRow(0)
        else:
            idx = self.plugin_view.selectionModel().currentIndex()
            self._plugin_current_changed(idx, 0)
        self.plugin_view.setFocus()

    def _filter_combo_changed(self, idx):
        self.filter_by_name_lineedit.setText("")  # clear the name filter text when a different group was selected
        self.proxy_model.set_filter_criteria(idx)
        if idx == FILTER_NOT_INSTALLED:
            self.plugin_view.sortByColumn(5, Qt.DescendingOrder)
        else:
            self.plugin_view.sortByColumn(0, Qt.AscendingOrder)
        self._select_and_focus_view()

    def _filter_name_lineedit_changed(self, text):
        self.proxy_model.set_filter_text(text)  # set the filter text for filterAcceptsRow

    def _forum_label_activated(self):
        if self.forum_link:
            open_url(QUrl(self.forum_link))

    def _selected_display_plugin(self):
        idx = self.plugin_view.selectionModel().currentIndex()
        actual_idx = self.proxy_model.mapToSource(idx)
        return self.model.display_plugins[actual_idx.row()]

    def _uninstall_plugin(self, name_to_remove):
        if DEBUG:
            prints('Removing plugin: ', name_to_remove)
        remove_plugin(name_to_remove)
        # Make sure that any other plugins that required this plugin
        # to be uninstalled first have the requirement removed
        for display_plugin in self.model.display_plugins:
            # Make sure we update the status and display of the
            # plugin we just uninstalled
            if name_to_remove in display_plugin.uninstall_plugins:
                if DEBUG:
                    prints('Removing uninstall dependency for: ', display_plugin.name)
                display_plugin.uninstall_plugins.remove(name_to_remove)
            if display_plugin.qname == name_to_remove:
                if DEBUG:
                    prints('Resetting plugin to uninstalled status: ', display_plugin.name)
                display_plugin.installed_version = None
                display_plugin.plugin = None
                display_plugin.uninstall_plugins = []
                if self.proxy_model.filter_criteria not in [FILTER_INSTALLED, FILTER_UPDATE_AVAILABLE]:
                    self.model.refresh_plugin(display_plugin)

    def _uninstall_clicked(self):
        display_plugin = self._selected_display_plugin()
        if not question_dialog(self, _('Are you sure?'), '<p>'+
                   _('Are you sure you want to uninstall the <b>%s</b> plugin?')%display_plugin.name,
                   show_copy_button=False):
            return
        self._uninstall_plugin(display_plugin.qname)
        if self.proxy_model.filter_criteria in [FILTER_INSTALLED, FILTER_UPDATE_AVAILABLE]:
            self.model.beginResetModel(), self.model.endResetModel()
            self._select_and_focus_view()
        else:
            self._select_and_focus_view(change_selection=False)

    def _install_clicked(self):
        display_plugin = self._selected_display_plugin()
        if not question_dialog(self, _('Install %s')%display_plugin.name, '<p>' +
                _('Installing plugins is a <b>security risk</b>. '
                'Plugins can contain a virus/malware. '
                    'Only install it if you got it from a trusted source.'
                    ' Are you sure you want to proceed?'),
                show_copy_button=False):
            return

        if display_plugin.uninstall_plugins:
            uninstall_names = list(display_plugin.uninstall_plugins)
            if DEBUG:
                prints('Uninstalling plugin: ', ', '.join(uninstall_names))
            for name_to_remove in uninstall_names:
                self._uninstall_plugin(name_to_remove)

        plugin_zip_url = display_plugin.zip_url
        if DEBUG:
            prints('Downloading plugin ZIP attachment: ', plugin_zip_url)
        self.gui.status_bar.showMessage(_('Downloading plugin ZIP attachment: %s') % plugin_zip_url)
        zip_path = self._download_zip(plugin_zip_url)

        if DEBUG:
            prints('Installing plugin: ', zip_path)
        self.gui.status_bar.showMessage(_('Installing plugin: %s') % zip_path)

        do_restart = False
        try:
            from calibre.customize.ui import config
            installed_plugins = frozenset(config['plugins'])
            try:
                plugin = add_plugin(zip_path)
            except NameConflict as e:
                return error_dialog(self.gui, _('Already exists'),
                        unicode_type(e), show=True)
            # Check for any toolbars to add to.
            widget = ConfigWidget(self.gui)
            widget.gui = self.gui
            widget.check_for_add_to_toolbars(plugin, previously_installed=plugin.name in installed_plugins)
            self.gui.status_bar.showMessage(_('Plugin installed: %s') % display_plugin.name)
            d = info_dialog(self.gui, _('Success'),
                    _('Plugin <b>{0}</b> successfully installed under <b>'
                        ' {1} plugins</b>. You may have to restart calibre '
                        'for the plugin to take effect.').format(plugin.name, plugin.type),
                    show_copy_button=False)
            b = d.bb.addButton(_('&Restart calibre now'), d.bb.AcceptRole)
            b.setIcon(QIcon(I('lt.png')))
            d.do_restart = False

            def rf():
                d.do_restart = True
            b.clicked.connect(rf)
            d.set_details('')
            d.exec_()
            b.clicked.disconnect()
            do_restart = d.do_restart

            display_plugin.plugin = plugin
            # We cannot read the 'actual' version information as the plugin will not be loaded yet
            display_plugin.installed_version = display_plugin.available_version
        except:
            if DEBUG:
                prints('ERROR occurred while installing plugin: %s'%display_plugin.name)
                traceback.print_exc()
            error_dialog(self.gui, _('Install plugin failed'),
                         _('A problem occurred while installing this plugin.'
                           ' This plugin will now be uninstalled.'
                           ' Please post the error message in details below into'
                           ' the forum thread for this plugin and restart calibre.'),
                         det_msg=traceback.format_exc(), show=True)
            if DEBUG:
                prints('Due to error now uninstalling plugin: %s'%display_plugin.name)
            remove_plugin(display_plugin.name)
            display_plugin.plugin = None

        display_plugin.uninstall_plugins = []
        if self.proxy_model.filter_criteria in [FILTER_NOT_INSTALLED, FILTER_UPDATE_AVAILABLE]:
            self.model.beginResetModel(), self.model.endResetModel()
            self._select_and_focus_view()
        else:
            self.model.refresh_plugin(display_plugin)
            self._select_and_focus_view(change_selection=False)
        if do_restart:
            self.do_restart = True
            self.accept()

    def _history_clicked(self):
        display_plugin = self._selected_display_plugin()
        text = self._read_version_history_html(display_plugin.forum_link)
        if text:
            dlg = VersionHistoryDialog(self, display_plugin.name, text)
            dlg.exec_()
        else:
            return error_dialog(self, _('Version history missing'),
                _('Unable to find the version history for %s')%display_plugin.name,
                show=True)

    def _configure_clicked(self):
        display_plugin = self._selected_display_plugin()
        plugin = display_plugin.plugin
        if not plugin.is_customizable():
            return info_dialog(self, _('Plugin not customizable'),
                _('Plugin: %s does not need customization')%plugin.name, show=True)
        from calibre.customize import InterfaceActionBase
        if isinstance(plugin, InterfaceActionBase) and not getattr(plugin,
                'actual_iaction_plugin_loaded', False):
            return error_dialog(self, _('Must restart'),
                    _('You must restart calibre before you can'
                        ' configure the <b>%s</b> plugin')%plugin.name, show=True)
        plugin.do_user_config(self.parent())

    def _toggle_enabled_clicked(self):
        display_plugin = self._selected_display_plugin()
        plugin = display_plugin.plugin
        if not plugin.can_be_disabled:
            return error_dialog(self,_('Plugin cannot be disabled'),
                         _('The plugin: %s cannot be disabled')%plugin.name, show=True)
        if is_disabled(plugin):
            enable_plugin(plugin)
        else:
            disable_plugin(plugin)
        self.model.refresh_plugin(display_plugin)

    def _read_version_history_html(self, forum_link):
        br = browser()
        br.set_handle_gzip(True)
        try:
            raw = br.open_novisit(forum_link).read()
            if not raw:
                return None
        except:
            traceback.print_exc()
            return None
        raw = raw.decode('utf-8', errors='replace')
        root = html.fromstring(raw)
        spoiler_nodes = root.xpath('//div[@class="smallfont" and strong="Spoiler"]')
        for spoiler_node in spoiler_nodes:
            try:
                if spoiler_node.getprevious() is None:
                    # This is a spoiler node that has been indented using [INDENT]
                    # Need to go up to parent div, then previous node to get header
                    heading_node = spoiler_node.getparent().getprevious()
                else:
                    # This is a spoiler node after a BR tag from the heading
                    heading_node = spoiler_node.getprevious().getprevious()
                if heading_node is None:
                    continue
                if heading_node.text_content().lower().find('version history') != -1:
                    div_node = spoiler_node.xpath('div')[0]
                    text = html.tostring(div_node, method='html', encoding='unicode')
                    return re.sub(r'<div\s.*?>', '<div>', text)
            except:
                if DEBUG:
                    prints('======= MobileRead Parse Error =======')
                    traceback.print_exc()
                    prints(html.tostring(spoiler_node))
        return None

    def _download_zip(self, plugin_zip_url):
        from calibre.ptempfile import PersistentTemporaryFile
        raw = get_https_resource_securely(plugin_zip_url, headers={'User-Agent':'%s %s' % (__appname__, __version__)})
        with PersistentTemporaryFile('.zip') as pt:
            pt.write(raw)
        return pt.name
Ejemplo n.º 32
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)
Ejemplo n.º 33
0
class FITSBrowse(QMainWindow):
    """ The main window of this GUI."""

    imdisp = None

    def __init__(self, parent=None):
        """ 
        """
        print("Skeet")

        super(FITSBrowse, self).__init__(parent)
        self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
        self.setWindowTitle("FITSBrowse")

        self.current_directory = CurrentDirectory(
            "/data/lemi-archive-2016-04/20160427")
        self.model = FitsFileTableModel(self.current_directory)
        self.d_model = DirectoryListModel(self.current_directory)
        self.main_widget = QWidget(self)

        self.dir_text = "Current Directory: {0}".format(
            self.current_directory.cur_dir_path)
        self.cur_dir_label = QLabel(self.dir_text, self)

        self.listLabel = QLabel("&Directories")

        self.list_view = QListView()
        self.listLabel.setBuddy(self.list_view)
        self.list_view.setModel(self.d_model)
        self.list_view.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)

        self.list_view.setMinimumWidth(self.list_view.sizeHintForColumn(0))
        self.list_view.setMaximumWidth(self.list_view.sizeHintForColumn(0))

        self.list_view_sm = self.list_view.selectionModel()
        self.list_view.doubleClicked.connect(self.change_directory)

        self.table_view = QTableView()
        self.table_view.setModel(self.model)
        self.table_view.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
        self.table_view_sm = self.table_view.selectionModel()
        self.table_view.doubleClicked.connect(self.display_image)

        list_vbox = QVBoxLayout()
        list_vbox.addWidget(self.listLabel)
        list_vbox.addWidget(self.list_view)
        self.list_widget = QWidget()
        self.list_widget.setLayout(list_vbox)

        #self.list_view.size

        main_layout = QHBoxLayout()
        main_layout.addWidget(self.list_widget)
        main_layout.addWidget(self.table_view)

        button_layout = QHBoxLayout()
        quit_button = QPushButton('Quit', self)
        quit_button.setToolTip('This button quits FITSBrowse')
        quit_button.clicked.connect(QApplication.instance().quit)
        button_layout.addStretch()
        button_layout.addWidget(quit_button)

        sep_line = QFrame()
        sep_line.setFrameShape(QFrame.HLine)

        super_layout = QVBoxLayout(self.main_widget)
        super_layout.addWidget(self.cur_dir_label)
        super_layout.addWidget(sep_line)
        super_layout.addLayout(main_layout)
        super_layout.addLayout(button_layout)

        self.setCentralWidget(self.main_widget)
        QtCore.QTimer.singleShot(0, self.initialLoad)
        x = self.table_view.frameSize()
        x = x.width()
        self.table_view.setMinimumWidth(x)
        self.main_widget.setMinimumHeight(500)

    def initialLoad(self):
        for column in (1, 2, 3, 4, 5, 6, 7):
            self.table_view.resizeColumnToContents(column)

    def display_image(self):
        indexes = self.table_view_sm.selectedIndexes()
        index = indexes[0]
        file_name = str(index.data())
        if (file_name.endswith("fits") or file_name.endswith("fit")):
            file_with_path = os.path.join(self.current_directory.cur_dir_path,
                                          file_name)
            self.im_disp = ImageAnalyzer(file_with_path, parent=self)

    def change_directory(self):
        indexes = self.list_view_sm.selectedRows()
        if (indexes != []):
            index = indexes[0]
            new_directory = str(index.data())
            current_path = self.current_directory.cur_dir_path
            if (new_directory == ".."):
                # Go up one directory
                if (current_path == "/"):
                    # Do nothing.
                    pass
                else:
                    self.current_directory = CurrentDirectory(
                        os.path.dirname(current_path))
            else:
                # Go down into the selected directory.
                self.current_directory = CurrentDirectory(
                    os.path.join(current_path, new_directory))
            print("change directory, new directory is:",
                  self.current_directory.cur_dir_path)
            self.model = FitsFileTableModel(self.current_directory)
            self.d_model = DirectoryListModel(self.current_directory)

            self.dir_text = "Current Directory: {0}".format(
                self.current_directory.cur_dir_path)
            self.cur_dir_label.setText(self.dir_text)

            self.list_view.setModel(self.d_model)
            self.list_view_sm = self.list_view.selectionModel()
            self.list_view.update()
            self.list_view.setMinimumWidth(self.list_view.sizeHintForColumn(0))
            self.list_view.setMaximumWidth(self.list_view.sizeHintForColumn(0))

            self.table_view.setModel(self.model)
            self.table_view_sm = self.table_view.selectionModel()
            self.table_view.update()
            QtCore.QTimer.singleShot(0, self.initialLoad)
            x = self.table_view.frameSize()
            x = x.width()
            self.table_view.setMinimumWidth(x)
        else:
            print("no directory selected")
Ejemplo n.º 34
0
    def __init__(self, parent=None):
        """ 
        """
        print("Skeet")

        super(FITSBrowse, self).__init__(parent)
        self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
        self.setWindowTitle("FITSBrowse")

        self.current_directory = CurrentDirectory(
            "/data/lemi-archive-2016-04/20160427")
        self.model = FitsFileTableModel(self.current_directory)
        self.d_model = DirectoryListModel(self.current_directory)
        self.main_widget = QWidget(self)

        self.dir_text = "Current Directory: {0}".format(
            self.current_directory.cur_dir_path)
        self.cur_dir_label = QLabel(self.dir_text, self)

        self.listLabel = QLabel("&Directories")

        self.list_view = QListView()
        self.listLabel.setBuddy(self.list_view)
        self.list_view.setModel(self.d_model)
        self.list_view.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)

        self.list_view.setMinimumWidth(self.list_view.sizeHintForColumn(0))
        self.list_view.setMaximumWidth(self.list_view.sizeHintForColumn(0))

        self.list_view_sm = self.list_view.selectionModel()
        self.list_view.doubleClicked.connect(self.change_directory)

        self.table_view = QTableView()
        self.table_view.setModel(self.model)
        self.table_view.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
        self.table_view_sm = self.table_view.selectionModel()
        self.table_view.doubleClicked.connect(self.display_image)

        list_vbox = QVBoxLayout()
        list_vbox.addWidget(self.listLabel)
        list_vbox.addWidget(self.list_view)
        self.list_widget = QWidget()
        self.list_widget.setLayout(list_vbox)

        #self.list_view.size

        main_layout = QHBoxLayout()
        main_layout.addWidget(self.list_widget)
        main_layout.addWidget(self.table_view)

        button_layout = QHBoxLayout()
        quit_button = QPushButton('Quit', self)
        quit_button.setToolTip('This button quits FITSBrowse')
        quit_button.clicked.connect(QApplication.instance().quit)
        button_layout.addStretch()
        button_layout.addWidget(quit_button)

        sep_line = QFrame()
        sep_line.setFrameShape(QFrame.HLine)

        super_layout = QVBoxLayout(self.main_widget)
        super_layout.addWidget(self.cur_dir_label)
        super_layout.addWidget(sep_line)
        super_layout.addLayout(main_layout)
        super_layout.addLayout(button_layout)

        self.setCentralWidget(self.main_widget)
        QtCore.QTimer.singleShot(0, self.initialLoad)
        x = self.table_view.frameSize()
        x = x.width()
        self.table_view.setMinimumWidth(x)
        self.main_widget.setMinimumHeight(500)
Ejemplo n.º 35
0
    def setup_ui(self):
        self.setAttribute(Qt.WA_DeleteOnClose, False)
        self.l = l = QVBoxLayout(self)
        self.setLayout(l)

        self.bb.clear()
        self.bb.addButton(self.bb.Close)
        self.splitter = s = QSplitter(self)
        self.h = h = QHBoxLayout()
        h.setContentsMargins(0, 0, 0, 0)
        self.install_fonts_button = b = QPushButton(_('&Install fonts'), self)
        h.addWidget(b), b.setIcon(QIcon(I('plus.png')))
        b.setToolTip(
            textwrap.fill(
                _('Install fonts from .ttf/.otf files to make them available for embedding'
                  )))
        b.clicked.connect(self.install_fonts)
        l.addWidget(s), l.addLayout(h), h.addStretch(10), h.addWidget(self.bb)

        self.fonts_view = fv = QTableView(self)
        fv.doubleClicked.connect(self.show_embedding_data)
        self.model = m = AllFonts(fv)
        fv.horizontalHeader().setStretchLastSection(True)
        fv.setModel(m)
        fv.setSortingEnabled(True)
        fv.setShowGrid(False)
        fv.setAlternatingRowColors(True)
        fv.setSelectionMode(fv.ExtendedSelection)
        fv.setSelectionBehavior(fv.SelectRows)
        fv.horizontalHeader().setSortIndicator(1, Qt.AscendingOrder)
        self.container = c = QWidget()
        l = c.l = QVBoxLayout(c)
        c.setLayout(l)
        s.addWidget(fv), s.addWidget(c)

        self.cb = b = QPushButton(_('&Change selected fonts'))
        b.setIcon(QIcon(I('wizard.png')))
        b.clicked.connect(self.change_fonts)
        l.addWidget(b)
        self.rb = b = QPushButton(_('&Remove selected fonts'))
        b.clicked.connect(self.remove_fonts)
        b.setIcon(QIcon(I('trash.png')))
        l.addWidget(b)
        self.eb = b = QPushButton(_('&Embed all fonts'))
        b.setIcon(QIcon(I('embed-fonts.png')))
        b.clicked.connect(self.embed_fonts)
        l.addWidget(b)
        self.sb = b = QPushButton(_('&Subset all fonts'))
        b.setIcon(QIcon(I('subset-fonts.png')))
        b.clicked.connect(self.subset_fonts)
        l.addWidget(b)
        self.refresh_button = b = self.bb.addButton(_('&Refresh'),
                                                    self.bb.ActionRole)
        b.setToolTip(
            _('Rescan the book for fonts in case you have made changes'))
        b.setIcon(QIcon(I('view-refresh.png')))
        b.clicked.connect(self.refresh)

        self.la = la = QLabel('<p>' + _(
            ''' All the fonts declared in this book are shown to the left, along with whether they are embedded or not.
            You can remove or replace any selected font and also embed any declared fonts that are not already embedded.'''
        ) + '<p>' + _(
            ''' Double click any font family to see if the font is available for embedding on your computer. '''
        ))
        la.setWordWrap(True)
        l.addWidget(la)

        l.setAlignment(Qt.AlignTop | Qt.AlignHCenter)
Ejemplo n.º 36
0
 def viewportEvent(self, event):
     if (event.type() == event.ToolTip and not gprefs['book_list_tooltips']):
         return False
     return QTableView.viewportEvent(self, event)
Ejemplo n.º 37
0
Archivo: views.py Proyecto: kba/calibre
    def __init__(self, parent, modelcls=BooksModel, use_edit_metadata_dialog=True):
        QTableView.__init__(self, parent)
        self.default_row_height = self.verticalHeader().defaultSectionSize()
        self.gui = parent
        self.setProperty('highlight_current_item', 150)
        self.row_sizing_done = False
        self.alternate_views = AlternateViews(self)

        if not tweaks['horizontal_scrolling_per_column']:
            self.setHorizontalScrollMode(self.ScrollPerPixel)

        self.setEditTriggers(self.EditKeyPressed)
        if tweaks['doubleclick_on_library_view'] == 'edit_cell':
            self.setEditTriggers(self.DoubleClicked|self.editTriggers())
        elif tweaks['doubleclick_on_library_view'] == 'open_viewer':
            self.setEditTriggers(self.SelectedClicked|self.editTriggers())
            self.doubleClicked.connect(parent.iactions['View'].view_triggered)
        elif tweaks['doubleclick_on_library_view'] == 'edit_metadata':
            # Must not enable single-click to edit, or the field will remain
            # open in edit mode underneath the edit metadata dialog
            if use_edit_metadata_dialog:
                self.doubleClicked.connect(
                        partial(parent.iactions['Edit Metadata'].edit_metadata,
                                checked=False))
            else:
                self.setEditTriggers(self.DoubleClicked|self.editTriggers())

        setup_dnd_interface(self)
        self.setAlternatingRowColors(True)
        self.setShowGrid(False)
        self.setWordWrap(False)

        self.rating_delegate = RatingDelegate(self)
        self.timestamp_delegate = DateDelegate(self)
        self.pubdate_delegate = PubDateDelegate(self)
        self.last_modified_delegate = DateDelegate(self,
                tweak_name='gui_last_modified_display_format')
        self.languages_delegate = LanguagesDelegate(self)
        self.tags_delegate = CompleteDelegate(self, ',', 'all_tag_names')
        self.authors_delegate = CompleteDelegate(self, '&', 'all_author_names', True)
        self.cc_names_delegate = CompleteDelegate(self, '&', 'all_custom', True)
        self.series_delegate = TextDelegate(self)
        self.publisher_delegate = TextDelegate(self)
        self.text_delegate = TextDelegate(self)
        self.cc_text_delegate = CcTextDelegate(self)
        self.cc_enum_delegate = CcEnumDelegate(self)
        self.cc_bool_delegate = CcBoolDelegate(self)
        self.cc_comments_delegate = CcCommentsDelegate(self)
        self.cc_template_delegate = CcTemplateDelegate(self)
        self.cc_number_delegate = CcNumberDelegate(self)
        self.display_parent = parent
        self._model = modelcls(self)
        self.setModel(self._model)
        self._model.count_changed_signal.connect(self.do_row_sizing,
                                                 type=Qt.QueuedConnection)
        self.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.setSortingEnabled(True)
        self.selectionModel().currentRowChanged.connect(self._model.current_changed)
        self.preserve_state = partial(PreserveViewState, self)
        self.marked_changed_listener = FunctionDispatcher(self.marked_changed)

        # {{{ Column Header setup
        self.can_add_columns = True
        self.was_restored = False
        self.column_header = HeaderView(Qt.Horizontal, self)
        self.setHorizontalHeader(self.column_header)
        self.column_header.sortIndicatorChanged.disconnect()
        self.column_header.sortIndicatorChanged.connect(self.user_sort_requested)
        self.column_header.setSectionsMovable(True)
        self.column_header.setSectionsClickable(True)
        self.column_header.sectionMoved.connect(self.save_state)
        self.column_header.setContextMenuPolicy(Qt.CustomContextMenu)
        self.column_header.customContextMenuRequested.connect(self.show_column_header_context_menu)
        self.column_header.sectionResized.connect(self.column_resized, Qt.QueuedConnection)
        self.row_header = HeaderView(Qt.Vertical, self)
        self.row_header.setSectionResizeMode(self.row_header.Fixed)
        self.setVerticalHeader(self.row_header)
        # }}}

        self._model.database_changed.connect(self.database_changed)
        hv = self.verticalHeader()
        hv.setSectionsClickable(True)
        hv.setCursor(Qt.PointingHandCursor)
        self.selected_ids = []
        self._model.about_to_be_sorted.connect(self.about_to_be_sorted)
        self._model.sorting_done.connect(self.sorting_done,
                type=Qt.QueuedConnection)
Ejemplo n.º 38
0
 def user_sort_requested(self, col, order=Qt.AscendingOrder):
     if col >= len(self.column_map) or col < 0:
         return QTableView.sortByColumn(self, col)
     field = self.column_map[col]
     self.intelligent_sort(field, order == Qt.AscendingOrder)
Ejemplo n.º 39
0
Archivo: views.py Proyecto: kba/calibre
 def user_sort_requested(self, col, order=Qt.AscendingOrder):
     if col >= len(self.column_map) or col < 0:
         return QTableView.sortByColumn(self, col)
     field = self.column_map[col]
     self.intelligent_sort(field, order == Qt.AscendingOrder)
Ejemplo n.º 40
0
 def keyPressEvent(self, ev):
     if self.DELETE_POSSIBLE and ev.key() == Qt.Key_Delete:
         self.delete_selected()
         ev.accept()
         return
     return QTableView.keyPressEvent(self, ev)
    def __init__(self, parent, book_list, get_annotations_as_HTML, source):
        self.opts = parent.opts
        self.parent = parent
        self.get_annotations_as_HTML = get_annotations_as_HTML
        self.show_confidence_colors = self.opts.prefs.get('annotated_books_dialog_show_confidence_as_bg_color', True)
        self.source = source

#         QDialog.__init__(self, parent=self.opts.gui)
        SizePersistedDialog.__init__(self, self.opts.gui, 'Annotations plugin:import annotations dialog')
        self.setWindowTitle(u'Import Annotations')
        self.setWindowIcon(self.opts.icon)
        self.l = QVBoxLayout(self)
        self.setLayout(self.l)
        self.perfect_width = 0

        from calibre_plugins.annotations.appearance import default_timestamp
        friendly_timestamp_format = plugin_prefs.get('appearance_timestamp_format', default_timestamp)

        # Are we collecting News clippings?
        collect_news_clippings = self.opts.prefs.get('cfg_news_clippings_checkbox', False)
        news_clippings_destination = self.opts.prefs.get('cfg_news_clippings_lineEdit', None)

        # Populate the table data
        self.tabledata = []
        for book_data in book_list:
            enabled = QCheckBox()
            enabled.setChecked(True)

            # last_annotation sorts by timestamp
            last_annotation = SortableTableWidgetItem(
                strftime(friendly_timestamp_format,
                         localtime(book_data['last_update'])),
                book_data['last_update'])

            # reader_app sorts case-insensitive
            reader_app = SortableTableWidgetItem(
                book_data['reader_app'],
                book_data['reader_app'].upper())

            # title, author sort by title_sort, author_sort
            if not book_data['title_sort']:
                book_data['title_sort'] = book_data['title']
            title = SortableTableWidgetItem(
                book_data['title'],
                book_data['title_sort'].upper())

            if not book_data['author_sort']:
                book_data['author_sort'] = book_data['author']
            author = SortableTableWidgetItem(
                book_data['author'],
                book_data['author_sort'].upper())

            genres = book_data['genre'].split(', ')
            if 'News' in genres and collect_news_clippings:
                cid = get_clippings_cid(self, news_clippings_destination)
                confidence = 5
            else:
                cid, confidence = parent.generate_confidence(book_data)

            # List order matches self.annotations_header
            this_book = [
                book_data['uuid'],
                book_data['book_id'],
                book_data['genre'],
                enabled,
                reader_app,
                title,
                author,
                last_annotation,
                book_data['annotations'],
                confidence]
            self.tabledata.append(this_book)

        self.tv = QTableView(self)
        self.l.addWidget(self.tv)
        self.annotations_header = ['uuid', 'book_id', 'genre', '', 'Reader App', 'Title',
                                   'Author', 'Last Annotation', 'Annotations', 'Confidence']
        self.ENABLED_COL = 3
        self.READER_APP_COL = 4
        self.TITLE_COL = 5
        self.AUTHOR_COL = 6
        self.LAST_ANNOTATION_COL = 7
        self.CONFIDENCE_COL = 9
        columns_to_center = [8]
        self.tm = MarkupTableModel(self, columns_to_center=columns_to_center)
        self.tv.setModel(self.tm)
        self.tv.setShowGrid(False)
        self.tv.setFont(self.FONT)
        self.tvSelectionModel = self.tv.selectionModel()
        self.tv.setAlternatingRowColors(not self.show_confidence_colors)
        self.tv.setShowGrid(False)
        self.tv.setWordWrap(False)
        self.tv.setSelectionBehavior(self.tv.SelectRows)

        # Connect signals
        self.tv.doubleClicked.connect(self.getTableRowDoubleClick)
        self.tv.horizontalHeader().sectionClicked.connect(self.capture_sort_column)

        # Hide the vertical self.header
        self.tv.verticalHeader().setVisible(False)

        # Hide uuid, book_id, genre, confidence
        self.tv.hideColumn(self.annotations_header.index('uuid'))
        self.tv.hideColumn(self.annotations_header.index('book_id'))
        self.tv.hideColumn(self.annotations_header.index('genre'))
        self.tv.hideColumn(self.annotations_header.index('Confidence'))

        # Set horizontal self.header props
        self.tv.horizontalHeader().setStretchLastSection(True)

        narrow_columns = ['Last Annotation', 'Reader App', 'Annotations']
        extra_width = 10
        breathing_space = 20

        # Set column width to fit contents
        self.tv.resizeColumnsToContents()
        perfect_width = 10 + (len(narrow_columns) * extra_width)
        for i in range(3, 8):
            perfect_width += self.tv.columnWidth(i) + breathing_space
        self.tv.setMinimumSize(perfect_width, 100)
        self.perfect_width = perfect_width

        # Add some width to narrow columns
        for nc in narrow_columns:
            cw = self.tv.columnWidth(self.annotations_header.index(nc))
            self.tv.setColumnWidth(self.annotations_header.index(nc), cw + extra_width)

        # Set row height
        fm = QFontMetrics(self.FONT)
        nrows = len(self.tabledata)
        for row in xrange(nrows):
            self.tv.setRowHeight(row, fm.height() + 4)

        self.tv.setSortingEnabled(True)
        sort_column = self.opts.prefs.get('annotated_books_dialog_sort_column',
                                          self.annotations_header.index('Confidence'))
        sort_order = self.opts.prefs.get('annotated_books_dialog_sort_order',
                                         Qt.DescendingOrder)
        self.tv.sortByColumn(sort_column, sort_order)

        # ~~~~~~~~ Create the ButtonBox ~~~~~~~~
        self.dialogButtonBox = QDialogButtonBox(QDialogButtonBox.Cancel | QDialogButtonBox.Help)
        self.dialogButtonBox.setOrientation(Qt.Horizontal)
        self.import_button = self.dialogButtonBox.addButton(self.dialogButtonBox.Ok)
        self.import_button.setText('Import Annotations')

        # Action buttons
        self.toggle_checkmarks_button = self.dialogButtonBox.addButton('Clear All', QDialogButtonBox.ActionRole)
        self.toggle_checkmarks_button.setObjectName('toggle_checkmarks_button')

        scb_text = 'Show match status'
        if self.show_confidence_colors:
            scb_text = "Hide match status"
        self.show_confidence_button = self.dialogButtonBox.addButton(scb_text, QDialogButtonBox.ActionRole)
        self.show_confidence_button.setObjectName('confidence_button')
        if self.show_confidence_colors:
            self.show_confidence_button.setIcon(get_icon('images/matches_hide.png'))
        else:
            self.show_confidence_button.setIcon(get_icon('images/matches_show.png'))

        self.preview_button = self.dialogButtonBox.addButton('Preview', QDialogButtonBox.ActionRole)
        self.preview_button.setObjectName('preview_button')

        self.dialogButtonBox.clicked.connect(self.show_annotated_books_dialog_clicked)
        self.l.addWidget(self.dialogButtonBox)

        # Cause our dialog size to be restored from prefs or created on first usage
        self.resize_dialog()
Ejemplo n.º 42
0
    def _initialize_controls(self):
        self.setWindowTitle(_('User plugins'))
        self.setWindowIcon(QIcon(I('plugins/plugin_updater.png')))
        layout = QVBoxLayout(self)
        self.setLayout(layout)
        title_layout = ImageTitleLayout(self, 'plugins/plugin_updater.png',
                _('User plugins'))
        layout.addLayout(title_layout)

        header_layout = QHBoxLayout()
        layout.addLayout(header_layout)
        self.filter_combo = PluginFilterComboBox(self)
        self.filter_combo.setMinimumContentsLength(20)
        self.filter_combo.currentIndexChanged[int].connect(self._filter_combo_changed)
        la = QLabel(_('Filter list of &plugins')+':', self)
        la.setBuddy(self.filter_combo)
        header_layout.addWidget(la)
        header_layout.addWidget(self.filter_combo)
        header_layout.addStretch(10)

        # filter plugins by name
        la = QLabel(_('Filter by &name')+':', self)
        header_layout.addWidget(la)
        self.filter_by_name_lineedit = QLineEdit(self)
        la.setBuddy(self.filter_by_name_lineedit)
        self.filter_by_name_lineedit.setText("")
        self.filter_by_name_lineedit.textChanged.connect(self._filter_name_lineedit_changed)

        header_layout.addWidget(self.filter_by_name_lineedit)

        self.plugin_view = QTableView(self)
        self.plugin_view.horizontalHeader().setStretchLastSection(True)
        self.plugin_view.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.plugin_view.setSelectionMode(QAbstractItemView.SingleSelection)
        self.plugin_view.setAlternatingRowColors(True)
        self.plugin_view.setSortingEnabled(True)
        self.plugin_view.setIconSize(QSize(28, 28))
        layout.addWidget(self.plugin_view)

        details_layout = QHBoxLayout()
        layout.addLayout(details_layout)
        forum_label = self.forum_label = QLabel('')
        forum_label.setTextInteractionFlags(Qt.LinksAccessibleByMouse | Qt.LinksAccessibleByKeyboard)
        forum_label.linkActivated.connect(self._forum_label_activated)
        details_layout.addWidget(QLabel(_('Description')+':', self), 0, Qt.AlignLeft)
        details_layout.addWidget(forum_label, 1, Qt.AlignRight)

        self.description = QLabel(self)
        self.description.setFrameStyle(QFrame.Panel | QFrame.Sunken)
        self.description.setAlignment(Qt.AlignTop | Qt.AlignLeft)
        self.description.setMinimumHeight(40)
        self.description.setWordWrap(True)
        layout.addWidget(self.description)

        self.button_box = QDialogButtonBox(QDialogButtonBox.Close)
        self.button_box.rejected.connect(self.reject)
        self.finished.connect(self._finished)
        self.install_button = self.button_box.addButton(_('&Install'), QDialogButtonBox.AcceptRole)
        self.install_button.setToolTip(_('Install the selected plugin'))
        self.install_button.clicked.connect(self._install_clicked)
        self.install_button.setEnabled(False)
        self.configure_button = self.button_box.addButton(' '+_('&Customize plugin ')+' ', QDialogButtonBox.ResetRole)
        self.configure_button.setToolTip(_('Customize the options for this plugin'))
        self.configure_button.clicked.connect(self._configure_clicked)
        self.configure_button.setEnabled(False)
        layout.addWidget(self.button_box)
 def __init__(self, parent=None):
     QTableView.__init__(self, parent)