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

This commit is contained in:
Kovid Goyal 2010-05-18 15:04:20 -06:00
parent 7491a8d20e
commit 49d70e0f9e
6 changed files with 76 additions and 88 deletions

View File

@ -8,7 +8,7 @@ from PyQt4.QtCore import QVariant, QFileInfo, QObject, SIGNAL, QBuffer, Qt, QSiz
QByteArray, QTranslator, QCoreApplication, QThread, \ QByteArray, QTranslator, QCoreApplication, QThread, \
QEvent, QTimer, pyqtSignal, QDate QEvent, QTimer, pyqtSignal, QDate
from PyQt4.QtGui import QFileDialog, QMessageBox, QPixmap, QFileIconProvider, \ from PyQt4.QtGui import QFileDialog, QMessageBox, QPixmap, QFileIconProvider, \
QIcon, QTableView, QApplication, QDialog, QPushButton QIcon, QApplication, QDialog, QPushButton
ORG_NAME = 'KovidsBrain' ORG_NAME = 'KovidsBrain'
APP_UID = 'libprs500' APP_UID = 'libprs500'
@ -294,34 +294,6 @@ class GetMetadata(QObject):
mi = MetaInformation('', [_('Unknown')]) mi = MetaInformation('', [_('Unknown')])
self.emit(SIGNAL('metadata(PyQt_PyObject, PyQt_PyObject)'), id, mi) 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): class FileIconProvider(QFileIconProvider):
ICONS = { ICONS = {

View File

@ -14,12 +14,12 @@
<string>Active Jobs</string> <string>Active Jobs</string>
</property> </property>
<property name="windowIcon"> <property name="windowIcon">
<iconset resource="../../../work/calibre/resources/images.qrc"> <iconset resource="../../../../resources/images.qrc">
<normaloff>:/images/jobs.svg</normaloff>:/images/jobs.svg</iconset> <normaloff>:/images/jobs.svg</normaloff>:/images/jobs.svg</iconset>
</property> </property>
<layout class="QVBoxLayout"> <layout class="QVBoxLayout">
<item> <item>
<widget class="JobsView" name="jobs_view"> <widget class="QTableView" name="jobs_view">
<property name="contextMenuPolicy"> <property name="contextMenuPolicy">
<enum>Qt::NoContextMenu</enum> <enum>Qt::NoContextMenu</enum>
</property> </property>
@ -66,15 +66,8 @@
</item> </item>
</layout> </layout>
</widget> </widget>
<customwidgets>
<customwidget>
<class>JobsView</class>
<extends>QTableView</extends>
<header>widgets.h</header>
</customwidget>
</customwidgets>
<resources> <resources>
<include location="../../../work/calibre/resources/images.qrc"/> <include location="../../../../resources/images.qrc"/>
</resources> </resources>
<connections/> <connections/>
</ui> </ui>

View File

@ -15,10 +15,11 @@ from PyQt4.Qt import QAbstractTableModel, QVariant, QModelIndex, Qt, \
from calibre.utils.ipc.server import Server from calibre.utils.ipc.server import Server
from calibre.utils.ipc.job import ParallelJob 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.device import DeviceJob
from calibre.gui2.dialogs.jobs_ui import Ui_JobsDialog from calibre.gui2.dialogs.jobs_ui import Ui_JobsDialog
from calibre import __appname__ from calibre import __appname__
from calibre.gui2.dialogs.job_view_ui import Ui_Dialog
class JobManager(QAbstractTableModel): class JobManager(QAbstractTableModel):
@ -243,7 +244,32 @@ class ProgressBarDelegate(QAbstractItemDelegate):
opts.text = QString(_('Unavailable') if percent == 0 else '%d%%'%percent) opts.text = QString(_('Unavailable') if percent == 0 else '%d%%'%percent)
QApplication.style().drawControl(QStyle.CE_ProgressBar, opts, painter) 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): class JobsDialog(QDialog, Ui_JobsDialog):
def __init__(self, window, model): def __init__(self, window, model):
QDialog.__init__(self, window) QDialog.__init__(self, window)
Ui_JobsDialog.__init__(self) Ui_JobsDialog.__init__(self)
@ -252,8 +278,6 @@ class JobsDialog(QDialog, Ui_JobsDialog):
self.model = model self.model = model
self.setWindowModality(Qt.NonModal) self.setWindowModality(Qt.NonModal)
self.setWindowTitle(__appname__ + _(' - Jobs')) self.setWindowTitle(__appname__ + _(' - Jobs'))
self.connect(self.jobs_view.model(), SIGNAL('modelReset()'),
self.jobs_view.resizeColumnsToContents)
self.connect(self.kill_button, SIGNAL('clicked()'), self.connect(self.kill_button, SIGNAL('clicked()'),
self.kill_job) self.kill_job)
self.connect(self.details_button, SIGNAL('clicked()'), self.connect(self.details_button, SIGNAL('clicked()'),
@ -264,7 +288,21 @@ class JobsDialog(QDialog, Ui_JobsDialog):
self.jobs_view.model().kill_job) self.jobs_view.model().kill_job)
self.pb_delegate = ProgressBarDelegate(self) self.pb_delegate = ProgressBarDelegate(self)
self.jobs_view.setItemDelegateForColumn(2, self.pb_delegate) 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): def kill_job(self):
for index in self.jobs_view.selectedIndexes(): for index in self.jobs_view.selectedIndexes():
@ -281,5 +319,9 @@ class JobsDialog(QDialog, Ui_JobsDialog):
self.model.kill_all_jobs() self.model.kill_all_jobs()
def closeEvent(self, e): 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() e.accept()

