%PDF- %PDF-
Direktori : /lib/calibre/calibre/gui2/lrf_renderer/ |
Current File : //lib/calibre/calibre/gui2/lrf_renderer/main.py |
__license__ = 'GPL v3' __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>' import sys, logging, os, traceback, time from qt.core import ( QKeySequence, QPainter, QDialog, QSpinBox, QSlider, QIcon, Qt, QCoreApplication, QThread, QScrollBar) from calibre import __appname__, setup_cli_handlers, islinux, isbsd, as_unicode from calibre.gui2 import gprefs from calibre.ebooks.lrf.lrfparser import LRFDocument from calibre.gui2 import ( error_dialog, choose_files, Application ) from calibre.gui2.dialogs.conversion_error import ConversionErrorDialog from calibre.gui2.lrf_renderer.main_ui import Ui_MainWindow from calibre.gui2.lrf_renderer.config_ui import Ui_ViewerConfig from calibre.gui2.main_window import MainWindow from calibre.gui2.lrf_renderer.document import Document from calibre.gui2.search_box import SearchBox2 class RenderWorker(QThread): def __init__(self, parent, lrf_stream, logger, opts): QThread.__init__(self, parent) self.stream, self.logger, self.opts = lrf_stream, logger, opts self.aborted = False self.lrf = None self.document = None self.exception = None def run(self): try: self.lrf = LRFDocument(self.stream) self.lrf.parse() self.stream.close() self.stream = None if self.aborted: self.lrf = None except Exception as err: self.lrf, self.stream = None, None self.exception = err self.formatted_traceback = traceback.format_exc() def abort(self): if self.lrf is not None: self.aborted = True self.lrf.keep_parsing = False class Config(QDialog, Ui_ViewerConfig): def __init__(self, parent, opts): QDialog.__init__(self, parent) Ui_ViewerConfig.__init__(self) self.setupUi(self) self.white_background.setChecked(opts.white_background) self.hyphenate.setChecked(opts.hyphenate) class Main(MainWindow, Ui_MainWindow): def create_document(self): self.document = Document(self.logger, self.opts) self.document.chapter_rendered.connect(self.chapter_rendered) self.document.page_changed.connect(self.page_changed) def __init__(self, logger, opts, parent=None): MainWindow.__init__(self, opts, parent) Ui_MainWindow.__init__(self) self.setupUi(self) self.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose) self.setWindowTitle(__appname__ + _(' - LRF viewer')) self.logger = logger self.opts = opts self.create_document() self.spin_box_action = self.spin_box = QSpinBox() self.tool_bar.addWidget(self.spin_box) self.tool_bar.addSeparator() self.slider_action = self.slider = QSlider(Qt.Orientation.Horizontal) self.tool_bar.addWidget(self.slider) self.tool_bar.addSeparator() self.search = SearchBox2(self) self.search.initialize('lrf_viewer_search_history') self.search_action = self.tool_bar.addWidget(self.search) self.search.search.connect(self.find) self.action_next_page.setShortcuts([QKeySequence.StandardKey.MoveToNextPage, QKeySequence(Qt.Key.Key_Space)]) self.action_previous_page.setShortcuts([QKeySequence.StandardKey.MoveToPreviousPage, QKeySequence(Qt.Key.Key_Backspace)]) self.action_next_match.setShortcuts(QKeySequence.StandardKey.FindNext) self.addAction(self.action_next_match) self.action_next_page.triggered[(bool)].connect(self.next) self.action_previous_page.triggered[(bool)].connect(self.previous) self.action_back.triggered[(bool)].connect(self.back) self.action_forward.triggered[(bool)].connect(self.forward) self.action_next_match.triggered[(bool)].connect(self.next_match) self.action_open_ebook.triggered[(bool)].connect(self.open_ebook) self.action_configure.triggered[(bool)].connect(self.configure) self.spin_box.valueChanged[(int)].connect(self.go_to_page) self.slider.valueChanged[(int)].connect(self.go_to_page) self.graphics_view.setRenderHint(QPainter.RenderHint.Antialiasing, True) self.graphics_view.setRenderHint(QPainter.RenderHint.TextAntialiasing, True) self.graphics_view.setRenderHint(QPainter.RenderHint.SmoothPixmapTransform, True) self.closed = False def configure(self, triggered): opts = self.opts d = Config(self, opts) d.exec() if d.result() == QDialog.DialogCode.Accepted: gprefs['lrf_viewer_white_background'] = opts.white_background = bool(d.white_background.isChecked()) gprefs['lrf_viewer_hyphenate'] = opts.hyphenate = bool(d.hyphenate.isChecked()) def set_ebook(self, stream): self.progress_bar.setMinimum(0) self.progress_bar.setMaximum(0) self.progress_bar.setValue(0) self.create_document() if stream is not None: self.file_name = os.path.basename(stream.name) if hasattr(stream, 'name') else '' self.progress_label.setText('Parsing '+ self.file_name) self.renderer = RenderWorker(self, stream, self.logger, self.opts) self.renderer.finished.connect(self.parsed, type=Qt.ConnectionType.QueuedConnection) self.search.clear() self.last_search = None else: self.stack.setCurrentIndex(0) self.renderer = None def open_ebook(self, triggered): files = choose_files(self, 'open ebook dialog', 'Choose ebook', [('Ebooks', ['lrf'])], all_files=False, select_only_single_file=True) if files: file = files[0] self.set_ebook(open(file, 'rb')) self.render() def page_changed(self, num): self.slider.setValue(num) self.spin_box.setValue(num) def render(self): if self.renderer is not None: self.stack.setCurrentIndex(1) self.renderer.start() def find(self, search): self.last_search = search try: self.document.search(search) except StopIteration: error_dialog(self, _('No matches found'), _('<b>No matches</b> for the search phrase <i>%s</i> were found.')%(search,)).exec() self.search.search_done(True) def parsed(self): if not self.renderer.aborted and self.renderer.lrf is not None: width, height = self.renderer.lrf.device_info.width, \ self.renderer.lrf.device_info.height hdelta = self.tool_bar.height()+3 s = QScrollBar(self) scrollbar_adjust = min(s.width(), s.height()) self.graphics_view.resize_for(width+scrollbar_adjust, height+scrollbar_adjust) screen_height = self.screen().availableSize().height() - 25 height = min(screen_height, height+hdelta+scrollbar_adjust) self.resize(width+scrollbar_adjust, height) self.setWindowTitle(self.renderer.lrf.metadata.title + ' - ' + __appname__) self.document_title = self.renderer.lrf.metadata.title if self.opts.profile: import cProfile lrf = self.renderer.lrf cProfile.runctx('self.document.render(lrf)', globals(), locals(), lrf.metadata.title+'.stats') print('Stats written to', self.renderer.lrf.metadata.title+'.stats') else: start = time.time() self.document.render(self.renderer.lrf) print('Layout time:', time.time()-start, 'seconds') self.renderer.lrf = None self.graphics_view.setScene(self.document) self.graphics_view.show() self.spin_box.setRange(1, self.document.num_of_pages) self.slider.setRange(1, self.document.num_of_pages) self.spin_box.setSuffix(' of %d'%(self.document.num_of_pages,)) self.spin_box.updateGeometry() self.stack.setCurrentIndex(0) self.graphics_view.setFocus(Qt.FocusReason.OtherFocusReason) elif self.renderer.exception is not None: exception = self.renderer.exception print('Error rendering document', file=sys.stderr) print(exception, file=sys.stderr) print(self.renderer.formatted_traceback, file=sys.stderr) msg = '<p><b>%s</b>: '%(exception.__class__.__name__,) + as_unicode(exception) + '</p>' msg += '<p>Failed to render document</p>' msg += '<p>Detailed <b>traceback</b>:<pre>' msg += self.renderer.formatted_traceback + '</pre>' d = ConversionErrorDialog(self, 'Error while rendering file', msg) d.exec() def chapter_rendered(self, num): if num > 0: self.progress_bar.setMinimum(0) self.progress_bar.setMaximum(num) self.progress_bar.setValue(0) self.progress_label.setText('Laying out '+ self.document_title) else: self.progress_bar.setValue(self.progress_bar.value()+1) QCoreApplication.processEvents() def next(self, triggered): self.document.next() def next_match(self, triggered): try: self.document.next_match() except StopIteration: pass def previous(self, triggered): self.document.previous() def go_to_page(self, num): self.document.show_page(num) def forward(self, triggered): self.document.forward() def back(self, triggered): self.document.back() def wheelEvent(self, ev): d = ev.angleDelta().y() if d > 0: self.document.previous() elif d < 0: self.document.next() def closeEvent(self, event): if self.renderer is not None and self.renderer.isRunning(): self.renderer.abort() self.renderer.wait() event.accept() def file_renderer(stream, opts, parent=None, logger=None): if logger is None: level = logging.DEBUG if opts.verbose else logging.INFO logger = logging.getLogger('lrfviewer') setup_cli_handlers(logger, level) if islinux or isbsd: try: # Set lrfviewer as the default for LRF files for this user from subprocess import call call('xdg-mime default calibre-lrfviewer.desktop application/lrf', shell=True) except: pass m = Main(logger, opts, parent=parent) m.set_ebook(stream) return m def option_parser(): from calibre.gui2.main_window import option_parser parser = option_parser(_('''\ %prog [options] book.lrf Read the LRF e-book book.lrf ''')) parser.add_option('--verbose', default=False, action='store_true', dest='verbose', help=_('Print more information about the rendering process')) parser.add_option('--visual-debug', help=_('Turn on visual aids to debugging the rendering engine'), default=False, action='store_true', dest='visual_debug') parser.add_option('--disable-hyphenation', dest='hyphenate', default=True, action='store_false', help=_('Disable hyphenation. Should significantly speed up rendering.')) parser.add_option('--white-background', dest='white_background', default=False, action='store_true', help=_('By default the background is off white as I find this easier on the eyes. Use this option to make the background pure white.')) parser.add_option('--profile', dest='profile', default=False, action='store_true', help=_('Profile the LRF renderer')) return parser def normalize_settings(parser, opts): saved_opts = opts dh = gprefs.get('lrf_viewer_hyphenate', None) if dh is not None: opts.hyphenate = bool(dh) wb = gprefs.get('lrf_viewer_white_background', None) if wb is not None: opts.white_background = bool(wb) return saved_opts def main(args=sys.argv, logger=None): parser = option_parser() opts, args = parser.parse_args(args) if hasattr(opts, 'help'): parser.print_help() return 1 pid = os.fork() if (islinux or isbsd) else -1 if pid <= 0: override = 'calibre-lrfviewer' if islinux else None app = Application(args, override_program_name=override) app.setWindowIcon(QIcon(I('viewer.png'))) opts = normalize_settings(parser, opts) stream = open(args[1], 'rb') if len(args) > 1 else None main = file_renderer(stream, opts, logger=logger) main.set_exception_handler() main.show() main.render() main.activateWindow() main.raise_() return app.exec() return 0 if __name__ == '__main__': sys.exit(main())