%PDF- %PDF-
Direktori : /proc/thread-self/root/usr/lib/calibre/calibre/gui2/tweak_book/ |
Current File : //proc/thread-self/root/usr/lib/calibre/calibre/gui2/tweak_book/check.py |
#!/usr/bin/env python3 __license__ = 'GPL v3' __copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>' import sys from qt.core import ( QIcon, Qt, QSplitter, QListWidget, QTextBrowser, QPalette, QMenu, QListWidgetItem, pyqtSignal, QApplication, QStyledItemDelegate, QAbstractItemView) from calibre.ebooks.oeb.polish.check.base import WARN, INFO, DEBUG, ERROR, CRITICAL from calibre.ebooks.oeb.polish.check.main import run_checks, fix_errors from calibre.gui2 import NO_URL_FORMATTING, safe_open_url from calibre.gui2.tweak_book import tprefs from calibre.gui2.tweak_book.widgets import BusyCursor def icon_for_level(level): if level > WARN: icon = 'dialog_error.png' elif level == WARN: icon = 'dialog_warning.png' elif level == INFO: icon = 'dialog_information.png' else: icon = None return QIcon(I(icon)) if icon else QIcon() def prefix_for_level(level): if level > WARN: text = _('ERROR') elif level == WARN: text = _('WARNING') elif level == INFO: text = _('INFO') else: text = '' if text: text += ': ' return text class Delegate(QStyledItemDelegate): def initStyleOption(self, option, index): super().initStyleOption(option, index) if index.row() == self.parent().currentRow(): option.font.setBold(True) option.backgroundBrush = self.parent().palette().brush(QPalette.ColorRole.AlternateBase) class Check(QSplitter): item_activated = pyqtSignal(object) check_requested = pyqtSignal() fix_requested = pyqtSignal(object) def __init__(self, parent=None): QSplitter.__init__(self, parent) self.setChildrenCollapsible(False) self.items = i = QListWidget(self) i.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu) i.customContextMenuRequested.connect(self.context_menu) self.items.setSpacing(3) self.items.itemDoubleClicked.connect(self.current_item_activated) self.items.currentItemChanged.connect(self.current_item_changed) self.items.setSelectionMode(QAbstractItemView.SelectionMode.NoSelection) self.delegate = Delegate(self.items) self.items.setItemDelegate(self.delegate) self.addWidget(i) self.help = h = QTextBrowser(self) h.anchorClicked.connect(self.link_clicked) h.setOpenLinks(False) self.addWidget(h) self.setStretchFactor(0, 100) self.setStretchFactor(1, 50) self.clear_at_startup() state = tprefs.get('check-book-splitter-state', None) if state is not None: self.restoreState(state) def clear_at_startup(self): self.clear_help(_('Check has not been run')) self.items.clear() def context_menu(self, pos): m = QMenu(self) if self.items.count() > 0: m.addAction(QIcon(I('edit-copy.png')), _('Copy list of errors to clipboard'), self.copy_to_clipboard) if list(m.actions()): m.exec(self.mapToGlobal(pos)) def copy_to_clipboard(self): items = [] for item in (self.items.item(i) for i in range(self.items.count())): msg = str(item.text()) msg = prefix_for_level(item.data(Qt.ItemDataRole.UserRole).level) + msg items.append(msg) if items: QApplication.clipboard().setText('\n'.join(items)) def save_state(self): tprefs.set('check-book-splitter-state', bytearray(self.saveState())) def clear_help(self, msg=None): if msg is None: msg = _('No problems found') self.help.setText('<h2>{}</h2><p><a style="text-decoration:none" title="{}" href="run:check">{}</a></p>'.format( msg, _('Click to run a check on the book'), _('Run check'))) def link_clicked(self, url): url = str(url.toString(NO_URL_FORMATTING)) if url == 'activate:item': self.current_item_activated() elif url == 'run:check': self.check_requested.emit() elif url == 'fix:errors': errors = [self.items.item(i).data(Qt.ItemDataRole.UserRole) for i in range(self.items.count())] self.fix_requested.emit(errors) elif url.startswith('fix:error,'): num = int(url.rpartition(',')[-1]) errors = [self.items.item(num).data(Qt.ItemDataRole.UserRole)] self.fix_requested.emit(errors) elif url.startswith('activate:item:'): index = int(url.rpartition(':')[-1]) self.location_activated(index) elif url.startswith('https://'): safe_open_url(url) def next_error(self, delta=1): row = self.items.currentRow() num = self.items.count() if num > 0: row = (row + delta) % num self.items.setCurrentRow(row) self.current_item_activated() def current_item_activated(self, *args): i = self.items.currentItem() if i is not None: err = i.data(Qt.ItemDataRole.UserRole) if err.has_multiple_locations: self.location_activated(0) else: self.item_activated.emit(err) def location_activated(self, index): i = self.items.currentItem() if i is not None: err = i.data(Qt.ItemDataRole.UserRole) err.current_location_index = index self.item_activated.emit(err) def current_item_changed(self, *args): i = self.items.currentItem() self.help.setText('') def loc_to_string(line, col): loc = '' if line is not None: loc = _('line: %d') % line if col is not None: loc += _(' column: %d') % col if loc: loc = ' (%s)' % loc return loc if i is not None: err = i.data(Qt.ItemDataRole.UserRole) header = {DEBUG:_('Debug'), INFO:_('Information'), WARN:_('Warning'), ERROR:_('Error'), CRITICAL:_('Error')}[err.level] ifix = '' loc = loc_to_string(err.line, err.col) if err.INDIVIDUAL_FIX: ifix = '<a href="fix:error,%d" title="%s">%s</a><br><br>' % ( self.items.currentRow(), _('Try to fix only this error'), err.INDIVIDUAL_FIX) open_tt = _('Click to open in editor') fix_tt = _('Try to fix all fixable errors automatically. Only works for some types of error.') fix_msg = _('Try to correct all fixable errors automatically') run_tt, run_msg = _('Re-run the check'), _('Re-run check') header = '<style>a { text-decoration: none}</style><h2>%s [%d / %d]</h2>' % ( header, self.items.currentRow()+1, self.items.count()) msg = '<p>%s</p>' footer = '<div>%s<a href="fix:errors" title="%s">%s</a><br><br> <a href="run:check" title="%s">%s</a></div>' if err.has_multiple_locations: activate = [] for i, (name, lnum, col) in enumerate(err.all_locations): activate.append('<a href="activate:item:%d" title="%s">%s %s</a>' % ( i, open_tt, name, loc_to_string(lnum, col))) many = len(activate) > 2 activate = '<div>%s</div>' % ('<br>'.join(activate)) if many: activate += '<br>' activate = activate.replace('%', '%%') template = header + ((msg + activate) if many else (activate + msg)) + footer else: activate = '<div><a href="activate:item" title="{}">{} {}</a></div>'.format( open_tt, err.name, loc) activate = activate.replace('%', '%%') template = header + activate + msg + footer self.help.setText( template % (err.HELP, ifix, fix_tt, fix_msg, run_tt, run_msg)) def run_checks(self, container): with BusyCursor(): self.show_busy() QApplication.processEvents() errors = run_checks(container) self.hide_busy() for err in sorted(errors, key=lambda e:(100 - e.level, e.name)): i = QListWidgetItem(f'{err.msg}\xa0\xa0\xa0\xa0[{err.name}]', self.items) i.setData(Qt.ItemDataRole.UserRole, err) i.setIcon(icon_for_level(err.level)) if errors: self.items.setCurrentRow(0) self.current_item_changed() self.items.setFocus(Qt.FocusReason.OtherFocusReason) else: self.clear_help() def fix_errors(self, container, errors): with BusyCursor(): self.show_busy(_('Running fixers, please wait...')) QApplication.processEvents() changed = fix_errors(container, errors) self.run_checks(container) return changed def show_busy(self, msg=_('Running checks, please wait...')): self.help.setText(msg) self.items.clear() def hide_busy(self): self.help.setText('') self.items.clear() def keyPressEvent(self, ev): if ev.key() in (Qt.Key.Key_Enter, Qt.Key.Key_Return): self.current_item_activated() return super().keyPressEvent(ev) def clear(self): self.items.clear() self.clear_help() def main(): from calibre.gui2 import Application from calibre.gui2.tweak_book.boss import get_container app = Application([]) # noqa path = sys.argv[-1] container = get_container(path) d = Check() d.run_checks(container) d.show() app.exec() if __name__ == '__main__': main()