diff --git a/src/calibre/ebooks/lrf/feeds/convert_from.py b/src/calibre/ebooks/lrf/feeds/convert_from.py index bfa7a5f9c8..d4ea3d4c63 100644 --- a/src/calibre/ebooks/lrf/feeds/convert_from.py +++ b/src/calibre/ebooks/lrf/feeds/convert_from.py @@ -35,7 +35,7 @@ def main(args=sys.argv, notification=None, handler=None): recipe_arg = args[1] if len(args) > 1 else None tdir = PersistentTemporaryDirectory('_feeds2lrf') - opts.output_dir = tdir + opts.output_dir = tdir recipe = run_recipe(opts, recipe_arg, parser, notification=notification, handler=handler) @@ -56,4 +56,4 @@ def main(args=sys.argv, notification=None, handler=None): return 0 if __name__ == '__main__': - sys.exit(main()) \ No newline at end of file + sys.exit(main()) diff --git a/src/calibre/gui2/cover_flow.py b/src/calibre/gui2/cover_flow.py index e091338693..a6820adfef 100644 --- a/src/calibre/gui2/cover_flow.py +++ b/src/calibre/gui2/cover_flow.py @@ -10,7 +10,7 @@ Module to implement the Cover Flow feature import sys, os from PyQt4.QtGui import QImage, QSizePolicy -from PyQt4.QtCore import Qt, QSize, SIGNAL +from PyQt4.QtCore import Qt, QSize, SIGNAL, QObject from calibre import pictureflow @@ -49,7 +49,7 @@ if pictureflow is not None: def __init__(self, model, buffer=20): pictureflow.FlowImages.__init__(self) self.model = model - self.connect(self.model, SIGNAL('modelReset()'), self.reset) + QObject.connect(self.model, SIGNAL('modelReset()'), self.reset) def count(self): return self.model.count() @@ -76,9 +76,9 @@ if pictureflow is not None: def wheelEvent(self, ev): ev.accept() - if ev.delta() > 0: + if ev.delta() < 0: self.showNext() - elif ev.delta() < 0: + elif ev.delta() > 0: self.showPrevious() @@ -105,4 +105,4 @@ if __name__ == '__main__': w.show() cf.setFocus(Qt.OtherFocusReason) - sys.exit(app.exec_()) \ No newline at end of file + sys.exit(app.exec_()) diff --git a/src/calibre/gui2/dialogs/jobs.py b/src/calibre/gui2/dialogs/jobs.py index 57711ce511..b601cf2c7c 100644 --- a/src/calibre/gui2/dialogs/jobs.py +++ b/src/calibre/gui2/dialogs/jobs.py @@ -2,12 +2,31 @@ __license__ = 'GPL v3' __copyright__ = '2008, Kovid Goyal ' '''Display active jobs''' -from PyQt4.QtCore import Qt, QObject, SIGNAL -from PyQt4.QtGui import QDialog +from PyQt4.QtCore import Qt, QObject, SIGNAL, QSize, QString +from PyQt4.QtGui import QDialog, QAbstractItemDelegate, QStyleOptionProgressBarV2, \ + QApplication, QStyle from calibre.gui2.dialogs.jobs_ui import Ui_JobsDialog from calibre import __appname__ +class ProgressBarDelegate(QAbstractItemDelegate): + + def sizeHint(self, option, index): + return QSize(120, 30) + + def paint(self, painter, option, index): + opts = QStyleOptionProgressBarV2() + opts.rect = option.rect + opts.minimum = 1 + opts.maximum = 100 + opts.textVisible = True + percent, ok = index.model().data(index, Qt.DisplayRole).toInt() + if not ok: + percent = 0 + opts.progress = percent + opts.text = QString(_('Unavailable') if percent == 0 else '%d%%'%percent) + QApplication.style().drawControl(QStyle.CE_ProgressBar, opts, painter) + class JobsDialog(QDialog, Ui_JobsDialog): def __init__(self, window, model): QDialog.__init__(self, window) @@ -23,6 +42,8 @@ class JobsDialog(QDialog, Ui_JobsDialog): self.kill_job) QObject.connect(self, SIGNAL('kill_job(int, PyQt_PyObject)'), self.jobs_view.model().kill_job) + self.pb_delegate = ProgressBarDelegate(self) + self.jobs_view.setItemDelegateForColumn(2, self.pb_delegate) def kill_job(self): for index in self.jobs_view.selectedIndexes(): diff --git a/src/calibre/gui2/jobs.py b/src/calibre/gui2/jobs.py index 63bb7ba197..b19b8cedcf 100644 --- a/src/calibre/gui2/jobs.py +++ b/src/calibre/gui2/jobs.py @@ -224,6 +224,18 @@ class JobManager(QAbstractTableModel): if isinstance(job, ConversionJob): yield job + def update_progress(self, id, percent): + row = -1 + for collection in (self.running_jobs, self.waiting_jobs, self.finished_jobs): + for job in collection: + row += 1 + if job.id == id: + job.percent_done = percent + index = self.index(row, 2) + self.emit(SIGNAL('dataChanged(QModelIndex, QModelIndex)'), index, index) + return + + def create_job(self, job_class, description, slot, priority, *args, **kwargs): self.next_id += 1 id = self.next_id @@ -312,8 +324,7 @@ class JobManager(QAbstractTableModel): st = [_('Working'), _('Waiting')][status] return QVariant(st) if col == 2: - p = str(job.percent_done) + r'%' if job.percent_done > 0 else _('Unavailable') - return QVariant(p) + return QVariant(int(100*job.percent_done)) if col == 3: if job.start_time is None: return NONE diff --git a/src/calibre/gui2/main.py b/src/calibre/gui2/main.py index 5498da55c1..98e44485d2 100644 --- a/src/calibre/gui2/main.py +++ b/src/calibre/gui2/main.py @@ -250,7 +250,7 @@ class Main(MainWindow, Ui_MainWindow): index = self.library_view.model().index(index, 0) self.library_view.setCurrentIndex(index) if hasattr(index, 'row') and self.cover_flow.isVisible() and self.cover_flow.currentSlide() != index.row(): - self.cover_flow.setCurrentSlide(index.row()) + self.cover_flow.setCurrentSlide(index.row()) def another_instance_wants_to_talk(self, msg): if msg.startswith('launched:'): @@ -266,6 +266,16 @@ class Main(MainWindow, Ui_MainWindow): elif msg.startswith('refreshdb:'): self.library_view.model().resort() self.library_view.model().research() + elif msg.startswith('progress:'): + try: + fields = msg.split(':') + job_id, percent = fields[1:3] + job_id, percent = int(job_id), float(percent) + self.job_manager.update_progress(job_id, percent) + except: + pass + else: + print msg def current_view(self): @@ -288,7 +298,7 @@ class Main(MainWindow, Ui_MainWindow): ''' Called when a device is connected to the computer. ''' - if connected: + if connected: self.device_manager = DeviceManager(device) self.job_manager.run_device_job(self.info_read, self.device_manager.info_func()) self.set_default_thumbnail(device.THUMBNAIL_HEIGHT) diff --git a/src/calibre/parallel.py b/src/calibre/parallel.py index e02031ef22..a75490e3c2 100644 --- a/src/calibre/parallel.py +++ b/src/calibre/parallel.py @@ -12,12 +12,25 @@ from calibre.ebooks.lrf.web.convert_from import main as web2lrf from calibre.ebooks.lrf.feeds.convert_from import main as feeds2lrf from calibre.gui2.lrf_renderer.main import main as lrfviewer from calibre import iswindows, __appname__ +try: + from calibre.utils.single_qt_application import SingleApplication +except: + SingleApplication = None + +sa = None +job_id = None + +def report_progress(percent, msg=''): + if sa is not None and job_id is not None: + msg = 'progress:%s:%f:%s'%(job_id, percent, msg) + sa.send_message(msg) + PARALLEL_FUNCS = { 'any2lrf' : partial(any2lrf, gui_mode=True), 'web2lrf' : web2lrf, 'lrfviewer' : lrfviewer, - 'feeds2lrf' : feeds2lrf, + 'feeds2lrf' : partial(feeds2lrf, notification=report_progress), } python = sys.executable @@ -46,7 +59,7 @@ class Server(object): def __init__(self): self.tdir = tempfile.mkdtemp('', '%s_IPC_'%__appname__) atexit.register(cleanup, self.tdir) - self.kill_jobs = [] + self.kill_jobs = [] def kill(self, job_id): ''' @@ -90,7 +103,7 @@ class Server(object): os.mkdir(job_dir) job_data = os.path.join(job_dir, 'job_data.pickle') - cPickle.dump((func, args, kwdargs), open(job_data, 'wb'), -1) + cPickle.dump((job_id, func, args, kwdargs), open(job_data, 'wb'), -1) prefix = '' if hasattr(sys, 'frameworks_dir'): fd = getattr(sys, 'frameworks_dir') @@ -116,23 +129,26 @@ class Server(object): p.poll() - output.close() + output.close() job_result = os.path.join(job_dir, 'job_result.pickle') if not os.path.exists(job_result): - result, exception, traceback = None, ('ParallelRuntimeError', + result, exception, traceback = None, ('ParallelRuntimeError', 'The worker process died unexpectedly.'), '' - else: + else: result, exception, traceback = cPickle.load(open(job_result, 'rb')) log = open(output.name, 'rb').read() return result, exception, traceback, log - + def run_job(job_data): + global sa, job_id + if SingleApplication is not None: + sa = SingleApplication('calibre GUI') job_data = binascii.unhexlify(job_data) base = os.path.dirname(job_data) job_result = os.path.join(base, 'job_result.pickle') - func, args, kwdargs = cPickle.load(open(job_data, 'rb')) + job_id, func, args, kwdargs = cPickle.load(open(job_data, 'rb')) func = PARALLEL_FUNCS[func] exception, tb = None, None try: @@ -154,3 +170,5 @@ def main(): if __name__ == '__main__': sys.exit(main()) + +from calibre.utils.single_qt_application import SingleApplication diff --git a/src/calibre/utils/single_qt_application.py b/src/calibre/utils/single_qt_application.py index 8e294b1550..0c1eb0936a 100644 --- a/src/calibre/utils/single_qt_application.py +++ b/src/calibre/utils/single_qt_application.py @@ -9,7 +9,7 @@ applications using a local socket. ''' import atexit -from PyQt4.QtCore import QByteArray, QDataStream, QIODevice, SIGNAL, QObject, Qt +from PyQt4.QtCore import QByteArray, QDataStream, QIODevice, SIGNAL, QObject, Qt, QString from PyQt4.QtNetwork import QLocalSocket, QLocalServer timeout_read = 5000 @@ -20,7 +20,7 @@ def write_message(socket, message, timeout = 5000): out = QDataStream(block, QIODevice.WriteOnly) out.writeInt32(0) - out.writeString(message) + out.writeString(QString(message)) out.device().seek(0) out.writeInt32(len(message))