diff --git a/src/calibre/gui2/__init__.py b/src/calibre/gui2/__init__.py
index 9cb68ea01a..0cf565c928 100644
--- a/src/calibre/gui2/__init__.py
+++ b/src/calibre/gui2/__init__.py
@@ -8,7 +8,7 @@ from PyQt4.QtCore import QVariant, QFileInfo, QObject, SIGNAL, QBuffer, Qt, QSiz
QByteArray, QTranslator, QCoreApplication, QThread, \
QEvent, QTimer, pyqtSignal, QDate
from PyQt4.QtGui import QFileDialog, QMessageBox, QPixmap, QFileIconProvider, \
- QIcon, QTableView, QApplication, QDialog, QPushButton
+ QIcon, QApplication, QDialog, QPushButton
ORG_NAME = 'KovidsBrain'
APP_UID = 'libprs500'
@@ -294,34 +294,6 @@ class GetMetadata(QObject):
mi = MetaInformation('', [_('Unknown')])
self.emit(SIGNAL('metadata(PyQt_PyObject, PyQt_PyObject)'), id, mi)
-class TableView(QTableView):
-
- def __init__(self, parent):
- QTableView.__init__(self, parent)
- self.read_settings()
-
- def read_settings(self):
- self.cw = dynamic[self.__class__.__name__+'column width map']
-
- def write_settings(self):
- m = dynamic[self.__class__.__name__+'column width map']
- if m is None:
- m = {}
- cmap = getattr(self.model(), 'column_map', None)
- if cmap is not None:
- for i,c in enumerate(cmap):
- m[c] = self.columnWidth(i)
- dynamic[self.__class__.__name__+'column width map'] = m
- self.cw = m
-
- def restore_column_widths(self):
- if self.cw and len(self.cw):
- for i,c in enumerate(self.model().column_map):
- if c in self.cw:
- self.setColumnWidth(i, self.cw[c])
- return True
- return False
-
class FileIconProvider(QFileIconProvider):
ICONS = {
diff --git a/src/calibre/gui2/dialogs/jobs.ui b/src/calibre/gui2/dialogs/jobs.ui
index e2e345ca28..1fb23269e6 100644
--- a/src/calibre/gui2/dialogs/jobs.ui
+++ b/src/calibre/gui2/dialogs/jobs.ui
@@ -14,12 +14,12 @@
Active Jobs
-
+
:/images/jobs.svg:/images/jobs.svg
-
-
+
Qt::NoContextMenu
@@ -66,15 +66,8 @@
-
-
- JobsView
- QTableView
-
-
-
-
+
diff --git a/src/calibre/gui2/jobs.py b/src/calibre/gui2/jobs.py
index a801e0db28..437e65632c 100644
--- a/src/calibre/gui2/jobs.py
+++ b/src/calibre/gui2/jobs.py
@@ -15,10 +15,11 @@ from PyQt4.Qt import QAbstractTableModel, QVariant, QModelIndex, Qt, \
from calibre.utils.ipc.server import Server
from calibre.utils.ipc.job import ParallelJob
-from calibre.gui2 import Dispatcher, error_dialog, NONE, config
+from calibre.gui2 import Dispatcher, error_dialog, NONE, config, gprefs
from calibre.gui2.device import DeviceJob
from calibre.gui2.dialogs.jobs_ui import Ui_JobsDialog
from calibre import __appname__
+from calibre.gui2.dialogs.job_view_ui import Ui_Dialog
class JobManager(QAbstractTableModel):
@@ -243,7 +244,32 @@ class ProgressBarDelegate(QAbstractItemDelegate):
opts.text = QString(_('Unavailable') if percent == 0 else '%d%%'%percent)
QApplication.style().drawControl(QStyle.CE_ProgressBar, opts, painter)
+class DetailView(QDialog, Ui_Dialog):
+
+ def __init__(self, parent, job):
+ QDialog.__init__(self, parent)
+ self.setupUi(self)
+ self.setWindowTitle(job.description)
+ self.job = job
+ self.next_pos = 0
+ self.update()
+ self.timer = QTimer(self)
+ self.timer.timeout.connect(self.update)
+ self.timer.start(1000)
+
+
+ def update(self):
+ f = self.job.log_file
+ f.seek(self.next_pos)
+ more = f.read()
+ self.next_pos = f.tell()
+ if more:
+ self.log.appendPlainText(more.decode('utf-8', 'replace'))
+
+
+
class JobsDialog(QDialog, Ui_JobsDialog):
+
def __init__(self, window, model):
QDialog.__init__(self, window)
Ui_JobsDialog.__init__(self)
@@ -252,8 +278,6 @@ class JobsDialog(QDialog, Ui_JobsDialog):
self.model = model
self.setWindowModality(Qt.NonModal)
self.setWindowTitle(__appname__ + _(' - Jobs'))
- self.connect(self.jobs_view.model(), SIGNAL('modelReset()'),
- self.jobs_view.resizeColumnsToContents)
self.connect(self.kill_button, SIGNAL('clicked()'),
self.kill_job)
self.connect(self.details_button, SIGNAL('clicked()'),
@@ -264,7 +288,21 @@ class JobsDialog(QDialog, Ui_JobsDialog):
self.jobs_view.model().kill_job)
self.pb_delegate = ProgressBarDelegate(self)
self.jobs_view.setItemDelegateForColumn(2, self.pb_delegate)
+ self.jobs_view.doubleClicked.connect(self.show_job_details)
+ self.jobs_view.horizontalHeader().setMovable(True)
+ state = gprefs.get('jobs view column layout', None)
+ if state is not None:
+ try:
+ self.jobs_view.horizontalHeader().restoreState(bytes(state))
+ except:
+ pass
+ def show_job_details(self, index):
+ row = index.row()
+ job = self.jobs_view.model().row_to_job(row)
+ d = DetailView(self, job)
+ d.exec_()
+ d.timer.stop()
def kill_job(self):
for index in self.jobs_view.selectedIndexes():
@@ -281,5 +319,9 @@ class JobsDialog(QDialog, Ui_JobsDialog):
self.model.kill_all_jobs()
def closeEvent(self, e):
- self.jobs_view.write_settings()
+ try:
+ state = bytearray(self.jobs_view.horizontalHeader().saveState())
+ gprefs['jobs view column layout'] = state
+ except:
+ pass
e.accept()
diff --git a/src/calibre/gui2/sidebar.py b/src/calibre/gui2/sidebar.py
index d6b58f165d..bd305912a0 100644
--- a/src/calibre/gui2/sidebar.py
+++ b/src/calibre/gui2/sidebar.py
@@ -33,16 +33,12 @@ class JobsButton(QFrame):
def initialize(self, jobs_dialog):
self.jobs_dialog = jobs_dialog
- self.jobs_dialog.jobs_view.restore_column_widths()
def mouseReleaseEvent(self, event):
if self.jobs_dialog.isVisible():
- self.jobs_dialog.jobs_view.write_settings()
self.jobs_dialog.hide()
else:
- self.jobs_dialog.jobs_view.read_settings()
self.jobs_dialog.show()
- self.jobs_dialog.jobs_view.restore_column_widths()
@property
def is_running(self):
diff --git a/src/calibre/gui2/widgets.py b/src/calibre/gui2/widgets.py
index 4b61677b12..8083cd4ba0 100644
--- a/src/calibre/gui2/widgets.py
+++ b/src/calibre/gui2/widgets.py
@@ -7,15 +7,15 @@ import re, os, traceback
from PyQt4.Qt import QListView, QIcon, QFont, QLabel, QListWidget, \
QListWidgetItem, QTextCharFormat, QApplication, \
QSyntaxHighlighter, QCursor, QColor, QWidget, \
- QPixmap, QPalette, QTimer, QDialog, QSplitterHandle, \
+ QPixmap, QPalette, QSplitterHandle, \
QAbstractListModel, QVariant, Qt, SIGNAL, pyqtSignal, \
QRegExp, QSettings, QSize, QModelIndex, QSplitter, \
QAbstractButton, QPainter, QLineEdit, QComboBox, \
QMenu, QStringListModel, QCompleter, QStringList
-from calibre.gui2 import human_readable, NONE, TableView, \
+from calibre.gui2 import human_readable, NONE, \
error_dialog, pixmap_to_data, dynamic
-from calibre.gui2.dialogs.job_view_ui import Ui_Dialog
+
from calibre.gui2.filename_pattern_ui import Ui_Form
from calibre import fit_image
from calibre.utils.fonts import fontconfig
@@ -399,41 +399,6 @@ class EjectButton(QAbstractButton):
painter.drawPixmap(0, 0, image)
-class DetailView(QDialog, Ui_Dialog):
-
- def __init__(self, parent, job):
- QDialog.__init__(self, parent)
- self.setupUi(self)
- self.setWindowTitle(job.description)
- self.job = job
- self.next_pos = 0
- self.update()
- self.timer = QTimer(self)
- self.connect(self.timer, SIGNAL('timeout()'), self.update)
- self.timer.start(1000)
-
-
- def update(self):
- f = self.job.log_file
- f.seek(self.next_pos)
- more = f.read()
- self.next_pos = f.tell()
- if more:
- self.log.appendPlainText(more.decode('utf-8', 'replace'))
-
-
-class JobsView(TableView):
-
- def __init__(self, parent):
- TableView.__init__(self, parent)
- self.connect(self, SIGNAL('doubleClicked(QModelIndex)'), self.show_details)
-
- def show_details(self, index):
- row = index.row()
- job = self.model().row_to_job(row)
- d = DetailView(self, job)
- d.exec_()
- d.timer.stop()
class FontFamilyModel(QAbstractListModel):
diff --git a/src/calibre/utils/config.py b/src/calibre/utils/config.py
index cb17085071..559721c193 100644
--- a/src/calibre/utils/config.py
+++ b/src/calibre/utils/config.py
@@ -6,7 +6,7 @@ __docformat__ = 'restructuredtext en'
'''
Manage application-wide preferences.
'''
-import os, re, cPickle, textwrap, traceback, plistlib, json
+import os, re, cPickle, textwrap, traceback, plistlib, json, base64
from copy import deepcopy
from functools import partial
from optparse import OptionParser as _OptionParser
@@ -636,11 +636,31 @@ class JSONConfig(XMLConfig):
EXTENSION = '.json'
+ def to_json(self, obj):
+ if isinstance(obj, bytearray):
+ return {'__class__': 'bytearray',
+ '__value__': base64.standard_b64encode(bytes(obj))}
+ raise TypeError(repr(obj) + ' is not JSON serializable')
+
+ def from_json(self, obj):
+ if '__class__' in obj:
+ if obj['__class__'] == 'bytearray':
+ return bytearray(base64.standard_b64decode(obj['__value__']))
+ return obj
+
def raw_to_object(self, raw):
- return json.loads(raw.decode('utf-8'))
+ return json.loads(raw.decode('utf-8'), object_hook=self.from_json)
def to_raw(self):
- return json.dumps(self, indent=2)
+ return json.dumps(self, indent=2, default=self.to_json)
+
+ def __getitem__(self, key):
+ return dict.__getitem__(self, key)
+
+ def __setitem__(self, key, val):
+ dict.__setitem__(self, key, val)
+ self.commit()
+
def _prefs():