From 49d70e0f9ee0c0d4a9e7100cf4bc05e1849aaa2a Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 18 May 2010 15:04:20 -0600 Subject: [PATCH] JSONConfig: Fix encoding of top level str objects. Add support for encoding bytearray objects. Remove pointless TableView class and have jobs view remember its column layout --- src/calibre/gui2/__init__.py | 30 +------------------ src/calibre/gui2/dialogs/jobs.ui | 13 ++------- src/calibre/gui2/jobs.py | 50 +++++++++++++++++++++++++++++--- src/calibre/gui2/sidebar.py | 4 --- src/calibre/gui2/widgets.py | 41 ++------------------------ src/calibre/utils/config.py | 26 +++++++++++++++-- 6 files changed, 76 insertions(+), 88 deletions(-) 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 -
widgets.h
-
-
- + 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():