def closeEvent(self, e): self.write_settings() if self.system_tray_icon.isVisible(): if not dynamic["systray_msg"] and not isosx: info_dialog( self, "calibre", "calibre " + _( "will keep running in the system tray. To close it, " "choose <b>Quit</b> in the context menu of the " "system tray." ), show_copy_button=False, ).exec_() dynamic["systray_msg"] = True self.hide_windows() e.ignore() else: if self.confirm_quit(): try: self.shutdown(write_settings=False) except: import traceback traceback.print_exc() e.accept() else: e.ignore()
def accept(self, *args): if self.shortcut_config.is_editing: from calibre.gui2 import info_dialog info_dialog(self, _('Still editing'), _('You are in the middle of editing a keyboard shortcut' ' first complete that, by clicking outside the ' ' shortcut editing box.'), show=True) return c = config() c.set('serif_family', unicode(self.serif_family.currentFont().family())) c.set('sans_family', unicode(self.sans_family.currentFont().family())) c.set('mono_family', unicode(self.mono_family.currentFont().family())) c.set('default_font_size', self.default_font_size.value()) c.set('mono_font_size', self.mono_font_size.value()) c.set('standard_font', {0:'serif', 1:'sans', 2:'mono'}[self.standard_font.currentIndex()]) c.set('user_css', unicode(self.css.toPlainText())) c.set('remember_window_size', self.opt_remember_window_size.isChecked()) c.set('fit_images', self.opt_fit_images.isChecked()) c.set('max_fs_width', int(self.max_fs_width.value())) c.set('hyphenate', self.hyphenate.isChecked()) c.set('remember_current_page', self.opt_remember_current_page.isChecked()) c.set('wheel_flips_pages', self.opt_wheel_flips_pages.isChecked()) c.set('page_flip_duration', self.opt_page_flip_duration.value()) c.set('font_magnification_step', float(self.opt_font_mag_step.value())/100.) idx = self.hyphenate_default_lang.currentIndex() c.set('hyphenate_default_lang', str(self.hyphenate_default_lang.itemData(idx).toString())) c.set('line_scrolling_stops_on_pagebreaks', self.opt_line_scrolling_stops_on_pagebreaks.isChecked()) c.set('fullscreen_clock', self.opt_fullscreen_clock.isChecked()) return QDialog.accept(self, *args)
def apply_diff(self, identical_msg, cache, syntax_map, changed_names, renamed_names, removed_names, added_names): self.view.clear() self.apply_diff_calls = calls = [] def add(args, kwargs): self.view.add_diff(*args, **kwargs) calls.append((args, kwargs)) if len(changed_names) + len(renamed_names) + len(removed_names) + len(added_names) < 1: info_dialog(self, _('No changes found'), identical_msg, show=True) return True kwargs = lambda name: {'context':self.context, 'beautify':self.beautify, 'syntax':syntax_map.get(name, None)} if isinstance(changed_names, dict): for name, other_name in sorted(changed_names.iteritems(), key=lambda x:numeric_sort_key(x[0])): args = (name, other_name, cache.left(name), cache.right(other_name)) add(args, kwargs(name)) else: for name in sorted(changed_names, key=numeric_sort_key): args = (name, name, cache.left(name), cache.right(name)) add(args, kwargs(name)) for name in sorted(added_names, key=numeric_sort_key): args = (_('[%s was added]') % name, name, None, cache.right(name)) add(args, kwargs(name)) for name in sorted(removed_names, key=numeric_sort_key): args = (name, _('[%s was removed]') % name, cache.left(name), None) add(args, kwargs(name)) for name, new_name in sorted(renamed_names.iteritems(), key=lambda x:numeric_sort_key(x[0])): args = (name, new_name, None, None) add(args, kwargs(name))
def copy_saved(self, job): if job.traceback is not None: return error_dialog(self.gui, _('Failed to save copy'), _('Failed to save copy, click Show details for more information.'), det_msg=job.traceback, show=True) msg = _('Copy saved to %s') % job.result info_dialog(self.gui, _('Copy saved'), msg, show=True) self.gui.show_status_message(msg, 5)
def reset_dialogs(self): for key in dynamic.keys(): if key.startswith('find_duplicates_') and key.endswith('_again') \ and dynamic[key] is False: dynamic[key] = True info_dialog(self, _('Done'), _('Confirmation dialogs have all been reset'), show=True)
def _files_added(self, paths=[], names=[], infos=[], on_card=None): self.gui.tags_view.disable_recounting = False if paths: self.gui.upload_books(paths, list(map(ascii_filename, names)), infos, on_card=on_card) self.gui.status_bar.show_message(_("Uploading books to device."), 2000) if getattr(self._adder, "number_of_books_added", 0) > 0: self.gui.library_view.model().books_added(self._adder.number_of_books_added) self.gui.library_view.set_current_row(0) if hasattr(self.gui, "db_images"): self.gui.db_images.reset() self.gui.tags_view.recount() if getattr(self._adder, "merged_books", False): books = u"\n".join( [ x if isinstance(x, unicode) else x.decode(preferred_encoding, "replace") for x in self._adder.merged_books ] ) info_dialog( self.gui, _("Merged some books"), _( "The following %d duplicate books were found and incoming " "book formats were processed and merged into your " "Calibre database according to your automerge " "settings:" ) % len(self._adder.merged_books), det_msg=books, show=True, ) if getattr(self._adder, "number_of_books_added", 0) > 0 or getattr(self._adder, "merged_books", False): # The formats of the current book could have changed if # automerge is enabled current_idx = self.gui.library_view.currentIndex() if current_idx.isValid(): self.gui.library_view.model().current_changed(current_idx, current_idx) if getattr(self._adder, "critical", None): det_msg = [] for name, log in self._adder.critical.items(): if isinstance(name, str): name = name.decode(filesystem_encoding, "replace") det_msg.append(name + "\n" + log) warning_dialog( self.gui, _("Failed to read metadata"), _("Failed to read metadata from the following") + ":", det_msg="\n\n".join(det_msg), show=True, ) if hasattr(self._adder, "cleanup"): self._adder.cleanup() self._adder.setParent(None) del self._adder self._adder = None
def help(self): """ To add help functionality for a specific format: In gui2.catalog.catalog_<format>.py, add the following: from calibre.gui2 import open_url from PyQt5.Qt import QUrl In the PluginWidget() class, add this method: def show_help(self): url = 'file:///' + P('catalog/help_<format>.html') open_url(QUrl(url)) Create the help file at resources/catalog/help_<format>.html """ if self.tabs.count() > 1 and hasattr(self.tabs.widget(1), "show_help"): try: self.tabs.widget(1).show_help() except: info_dialog( self, _("No help available"), _("No help available for this output format."), show_copy_button=False, show=True, )
def reset_confirmation_dialogs(self, *args): for key in dynamic.keys(): if key.endswith('_again') and dynamic[key] is False: dynamic[key] = True gprefs['questions_to_auto_skip'] = [] info_dialog(self, _('Done'), _('Confirmation dialogs have all been reset'), show=True)
def add_key(self): d = self.create_key(self) d.exec_() if d.result() != d.Accepted: # New key generation cancelled. return new_key_value = d.key_value if type(self.plugin_keys) == dict: if new_key_value in self.plugin_keys.values(): old_key_name = [name for name, value in self.plugin_keys.iteritems() if value == new_key_value][0] info_dialog( None, "{0} {1}: Duplicate {2}".format(PLUGIN_NAME, PLUGIN_VERSION, self.key_type_name), u"The new {1} is the same as the existing {1} named <strong>{0}</strong> and has not been added.".format( old_key_name, self.key_type_name ), show=True, ) return self.plugin_keys[d.key_name] = new_key_value else: if new_key_value in self.plugin_keys: info_dialog( None, "{0} {1}: Duplicate {2}".format(PLUGIN_NAME, PLUGIN_VERSION, self.key_type_name), u"This {0} is already in the list of {0}s has not been added.".format(self.key_type_name), show=True, ) return self.plugin_keys.append(d.key_value) self.listy.clear() self.populate_list()
def check_multi_selection(self): if len(self.selectedItems()) > 1: info_dialog(self, _('Multiple items selected'), _( 'You are trying to move multiple items at once, this is not supported. Instead use' ' Drag and Drop to move multiple items'), show=True) return False return True
def get_results(self): # We only want the search plugins to run # a maximum set amount of time before giving up. self.hang_check += 1 if self.hang_check >= self.hang_time: self.search_pool.abort() self.checker.stop() else: # Stop the checker if not threads are running. if not self.search_pool.threads_running() and not self.search_pool.has_tasks(): self.checker.stop() while self.search_pool.has_results(): res, store_plugin = self.search_pool.get_result() if res: self.results_view.model().add_result(res, store_plugin) if not self.search_pool.threads_running() and not self.results_view.model().has_results(): info_dialog( self, _("No matches"), _("Couldn't find any books matching your query."), show=True, show_copy_button=False, )
def mark_groups_as_duplicate_exemptions(self, all_groups): can_exempt = self.duplicate_finder.check_can_mark_exemption(all_groups) if can_exempt: # Ensure that the selection is moved onto the current duplicate group duplicate_ids = self.duplicate_finder.get_current_duplicate_group_ids() self.gui.library_view.select_rows(duplicate_ids) exemption_type = 'books' if self.duplicate_finder.is_searching_for_authors(): exemption_type = 'authors' dialog_name = 'find_duplicates_mark_all_groups' if all_groups else 'find_duplicates_mark_group' if not confirm('<p>' + _( 'This action will ensure that each of the %s in the group ' 'are exempt from appearing together again in future.<p>' 'Are you <b>sure</b> you want to proceed?')%exemption_type, dialog_name, self.gui): return if all_groups: self.duplicate_finder.mark_groups_as_duplicate_exemptions() else: self.duplicate_finder.mark_current_group_as_duplicate_exemptions() else: info_dialog(self.gui, _('No duplicates in group'), _('There are no duplicates remaining in this group.'), show=True, show_copy_button=False) self.update_actions_enabled()
def view_specific_format(self, triggered): rows = list(self.gui.library_view.selectionModel().selectedRows()) if not rows or len(rows) == 0: d = error_dialog(self.gui, _('Cannot view'), _('No book selected')) d.exec_() return db = self.gui.library_view.model().db rows = [r.row() for r in rows] formats = [db.formats(row) for row in rows] formats = [list(f.upper().split(',')) if f else None for f in formats] all_fmts = set([]) for x in formats: if x: for f in x: all_fmts.add(f) if not all_fmts: error_dialog(self.gui, _('Format unavailable'), _('Selected books have no formats'), show=True) return d = ChooseFormatDialog(self.gui, _('Choose the format to view'), list(sorted(all_fmts))) if d.exec_() == d.Accepted: fmt = d.format() orig_num = len(rows) rows = [rows[i] for i in range(len(rows)) if formats[i] and fmt in formats[i]] if self._view_check(len(rows)): for row in rows: self.view_format(row, fmt) if len(rows) < orig_num: info_dialog(self.gui, _('Format unavailable'), _('Not all the selected books were available in' ' the %s format. You should convert' ' them first.')%fmt, show=True)
def restore_confirmations(self): changed = 0 for key in tuple(tprefs): if key.endswith('_again') and tprefs.get(key) is False: del tprefs[key] changed += 1 info_dialog(self, _('Disabled confirmations restored'), _( '%d disabled confirmation prompts were restored') % changed, show=True)
def install_fonts(self): from calibre.gui2.font_family_chooser import add_fonts families = add_fonts(self) if not families: return font_scanner.do_scan() self.refresh() info_dialog(self, _('Added fonts'), _('Added font families: %s')%(', '.join(families)), show=True)
def copy_to_library(self, loc, delete_after=False): rows = self.gui.library_view.selectionModel().selectedRows() if not rows or len(rows) == 0: return error_dialog(self.gui, _('Cannot copy'), _('No books selected'), show=True) ids = list(map(self.gui.library_view.model().id, rows)) db = self.gui.library_view.model().db if not db.exists_at(loc): return error_dialog(self.gui, _('No library'), _('No library found at %s')%loc, show=True) aname = _('Moving to') if delete_after else _('Copying to') dtitle = '%s %s'%(aname, os.path.basename(loc)) self.pd = ProgressDialog(dtitle, min=0, max=len(ids)-1, parent=self.gui, cancelable=False) def progress(idx, title): self.pd.set_msg(title) self.pd.set_value(idx) self.worker = Worker(ids, db, loc, Dispatcher(progress), Dispatcher(self.pd.accept), delete_after) self.worker.start() self.pd.exec_() donemsg = _('Copied %(num)d books to %(loc)s') if delete_after: donemsg = _('Moved %(num)d books to %(loc)s') if self.worker.error is not None: e, tb = self.worker.error error_dialog(self.gui, _('Failed'), _('Could not copy books: ') + e, det_msg=tb, show=True) else: self.gui.status_bar.show_message(donemsg % dict(num=len(ids), loc=loc), 2000) if self.worker.auto_merged_ids: books = '\n'.join(self.worker.auto_merged_ids.itervalues()) info_dialog(self.gui, _('Auto merged'), _('Some books were automatically merged into existing ' 'records in the target library. Click Show ' 'details to see which ones. This behavior is ' 'controlled by the Auto merge option in ' 'Preferences->Adding books.'), det_msg=books, show=True) if delete_after and self.worker.processed: v = self.gui.library_view ci = v.currentIndex() row = None if ci.isValid(): row = ci.row() v.model().delete_books_by_id(self.worker.processed, permanent=True) self.gui.iactions['Remove Books'].library_ids_deleted( self.worker.processed, row)
def migrate_files(self): unique_dlg_name = PLUGIN_NAME + u"import {0} keys".format(self.key_type_name).replace(' ', '_') #takes care of automatically remembering last directory caption = u"Select {0} files to import".format(self.key_type_name) filters = [(u"{0} files".format(self.key_type_name), [self.keyfile_ext])] files = choose_files(self, unique_dlg_name, caption, filters, all_files=False) counter = 0 skipped = 0 if files: for filename in files: fpath = os.path.join(config_dir, filename) filename = os.path.basename(filename) if type(self.plugin_keys) != dict: # must be the new Kindle for Android section print u"Getting keys from "+fpath new_keys = get_serials(fpath) for key in new_keys: if key in self.plugin_keys: skipped += 1 else: counter += 1 self.plugin_keys.append(key) else: new_key_name = os.path.splitext(os.path.basename(filename))[0] with open(fpath,'rb') as keyfile: new_key_value = keyfile.read() if self.binary_file: new_key_value = new_key_value.encode('hex') elif self.json_file: new_key_value = json.loads(new_key_value) match = False for key in self.plugin_keys.keys(): if uStrCmp(new_key_name, key, True): skipped += 1 msg = u"A key with the name <strong>{0}</strong> already exists!\nSkipping key file <strong>{1}</strong>.\nRename the existing key and import again".format(new_key_name,filename) inf = info_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), _(msg), show_copy_button=False, show=True) match = True break if not match: if new_key_value in self.plugin_keys.values(): old_key_name = [name for name, value in self.plugin_keys.iteritems() if value == new_key_value][0] skipped += 1 info_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), u"The key in file {0} is the same as the existing key <strong>{1}</strong> and has been skipped.".format(filename,old_key_name), show_copy_button=False, show=True) else: counter += 1 self.plugin_keys[new_key_name] = new_key_value msg = u"" if counter+skipped > 1: if counter > 0: msg += u"Imported <strong>{0:d}</strong> key {1}. ".format(counter, u"file" if counter == 1 else u"files") if skipped > 0: msg += u"Skipped <strong>{0:d}</strong> key {1}.".format(skipped, u"file" if counter == 1 else u"files") inf = info_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), _(msg), show_copy_button=False, show=True) return counter > 0
def _files_added(self, paths=[], names=[], infos=[], on_card=None): self.gui.tags_view.disable_recounting = False if paths: self.gui.upload_books(paths, list(map(ascii_filename, names)), infos, on_card=on_card) self.gui.status_bar.show_message( _('Uploading books to device.'), 2000) if getattr(self._adder, 'number_of_books_added', 0) > 0: self.gui.library_view.model().books_added(self._adder.number_of_books_added) self.gui.library_view.set_current_row(0) if hasattr(self.gui, 'db_images'): self.gui.db_images.reset() self.gui.tags_view.recount() if getattr(self._adder, 'merged_books', False): merged = defaultdict(list) for title, author in self._adder.merged_books: merged[author].append(title) lines = [] for author in sorted(merged, key=sort_key): lines.append(author) for title in sorted(merged[author], key=sort_key): lines.append('\t' + title) lines.append('') info_dialog(self.gui, _('Merged some books'), _('The following %d duplicate books were found and incoming ' 'book formats were processed and merged into your ' 'Calibre database according to your automerge ' 'settings:')%len(self._adder.merged_books), det_msg='\n'.join(lines), show=True) if getattr(self._adder, 'number_of_books_added', 0) > 0 or \ getattr(self._adder, 'merged_books', False): # The formats of the current book could have changed if # automerge is enabled current_idx = self.gui.library_view.currentIndex() if current_idx.isValid(): self.gui.library_view.model().current_changed(current_idx, current_idx) if getattr(self._adder, 'critical', None): det_msg = [] for name, log in self._adder.critical.items(): if isinstance(name, str): name = name.decode(filesystem_encoding, 'replace') det_msg.append(name+'\n'+log) warning_dialog(self.gui, _('Failed to read metadata'), _('Failed to read metadata from the following')+':', det_msg='\n\n'.join(det_msg), show=True) if hasattr(self._adder, 'cleanup'): self._adder.cleanup() self._adder.setParent(None) del self._adder self._adder = None
def reset_dialogs(self): for key in dynamic.keys(): if key.startswith('epubmerge_') and key.endswith('_again') \ and dynamic[key] is False: dynamic[key] = True info_dialog(self, _('Done'), _('Confirmation dialogs have all been reset'), show=True, show_copy_button=False)
def check_library(self): from calibre.gui2.dialogs.check_library import CheckLibraryDialog, DBCheck, DBCheckNew self.gui.library_view.save_state() m = self.gui.library_view.model() m.stop_metadata_backup() db = m.db db.prefs.disable_setting = True if hasattr(db, "new_api"): d = DBCheckNew(self.gui, db) else: d = DBCheck(self.gui, db) d.start() try: d.conn.close() except: pass d.break_cycles() self.gui.library_moved(db.library_path, call_close=not d.closed_orig_conn) if d.rejected: return if d.error is None: if not question_dialog( self.gui, _("Success"), _( "Found no errors in your calibre library database." " Do you want calibre to check if the files in your " " library match the information in the database?" ), ): return else: return error_dialog( self.gui, _("Failed"), _("Database integrity check failed, click Show details" " for details."), show=True, det_msg=d.error[1], ) self.gui.status_bar.show_message(_("Starting library scan, this may take a while")) try: QCoreApplication.processEvents() d = CheckLibraryDialog(self.gui, m.db) if not d.do_exec(): info_dialog( self.gui, _("No problems found"), _("The files in your library match the information " "in the database."), show=True, ) finally: self.gui.status_bar.clear_message()
def add(self, books): if isinstance(books, basestring): error_dialog( self.pd, _("Path error"), _("The specified directory could not be processed."), det_msg=books, show=True ) return self.canceled() if not books: info_dialog(self.pd, _("No books"), _("No books found"), show=True) return self.canceled() books = [[b] if isinstance(b, basestring) else b for b in books] restricted = set() for i in xrange(len(books)): files = books[i] restrictedi = set(f for f in files if not os.access(f, os.R_OK)) if restrictedi: files = [f for f in files if os.access(f, os.R_OK)] books[i] = files restricted |= restrictedi if restrictedi: det_msg = u"\n".join(restrictedi) warning_dialog( self.pd, _("No permission"), _( "Cannot add some files as you do not have " " permission to access them. Click Show" " Details to see the list of such files." ), det_msg=det_msg, show=True, ) books = list(filter(None, books)) if not books: return self.canceled() self.rfind = None from calibre.ebooks.metadata.worker import read_metadata self.rq = Queue() tasks = [] self.ids = {} self.nmap = {} self.duplicates = [] for i, b in enumerate(books): tasks.append((i, b)) self.ids[i] = b self.nmap[i] = os.path.basename(b[0]) self.worker = read_metadata(tasks, self.rq, spare_server=self.spare_server) self.pd.set_min(0) self.pd.set_max(len(self.ids)) self.pd.value = 0 self.db_adder = DBAdder(self, self.db, self.ids, self.nmap) self.db_adder.start() self.last_added_at = time.time() self.entry_count = len(self.ids) self.continue_updating = True single_shot(self.update)
def backup_status(self, location): dirty_text = 'no' try: dirty_text = \ unicode(self.gui.library_view.model().db.dirty_queue_length()) except: dirty_text = _('none') info_dialog(self.gui, _('Backup status'), '<p>'+ _('Book metadata files remaining to be written: %s') % dirty_text, show=True)
def count_message(action, count, show_diff=False): msg = _('%(action)s %(num)s occurrences of %(query)s' % dict(num=count, query=errfind, action=action)) if show_diff and count > 0: d = MessageBox(MessageBox.INFO, _('Searching done'), prepare_string_for_xml(msg), parent=gui_parent, show_copy_button=False) d.diffb = b = d.bb.addButton(_('See what &changed'), d.bb.ActionRole) b.setIcon(QIcon(I('diff.png'))), d.set_details(None), b.clicked.connect(d.accept) b.clicked.connect(partial(show_current_diff, allow_revert=True)) d.exec_() else: info_dialog(gui_parent, _('Searching done'), prepare_string_for_xml(msg), show=True)
def accept(self, *args): if self.shortcut_config.is_editing: from calibre.gui2 import info_dialog info_dialog(self, _('Still editing'), _('You are in the middle of editing a keyboard shortcut' ' first complete that, by clicking outside the ' ' shortcut editing box.'), show=True) return self.save_options(config()) return QDialog.accept(self, *args)
def restore_confirmations(self): changed = 0 for key in tuple(tprefs): if key.endswith('_again') and tprefs.get(key) is False: del tprefs[key] changed += 1 msg = _('There are no disabled confirmation prompts') if changed: msg = ngettext( 'One disabled confirmation prompt was restored', '{} disabled confirmation prompts were restored', changed).format(changed) info_dialog(self, _('Disabled confirmations restored'), msg, show=True)
def add_plugin(self): info = "" if iswindows else " [.zip %s]" % _("files") path = choose_files( self, "add a plugin dialog", _("Add plugin"), filters=[(_("Plugins") + info, ["zip"])], all_files=False, select_only_single_file=True, ) if not path: return path = path[0] if path and os.access(path, os.R_OK) and path.lower().endswith(".zip"): if not question_dialog( self, _("Are you sure?"), "<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 from calibre.customize.ui import config installed_plugins = frozenset(config["plugins"]) try: plugin = add_plugin(path) except NameConflict as e: return error_dialog(self, _("Already exists"), unicode(e), show=True) self._plugin_model.populate() self._plugin_model.reset() self.changed_signal.emit() self.check_for_add_to_toolbars(plugin, previously_installed=plugin.name in installed_plugins) info_dialog( self, _("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=True, show_copy_button=False, ) idx = self._plugin_model.plugin_to_index_by_properties(plugin) if idx.isValid(): self.highlight_index(idx) else: error_dialog(self, _("No valid plugin path"), _("%s is not a valid plugin path") % path).exec_()
def delete_requested(self, name, location): loc = location.replace('/', os.sep) self.stats.remove(location) self.build_menus() self.gui.iactions['Copy To Library'].build_menus() info_dialog(self.gui, _('Library removed'), _('The library %s has been removed from calibre. ' 'The files remain on your computer, if you want ' 'to delete them, you will have to do so manually.') % loc, show=True) if os.path.exists(loc): open_local_file(loc)
def mark_dirty(self): db = self.gui.library_view.model().db db.dirtied(list(db.data.iterallids())) info_dialog( self.gui, _("Backup metadata"), _( "Metadata will be backed up while calibre is running, at the " "rate of approximately 1 book every three seconds." ), show=True, )
def modify_plugin(self, op=''): index = self.plugin_view.currentIndex() if index.isValid(): if not index.parent().isValid(): name = unicode(index.data().toString()) return error_dialog(self, _('Error'), '<p>'+ _('Select an actual plugin under <b>%s</b> to customize')%name, show=True, show_copy_button=False) plugin = self._plugin_model.index_to_plugin(index) if op == 'toggle': if not plugin.can_be_disabled: error_dialog(self,_('Plugin cannot be disabled'), _('The plugin: %s cannot be disabled')%plugin.name).exec_() return if is_disabled(plugin): enable_plugin(plugin) else: disable_plugin(plugin) self._plugin_model.refresh_plugin(plugin) self.changed_signal.emit() if op == 'customize': if not plugin.is_customizable(): info_dialog(self, _('Plugin not customizable'), _('Plugin: %s does not need customization')%plugin.name).exec_() return self.changed_signal.emit() 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) if plugin.do_user_config(self.gui): self._plugin_model.refresh_plugin(plugin) elif op == 'remove': if not confirm('<p>' + _('Are you sure you want to remove the plugin: %s?')% '<b>{0}</b>'.format(plugin.name), 'confirm_plugin_removal_msg', parent=self): return msg = _('Plugin <b>{0}</b> successfully removed').format(plugin.name) if remove_plugin(plugin): self._plugin_model.populate() self._plugin_model.reset() self.changed_signal.emit() info_dialog(self, _('Success'), msg, show=True, show_copy_button=False) else: error_dialog(self, _('Cannot remove builtin plugin'), plugin.name + _(' cannot be removed. It is a ' 'builtin plugin. Try disabling it instead.')).exec_()
def _show_success_msg(restorer, parent=None): r = restorer olddb = _('The old database was saved as: %s')%force_unicode(r.olddb, filesystem_encoding) if r.errors_occurred: warning_dialog(parent, _('Success'), _('Restoring the database succeeded with some warnings' ' click Show details to see the details. %s')%olddb, det_msg=r.report, show=True) else: info_dialog(parent, _('Success'), _('Restoring database was successful. %s')%olddb, show=True, show_copy_button=False)
def process_files(self, criteria): container = self.current_container # The book being edited as a container object self.language = 'lang=\"' + get_language_code(criteria) + '\"' if criteria[0]: # Only convert the selected file name = editor_name(self.gui.central.current_editor) if not name or container.mime_map[name] not in OEB_DOCS: return info_dialog(self.gui, _('Cannot Process'), _('No file open for editing or the current file is not an (x)html file.'), show=True) data = container.raw_data(name) htmlstr = self.convert_text(data, criteria) if htmlstr != data: self.filesChanged = True self.changed_files.append(name) container.open(name, 'wb').write(htmlstr) else: # Cover the entire book # Set metadata and Table of Contents (TOC) if language changed if criteria[1] != 0: self.filesChanged = set_metadata_toc(container, get_language_code(criteria), criteria, self.changed_files, self.converter) # Check for orientation change direction_changed = False if criteria[7] != 0: direction_changed = set_flow_direction(container, get_language_code(criteria), criteria, self.changed_files, self.converter) # Cover the text portion from calibre_plugins.chinese_text.resources.dialogs import ShowProgressDialog d = ShowProgressDialog(self.gui, container, OEB_DOCS, criteria, self.convert_text, _('Converting')) cancelled_msg = '' if d.wasCanceled(): cancelled_msg = ' (cancelled)' self.filesChanged = self.filesChanged or (not d.clean) or direction_changed self.changed_files.extend(d.changed_files)
def _save_settings(self): from calibre.gui2.dialogs.confirm_delete import confirm message = '<p>' + _('Are you sure you want to save this setting in this library for this plugin?') + '</p>' \ + '<p>' + _('Any settings in other libraries or stored in a JSON file in your calibre plugins folder will not be touched.') + '</p>' \ + '<p>' + _('You must restart calibre afterwards.') + '</p>' if not confirm(message, self.namespace+'_save_settings', self): return ns_prefix = self._get_ns_prefix() key = unicode(self.keys_list.currentItem().text()) self.db.prefs.set_namespaced(self.namespace, key, self.db.prefs.raw_to_object(self.value_text.toPlainText())) d = info_dialog(self, 'Settings saved', '<p>' + _('All settings for this plugin in this library have been saved.') + '</p>' \ + '<p>' + _('Please restart calibre now.') + '</p>', 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() self.close() if d.do_restart: self.gui.quit(restart=True)
def _clear_settings(self): from calibre.gui2.dialogs.confirm_delete import confirm message = '<p>' + _('Are you sure you want to clear your settings in this library for this plugin?') + '</p>' \ + '<p>' + _('Any settings in other libraries or stored in a JSON file in your calibre plugins folder will not be touched.') + '</p>' \ + '<p>' + _('You must restart calibre afterwards.') + '</p>' if not confirm(message, self.namespace+'_clear_settings', self): return ns_prefix = self._get_ns_prefix() keys = [k for k in six.iterkeys(self.db.prefs) if k.startswith(ns_prefix)] for k in keys: del self.db.prefs[k] self._populate_settings() d = info_dialog(self, 'Settings deleted', '<p>' + _('All settings for this plugin in this library have been cleared.') + '</p>' \ + '<p>' + _('Please restart calibre now.') + '</p>', 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() self.close() if d.do_restart: self.gui.quit(restart=True)
def find(self, query): idx = self._plugin_model.find(query) if not idx.isValid(): return info_dialog(self, _('No matches'), _('Could not find any matching plugins'), show=True, show_copy_button=False) self.highlight_index(idx)
def view_specific_format(self, triggered): rows = list(self.gui.library_view.selectionModel().selectedRows()) if not rows or len(rows) == 0: d = error_dialog(self.gui, _('Cannot view'), _('No book selected')) d.exec_() return db = self.gui.library_view.model().db rows = [r.row() for r in rows] book_ids = [db.id(r) for r in rows] formats = [[x.upper() for x in db.new_api.formats(book_id)] for book_id in book_ids] all_fmts = set([]) for x in formats: if x: for f in x: all_fmts.add(f) if not all_fmts: error_dialog(self.gui, _('Format unavailable'), _('Selected books have no formats'), show=True) return d = ChooseFormatDialog(self.gui, _('Choose the format to view'), list(sorted(all_fmts))) self.gui.book_converted.connect(d.book_converted) if d.exec_() == d.Accepted: formats = [[x.upper() for x in db.new_api.formats(book_id)] for book_id in book_ids] fmt = d.format() orig_num = len(rows) rows = [ rows[i] for i in range(len(rows)) if formats[i] and fmt in formats[i] ] if self._view_check(len(rows)): for row in rows: self.view_format(row, fmt) if len(rows) < orig_num: info_dialog( self.gui, _('Format unavailable'), _('Not all the selected books were available in' ' the %s format. You should convert' ' them first.') % fmt, show=True) self.gui.book_converted.disconnect(d.book_converted)
def _files_added(self, adder, on_card=None): if adder.items: paths, infos, names = [], [], [] for mi, cover_path, format_paths in adder.items: mi.cover = cover_path paths.append(format_paths[0]), infos.append(mi) names.append(ascii_filename(os.path.basename(paths[-1]))) self.gui.upload_books(paths, names, infos, on_card=on_card) self.gui.status_bar.show_message(_('Uploading books to device.'), 2000) return if adder.number_of_books_added > 0: self.refresh_gui(adder.number_of_books_added, set_current_row=0) if adder.merged_books: merged = defaultdict(list) for title, author in adder.merged_books: merged[author].append(title) lines = [] for author in sorted(merged, key=sort_key): lines.append(author) for title in sorted(merged[author], key=sort_key): lines.append('\t' + title) lines.append('') pm = ngettext('The following duplicate book was found.', 'The following {} duplicate books were found.', len(adder.merged_books)).format( len(adder.merged_books)) info_dialog( self.gui, _('Merged some books'), pm + ' ' + _('Incoming book formats were processed and merged into your ' 'calibre database according to your auto-merge ' 'settings. Click "Show details" to see the list of merged books.' ), det_msg='\n'.join(lines), show=True) if adder.number_of_books_added > 0 or adder.merged_books: # The formats of the current book could have changed if # automerge is enabled current_idx = self.gui.library_view.currentIndex() if current_idx.isValid(): self.gui.library_view.model().current_changed( current_idx, current_idx)
def main(self): # create a restore point self.boss.add_savepoint('Before: Access Aide') container = self.current_container if not container: return error_dialog(self.gui, 'Access Aide', 'No book open, please open a book first.', show=True) if container.book_type != 'epub' or \ container.opf_version_parsed.major not in [2, 3]: message = 'Access Aide supports EPUB 2 and 3, {} {} given.' \ .format(container.book_type.upper(), container.opf_version_parsed.major) return error_dialog(self.gui, 'Access Aide', message, show=True) blacklist = ['toc.xhtml'] # iterate over book files for name, media_type in list(container.mime_map.items()): if media_type in OEB_DOCS \ and name not in blacklist: self.add_lang(container.parsed(name), self.get_lang(container)) self.add_aria(container.parsed(name)) elif media_type in OPF_MIME: self.add_metadata(container) else: continue container.dirty(name) info_dialog(self.gui, 'Access Aide', self.stats_report(), show=True) self.lang_stat.reset() self.aria_stat.reset() self.meta_stat.reset() # update the editor UI self.boss.apply_container_update_to_gui()
def _show_success_msg(restorer, parent=None): r = restorer olddb = _('The old database was saved as: %s') % force_unicode( r.olddb, filesystem_encoding) if r.errors_occurred: warning_dialog(parent, _('Success'), _('Restoring the database succeeded with some warnings' ' click Show details to see the details. %s') % olddb, det_msg=r.report, show=True) else: info_dialog(parent, _('Success'), _('Restoring database was successful. %s') % olddb, show=True, show_copy_button=False)
def migrate_files(self): unique_dlg_name = PLUGIN_NAME + u"import {0} keys".format(self.key_type_name).replace(' ', '_') #takes care of automatically remembering last directory caption = u"Select {0} files to import".format(self.key_type_name) filters = [(u"{0} files".format(self.key_type_name), [self.keyfile_ext])] files = choose_files(self, unique_dlg_name, caption, filters, all_files=False) counter = 0 skipped = 0 if files: for filename in files: fpath = os.path.join(config_dir, filename) filename = os.path.basename(filename) new_key_name = os.path.splitext(os.path.basename(filename))[0] with open(fpath,'rb') as keyfile: new_key_value = keyfile.read() if self.binary_file: new_key_value = new_key_value.encode('hex') elif self.json_file: new_key_value = json.loads(new_key_value) match = False for key in self.plugin_keys.keys(): if uStrCmp(new_key_name, key, True): skipped += 1 msg = u"A key with the name <strong>{0}</strong> already exists!\nSkipping key file <strong>{1}</strong>.\nRename the existing key and import again".format(new_key_name,filename) inf = info_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), _(msg), show_copy_button=False, show=True) match = True break if not match: if new_key_value in self.plugin_keys.values(): old_key_name = [name for name, value in self.plugin_keys.iteritems() if value == new_key_value][0] skipped += 1 info_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), u"The key in file {0} is the same as the existing key <strong>{1}</strong> and has been skipped.".format(filename,old_key_name), show_copy_button=False, show=True) else: counter += 1 self.plugin_keys[new_key_name] = new_key_value msg = u"" if counter+skipped > 1: if counter > 0: msg += u"Imported <strong>{0:d}</strong> key {1}. ".format(counter, u"file" if counter == 1 else u"files") if skipped > 0: msg += u"Skipped <strong>{0:d}</strong> key {1}.".format(skipped, u"file" if counter == 1 else u"files") inf = info_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), _(msg), show_copy_button=False, show=True) return counter > 0
def apply_diff(self, identical_msg, cache, syntax_map, changed_names, renamed_names, removed_names, added_names): self.view.clear() self.apply_diff_calls = calls = [] def add(args, kwargs): self.view.add_diff(*args, **kwargs) calls.append((args, kwargs)) if len(changed_names) + len(renamed_names) + len(removed_names) + len( added_names) < 1: self.busy.setVisible(False) info_dialog(self, _('No changes found'), identical_msg, show=True) self.busy.setVisible(True) return True kwargs = lambda name: { 'context': self.context, 'beautify': self.beautify, 'syntax': syntax_map.get(name, None) } if isinstance(changed_names, dict): for name, other_name in sorted( changed_names.iteritems(), key=lambda x: numeric_sort_key(x[0])): args = (name, other_name, cache.left(name), cache.right(other_name)) add(args, kwargs(name)) else: for name in sorted(changed_names, key=numeric_sort_key): args = (name, name, cache.left(name), cache.right(name)) add(args, kwargs(name)) for name in sorted(added_names, key=numeric_sort_key): args = (_('[%s was added]') % name, name, None, cache.right(name)) add(args, kwargs(name)) for name in sorted(removed_names, key=numeric_sort_key): args = (name, _('[%s was removed]') % name, cache.left(name), None) add(args, kwargs(name)) for name, new_name in sorted(renamed_names.iteritems(), key=lambda x: numeric_sort_key(x[0])): args = (name, new_name, None, None) add(args, kwargs(name))
def dispatcher(self): container = self.current_container # The book being edited as a container object if not container: return info_dialog(self.gui, _('No book open'), _('Need to have a book open first.'), show=True) if self.parse_current: name = editor_name(self.gui.central.current_editor) if not name or container.mime_map[name] not in OEB_DOCS: return info_dialog(self.gui, _('Cannot Process'), _('No file open for editing or the current file is not an (x)html file.'), show=True) self.cleanasawhistle = True self.changed_files = [] from calibre_plugins.diaps_toolbag.dialogs import RemoveDialog dlg = RemoveDialog(self.gui) if dlg.exec_(): criteria = dlg.getCriteria() # Ensure any in progress editing the user is doing is present in the container self.boss.commit_all_editors_to_container() self.boss.add_savepoint(_('Before: Span Div Edit')) try: self.process_files(criteria) except Exception: # Something bad happened report the error to the user import traceback error_dialog(self.gui, _('Failed'), _('Failed to process divs or spans, click "Show details" for more info'), det_msg=traceback.format_exc(), show=True) # Revert to the saved restore point self.boss.revert_requested(self.boss.global_undo.previous_container) else: if not self.cleanasawhistle: # Show the user what changes we have made, # allowing then to revert them if necessary accepted = ResultsDialog(self.gui, self.changed_files).exec_() if accepted == QDialog.Accepted: self.boss.show_current_diff() # Update the editor UI to take into account all the changes we # have made self.boss.apply_container_update_to_gui() else: info_dialog(self.gui, _('Nothing changed'), '<p>{0}'.format(_('Nothing matching your criteria was found.')), show=True)
def find(self, query): if not query: return try: idx = self._model.find(query) except ParseException: self.search.search_done(False) return self.search.search_done(True) if not idx.isValid(): info_dialog(self, _('No matches'), _('Could not find any shortcuts matching %s') % query, show=True, show_copy_button=False) return self.highlight_index(idx)
def add_books_from_device(self, view, paths=None): backloading_err = self.gui.device_manager.device.BACKLOADING_ERROR_MESSAGE if backloading_err is not None: return error_dialog(self.gui, _('Add to library'), backloading_err, show=True) if paths is None: rows = view.selectionModel().selectedRows() if not rows or len(rows) == 0: d = error_dialog(self.gui, _('Add to library'), _('No book selected')) d.exec_() return paths = [p for p in view.model().paths(rows) if p is not None] ve = self.gui.device_manager.device.VIRTUAL_BOOK_EXTENSIONS def ext(x): ans = os.path.splitext(x)[1] ans = ans[1:] if len(ans) > 1 else ans return ans.lower() remove = set([p for p in paths if ext(p) in ve]) if remove: paths = [p for p in paths if p not in remove] info_dialog(self.gui, _('Not Implemented'), _('The following books are virtual and cannot be added' ' to the calibre library:'), '\n'.join(remove), show=True) if not paths: return if not paths or len(paths) == 0: d = error_dialog(self.gui, _('Add to library'), _('No book files found')) d.exec_() return self.gui.device_manager.prepare_addable_books( self.Dispatcher(partial(self.books_prepared, view)), paths) self.bpd = ProgressDialog(_('Downloading books'), msg=_('Downloading books from device'), parent=self.gui, cancelable=False) QTimer.singleShot(1000, self.show_bpd)
def add_fonts(self): from calibre.utils.fonts.metadata import FontMetadata files = choose_files(self, 'add fonts to calibre', _('Select font files'), filters=[(_('TrueType/OpenType Fonts'), ['ttf', 'otf'])], all_files=False) if not files: return families = set() for f in files: try: with open(f, 'rb') as stream: fm = FontMetadata(stream) except: import traceback error_dialog( self, _('Corrupt font'), _('Failed to read metadata from the font file: %s') % f, det_msg=traceback.format_exc(), show=True) return families.add(fm.font_family) families = sorted(families) dest = os.path.join(config_dir, 'fonts') for f in files: shutil.copyfile(f, os.path.join(dest, os.path.basename(f))) self.font_scanner.do_scan() self.m.beginResetModel() self.build_font_list() self.m.endResetModel() self.view.setCurrentIndex(self.m.index(0)) if families: for i, val in enumerate(self.families): if icu_lower(val) == icu_lower(families[0]): self.view.setCurrentIndex(self.m.index(i)) break info_dialog(self, _('Added fonts'), _('Added font families: %s') % (', '.join(families)), show=True)
def stop_server(self): self.gui.content_server.threaded_exit() self.stopping_msg = info_dialog( self, _('Stopping'), _('Stopping server, this could take upto a minute, please wait...' ), show_copy_button=False) QTimer.singleShot(500, self.check_exited)
def restore_confirmations(self): changed = 0 for key in tuple(tprefs): if key.endswith('_again') and tprefs.get(key) is False: del tprefs[key] changed += 1 elif key.startswith('skip_ask_to_show_current_diff_for_'): del tprefs[key] changed += 1 elif key == 'questions_to_auto_skip': changed += len(tprefs[key] or ()) del tprefs[key] msg = _('There are no disabled confirmation prompts') if changed: msg = ngettext( 'One disabled confirmation prompt was restored', '{} disabled confirmation prompts were restored', changed).format(changed) info_dialog(self, _('Disabled confirmations restored'), msg, show=True)
def check_multi_selection(self): if len(self.selectedItems()) > 1: return info_dialog( self, _('Multiple items selected'), _('You are trying to move multiple items at once, this is not supported. Instead use' ' Drag and Drop to move multiple items'), show=True) return True
def help_requested(self): d = info_dialog(self, _('About semantics'), _( 'Semantics refer to additional information about specific locations in the book.' ' For example, you can specify that a particular location is the dedication or the preface' ' or the table of contents and so on.\n\nFirst choose the type of semantic information, then' ' choose a file and optionally a location within the file to point to.\n\nThe' ' semantic information will be written in the <guide> section of the opf file.')) d.resize(d.sizeHint()) d.exec_()
def add_fonts(self): families = add_fonts(self) if not families: return self.font_scanner.do_scan() self.m.beginResetModel() self.build_font_list() self.m.endResetModel() self.view.setCurrentIndex(self.m.index(0)) if families: for i, val in enumerate(self.families): if icu_lower(val) == icu_lower(families[0]): self.view.setCurrentIndex(self.m.index(i)) break info_dialog(self, _('Added fonts'), _('Added font families: %s')%( ', '.join(families)), show=True)
def toggle_content_server(self): if self.gui.content_server is None: self.gui.start_content_server() else: self.gui.content_server.threaded_exit() self.stopping_msg = info_dialog(self.gui, _('Stopping'), _('Stopping server, this could take upto a minute, please wait...'), show_copy_button=False) QTimer.singleShot(1000, self.check_exited)
def add_plugin(self): info = '' if iswindows else ' [.zip %s]' % _('files') path = choose_files(self, 'add a plugin dialog', _('Add plugin'), filters=[(_('Plugins') + info, ['zip'])], all_files=False, select_only_single_file=True) if not path: return path = path[0] if path and os.access(path, os.R_OK) and path.lower().endswith('.zip'): if not question_dialog(self, _('Are you sure?'), '<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 try: plugin = add_plugin(path) except NameConflict as e: return error_dialog(self, _('Already exists'), unicode(e), show=True) self._plugin_model.populate() self._plugin_model.reset() self.changed_signal.emit() self.check_for_add_to_toolbars(plugin) info_dialog(self, _('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=True, show_copy_button=False) idx = self._plugin_model.plugin_to_index_by_properties(plugin) if idx.isValid(): self.highlight_index(idx) else: error_dialog(self, _('No valid plugin path'), _('%s is not a valid plugin path') % path).exec_()
def iterate_over_books( ia, func, title, ptext, notptext, should_convert=None, convtext=_L["The following comics were converted to cbz: {}"]): ''' Iterates over all selected books. For each book, it checks if it should be converted to cbz and then applies func to the book. After all books are processed, gives a completion message. ''' processed = [] not_processed = [] converted = [] if should_convert is None: should_convert = prefs["convert_cbr"] # iterate through the books for book_id in get_selected_books(ia): metadata = ComicMetadata(book_id, ia) # sanity check if metadata.format is None: not_processed.append(metadata.info) continue if should_convert and convert_to_cbz(ia, metadata): converted.append(metadata.info) if func(metadata): processed.append(metadata.info) else: not_processed.append(metadata.info) # show a completion message msg = ptext.format(len(processed)) if should_convert and len(converted) > 0: msg += '\n' + convtext.format(lst2string(converted)) if len(not_processed) > 0: msg += '\n' + notptext.format(lst2string(not_processed)) info_dialog(ia.gui, title, msg, show=True)
def update_metadata(self): """ Set the metadata in the files in the selected book's record to match the current metadata in the database. """ from calibre.ebooks.metadata.meta import set_metadata from calibre.gui2 import error_dialog, info_dialog # Get currently selected books rows = self.gui.library_view.selectionModel().selectedRows() if not rows or len(rows) == 0: return error_dialog(self.gui, "Cannot update metadata", "No books selected", show=True) # Map the rows to book ids ids = list(map(self.gui.library_view.model().id, rows)) db = self.db.new_api for book_id in ids: # Get the current metadata for this book from the db mi = db.get_metadata(book_id, get_cover=True, cover_as_data=True) fmts = db.formats(book_id) if not fmts: continue for fmt in fmts: fmt = fmt.lower() # Get a python file object for the format. This will be either # an in memory file or a temporary on disk file ffile = db.format(book_id, fmt, as_file=True) ffile.seek(0) # Set metadata in the format set_metadata(ffile, mi, fmt) ffile.seek(0) # Now replace the file in the calibre library with the updated # file. We dont use add_format_with_hooks as the hooks were # already run when the file was first added to calibre. db.add_format(book_id, fmt, ffile, run_hooks=False) info_dialog( self, "Updated files", "Updated the metadata in the files of %d book(s)" % len(ids), show=True, )
def open_book(self, path=None, edit_file=None, clear_notify_data=True): if self.gui.action_save.isEnabled(): if not question_dialog( self.gui, _('Unsaved changes'), _('The current book has unsaved changes. If you open a new book, they will be lost' ' are you sure you want to proceed?')): return if self.save_manager.has_tasks: return info_dialog( self.gui, _('Cannot open'), _('The current book is being saved, you cannot open a new book until' ' the saving is completed'), show=True) if not hasattr(path, 'rpartition'): path = choose_files(self.gui, 'open-book-for-tweaking', _('Choose book'), [(_('Books'), [x.lower() for x in SUPPORTED])], all_files=False, select_only_single_file=True) if not path: return path = path[0] ext = path.rpartition('.')[-1].upper() if ext not in SUPPORTED: return error_dialog( self.gui, _('Unsupported format'), _('Tweaking is only supported for books in the %s formats.' ' Convert your book to one of these formats first.') % _(' and ').join(sorted(SUPPORTED)), show=True) if not os.path.exists(path): return error_dialog(self.gui, _('File not found'), _('The file %s does not exist.') % path, show=True) for name in tuple(editors): self.close_editor(name) self.gui.preview.clear() self.container_count = -1 if self.tdir: shutil.rmtree(self.tdir, ignore_errors=True) self.tdir = PersistentTemporaryDirectory() self._edit_file_on_open = edit_file self._clear_notify_data = clear_notify_data self.gui.blocking_job('open_book', _('Opening book, please wait...'), self.book_opened, get_container, path, tdir=self.mkdtemp())
def find(self, query): if not query: return try: idx = self._model.find(query) except ParseException: self.search.search_done(False) return self.search.search_done(True) if not idx.isValid(): info_dialog( self, _('No matches'), _('Could not find any tweaks matching <i>{}</i>').format( prepare_string_for_xml(query)), show=True, show_copy_button=False) return self.highlight_index(idx)
def _get_done(self, x, tb): if not self.isVisible(): return self.reject() if tb is not None: error_dialog(self, _('Scan failed'), _( 'Failed to scan for external resources, click "Show details" for more information.'), det_msg=tb, show=True) self.reject() else: self.wait.stop() self.state = 1 resources = x if not resources: info_dialog(self, _('No external resources found'), _( 'No external resources were found in this book.'), show=True) self.reject() return self.choose_resources.resources = resources self.bb.setVisible(True)
def upload_metadata(self): ''' Upload any changes we have made locally to the Casanova metadata server ''' row = self.get_selected_row() if self.gui.current_view() is self.gui.library_view: id = self.gui.library_view.model().id(row) else: id = self.gui.current_view().model().id(row) result = self.mm.commit(id) if isinstance(result, str): return info_dialog(self.gui, 'Casanova message', unicode(result), show=True)
def get_results(self): # We only want the search plugins to run # a maximum set amount of time before giving up. self.hang_check += 1 if self.hang_check >= self.hang_time: self.search_pool.abort() self.checker.stop() else: # Stop the checker if not threads are running. if not self.search_pool.threads_running() and not self.search_pool.has_tasks(): self.checker.stop() while self.search_pool.has_results(): res, store_plugin = self.search_pool.get_result() if res: self.results_view.model().add_result(res, store_plugin) if not self.search_pool.threads_running() and not self.results_view.model().has_results(): info_dialog(self, _('No matches'), _('Couldn\'t find any books matching your query.'), show=True, show_copy_button=False)
def search_clicked(self): search_for = icu_lower(unicode(self.search_box.text())) if not search_for: error_dialog(self, _('Find'), _('You must enter some text to search for'), show=True, show_copy_button=False) return row = self.table.currentRow() if row < 0: row = 0 rows = self.table.rowCount() for i in range(0, rows): row += 1 if row >= rows: row = 0 item = self.table.item(row, 0) if search_for in icu_lower(unicode(item.text())): self.table.setCurrentItem(item) return info_dialog(self, _('Find'), _('No tag found'), show=True, show_copy_button=False)
def check_library(self): from calibre.gui2.dialogs.check_library import CheckLibraryDialog, DBCheck self.gui.library_view.save_state() m = self.gui.library_view.model() m.stop_metadata_backup() db = m.db db.prefs.disable_setting = True d = DBCheck(self.gui, db) d.start() try: d.conn.close() except: pass d.break_cycles() self.gui.library_moved(db.library_path, call_close=not d.closed_orig_conn) if d.rejected: return if d.error is None: if not question_dialog(self.gui, _('Success'), _('Found no errors in your calibre library database.' ' Do you want calibre to check if the files in your ' ' library match the information in the database?')): return else: return error_dialog(self.gui, _('Failed'), _('Database integrity check failed, click Show details' ' for details.'), show=True, det_msg=d.error[1]) self.gui.status_bar.show_message( _('Starting library scan, this may take a while')) try: QCoreApplication.processEvents() d = CheckLibraryDialog(self.gui, m.db) if not d.do_exec(): info_dialog(self.gui, _('No problems found'), _('The files in your library match the information ' 'in the database.'), show=True) finally: self.gui.status_bar.clear_message()