class ProgressIndicator(QWidget): # {{{ def __init__(self, *args): QWidget.__init__(self, *args) self.setGeometry(0, 0, 300, 350) self.pi = _ProgressIndicator(self) self.status = QLabel(self) self.status.setWordWrap(True) self.status.setAlignment(Qt.AlignmentFlag.AlignHCenter | Qt.AlignmentFlag.AlignTop) self.setVisible(False) self.pos = None def start(self, msg=''): view = self.parent() pwidth, pheight = view.size().width(), view.size().height() self.resize(pwidth, min(pheight, 250)) if self.pos is None: self.move(0, int((pheight - self.size().height()) / 2)) else: self.move(self.pos[0], self.pos[1]) self.pi.resize(self.pi.sizeHint()) self.pi.move(int((self.size().width() - self.pi.size().width()) / 2), 0) self.status.resize(self.size().width(), self.size().height() - self.pi.size().height() - 10) self.status.move(0, self.pi.size().height() + 10) self.status.setText('<h1>' + msg + '</h1>') self.setVisible(True) self.pi.startAnimation() def stop(self): self.pi.stopAnimation() self.setVisible(False)
class TitleBar(QWidget): def __init__(self, parent=None): QWidget.__init__(self, parent) self.l = l = QHBoxLayout(self) self.icon = Icon(self, size=ICON_SIZE) l.addWidget(self.icon) self.title = QLabel('') self.title.setAlignment(Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter) l.addWidget(self.title) l.addStrut(25) self.msg = la = Message(self) l.addWidget(la) self.default_message = __appname__ + ' ' + _('version') + ' ' + \ __version__ + ' ' + _('created by Kovid Goyal') self.show_plugin() self.show_msg() def show_plugin(self, plugin=None): self.icon.set_icon( QIcon(I('lt.png') if plugin is None else plugin.icon)) self.title.setText( '<h1>' + (_('Preferences') if plugin is None else plugin.gui_name)) def show_msg(self, msg=None): msg = msg or self.default_message self.msg.setText(' '.join(msg.splitlines()).strip())
class LoadingOverlay(QWidget): def __init__(self, parent=None): QWidget.__init__(self, parent) self.l = l = QVBoxLayout(self) self.pi = ProgressIndicator(self, 96) self.setVisible(False) self.label = QLabel(self) self.label.setText( '<i>testing with some long and wrap worthy message that should hopefully still render well' ) self.label.setTextFormat(Qt.TextFormat.RichText) self.label.setAlignment(Qt.AlignmentFlag.AlignTop | Qt.AlignmentFlag.AlignHCenter) self.label.setWordWrap(True) if parent is None: self.resize(300, 300) else: self.resize(parent.size()) self.setAutoFillBackground(True) pal = self.palette() col = pal.color(QPalette.ColorRole.Window) col.setAlphaF(0.8) pal.setColor(QPalette.ColorRole.Window, col) self.setPalette(pal) self.move(0, 0) f = self.font() f.setBold(True) fm = QFontInfo(f) f.setPixelSize(int(fm.pixelSize() * 1.5)) self.label.setFont(f) l.addStretch(10) l.addWidget(self.pi) l.addWidget(self.label) l.addStretch(10) def __call__(self, msg=''): self.label.setText(msg) self.resize(self.parent().size()) self.move(0, 0) self.setVisible(True) self.raise_() self.setFocus(Qt.FocusReason.OtherFocusReason) self.update() def hide(self): self.parent().web_view.setFocus(Qt.FocusReason.OtherFocusReason) self.pi.stop() return QWidget.hide(self) def showEvent(self, ev): # import time # self.st = time.monotonic() self.pi.start() def hideEvent(self, ev): # import time # print(1111111, time.monotonic() - self.st) self.pi.stop()
def __init__(self, parent): QFrame.__init__(self, parent) self.setFrameStyle(QFrame.Shape.NoFrame if gprefs['tag_browser_old_look'] else QFrame.Shape.StyledPanel) self._parent = parent self._layout = QVBoxLayout(self) self._layout.setContentsMargins(0,0,0,0) # Set up the find box & button self.tb_bar = tbb = TagBrowserBar(self) tbb.clear_find.connect(self.reset_find) self.alter_tb, self.item_search, self.search_button = tbb.alter_tb, tbb.item_search, tbb.search_button self.toggle_search_button = tbb.toggle_search_button self._layout.addWidget(tbb) self.current_find_position = None self.search_button.clicked.connect(self.find) self.item_search.lineEdit().textEdited.connect(self.find_text_changed) self.item_search.activated[str].connect(self.do_find) # The tags view parent.tags_view = TagsView(parent) self.tags_view = parent.tags_view self._layout.insertWidget(0, parent.tags_view) # Now the floating 'not found' box l = QLabel(self.tags_view) self.not_found_label = l l.setFrameStyle(QFrame.Shape.StyledPanel) l.setAutoFillBackground(True) l.setText('<p><b>'+_('No more matches.</b><p> Click Find again to go to first match')) l.setAlignment(Qt.AlignmentFlag.AlignVCenter) l.setWordWrap(True) l.resize(l.sizeHint()) l.move(10,20) l.setVisible(False) self.not_found_label_timer = QTimer() self.not_found_label_timer.setSingleShot(True) self.not_found_label_timer.timeout.connect(self.not_found_label_timer_event, type=Qt.ConnectionType.QueuedConnection) self.collapse_all_action = ac = QAction(parent) parent.addAction(ac) parent.keyboard.register_shortcut('tag browser collapse all', _('Collapse all'), default_keys=(), action=ac, group=_('Tag browser')) connect_lambda(ac.triggered, self, lambda self: self.tags_view.collapseAll()) # The Configure Tag Browser button l = self.alter_tb ac = QAction(parent) parent.addAction(ac) parent.keyboard.register_shortcut('tag browser alter', _('Configure Tag browser'), default_keys=(), action=ac, group=_('Tag browser')) ac.triggered.connect(l.showMenu) l.m.aboutToShow.connect(self.about_to_show_configure_menu) l.m.show_counts_action = ac = l.m.addAction('counts') ac.triggered.connect(self.toggle_counts) l.m.show_avg_rating_action = ac = l.m.addAction('avg rating') ac.triggered.connect(self.toggle_avg_rating) sb = l.m.addAction(_('Sort by')) sb.m = l.sort_menu = QMenu(l.m) sb.setMenu(sb.m) sb.bg = QActionGroup(sb) # Must be in the same order as db2.CATEGORY_SORTS for i, x in enumerate((_('Name'), _('Number of books'), _('Average rating'))): a = sb.m.addAction(x) sb.bg.addAction(a) a.setCheckable(True) if i == 0: a.setChecked(True) sb.setToolTip( _('Set the sort order for entries in the Tag browser')) sb.setStatusTip(sb.toolTip()) ma = l.m.addAction(_('Search type when selecting multiple items')) ma.m = l.match_menu = QMenu(l.m) ma.setMenu(ma.m) ma.ag = QActionGroup(ma) # Must be in the same order as db2.MATCH_TYPE for i, x in enumerate((_('Match any of the items'), _('Match all of the items'))): a = ma.m.addAction(x) ma.ag.addAction(a) a.setCheckable(True) if i == 0: a.setChecked(True) ma.setToolTip( _('When selecting multiple entries in the Tag browser ' 'match any or all of them')) ma.setStatusTip(ma.toolTip()) mt = l.m.addAction(_('Manage authors, tags, etc.')) mt.setToolTip(_('All of these category_managers are available by right-clicking ' 'on items in the Tag browser above')) mt.m = l.manage_menu = QMenu(l.m) mt.setMenu(mt.m) ac = QAction(parent) parent.addAction(ac) parent.keyboard.register_shortcut('tag browser toggle item', _("'Click' found item"), default_keys=(), action=ac, group=_('Tag browser')) ac.triggered.connect(self.toggle_item) ac = QAction(parent) parent.addAction(ac) parent.keyboard.register_shortcut('tag browser set focus', _("Give the Tag browser keyboard focus"), default_keys=(), action=ac, group=_('Tag browser')) ac.triggered.connect(self.give_tb_focus)
def __init__(self, window, cat_name, tag_to_match, get_book_ids, sorter, ttm_is_first_letter=False, category=None, fm=None): QDialog.__init__(self, window) Ui_TagListEditor.__init__(self) self.setupUi(self) self.verticalLayout_2.setAlignment(Qt.AlignmentFlag.AlignCenter) self.search_box.setMinimumContentsLength(25) # Put the category name into the title bar t = self.windowTitle() self.category_name = cat_name self.category = category self.setWindowTitle(t + ' (' + cat_name + ')') # Remove help icon on title bar icon = self.windowIcon() self.setWindowFlags(self.windowFlags() & (~Qt.WindowType.WindowContextHelpButtonHint)) self.setWindowIcon(icon) # Get saved geometry info try: self.table_column_widths = \ gprefs.get('tag_list_editor_table_widths', None) except: pass # initialization self.to_rename = {} self.to_delete = set() self.all_tags = {} self.original_names = {} self.ordered_tags = [] self.sorter = sorter self.get_book_ids = get_book_ids self.text_before_editing = '' # Capture clicks on the horizontal header to sort the table columns hh = self.table.horizontalHeader() hh.sectionResized.connect(self.table_column_resized) hh.setSectionsClickable(True) hh.sectionClicked.connect(self.do_sort) hh.setSortIndicatorShown(True) self.last_sorted_by = 'name' self.name_order = 0 self.count_order = 1 self.was_order = 1 self.edit_delegate = EditColumnDelegate(self.table) self.edit_delegate.editing_finished.connect(self.stop_editing) self.edit_delegate.editing_started.connect(self.start_editing) self.table.setItemDelegateForColumn(0, self.edit_delegate) if prefs['case_sensitive']: self.string_contains = contains else: self.string_contains = self.case_insensitive_compare self.delete_button.clicked.connect(self.delete_tags) self.table.delete_pressed.connect(self.delete_pressed) self.rename_button.clicked.connect(self.rename_tag) self.undo_button.clicked.connect(self.undo_edit) self.table.itemDoubleClicked.connect(self._rename_tag) self.table.itemChanged.connect(self.finish_editing) self.buttonBox.button(QDialogButtonBox.StandardButton.Ok).setText( _('&OK')) self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel).setText( _('&Cancel')) self.buttonBox.accepted.connect(self.accepted) self.search_box.initialize('tag_list_search_box_' + cat_name) le = self.search_box.lineEdit() ac = le.findChild(QAction, QT_HIDDEN_CLEAR_ACTION) if ac is not None: ac.triggered.connect(self.clear_search) self.search_box.textChanged.connect(self.search_text_changed) self.search_button.clicked.connect(self.do_search) self.search_button.setDefault(True) l = QLabel(self.table) self.not_found_label = l l.setFrameStyle(QFrame.Shape.StyledPanel) l.setAutoFillBackground(True) l.setText(_('No matches found')) l.setAlignment(Qt.AlignmentFlag.AlignVCenter) l.resize(l.sizeHint()) l.move(10, 0) l.setVisible(False) self.not_found_label_timer = QTimer() self.not_found_label_timer.setSingleShot(True) self.not_found_label_timer.timeout.connect( self.not_found_label_timer_event, type=Qt.ConnectionType.QueuedConnection) self.filter_box.initialize('tag_list_filter_box_' + cat_name) le = self.filter_box.lineEdit() ac = le.findChild(QAction, QT_HIDDEN_CLEAR_ACTION) if ac is not None: ac.triggered.connect(self.clear_filter) le.returnPressed.connect(self.do_filter) self.filter_button.clicked.connect(self.do_filter) self.apply_vl_checkbox.clicked.connect(self.vl_box_changed) self.table.setEditTriggers( QAbstractItemView.EditTrigger.EditKeyPressed) try: geom = gprefs.get('tag_list_editor_dialog_geometry', None) if geom is not None: QApplication.instance().safe_restore_geometry( self, QByteArray(geom)) else: self.resize(self.sizeHint() + QSize(150, 100)) except: pass self.is_enumerated = False if fm: if fm['datatype'] == 'enumeration': self.is_enumerated = True self.enum_permitted_values = fm.get('display', {}).get( 'enum_values', None) # Add the data self.search_item_row = -1 self.fill_in_table(None, tag_to_match, ttm_is_first_letter) self.table.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu) self.table.customContextMenuRequested.connect(self.show_context_menu)
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}</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
def __init__(self, db, book_id_map, parent=None): from calibre.ebooks.oeb.polish.main import HELP QDialog.__init__(self, parent) self.db, self.book_id_map = weakref.ref(db), book_id_map self.setWindowIcon(QIcon(I('polish.png'))) title = _('Polish book') if len(book_id_map) > 1: title = _('Polish %d books') % len(book_id_map) self.setWindowTitle(title) self.help_text = { 'polish': _('<h3>About Polishing books</h3>%s') % HELP['about'].format( _('''<p>If you have both EPUB and ORIGINAL_EPUB in your book, then polishing will run on ORIGINAL_EPUB (the same for other ORIGINAL_* formats). So if you want Polishing to not run on the ORIGINAL_* format, delete the ORIGINAL_* format before running it.</p>''')), 'embed': _('<h3>Embed referenced fonts</h3>%s') % HELP['embed'], 'subset': _('<h3>Subsetting fonts</h3>%s') % HELP['subset'], 'smarten_punctuation': _('<h3>Smarten punctuation</h3>%s') % HELP['smarten_punctuation'], 'metadata': _('<h3>Updating metadata</h3>' '<p>This will update all metadata <i>except</i> the cover in the' ' e-book files to match the current metadata in the' ' calibre library.</p>' ' <p>Note that most e-book' ' formats are not capable of supporting all the' ' metadata in calibre.</p><p>There is a separate option to' ' update the cover.</p>'), 'do_cover': _('<h3>Update cover</h3><p>Update the covers in the e-book files to match the' ' current cover in the calibre library.</p>' '<p>If the e-book file does not have' ' an identifiable cover, a new cover is inserted.</p>'), 'jacket': _('<h3>Book jacket</h3>%s') % HELP['jacket'], 'remove_jacket': _('<h3>Remove book jacket</h3>%s') % HELP['remove_jacket'], 'remove_unused_css': _('<h3>Remove unused CSS rules</h3>%s') % HELP['remove_unused_css'], 'compress_images': _('<h3>Losslessly compress images</h3>%s') % HELP['compress_images'], 'add_soft_hyphens': _('<h3>Add soft-hyphens</h3>%s') % HELP['add_soft_hyphens'], 'remove_soft_hyphens': _('<h3>Remove soft-hyphens</h3>%s') % HELP['remove_soft_hyphens'], 'upgrade_book': _('<h3>Upgrade book internals</h3>%s') % HELP['upgrade_book'], } self.l = l = QGridLayout() self.setLayout(l) self.la = la = QLabel('<b>' + _('Select actions to perform:')) l.addWidget(la, 0, 0, 1, 2) count = 0 self.all_actions = OrderedDict([ ('embed', _('&Embed all referenced fonts')), ('subset', _('&Subset all embedded fonts')), ('smarten_punctuation', _('Smarten &punctuation')), ('metadata', _('Update &metadata in the book files')), ('do_cover', _('Update the &cover in the book files')), ('jacket', _('Add/replace metadata as a "book &jacket" page')), ('remove_jacket', _('&Remove a previously inserted book jacket')), ('remove_unused_css', _('Remove &unused CSS rules from the book')), ('compress_images', _('Losslessly &compress images')), ('add_soft_hyphens', _('Add s&oft hyphens')), ('remove_soft_hyphens', _('Remove so&ft hyphens')), ('upgrade_book', _('&Upgrade book internals')), ]) prefs = gprefs.get('polishing_settings', {}) for name, text in iteritems(self.all_actions): count += 1 x = QCheckBox(text, self) x.setChecked(prefs.get(name, False)) x.setObjectName(name) connect_lambda( x.stateChanged, self, lambda self, state: self.option_toggled( self.sender().objectName(), state)) l.addWidget(x, count, 0, 1, 1) setattr(self, 'opt_' + name, x) la = QLabel(' <a href="#%s">%s</a>' % (name, _('About'))) setattr(self, 'label_' + name, x) la.linkActivated.connect(self.help_link_activated) l.addWidget(la, count, 1, 1, 1) count += 1 l.addItem(QSpacerItem(10, 10, vPolicy=QSizePolicy.Policy.Expanding), count, 1, 1, 2) la = self.help_label = QLabel('') self.help_link_activated('#polish') la.setWordWrap(True) la.setTextFormat(Qt.TextFormat.RichText) la.setFrameShape(QFrame.Shape.StyledPanel) la.setAlignment(Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignTop) la.setLineWidth(2) la.setStyleSheet('QLabel { margin-left: 75px }') l.addWidget(la, 0, 2, count + 1, 1) l.setColumnStretch(2, 1) self.show_reports = sr = QCheckBox(_('Show &report'), self) sr.setChecked(gprefs.get('polish_show_reports', True)) sr.setToolTip( textwrap.fill( _('Show a report of all the actions performed' ' after polishing is completed'))) l.addWidget(sr, count + 1, 0, 1, 1) self.bb = bb = QDialogButtonBox( QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel) bb.accepted.connect(self.accept) bb.rejected.connect(self.reject) self.save_button = sb = bb.addButton( _('&Save settings'), QDialogButtonBox.ButtonRole.ActionRole) sb.clicked.connect(self.save_settings) self.load_button = lb = bb.addButton( _('&Load settings'), QDialogButtonBox.ButtonRole.ActionRole) self.load_menu = QMenu(lb) lb.setMenu(self.load_menu) self.all_button = b = bb.addButton( _('Select &all'), QDialogButtonBox.ButtonRole.ActionRole) connect_lambda(b.clicked, self, lambda self: self.select_all(True)) self.none_button = b = bb.addButton( _('Select &none'), QDialogButtonBox.ButtonRole.ActionRole) connect_lambda(b.clicked, self, lambda self: self.select_all(False)) l.addWidget(bb, count + 1, 1, 1, -1) self.setup_load_button() self.resize(QSize(950, 600))