%PDF- %PDF-
| Direktori : /lib/calibre/calibre/gui2/viewer/ |
| Current File : //lib/calibre/calibre/gui2/viewer/main.py |
#!/usr/bin/env python3
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
import json
import os
import sys
from qt.core import QIcon, QObject, Qt, QTimer, pyqtSignal
from qt.webengine import QWebEngineUrlScheme
from contextlib import closing
from calibre.constants import FAKE_PROTOCOL, VIEWER_APP_UID, islinux
from calibre.gui2 import Application, error_dialog, setup_gui_option_parser
from calibre.gui2.viewer.config import get_session_pref, vprefs
from calibre.gui2.viewer.ui import EbookViewer, is_float
from calibre.gui2.listener import send_message_in_process
from calibre.ptempfile import reset_base_dir
from calibre.utils.config import JSONConfig
from calibre.utils.ipc import viewer_socket_address
singleinstance_name = 'calibre_viewer'
def migrate_previous_viewer_prefs():
new_prefs = vprefs
if new_prefs['old_prefs_migrated']:
return
old_vprefs = JSONConfig('viewer')
old_prefs = JSONConfig('viewer.py')
with new_prefs:
sd = new_prefs['session_data']
fs = sd.get('standalone_font_settings', {})
for k in ('serif', 'sans', 'mono'):
defval = 'Liberation ' + k.capitalize()
k += '_family'
if old_prefs.get(k) and old_prefs[k] != defval:
fs[k] = old_prefs[k]
if old_prefs.get('standard_font') in ('serif', 'sans', 'mono'):
fs['standard_font'] = old_prefs['standard_font']
if old_prefs.get('minimum_font_size') is not None and old_prefs['minimum_font_size'] != 8:
fs['minimum_font_size'] = old_prefs['minimum_font_size']
sd['standalone_font_settings'] = fs
ms = sd.get('standalone_misc_settings', {})
ms['remember_window_geometry'] = bool(old_prefs.get('remember_window_size', False))
ms['remember_last_read'] = bool(old_prefs.get('remember_current_page', True))
ms['save_annotations_in_ebook'] = bool(old_prefs.get('copy_bookmarks_to_file', False))
ms['singleinstance'] = bool(old_vprefs.get('singleinstance', False))
sd['standalone_misc_settings'] = ms
for k in ('top', 'bottom'):
v = old_prefs.get(k + '_margin')
if v != 20 and v is not None:
sd['margin_' + k] = v
v = old_prefs.get('side_margin')
if v is not None and v != 40:
sd['margin_left'] = sd['margin_right'] = v // 2
if old_prefs.get('user_css'):
sd['user_stylesheet'] = old_prefs['user_css']
cps = {'portrait': 0, 'landscape': 0}
cp = old_prefs.get('cols_per_screen_portrait')
if cp and cp > 1:
cps['portrait'] = cp
cl = old_prefs.get('cols_per_screen_landscape')
if cl and cl > 1:
cps['landscape'] = cp
if cps['portrait'] or cps['landscape']:
sd['columns_per_screen'] = cps
if old_vprefs.get('in_paged_mode') is False:
sd['read_mode'] = 'flow'
new_prefs.set('session_data', sd)
new_prefs.set('old_prefs_migrated', True)
class EventAccumulator(QObject):
got_file = pyqtSignal(object)
def __init__(self, parent):
QObject.__init__(self, parent)
self.events = []
def __call__(self, paths):
for path in paths:
if os.path.exists(path):
self.events.append(path)
self.got_file.emit(path)
def flush(self):
if self.events:
self.got_file.emit(self.events[-1])
self.events = []
def send_message_to_viewer_instance(args, open_at):
if len(args) > 1:
msg = json.dumps((os.path.abspath(args[1]), open_at))
try:
send_message_in_process(msg, address=viewer_socket_address())
except Exception as err:
error_dialog(None, _('Connecting to E-book viewer failed'), _(
'Unable to connect to existing E-book viewer window, try restarting the viewer.'), det_msg=str(err), show=True)
raise SystemExit(1)
print('Opened book in existing viewer instance')
def option_parser():
from calibre.gui2.main_window import option_parser
parser = option_parser(_('''\
%prog [options] file
View an e-book.
'''))
a = parser.add_option
a('--raise-window', default=False, action='store_true',
help=_('If specified, the E-book viewer window will try to come to the '
'front when started.'))
a('--full-screen', '--fullscreen', '-f', default=False, action='store_true',
help=_('If specified, the E-book viewer window will try to open '
'full screen when started.'))
a('--force-reload', default=False, action='store_true',
help=_('Force reload of all opened books'))
a('--open-at', default=None, help=_(
'The position at which to open the specified book. The position is '
'a location or position you can get by using the Go to->Location action in the viewer controls. '
'Alternately, you can use the form toc:something and it will open '
'at the location of the first Table of Contents entry that contains '
'the string "something". The form toc-href:something will match the '
'href (internal link destination) of toc nodes. The matching is exact. '
'If you want to match a substring, use the form toc-href-contains:something. '
'The form ref:something will use Reference mode references.'))
a('--continue', default=False, action='store_true', dest='continue_reading',
help=_('Continue reading the last opened book'))
setup_gui_option_parser(parser)
return parser
def run_gui(app, opts, args, internal_book_data, listener=None):
acc = EventAccumulator(app)
app.file_event_hook = acc
app.load_builtin_fonts()
app.setWindowIcon(QIcon(I('viewer.png')))
migrate_previous_viewer_prefs()
main = EbookViewer(
open_at=opts.open_at, continue_reading=opts.continue_reading, force_reload=opts.force_reload,
calibre_book_data=internal_book_data)
main.set_exception_handler()
if len(args) > 1:
acc.events.append(os.path.abspath(args[-1]))
acc.got_file.connect(main.handle_commandline_arg)
main.show()
if listener is not None:
listener.message_received.connect(main.message_from_other_instance, type=Qt.ConnectionType.QueuedConnection)
QTimer.singleShot(0, acc.flush)
if opts.raise_window:
main.raise_()
if opts.full_screen:
main.set_full_screen(True)
app.exec()
def main(args=sys.argv):
# Ensure viewer can continue to function if GUI is closed
os.environ.pop('CALIBRE_WORKER_TEMP_DIR', None)
reset_base_dir()
scheme = QWebEngineUrlScheme(FAKE_PROTOCOL.encode('ascii'))
scheme.setSyntax(QWebEngineUrlScheme.Syntax.Host)
scheme.setFlags(QWebEngineUrlScheme.Flag.SecureScheme)
QWebEngineUrlScheme.registerScheme(scheme)
override = 'calibre-ebook-viewer' if islinux else None
processed_args = []
internal_book_data = internal_book_data_path = None
for arg in args:
if arg.startswith('--internal-book-data='):
internal_book_data_path = arg.split('=', 1)[1]
continue
processed_args.append(arg)
if internal_book_data_path:
try:
with lopen(internal_book_data_path, 'rb') as f:
internal_book_data = json.load(f)
finally:
try:
os.remove(internal_book_data_path)
except OSError:
pass
args = processed_args
app = Application(args, override_program_name=override, windows_app_uid=VIEWER_APP_UID)
parser = option_parser()
opts, args = parser.parse_args(args)
oat = opts.open_at
if oat and not (
oat.startswith('toc:') or oat.startswith('toc-href:') or oat.startswith('toc-href-contains:') or
oat.startswith('epubcfi(/') or is_float(oat) or oat.startswith('ref:')):
raise SystemExit(f'Not a valid --open-at value: {opts.open_at}')
if get_session_pref('singleinstance', False):
from calibre.utils.lock import SingleInstance
from calibre.gui2.listener import Listener
with SingleInstance(singleinstance_name) as si:
if si:
try:
listener = Listener(address=viewer_socket_address(), parent=app)
listener.start_listening()
except Exception as err:
error_dialog(None, _('Failed to start listener'), _(
'Could not start the listener used for single instance viewers. Try rebooting your computer.'),
det_msg=str(err), show=True)
else:
with closing(listener):
run_gui(app, opts, args, internal_book_data, listener=listener)
else:
send_message_to_viewer_instance(args, opts.open_at)
else:
run_gui(app, opts, args, internal_book_data)
if __name__ == '__main__':
sys.exit(main())