mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
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:
parent
7491a8d20e
commit
49d70e0f9e
@ -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 = {
|
||||||
|
@ -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>
|
||||||
|
@ -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()
|
||||||
|
@ -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):
|
||||||
|
@ -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):
|
||||||
|
@ -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():
|
||||||
|
Loading…
x
Reference in New Issue
Block a user