def download_metadata(self, ids=None, ensure_fields=None): if ids is None: rows = self.gui.library_view.selectionModel().selectedRows() if not rows or len(rows) == 0: return error_dialog(self.gui, _('Cannot download metadata'), _('No books selected'), show=True) db = self.gui.library_view.model().db ids = [db.id(row.row()) for row in rows] from calibre.gui2.metadata.bulk_download import start_download start_download(self.gui, ids, Dispatcher(self.metadata_downloaded), ensure_fields=ensure_fields)
def parse(self): # get currently selected books rows = self.gui.library_view.selectionModel().selectedRows() if not rows or len(rows) == 0: return self.ids = list(map(self.gui.library_view.model().id, rows)) if len(self.ids) == 0: return job = ThreadedJob('Generating Word Wise', 'Generating Word Wise', do_job, (self.gui.current_db.new_api, self.ids, self.plugin_path), {}, Dispatcher(self.done)) self.gui.job_manager.run_threaded_job(job) self.gui.status_bar.show_message("Generating Word Wise")
def download_ebook(self, url='', cookie_file=None, filename='', save_loc='', add_to_lib=True, tags=[]): if tags: if isinstance(tags, basestring): tags = tags.split(',') start_ebook_download(Dispatcher(self.downloaded_ebook), self.job_manager, self, cookie_file, url, filename, save_loc, add_to_lib, tags) self.status_bar.show_message( _('Downloading') + ' ' + filename.decode('utf-8', 'ignore') if filename else url.decode('utf-8', 'ignore'), 3000)
def scan_for_isbns(self): rows = self.gui.library_view.selectionModel().selectedRows() if not rows or len(rows) == 0: return error_dialog(self.gui, 'No rows selected', 'You must select one or more books to perform this action.', show=True) book_ids = self.gui.library_view.get_selected_ids() db = self.gui.library_view.model().db c = cfg.plugin_prefs[cfg.STORE_NAME] worker_threshold = c.get(cfg.KEY_WORKER_THRESHOLD, cfg.DEFAULT_STORE_VALUES[cfg.KEY_WORKER_THRESHOLD]) if len(book_ids) > worker_threshold: # Run the extraction as a background job with workers QueueProgressDialog(self.gui, book_ids, self._queue_job, db) else: # For performance reasons, still do single book extraction as a threaded # job in-process start_extract_threaded(self.gui, book_ids, Dispatcher(self._scan_for_isbns_complete))
def do_polish(self, book_id_map): d = Polish(self.gui.library_view.model().db, book_id_map, parent=self.gui) if d.exec_() == d.Accepted and d.jobs: show_reports = bool(d.show_reports.isChecked()) for desc, data, book_id, base, is_orig in reversed(d.jobs): job = self.gui.job_manager.run_job(Dispatcher( self.book_polished), 'gui_polish', args=(data, ), description=desc) job.polish_args = (book_id, base, data['files'], show_reports, is_orig) if d.jobs: self.gui.jobs_pointer.start() self.gui.status_bar.show_message( _('Start polishing of %d book(s)') % len(d.jobs), 2000)
def download_ebook(self, url='', cookie_file=None, filename='', save_loc='', add_to_lib=True, tags=[], create_browser=None): if tags: if isinstance(tags, string_or_bytes): tags = tags.split(',') start_ebook_download(Dispatcher(self.downloaded_ebook), self.job_manager, self, cookie_file, url, filename, save_loc, add_to_lib, tags, create_browser) self.status_bar.show_message( _('Downloading') + ' ' + as_unicode(filename or url, errors='replace'), 3000)
def save_to_disk(self, checked, single_dir=False, single_format=None, rows=None, write_opf=None, save_cover=None): if rows is None: rows = self.gui.current_view().selectionModel().selectedRows() if not rows or len(rows) == 0: return error_dialog(self.gui, _('Cannot save to disk'), _('No books selected'), show=True) path = choose_dir(self.gui, 'save to disk dialog', _('Choose destination directory')) if not path: return dpath = os.path.abspath(path).replace('/', os.sep)+os.sep lpath = self.gui.library_view.model().db.library_path.replace('/', os.sep)+os.sep if dpath.startswith(lpath): return error_dialog(self.gui, _('Not allowed'), _('You are trying to save files into the calibre ' 'library. This can cause corruption of your ' 'library. Save to disk is meant to export ' 'files from your calibre library elsewhere.'), show=True) if self.gui.current_view() is self.gui.library_view: from calibre.gui2.save import Saver from calibre.library.save_to_disk import config opts = config().parse() if single_format is not None: opts.formats = single_format # Special case for Kindle annotation files if single_format.lower() in ['mbp','pdr','tan']: opts.to_lowercase = False opts.save_cover = False opts.write_opf = False opts.template = opts.send_template opts.single_dir = single_dir if write_opf is not None: opts.write_opf = write_opf if save_cover is not None: opts.save_cover = save_cover book_ids = set(map(self.gui.library_view.model().id, rows)) Saver(book_ids, self.gui.current_db, opts, path, parent=self.gui, pool=self.gui.spare_pool()) else: paths = self.gui.current_view().model().paths(rows) self.gui.device_manager.save_books( Dispatcher(self.books_saved), paths, path)
def __init__(self): QAbstractTableModel.__init__(self) SearchQueryParser.__init__(self, ['all']) self.wait_icon = (QIcon(I('jobs.png'))) self.running_icon = (QIcon(I('exec.png'))) self.error_icon = (QIcon(I('dialog_error.png'))) self.done_icon = (QIcon(I('ok.png'))) self.jobs = [] self.add_job = Dispatcher(self._add_job) self.server = Server(limit=int(config['worker_limit'] / 2.0), enforce_cpu_limit=config['enforce_cpu_limit']) self.threaded_server = ThreadedJobServer() self.changed_queue = Queue() self.timer = QTimer(self) self.timer.timeout.connect(self.update, type=Qt.QueuedConnection) self.timer.start(1000)
class ParseBook(): def __init__(self, gui): self.gui = gui self.metadata_list = [] def parse(self, create_ww=True, create_x=True): # get currently selected books rows = self.gui.library_view.selectionModel().selectedRows() if not rows or len(rows) == 0: return ids = list(map(self.gui.library_view.model().id, rows)) if len(ids) == 0: return for book_id in ids: if (data := check_metadata(self.gui.current_db.new_api, book_id)) is None: continue self.metadata_list.append(data) if len(self.metadata_list) == 0: return for data in self.metadata_list: if data[-1]['wiki'] != 'en': create_ww = False title = data[-2].get('title') notif = [] if create_ww: notif.append('Word Wise') if create_x: notif.append('X-Ray') notif = ' and '.join(notif) job = ThreadedJob( "WordDumb's dumb job", f'Generating {notif} for {title}', do_job, (data, create_ww, create_x), {}, Dispatcher(partial(self.done, data=data, notif=f'{notif} generated for {title}'))) self.gui.job_manager.run_threaded_job(job) self.gui.jobs_pointer.start()
def _view_books(self, rows): if not rows or len(rows) == 0: self._launch_viewer() return if not self._view_check(len(rows)): return if self.gui.current_view() is self.gui.library_view: ids = list(map(self.gui.library_view.model().id, rows)) self._view_calibre_books(ids) else: paths = self.gui.current_view().model().paths(rows) for path in paths: pt = PersistentTemporaryFile('_viewer_'+\ os.path.splitext(path)[1]) self.persistent_files.append(pt) pt.close() self.gui.device_manager.view_book(\ Dispatcher(self.book_downloaded_for_viewing), path, pt.name)
def convert_vtt_files(self): main_lang = self.main_lang_combo.currentData() if main_lang == '-': QMessageBox.about(self, 'Information', 'Select the main language before conversion.') return sub_lang = str(self.sub_lang_combo.currentData()) # if user does not select sub_lang, set it to main_lang, so that when converting, it won't generate sub language. if sub_lang == '-': sub_lang = main_lang # convert to html if hasattr(self, 'cover_file_path'): cover_file_path = self.cover_file_path else: cover_file_path = None self.book_id = self.convert_to_html_add_to_library( self.vtt_dir, main_lang, sub_lang, cover_file_path) # add html to epub conversion job self.jobs, changed, bad = convert_single_ebook( self.gui, self.gui.library_view.model().db, [self.book_id], True, self.outputFmt) func, args, desc, fmt, id, temp_files = self.jobs[0] core_usage = 1 plugin = plugin_for_input_format('html') if plugin is not None: core_usage = plugin.core_usage self.gui.job_manager.run_job(Dispatcher(self.converted_func), func, args=args, description=desc, core_usage=core_usage) self.close()
def queue_convert_jobs(self, jobs, changed, bad, rows, previous, converted_func, extra_job_args=[], rows_are_ids=False): for func, args, desc, fmt, id, temp_files in jobs: func, _, parts = func.partition(':') parts = {x for x in parts.split(';')} input_file = args[0] input_fmt = os.path.splitext(input_file)[1] core_usage = 1 if input_fmt: input_fmt = input_fmt[1:] plugin = plugin_for_input_format(input_fmt) if plugin is not None: core_usage = plugin.core_usage if id not in bad: job = self.gui.job_manager.run_job(Dispatcher(converted_func), func, args=args, description=desc, core_usage=core_usage) job.conversion_of_same_fmt = 'same_fmt' in parts job.manually_fine_tune_toc = 'manually_fine_tune_toc' in parts args = [temp_files, fmt, id] + extra_job_args self.conversion_jobs[job] = tuple(args) if changed: m = self.gui.library_view.model() if rows_are_ids: m.refresh_ids(rows) else: m.refresh_rows(rows) current = self.gui.library_view.currentIndex() self.gui.library_view.model().current_changed(current, previous)
def enqueue(self, account, downloader): prefs = self.prefs self.notify("Account: '%s'" % account[prefs.USERNAME]) # downloader.login(account) func = 'arbitrary_n' # func = 'arbitrary' cpus = self.gui.job_manager.server.pool_size print "CPUs: %s" % (cpus) args = [ 'calibre_plugins.beam_ebooks_downloader.jobs', 'do_obtain_new_books', (cpus, account) ] desc = 'Beam EBooks Downloader' job = self.gui.job_manager.run_job(Dispatcher(self._done), func, args=args, description=desc) print "Job: %s" % (job) self.notify(" Start parsing OPDS catalog")
def _add_ebooks(self, payload): print "Done Downloading, Step 2" print "Self: %s" % (self) # print "Payload: %s" % (payload) # Printing the complete payload at once gives [IOError 12] - Out of space (memory, that is...) self.notify(" Finished adding books...") for entry in payload: print "Entry: %s" % entry # TODO allow for checkbox list of entries to download func = 'arbitrary_n' # func = 'arbitrary' cpus = self.gui.job_manager.server.pool_size print "CPUs: %s" % (cpus) args = [ 'calibre_plugins.beam_ebooks_downloader.jobs', 'do_download_book', (cpus, entry) ] desc = 'Beam EBooks Downloader' job = self.gui.job_manager.run_job(Dispatcher(self._done_2), func, args=args, description=desc) print "Job: %s" % (job)
def build_menus(self): if os.environ.get('CALIBRE_OVERRIDE_DATABASE_PATH', None): return db = self.gui.library_view.model().db locations = list(self.stats.locations(db)) for ac in self.switch_actions: ac.setVisible(False) self.quick_menu.clear() self.rename_menu.clear() self.delete_menu.clear() quick_actions, rename_actions, delete_actions = [], [], [] for name, loc in locations: is_prev_lib = name == self.prev_lname name = name.replace('&', '&&') ac = self.quick_menu.addAction( name, Dispatcher(partial(self.switch_requested, loc))) ac.setStatusTip(_('Switch to: %s') % loc) if is_prev_lib: f = ac.font() f.setBold(True) ac.setFont(f) quick_actions.append(ac) ac = self.rename_menu.addAction( name, Dispatcher(partial(self.rename_requested, name, loc))) rename_actions.append(ac) ac.setStatusTip(_('Rename: %s') % loc) ac = self.delete_menu.addAction( name, Dispatcher(partial(self.delete_requested, name, loc))) delete_actions.append(ac) ac.setStatusTip(_('Remove: %s') % loc) if is_prev_lib: ac.setFont(f) qs_actions = [] locations_by_frequency = locations if len(locations) >= tweaks['many_libraries']: locations_by_frequency = list( self.stats.locations(db, limit=sys.maxsize)) for i, x in enumerate( locations_by_frequency[:len(self.switch_actions)]): name, loc = x name = name.replace('&', '&&') ac = self.switch_actions[i] ac.setText(name) ac.setStatusTip(_('Switch to: %s') % loc) ac.setVisible(True) qs_actions.append(ac) self.qs_locations = [i[1] for i in locations_by_frequency] self.quick_menu_action.setVisible(bool(locations)) self.rename_menu_action.setVisible(bool(locations)) self.delete_menu_action.setVisible(bool(locations)) self.gui.location_manager.set_switch_actions(quick_actions, rename_actions, delete_actions, qs_actions, self.action_choose) # Allow the cloned actions in the OS X global menubar to update for a in (self.qaction, self.menuless_qaction): a.changed.emit()
def send_by_mail(self, to, fmts, delete_from_library, subject='', send_ids=None, do_auto_convert=True, specific_format=None): ids = [ self.library_view.model().id(r) for r in self.library_view.selectionModel().selectedRows() ] if send_ids is None else send_ids if not ids or len(ids) == 0: return files, _auto_ids = self.library_view.model( ).get_preferred_formats_from_ids( ids, fmts, set_metadata=True, specific_format=specific_format, exclude_auto=do_auto_convert, use_plugboard=plugboard_email_value, plugboard_formats=plugboard_email_formats) if do_auto_convert: nids = list(set(ids).difference(_auto_ids)) ids = [i for i in ids if i in nids] else: _auto_ids = [] full_metadata = self.library_view.model().metadata_for(ids, get_cover=False) bad, remove_ids, jobnames = [], [], [] texts, subjects, attachments, attachment_names = [], [], [], [] for f, mi, id in zip(files, full_metadata, ids): t = mi.title if not t: t = _('Unknown') if f is None: bad.append(t) else: remove_ids.append(id) jobnames.append(t) attachments.append(f) if not subject: subjects.append(_('E-book:') + ' ' + t) else: components = get_components(subject, mi, id) if not components: components = [mi.title] subjects.append(os.path.join(*components)) a = authors_to_string(mi.authors if mi.authors else \ [_('Unknown')]) texts.append(_('Attached, you will find the e-book') + \ '\n\n' + t + '\n\t' + _('by') + ' ' + a + '\n\n' + \ _('in the %s format.') % os.path.splitext(f)[1][1:].upper()) prefix = ascii_filename(t + ' - ' + a) if not isinstance(prefix, unicode): prefix = prefix.decode(preferred_encoding, 'replace') attachment_names.append(prefix + os.path.splitext(f)[1]) remove = remove_ids if delete_from_library else [] to_s = list(repeat(to, len(attachments))) if attachments: send_mails(jobnames, Dispatcher(partial(self.email_sent, remove=remove)), attachments, to_s, subjects, texts, attachment_names, self.job_manager) self.status_bar.show_message( _('Sending email to') + ' ' + to, 3000) auto = [] if _auto_ids != []: for id in _auto_ids: if specific_format == None: dbfmts = self.library_view.model().db.formats( id, index_is_id=True) formats = [ f.lower() for f in (dbfmts.split(',') if dbfmts else []) ] if list( set(formats).intersection( available_input_formats())) != [] and list( set(fmts).intersection( available_output_formats())) != []: auto.append(id) else: bad.append(self.library_view.model().db.title( id, index_is_id=True)) else: if specific_format in list( set(fmts).intersection( set(available_output_formats()))): auto.append(id) else: bad.append(self.library_view.model().db.title( id, index_is_id=True)) if auto != []: format = specific_format if specific_format in list( set(fmts).intersection(set( available_output_formats()))) else None if not format: for fmt in fmts: if fmt in list( set(fmts).intersection( set(available_output_formats()))): format = fmt break if format is None: bad += auto else: autos = [ self.library_view.model().db.title(id, index_is_id=True) for id in auto ] if self.auto_convert_question( _('Auto convert the following books before sending via ' 'email?'), autos): self.iactions['Convert Books'].auto_convert_mail( to, fmts, delete_from_library, auto, format, subject) if bad: bad = '\n'.join('%s' % (i, ) for i in bad) d = warning_dialog( self, _('No suitable formats'), _('Could not email the following books ' 'as no suitable formats were found:'), bad) d.exec_()
def do_copy(self, ids, db, loc, delete_after, add_duplicates=False): 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=True, icon='lt.png') 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, add_duplicates) self.worker.start() self.pd.canceled_signal.connect(self.worker.cancel_processing) self.pd.exec_() self.pd.canceled_signal.disconnect() if self.worker.left_after_cancel: msg = _( 'The copying process was interrupted. {} books were copied.' ).format(len(self.worker.processed)) if delete_after: msg += ' ' + _('No books were deleted from this library.') msg += ' ' + _( 'The best way to resume this operation is to re-copy all the books with the option to' ' "Check for duplicates when copying to library" in Preferences->Import/export->Adding books turned on.' ) warning_dialog(self.gui, _('Canceled'), msg, show=True) return 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) return if delete_after: donemsg = ngettext('Moved the book to {loc}', 'Moved {num} books to {loc}', len(self.worker.processed)) else: donemsg = ngettext('Copied the book to {loc}', 'Copied {num} books to {loc}', len(self.worker.processed)) self.gui.status_bar.show_message( donemsg.format(num=len(self.worker.processed), 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->Import/export->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) if self.worker.failed_books: def fmt_err(book_id): err, tb = self.worker.failed_books[book_id] title = db.title(book_id, index_is_id=True) return _('Copying: {0} failed, with error:\n{1}').format( title, tb) title, msg = _('Failed to copy some books'), _( 'Could not copy some books, click "Show Details" for more information.' ) tb = '\n\n'.join(map(fmt_err, self.worker.failed_books)) tb = ngettext('Failed to copy a book, see below for details', 'Failed to copy {} books, see below for details', len(self.worker.failed_books)).format( len(self.worker.failed_books)) + '\n\n' + tb if len(ids) == len(self.worker.failed_books): title, msg = _('Failed to copy books'), _( 'Could not copy any books, click "Show Details" for more information.' ) error_dialog(self.gui, title, msg, det_msg=tb, show=True) return self.worker.duplicate_ids
for book_id in ids: if (data := check_metadata(self.gui.current_db.new_api, book_id)) is None: continue self.metadata_list.append(data) if (books := len(self.metadata_list)) == 0: return if books == 1: self.create_jobs(install=True) else: job = ThreadedJob("WordDumb's dumb job", 'Install dependencies', install_libs, (), {}, Dispatcher(self.create_jobs)) self.gui.job_manager.run_threaded_job(job) self.gui.jobs_pointer.start() self.gui.status_bar.show_message(f'Generating Word Wise for {books} ' f'{"books" if books > 1 else "book"}') def create_jobs(self, job=None, install=False): if self.job_failed(job): return for data in self.metadata_list: title = data[-1].get('title') job = ThreadedJob( "WordDumb's dumb job", f'Generating Word Wise for {title}', do_job, (data, install), {},
def __call__(self, name, user_text, callback, function, *args, **kwargs): ' Run a job that blocks the GUI providing some feedback to the user ' self.set_msg(user_text) job = LongJob(name, user_text, Dispatcher(partial(self.job_done, callback)), function, *args, **kwargs) job.start() self.start()
def do_copy(self, ids, db, loc, delete_after, add_duplicates=False): 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, icon='lt.png') 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, add_duplicates) 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) return self.gui.status_bar.show_message( donemsg % dict(num=len(self.worker.processed), 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) if self.worker.failed_books: def fmt_err(book_id): err, tb = self.worker.failed_books[book_id] title = db.title(book_id, index_is_id=True) return _('Copying: {0} failed, with error:\n{1}').format( title, tb) title, msg = _('Failed to copy some books'), _( 'Could not copy some books, click "Show Details" for more information.' ) tb = '\n\n'.join(map(fmt_err, self.worker.failed_books)) tb = _('Failed to copy {0} book(s), see below for details').format( len(self.worker.failed_books)) + '\n\n' + tb if len(ids) == len(self.worker.failed_books): title, msg = _('Failed to copy books'), _( 'Could not copy any books, click "Show Details" for more information.' ) error_dialog(self.gui, title, msg, det_msg=tb, show=True) return self.worker.duplicate_ids
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) self.pd = ProgressDialog(_('Copying'), min=0, max=len(ids) - 1, parent=self.gui, cancelable=False) def progress(idx, title): self.pd.set_msg(_('Copying') + ' ' + 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_() 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( _('Copied %(num)d books to %(loc)s') % 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 refresh_gui(self, gui): gui.content_server = self.server if gui.content_server is not None: gui.content_server.state_callback = \ Dispatcher(gui.iactions['Connect Share'].content_server_state_changed) gui.content_server.state_callback(gui.content_server.is_running)
def build_menus(self): if os.environ.get('CALIBRE_OVERRIDE_DATABASE_PATH', None): return db = self.gui.library_view.model().db lname = self.stats.library_used(db) self.vl_to_apply_action.setText( _('Apply Virtual library when %s is opened') % lname) locations = list(self.stats.locations(db)) for ac in self.switch_actions: ac.setVisible(False) self.quick_menu.clear() self.rename_menu.clear() self.delete_menu.clear() quick_actions, rename_actions, delete_actions = [], [], [] for name, loc in locations: is_prev_lib = name == self.prev_lname ic = library_qicon(name) name = name.replace('&', '&&') ac = self.quick_menu.addAction( ic, name, Dispatcher(partial(self.switch_requested, loc))) ac.setStatusTip(_('Switch to: %s') % loc) if is_prev_lib: f = ac.font() f.setBold(True) ac.setFont(f) quick_actions.append(ac) ac = self.rename_menu.addAction( name, Dispatcher(partial(self.rename_requested, name, loc))) rename_actions.append(ac) ac.setStatusTip(_('Rename: %s') % loc) ac = self.delete_menu.addAction( name, Dispatcher(partial(self.delete_requested, name, loc))) delete_actions.append(ac) ac.setStatusTip(_('Remove: %s') % loc) if is_prev_lib: ac.setFont(f) qs_actions = [] locations_by_frequency = locations if len(locations) >= tweaks['many_libraries']: locations_by_frequency = list( self.stats.locations(db, limit=sys.maxsize)) for i, x in enumerate( locations_by_frequency[:len(self.switch_actions)]): name, loc = x ic = library_qicon(name) name = name.replace('&', '&&') ac = self.switch_actions[i] ac.setText(name) ac.setIcon(ic) ac.setStatusTip(_('Switch to: %s') % loc) ac.setVisible(True) qs_actions.append(ac) self.qs_locations = [i[1] for i in locations_by_frequency] self.quick_menu_action.setVisible(bool(locations)) self.rename_menu_action.setVisible(bool(locations)) self.delete_menu_action.setVisible(bool(locations)) self.gui.location_manager.set_switch_actions(quick_actions, rename_actions, delete_actions, qs_actions, self.action_choose) # VL at startup self.vl_to_apply_menu.clear() restrictions = sorted(db.prefs['virtual_libraries'], key=sort_key) # check that the virtual library choice still exists vl_at_startup = db.prefs['virtual_lib_on_startup'] if vl_at_startup and vl_at_startup not in restrictions: vl_at_startup = db.prefs['virtual_lib_on_startup'] = '' restrictions.insert(0, '') for vl in restrictions: if vl == vl_at_startup: self.vl_to_apply_menu.addAction( QIcon(I('ok.png')), vl if vl else _('No Virtual library'), Dispatcher(partial(self.change_vl_at_startup_requested, vl))) else: self.vl_to_apply_menu.addAction( vl if vl else _('No Virtual library'), Dispatcher(partial(self.change_vl_at_startup_requested, vl))) # Allow the cloned actions in the OS X global menubar to update for a in (self.qaction, self.menuless_qaction): a.changed.emit()