View File

@ -33,16 +33,12 @@ class JobsButton(QFrame):
def initialize(self, jobs_dialog): def initialize(self, jobs_dialog):
self.jobs_dialog = jobs_dialog self.jobs_dialog = jobs_dialog
self.jobs_dialog.jobs_view.restore_column_widths()
def mouseReleaseEvent(self, event): def mouseReleaseEvent(self, event):
if self.jobs_dialog.isVisible(): if self.jobs_dialog.isVisible():
self.jobs_dialog.jobs_view.write_settings()
self.jobs_dialog.hide() self.jobs_dialog.hide()
else: else:
self.jobs_dialog.jobs_view.read_settings()
self.jobs_dialog.show() self.jobs_dialog.show()
self.jobs_dialog.jobs_view.restore_column_widths()
@property @property
def is_running(self): def is_running(self):

View File

@ -7,15 +7,15 @@ import re, os, traceback
from PyQt4.Qt import QListView, QIcon, QFont, QLabel, QListWidget, \ from PyQt4.Qt import QListView, QIcon, QFont, QLabel, QListWidget, \
QListWidgetItem, QTextCharFormat, QApplication, \ QListWidgetItem, QTextCharFormat, QApplication, \
QSyntaxHighlighter, QCursor, QColor, QWidget, \ QSyntaxHighlighter, QCursor, QColor, QWidget, \
QPixmap, QPalette, QTimer, QDialog, QSplitterHandle, \ QPixmap, QPalette, QSplitterHandle, \
QAbstractListModel, QVariant, Qt, SIGNAL, pyqtSignal, \ QAbstractListModel, QVariant, Qt, SIGNAL, pyqtSignal, \
QRegExp, QSettings, QSize, QModelIndex, QSplitter, \ QRegExp, QSettings, QSize, QModelIndex, QSplitter, \
QAbstractButton, QPainter, QLineEdit, QComboBox, \ QAbstractButton, QPainter, QLineEdit, QComboBox, \
QMenu, QStringListModel, QCompleter, QStringList 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 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.gui2.filename_pattern_ui import Ui_Form
from calibre import fit_image from calibre import fit_image
from calibre.utils.fonts import fontconfig from calibre.utils.fonts import fontconfig
@ -399,41 +399,6 @@ class EjectButton(QAbstractButton):
painter.drawPixmap(0, 0, image) 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): class FontFamilyModel(QAbstractListModel):

View File

@ -6,7 +6,7 @@ __docformat__ = 'restructuredtext en'
''' '''
Manage application-wide preferences. 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 copy import deepcopy
from functools import partial from functools import partial
from optparse import OptionParser as _OptionParser from optparse import OptionParser as _OptionParser
@ -636,11 +636,31 @@ class JSONConfig(XMLConfig):
EXTENSION = '.json' 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): 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): 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(): def _prefs():