diff --git a/src/calibre/gui2/tweak_book/boss.py b/src/calibre/gui2/tweak_book/boss.py index 0b6eb73944..076f6edb82 100644 --- a/src/calibre/gui2/tweak_book/boss.py +++ b/src/calibre/gui2/tweak_book/boss.py @@ -16,14 +16,16 @@ from calibre.ebooks.oeb.polish.main import SUPPORTED from calibre.ebooks.oeb.polish.container import get_container, clone_container from calibre.gui2.tweak_book import set_current_container, current_container, tprefs from calibre.gui2.tweak_book.undo import GlobalUndoHistory +from calibre.gui2.tweak_book.save import SaveManager class Boss(QObject): - def __init__(self, parent=None): + def __init__(self, parent): QObject.__init__(self, parent) self.global_undo = GlobalUndoHistory() self.container_count = 0 self.tdir = None + self.save_manager = SaveManager(parent) def __call__(self, gui): self.gui = gui @@ -40,6 +42,7 @@ class Boss(QObject): def open_book(self, path=None): if not self.check_dirtied(): return + # TODO: Check if a save is in progress and abort if it is if not hasattr(path, 'rpartition'): path = choose_files(self.gui, 'open-book-for-tweaking', _('Choose book'), @@ -119,7 +122,10 @@ class Boss(QObject): # TODO: Update other GUI elements def save_book(self): - pass + self.gui.action_save.setEnabled(False) + tdir = tempfile.mkdtemp(prefix='save-%05d-' % self.container_count, dir=self.tdir) + container = clone_container(current_container(), tdir) + self.save_manager.schedule(tdir, container) def quit(self): if not self.confirm_quit(): diff --git a/src/calibre/gui2/tweak_book/save.py b/src/calibre/gui2/tweak_book/save.py new file mode 100644 index 0000000000..c18ce6de11 --- /dev/null +++ b/src/calibre/gui2/tweak_book/save.py @@ -0,0 +1,125 @@ +#!/usr/bin/env python +# vim:fileencoding=utf-8 +from __future__ import (unicode_literals, division, absolute_import, + print_function) + +__license__ = 'GPL v3' +__copyright__ = '2013, Kovid Goyal ' + +import shutil +from threading import Thread +from Queue import LifoQueue, Empty + +from PyQt4.Qt import (QObject, pyqtSignal, QLabel, QWidget, QHBoxLayout, Qt) + +from calibre.utils import join_with_timeout +from calibre.gui2.progress_indicator import ProgressIndicator + +class SaveWidget(QWidget): + + def __init__(self, parent=None): + QWidget.__init__(self, parent) + self.l = l = QHBoxLayout(self) + self.setLayout(l) + self.label = QLabel('') + self.pi = ProgressIndicator(self, 24) + l.addWidget(self.label) + l.addWidget(self.pi) + l.setContentsMargins(0, 0, 0, 0) + self.pi.setVisible(False) + self.stop() + + def start(self): + self.pi.setVisible(True) + self.pi.startAnimation() + self.label.setText(_('Saving...')) + + def stop(self): + self.pi.setVisible(False) + self.pi.stopAnimation() + self.label.setText(_('Saved')) + +class SaveManager(QObject): + + start_save = pyqtSignal() + report_error = pyqtSignal(object) + save_done = pyqtSignal() + + def __init__(self, parent): + QObject.__init__(self, parent) + self.count = 0 + self.last_saved = -1 + self.requests = LifoQueue() + t = Thread(name='save-thread', target=self.run) + t.daemon = True + t.start() + self.status_widget = w = SaveWidget(parent) + self.start_save.connect(w.start, type=Qt.QueuedConnection) + self.save_done.connect(w.stop, type=Qt.QueuedConnection) + + def schedule(self, tdir, container): + self.count += 1 + self.requests.put((self.count, tdir, container)) + + def run(self): + while True: + x = self.requests.get() + if x is None: + self.requests.task_done() + self.__empty_queue() + break + try: + count, tdir, container = x + self.process_save(count, tdir, container) + except: + import traceback + traceback.print_exc() + finally: + self.requests.task_done() + + def __empty_queue(self): + ' Only to be used during shutdown ' + while True: + try: + self.requests.get_nowait() + except Empty: + break + else: + self.requests.task_done() + + def process_save(self, count, tdir, container): + if count <= self.last_saved: + shutil.rmtree(tdir, ignore_errors=True) + return + self.last_saved = count + self.start_save.emit() + try: + self.do_save(tdir, container) + except: + import traceback + self.report_error.emit(traceback.format_exc()) + self.save_done.emit() + + def do_save(self, tdir, container): + try: + import time + time.sleep(10) + finally: + shutil.rmtree(tdir, ignore_errors=True) + + @property + def has_tasks(self): + return bool(self.requests.unfinished_tasks) + + def wait(self, timeout=30): + if timeout is None: + self.requests.join() + else: + try: + join_with_timeout(self.requests, timeout) + except RuntimeError: + return False + return True + + def shutdown(self): + self.requests.put(None) diff --git a/src/calibre/gui2/tweak_book/ui.py b/src/calibre/gui2/tweak_book/ui.py index bd8b90b2c4..66ffd5ce54 100644 --- a/src/calibre/gui2/tweak_book/ui.py +++ b/src/calibre/gui2/tweak_book/ui.py @@ -8,6 +8,7 @@ __copyright__ = '2013, Kovid Goyal ' from PyQt4.Qt import QDockWidget, Qt, QLabel, QIcon, QAction, QApplication +from calibre.constants import __appname__, get_version from calibre.gui2.main_window import MainWindow from calibre.gui2.tweak_book import current_container, tprefs from calibre.gui2.tweak_book.file_list import FileListWidget @@ -39,6 +40,11 @@ class Main(MainWindow): self.status_bar = self.statusBar() self.l = QLabel('Placeholder') + self.status_bar.addPermanentWidget(self.boss.save_manager.status_widget) + self.status_bar.addWidget(QLabel(_('%s %s created by %s') % (__appname__, get_version(), 'Kovid Goyal'))) + f = self.status_bar.font() + f.setBold(True) + self.status_bar.setFont(f) self.setCentralWidget(self.l) self.boss(self)