mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Cleanup ebook viewer code
This commit is contained in:
parent
f5ad1f38ba
commit
6d0b2ec553
@ -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 MainWindow.closeEvent(self, e)
|
return True
|
||||||
|
|
||||||
|
def quit(self):
|
||||||
|
if self.shutdown():
|
||||||
|
QApplication.instance().quit()
|
||||||
|
|
||||||
|
def closeEvent(self, e):
|
||||||
|
if self.shutdown():
|
||||||
|
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
|
||||||
|
39
src/calibre/gui2/viewer/toc.py
Normal file
39
src/calibre/gui2/viewer/toc.py
Normal 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')))
|
||||||
|
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user