Cleanup ebook viewer code

This commit is contained in:
Kovid Goyal 2012-05-06 23:10:25 +05:30
parent f5ad1f38ba
commit 6d0b2ec553
2 changed files with 105 additions and 93 deletions

View File

@ -1,23 +1,23 @@
from __future__ import with_statement
__license__ = 'GPL v3' __license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>' __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
import traceback, os, sys, functools, collections, re
import traceback, os, sys, functools, collections, textwrap
from functools import partial from functools import partial
from threading import Thread from threading import Thread
from PyQt4.Qt import (QApplication, Qt, QIcon, QTimer, SIGNAL, QByteArray, from PyQt4.Qt import (QApplication, Qt, QIcon, QTimer, QByteArray, QSize,
QSize, QDoubleSpinBox, QLabel, QTextBrowser, QPropertyAnimation, QDoubleSpinBox, QLabel, QTextBrowser, QPropertyAnimation, QPainter,
QPainter, QBrush, QColor, QStandardItemModel, QPalette, QStandardItem, QBrush, QColor, pyqtSignal, QUrl, QRegExpValidator, QRegExp, QLineEdit,
QUrl, QRegExpValidator, QRegExp, QLineEdit, QToolButton, QMenu, QToolButton, QMenu, QInputDialog, QAction, QKeySequence, QModelIndex)
QInputDialog, QAction, QKeySequence)
from calibre.gui2.viewer.main_ui import Ui_EbookViewer from calibre.gui2.viewer.main_ui import Ui_EbookViewer
from calibre.gui2.viewer.printing import Printing from calibre.gui2.viewer.printing import Printing
from calibre.gui2.viewer.bookmarkmanager import BookmarkManager from calibre.gui2.viewer.bookmarkmanager import BookmarkManager
from calibre.gui2.viewer.toc import TOC
from calibre.gui2.widgets import ProgressIndicator from calibre.gui2.widgets import ProgressIndicator
from calibre.gui2.main_window import MainWindow from calibre.gui2.main_window import MainWindow
from calibre.gui2 import Application, ORG_NAME, APP_UID, choose_files, \ from calibre.gui2 import (Application, ORG_NAME, APP_UID, choose_files,
info_dialog, error_dialog, open_url, available_height info_dialog, error_dialog, open_url, available_height)
from calibre.ebooks.oeb.iterator import EbookIterator from calibre.ebooks.oeb.iterator import EbookIterator
from calibre.ebooks import DRMError from calibre.ebooks import DRMError
from calibre.constants import islinux, isbsd, isosx, filesystem_encoding from calibre.constants import islinux, isbsd, isosx, filesystem_encoding
@ -31,31 +31,6 @@ from calibre.ptempfile import reset_base_dir
vprefs = JSONConfig('viewer') vprefs = JSONConfig('viewer')
class TOCItem(QStandardItem):
def __init__(self, toc):
text = toc.text
if text:
text = re.sub(r'\s', ' ', text)
QStandardItem.__init__(self, text if text else '')
self.abspath = toc.abspath
self.fragment = toc.fragment
for t in toc:
self.appendRow(TOCItem(t))
self.setFlags(Qt.ItemIsEnabled|Qt.ItemIsSelectable)
@classmethod
def type(cls):
return QStandardItem.UserType+10
class TOC(QStandardItemModel):
def __init__(self, toc):
QStandardItemModel.__init__(self)
for t in toc:
self.appendRow(TOCItem(t))
self.setHorizontalHeaderItem(0, QStandardItem(_('Table of Contents')))
class Worker(Thread): class Worker(Thread):
def run(self): def run(self):
@ -142,31 +117,22 @@ class DoubleSpinBox(QDoubleSpinBox):
' [{0:.0%}]'.format(float(val)/self.maximum())) ' [{0:.0%}]'.format(float(val)/self.maximum()))
self.blockSignals(False) self.blockSignals(False)
class HelpfulLineEdit(QLineEdit): class Reference(QLineEdit):
HELP_TEXT = _('Go to...') goto = pyqtSignal(object)
def __init__(self, *args): def __init__(self, *args):
QLineEdit.__init__(self, *args) QLineEdit.__init__(self, *args)
self.default_palette = QApplication.palette(self) self.setValidator(QRegExpValidator(QRegExp(r'\d+\.\d+'), self))
self.gray = QPalette(self.default_palette) self.setToolTip(textwrap.fill('<p>'+_(
self.gray.setBrush(QPalette.Text, QBrush(QColor('gray'))) 'Go to a reference. To get reference numbers, use the <i>reference '
self.connect(self, SIGNAL('editingFinished()'), 'mode</i>, by clicking the reference mode button in the toolbar.')))
lambda : self.emit(SIGNAL('goto(PyQt_PyObject)'), unicode(self.text()))) if hasattr(self, 'setPlaceholderText'):
self.clear_to_help_mode() self.setPlaceholderText(_('Go to...'))
self.editingFinished.connect(self.editing_finished)
def focusInEvent(self, ev): def editing_finished(self):
self.setPalette(QApplication.palette(self)) self.goto.emit(unicode(self.text()))
if self.in_help_mode():
self.setText('')
return QLineEdit.focusInEvent(self, ev)
def in_help_mode(self):
return unicode(self.text()) == self.HELP_TEXT
def clear_to_help_mode(self):
self.setPalette(self.gray)
self.setText(self.HELP_TEXT)
class RecentAction(QAction): class RecentAction(QAction):
@ -207,9 +173,7 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
self.pos.setMinimum(1.) self.pos.setMinimum(1.)
self.pos.setMinimumWidth(150) self.pos.setMinimumWidth(150)
self.tool_bar2.insertWidget(self.action_find_next, self.pos) self.tool_bar2.insertWidget(self.action_find_next, self.pos)
self.reference = HelpfulLineEdit() self.reference = Reference()
self.reference.setValidator(QRegExpValidator(QRegExp(r'\d+\.\d+'), self.reference))
self.reference.setToolTip(_('Go to a reference. To get reference numbers, use the reference mode.'))
self.tool_bar2.insertSeparator(self.action_find_next) self.tool_bar2.insertSeparator(self.action_find_next)
self.tool_bar2.insertWidget(self.action_find_next, self.reference) self.tool_bar2.insertWidget(self.action_find_next, self.reference)
self.tool_bar2.insertSeparator(self.action_find_next) self.tool_bar2.insertSeparator(self.action_find_next)
@ -233,8 +197,7 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
if isosx: if isosx:
qs += [Qt.CTRL+Qt.Key_W] qs += [Qt.CTRL+Qt.Key_W]
self.action_quit.setShortcuts(qs) self.action_quit.setShortcuts(qs)
self.connect(self.action_quit, SIGNAL('triggered(bool)'), self.action_quit.triggered.connect(self.quit)
lambda x:QApplication.instance().quit())
self.action_focus_search = QAction(self) self.action_focus_search = QAction(self)
self.addAction(self.action_focus_search) self.addAction(self.action_focus_search)
self.action_focus_search.setShortcuts([Qt.Key_Slash, self.action_focus_search.setShortcuts([Qt.Key_Slash,
@ -247,42 +210,34 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
self.action_table_of_contents.setCheckable(True) self.action_table_of_contents.setCheckable(True)
self.toc.setMinimumWidth(80) self.toc.setMinimumWidth(80)
self.action_reference_mode.setCheckable(True) self.action_reference_mode.setCheckable(True)
self.connect(self.action_reference_mode, SIGNAL('triggered(bool)'), self.action_reference_mode.triggered[bool].connect(self.view.reference_mode)
lambda x: self.view.reference_mode(x)) self.action_metadata.triggered[bool].connect(self.metadata.setVisible)
self.connect(self.action_metadata, SIGNAL('triggered(bool)'), lambda x:self.metadata.setVisible(x))
self.action_table_of_contents.toggled[bool].connect(self.set_toc_visible) self.action_table_of_contents.toggled[bool].connect(self.set_toc_visible)
self.connect(self.action_copy, SIGNAL('triggered(bool)'), self.copy) self.action_copy.triggered[bool].connect(self.copy)
self.action_font_size_larger.triggered.connect(self.font_size_larger) self.action_font_size_larger.triggered.connect(self.font_size_larger)
self.action_font_size_smaller.triggered.connect(self.font_size_smaller) self.action_font_size_smaller.triggered.connect(self.font_size_smaller)
self.connect(self.action_open_ebook, SIGNAL('triggered(bool)'), self.action_open_ebook.triggered[bool].connect(self.open_ebook)
self.open_ebook) self.action_next_page.triggered.connect(self.view.next_page)
self.connect(self.action_next_page, SIGNAL('triggered(bool)'), self.action_previous_page.triggered.connect(self.view.previous_page)
lambda x:self.view.next_page()) self.action_find_next.triggered.connect(self.find_next)
self.connect(self.action_previous_page, SIGNAL('triggered(bool)'), self.action_find_previous.triggered.connect(self.find_previous)
lambda x:self.view.previous_page()) self.action_full_screen.triggered[bool].connect(self.toggle_fullscreen)
self.connect(self.action_find_next, SIGNAL('triggered(bool)'),
lambda x:self.find(unicode(self.search.text()), repeat=True))
self.connect(self.action_find_previous, SIGNAL('triggered(bool)'),
lambda x:self.find(unicode(self.search.text()),
repeat=True, backwards=True))
self.connect(self.action_full_screen, SIGNAL('triggered(bool)'),
self.toggle_fullscreen)
self.action_full_screen.setShortcuts([Qt.Key_F11, Qt.CTRL+Qt.SHIFT+Qt.Key_F]) self.action_full_screen.setShortcuts([Qt.Key_F11, Qt.CTRL+Qt.SHIFT+Qt.Key_F])
self.action_full_screen.setToolTip(_('Toggle full screen (%s)') % self.action_full_screen.setToolTip(_('Toggle full screen (%s)') %
_(' or ').join([unicode(x.toString(x.NativeText)) for x in _(' or ').join([unicode(x.toString(x.NativeText)) for x in
self.action_full_screen.shortcuts()])) self.action_full_screen.shortcuts()]))
self.connect(self.action_back, SIGNAL('triggered(bool)'), self.back) self.action_back.triggered[bool].connect(self.back)
self.connect(self.action_bookmark, SIGNAL('triggered(bool)'), self.bookmark) self.action_forward.triggered[bool].connect(self.forward)
self.connect(self.action_forward, SIGNAL('triggered(bool)'), self.forward) self.action_bookmark.triggered[bool].connect(self.bookmark)
self.connect(self.action_preferences, SIGNAL('triggered(bool)'), lambda x: self.view.config(self)) self.action_preferences.triggered.connect(lambda :
self.view.config(self))
self.pos.editingFinished.connect(self.goto_page_num) self.pos.editingFinished.connect(self.goto_page_num)
self.connect(self.vertical_scrollbar, SIGNAL('valueChanged(int)'), self.vertical_scrollbar.valueChanged[int].connect(lambda
lambda x: self.goto_page(x/100.)) x:self.goto_page(x/100.))
self.search.search.connect(self.find) self.search.search.connect(self.find)
self.search.focus_to_library.connect(lambda: self.view.setFocus(Qt.OtherFocusReason)) self.search.focus_to_library.connect(lambda: self.view.setFocus(Qt.OtherFocusReason))
self.connect(self.toc, SIGNAL('clicked(QModelIndex)'), self.toc_clicked) self.toc.clicked[QModelIndex].connect(self.toc_clicked)
self.connect(self.reference, SIGNAL('goto(PyQt_PyObject)'), self.goto) self.reference.goto.connect(self.goto)
self.bookmarks_menu = QMenu() self.bookmarks_menu = QMenu()
self.action_bookmark.setMenu(self.bookmarks_menu) self.action_bookmark.setMenu(self.bookmarks_menu)
@ -335,8 +290,8 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
self.print_menu.addAction(QIcon(I('print-preview.png')), _('Print Preview')) self.print_menu.addAction(QIcon(I('print-preview.png')), _('Print Preview'))
self.action_print.setMenu(self.print_menu) self.action_print.setMenu(self.print_menu)
self.tool_bar.widgetForAction(self.action_print).setPopupMode(QToolButton.MenuButtonPopup) self.tool_bar.widgetForAction(self.action_print).setPopupMode(QToolButton.MenuButtonPopup)
self.connect(self.action_print, SIGNAL("triggered(bool)"), partial(self.print_book, preview=False)) self.action_print.triggered.connect(self.print_book)
self.connect(self.print_menu.actions()[0], SIGNAL("triggered(bool)"), partial(self.print_book, preview=True)) self.print_menu.actions()[0].triggered.connect(self.print_preview)
ca = self.view.copy_action ca = self.view.copy_action
ca.setShortcut(QKeySequence.Copy) ca.setShortcut(QKeySequence.Copy)
self.addAction(ca) self.addAction(ca)
@ -381,13 +336,22 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
m.addAction(RecentAction(path, m)) m.addAction(RecentAction(path, m))
count += 1 count += 1
def closeEvent(self, e): def shutdown(self):
if self.isFullScreen(): if self.isFullScreen():
self.action_full_screen.trigger() self.action_full_screen.trigger()
e.ignore() return False
return
self.save_state() self.save_state()
return True
def quit(self):
if self.shutdown():
QApplication.instance().quit()
def closeEvent(self, e):
if self.shutdown():
return MainWindow.closeEvent(self, e) return MainWindow.closeEvent(self, e)
else:
e.ignore()
def toggle_toolbars(self): def toggle_toolbars(self):
for x in ('tool_bar', 'tool_bar2'): for x in ('tool_bar', 'tool_bar2'):
@ -440,8 +404,11 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
c = config().parse() c = config().parse()
return c.remember_current_page return c.remember_current_page
def print_book(self, preview): def print_book(self):
Printing(self.iterator.spine, preview) Printing(self.iterator.spine, False)
def print_preview(self):
Printing(self.iterator.spine, True)
def toggle_fullscreen(self, x): def toggle_fullscreen(self, x):
if self.isFullScreen(): if self.isFullScreen():
@ -629,6 +596,12 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
self.pending_search_dir = 'backwards' if backwards else 'forwards' self.pending_search_dir = 'backwards' if backwards else 'forwards'
self.load_path(self.iterator.spine[index]) self.load_path(self.iterator.spine[index])
def find_next(self):
self.find(unicode(self.search.text()), repeat=True)
def find_previous(self):
self.find(unicode(self.search.text()), repeat=True, backwards=True)
def do_search(self, text, backwards): def do_search(self, text, backwards):
self.pending_search = None self.pending_search = None
self.pending_search_dir = None self.pending_search_dir = None

View File

@ -0,0 +1,39 @@
#!/usr/bin/env python
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
from __future__ import (unicode_literals, division, absolute_import,
print_function)
__license__ = 'GPL v3'
__copyright__ = '2012, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
import re
from PyQt4.Qt import QStandardItem, QStandardItemModel, Qt
class TOCItem(QStandardItem):
def __init__(self, toc):
text = toc.text
if text:
text = re.sub(r'\s', ' ', text)
QStandardItem.__init__(self, text if text else '')
self.abspath = toc.abspath
self.fragment = toc.fragment
for t in toc:
self.appendRow(TOCItem(t))
self.setFlags(Qt.ItemIsEnabled|Qt.ItemIsSelectable)
@classmethod
def type(cls):
return QStandardItem.UserType+10
class TOC(QStandardItemModel):
def __init__(self, toc):
QStandardItemModel.__init__(self)
for t in toc:
self.appendRow(TOCItem(t))
self.setHorizontalHeaderItem(0, QStandardItem(_('Table of Contents')))