diff --git a/osx_installer.py b/osx_installer.py index ce9ee98f5c..79e363618f 100644 --- a/osx_installer.py +++ b/osx_installer.py @@ -316,10 +316,6 @@ def main(): }, setup_requires = ['py2app'], ) - subprocess.check_call('scp dist/*.dmg giskard:work/calibre/dist', shell=True) -# if '--shutdown' in sys.argv: -# print 'Shutting down' -# subprocess.call(('/usr/bin/sudo', '/sbin/shutdown', '-h', '+0')) return 0 if __name__ == '__main__': diff --git a/src/calibre/__init__.py b/src/calibre/__init__.py index 32a883b93f..7c1656a614 100644 --- a/src/calibre/__init__.py +++ b/src/calibre/__init__.py @@ -15,7 +15,7 @@ from optparse import OptionParser as _OptionParser from optparse import IndentedHelpFormatter from logging import Formatter -from PyQt4.QtCore import QSettings, QVariant, QUrl, QByteArray +from PyQt4.QtCore import QSettings, QVariant, QUrl, QByteArray, QString from PyQt4.QtGui import QDesktopServices from calibre.translations.msgfmt import make @@ -46,7 +46,7 @@ def osx_version(): m = re.match(r'(\d+)\.(\d+)\.(\d+)', src) if m: return int(m.group(1)), int(m.group(2)), int(m.group(3)) - + # Default translation is NOOP import __builtin__ @@ -56,7 +56,7 @@ class CommandLineError(Exception): pass class ColoredFormatter(Formatter): - + def format(self, record): ln = record.__dict__['levelname'] col = '' @@ -72,7 +72,7 @@ class ColoredFormatter(Formatter): col = terminal_controller.CYAN record.__dict__['levelname'] = col + record.__dict__['levelname'] + terminal_controller.NORMAL return Formatter.format(self, record) - + def setup_cli_handlers(logger, level): logger.setLevel(level) @@ -91,31 +91,31 @@ def setup_cli_handlers(logger, level): logger.addHandler(handler) class CustomHelpFormatter(IndentedHelpFormatter): - + def format_usage(self, usage): return _("%sUsage%s: %s\n") % (terminal_controller.BLUE, terminal_controller.NORMAL, usage) - + def format_heading(self, heading): - return "%*s%s%s%s:\n" % (self.current_indent, terminal_controller.BLUE, + return "%*s%s%s%s:\n" % (self.current_indent, terminal_controller.BLUE, "", heading, terminal_controller.NORMAL) - + def format_option(self, option): result = [] opts = self.option_strings[option] opt_width = self.help_position - self.current_indent - 2 if len(opts) > opt_width: - opts = "%*s%s\n" % (self.current_indent, "", + opts = "%*s%s\n" % (self.current_indent, "", terminal_controller.GREEN+opts+terminal_controller.NORMAL) indent_first = self.help_position else: # start help on same line as opts - opts = "%*s%-*s " % (self.current_indent, "", opt_width + len(terminal_controller.GREEN + terminal_controller.NORMAL), + opts = "%*s%-*s " % (self.current_indent, "", opt_width + len(terminal_controller.GREEN + terminal_controller.NORMAL), terminal_controller.GREEN + opts + terminal_controller.NORMAL) indent_first = 0 result.append(opts) if option.help: help_text = self.expand_default(option).split('\n') help_lines = [] - + for line in help_text: help_lines.extend(textwrap.wrap(line, self.help_width)) result.append("%*s%s\n" % (indent_first, "", help_lines[0])) @@ -126,7 +126,7 @@ class CustomHelpFormatter(IndentedHelpFormatter): return "".join(result)+'\n' class OptionParser(_OptionParser): - + def __init__(self, usage='%prog [options] filename', version='%%prog (%s %s)'%(__appname__, __version__), @@ -136,16 +136,16 @@ class OptionParser(_OptionParser): **kwds): usage += '''\n\nWhenever you pass arguments to %prog that have spaces in them, '''\ '''enclose the arguments in quotation marks.''' - _OptionParser.__init__(self, usage=usage, version=version, epilog=epilog, - formatter=CustomHelpFormatter(), + _OptionParser.__init__(self, usage=usage, version=version, epilog=epilog, + formatter=CustomHelpFormatter(), conflict_handler=conflict_handler, **kwds) self.gui_mode = gui_mode - + def error(self, msg): if self.gui_mode: raise Exception(msg) _OptionParser.error(self, msg) - + def merge(self, parser): ''' Add options from parser to self. In case of conflicts, confilicting options from @@ -153,18 +153,18 @@ class OptionParser(_OptionParser): ''' opts = list(parser.option_list) groups = list(parser.option_groups) - + def merge_options(options, container): for opt in copy.deepcopy(options): if not self.has_option(opt.get_opt_string()): container.add_option(opt) - + merge_options(opts, self) - + for group in groups: g = self.add_option_group(group.title) merge_options(group.option_list, g) - + def subsume(self, group_name, msg=''): ''' Move all existing options into a subgroup named @@ -176,7 +176,7 @@ class OptionParser(_OptionParser): for opt in opts: self.remove_option(opt.get_opt_string()) subgroup.add_option(opt) - + def options_iter(self): for opt in self.option_list: if str(opt).strip(): @@ -185,12 +185,12 @@ class OptionParser(_OptionParser): for opt in gr.option_list: if str(opt).strip(): yield opt - + def option_by_dest(self, dest): for opt in self.options_iter(): if opt.dest == dest: return opt - + def merge_options(self, lower, upper): ''' Merge options in lower and upper option lists into upper. @@ -204,7 +204,7 @@ class OptionParser(_OptionParser): if lower.__dict__[dest] != opt.default and \ upper.__dict__[dest] == opt.default: upper.__dict__[dest] = lower.__dict__[dest] - + def load_library(name, cdll): if iswindows: @@ -250,12 +250,12 @@ def browser(honor_time=False): def fit_image(width, height, pwidth, pheight): ''' - Fit image in box of width pwidth and height pheight. + Fit image in box of width pwidth and height pheight. @param width: Width of image @param height: Height of image - @param pwidth: Width of box + @param pwidth: Width of box @param pheight: Height of box - @return: scaled, new_width, new_height. scaled is True iff new_widdth and/or new_height is different from width or height. + @return: scaled, new_width, new_height. scaled is True iff new_widdth and/or new_height is different from width or height. ''' scaled = height > pheight or width > pwidth if height > pheight: @@ -267,7 +267,7 @@ def fit_image(width, height, pwidth, pheight): if height > pheight: corrf = pheight/float(height) width, height = floor(corrf*width), pheight - + return scaled, int(width), int(height) def get_lang(): @@ -290,7 +290,7 @@ def set_translator(): from calibre.translations.compiled import translations except: return - lang = get_lang() + lang = get_lang() if lang: buf = None if os.access(lang+'.po', os.R_OK): @@ -302,12 +302,12 @@ def set_translator(): if buf is not None: t = GNUTranslations(buf) t.install(unicode=True) - + set_translator() def sanitize_file_name(name): ''' - Remove characters that are illegal in filenames from name. + Remove characters that are illegal in filenames from name. Also remove path separators. All illegal characters are replaced by underscores. ''' @@ -340,7 +340,7 @@ def detect_ncpus(): return 1 except ValueError: # On some systems the sysctl call fails return 1 - + #for Windows if os.environ.has_key("NUMBER_OF_PROCESSORS"): ncpus = int(os.environ["NUMBER_OF_PROCESSORS"]); @@ -354,7 +354,7 @@ def launch(path_or_url): if os.path.exists(path_or_url): path_or_url = 'file:'+path_or_url QDesktopServices.openUrl(QUrl(path_or_url)) - + def relpath(target, base=os.curdir): """ Return a relative path to the target from either the current dir or an optional base dir. @@ -396,13 +396,13 @@ def _clean_lock_file(file): os.remove(file.name) except: pass - + def singleinstance(name): ''' - Return True if no other instance of the application identified by name is running, + Return True if no other instance of the application identified by name is running, False otherwise. @param name: The name to lock. - @type name: string + @type name: string ''' if iswindows: from win32event import CreateMutex @@ -424,19 +424,15 @@ def singleinstance(name): return True except IOError: return False - + return False class Settings(QSettings): - - def __init__(self): + + def __init__(self, name='calibre2'): QSettings.__init__(self, QSettings.IniFormat, QSettings.UserScope, - 'kovidgoyal.net', 'calibre') - - def migrate(self, settings): - for key in settings.allKeys(): - self.setValue(key, settings.value(key, QVariant())) - + 'kovidgoyal.net', name) + def get(self, key, default=None): key = str(key) if not self.contains(key): @@ -445,17 +441,30 @@ class Settings(QSettings): if not val: return None return cPickle.loads(val) - + def set(self, key, val): val = cPickle.dumps(val, -1) self.setValue(str(key), QVariant(QByteArray(val))) - + _settings = Settings() -if not _settings.get('migrated from QSettings'): - _settings.migrate(QSettings('KovidsBrain', 'libprs500')) - _settings.set('migrated from QSettings', True) - _settings.sync() - +if not _settings.get('rationalized'): + __settings = Settings(name='calibre') + dbpath = os.path.join(os.path.expanduser('~'), 'library1.db').decode(sys.getfilesystemencoding()) + dbpath = unicode(__settings.value('database path', + QVariant(QString.fromUtf8(dbpath.encode('utf-8')))).toString()) + cmdline = __settings.value('LRF conversion defaults', QVariant(QByteArray(''))).toByteArray().data() + + + _settings.set('database path', dbpath) + if cmdline: + cmdline = cPickle.loads(cmdline) + _settings.set('LRF conversion defaults', cmdline) + _settings.set('rationalized', True) + try: + os.unlink(unicode(__settings.fileName())) + except: + pass + _spat = re.compile(r'^the\s+|^a\s+|^an\s+', re.IGNORECASE) def english_sort(x, y): ''' @@ -464,40 +473,40 @@ def english_sort(x, y): return cmp(_spat.sub('', x), _spat.sub('', y)) class LoggingInterface: - + def __init__(self, logger): self.__logger = logger - + def ___log(self, func, msg, args, kwargs): args = [msg] + list(args) for i in range(len(args)): if isinstance(args[i], unicode): args[i] = args[i].encode(preferred_encoding, 'replace') - + func(*args, **kwargs) - + def log_debug(self, msg, *args, **kwargs): self.___log(self.__logger.debug, msg, args, kwargs) - + def log_info(self, msg, *args, **kwargs): self.___log(self.__logger.info, msg, args, kwargs) - + def log_warning(self, msg, *args, **kwargs): self.___log(self.__logger.warning, msg, args, kwargs) - + def log_warn(self, msg, *args, **kwargs): self.___log(self.__logger.warning, msg, args, kwargs) - + def log_error(self, msg, *args, **kwargs): self.___log(self.__logger.error, msg, args, kwargs) - + def log_critical(self, msg, *args, **kwargs): self.___log(self.__logger.critical, msg, args, kwargs) - + def log_exception(self, msg, *args): self.___log(self.__logger.exception, msg, args, {}) - - + + def strftime(fmt, t=time.localtime()): ''' A version of strtime that returns unicode strings. @@ -512,10 +521,10 @@ if islinux and not getattr(sys, 'frozen', False): import pkg_resources plugins = pkg_resources.resource_filename(__appname__, 'plugins') sys.path.insert(1, plugins) - + if iswindows and hasattr(sys, 'frozen'): sys.path.insert(1, os.path.dirname(sys.executable)) - + try: import pictureflow pictureflowerror = '' @@ -523,12 +532,12 @@ except Exception, err: pictureflow = None pictureflowerror = str(err) - + def entity_to_unicode(match, exceptions=[], encoding='cp1252'): ''' @param match: A match object such that '&'+match.group(1)';' is the entity. - @param exceptions: A list of entities to not convert (Each entry is the name of the entity, for e.g. 'apos' or '#1234' - @param encoding: The encoding to use to decode numeric entities between 128 and 256. + @param exceptions: A list of entities to not convert (Each entry is the name of the entity, for e.g. 'apos' or '#1234' + @param encoding: The encoding to use to decode numeric entities between 128 and 256. If None, the Unicode UCS encoding is used. A common encoding is cp1252. ''' ent = match.group(1) @@ -556,7 +565,7 @@ def entity_to_unicode(match, exceptions=[], encoding='cp1252'): return unichr(name2codepoint[ent]) except KeyError: return '&'+ent+';' - + if isosx: fdir = os.path.expanduser('~/.fonts') if not os.path.exists(fdir): diff --git a/src/calibre/ebooks/chardet/__init__.py b/src/calibre/ebooks/chardet/__init__.py index c0a9b45d0f..36d3b909de 100644 --- a/src/calibre/ebooks/chardet/__init__.py +++ b/src/calibre/ebooks/chardet/__init__.py @@ -46,7 +46,10 @@ def xml_to_unicode(raw, verbose=False): if match is not None: encoding = match.group(1) if encoding is None: - chardet = detect(raw) + try: + chardet = detect(raw) + except: + chardet = {'encoding':'utf-8', 'confidence':0} encoding = chardet['encoding'] if chardet['confidence'] < 1 and verbose: print 'WARNING: Encoding detection confidence %d%%'%(chardet['confidence']*100) diff --git a/src/calibre/ebooks/lrf/pylrs/pylrs.py b/src/calibre/ebooks/lrf/pylrs/pylrs.py index 55069b9934..a8cbb6fb1c 100644 --- a/src/calibre/ebooks/lrf/pylrs/pylrs.py +++ b/src/calibre/ebooks/lrf/pylrs/pylrs.py @@ -553,54 +553,40 @@ class Book(Delegator): if isinstance(obj, Main): main = obj break - pages = [obj for obj in main.contents if isinstance(obj, Page)] - - text_blocks = [] - for p in pages: - for obj in p.contents: - if isinstance(obj, TextBlock): - text_blocks.append(obj) - elif isinstance(obj, Canvas): - for o in obj.contents: - if isinstance(o.content, TextBlock): - text_blocks.append(o.content) - - text_styles = set([t.textStyle for t in text_blocks]) - important_text_styles = [] - for ts in text_styles: - temp = [len(tb.contents) for tb in text_blocks if tb.textStyle == ts] - avg_content_length = 0 - if len(temp) > 0: - avg_content_length = sum(temp)/len(temp) - if avg_content_length > 4: - important_text_styles.append(ts) - + fonts = {} - if not important_text_styles: - important_text_styles = text_styles - - for ts in important_text_styles: - fs = int(ts.attrs['fontsize']) - if fonts.has_key(fs): - fonts[fs] += 1 - else: - fonts[fs] = 1 - + for text in main.get_all(lambda x: isinstance(x, Text)): + fs = base_font_size + ancestor = text.parent + while ancestor: + try: + fs = int(ancestor.attrs['fontsize']) + break + except (AttributeError, KeyError): + pass + try: + fs = int(ancestor.textSettings['fontsize']) + break + except (AttributeError, KeyError): + pass + try: + fs = int(ancestor.textStyle.attrs['fontsize']) + break + except (AttributeError, KeyError): + pass + ancestor = ancestor.parent + length = len(text.text) + fonts[fs] = fonts.get(fs, 0) + length if not fonts: print 'WARNING: LRF seems to have no textual content. Cannot rationalize font sizes.' return - old_base_font_size = float(max(zip(fonts.keys(), fonts.values()), key=operator.itemgetter(1))[0]) - - factor = base_font_size/old_base_font_size - + old_base_font_size = float(max(fonts.items(), key=operator.itemgetter(1))[0]) + factor = base_font_size / old_base_font_size def rescale(old): return str(int(int(old) * factor)) - - for ts in text_styles: - ts.attrs['fontsize'] = rescale(ts.attrs['fontsize']) - ts.attrs['baselineskip'] = rescale(ts.attrs['baselineskip']) - + + text_blocks = list(main.get_all(lambda x: isinstance(x, TextBlock))) for tb in text_blocks: if tb.textSettings.has_key('fontsize'): tb.textSettings['fontsize'] = rescale(tb.textSettings['fontsize']) @@ -609,7 +595,12 @@ class Book(Delegator): span.attrs['fontsize'] = rescale(span.attrs['fontsize']) if span.attrs.has_key('baselineskip'): span.attrs['baselineskip'] = rescale(span.attrs['baselineskip']) - + + text_styles = set(tb.textStyle for tb in text_blocks) + for ts in text_styles: + ts.attrs['fontsize'] = rescale(ts.attrs['fontsize']) + ts.attrs['baselineskip'] = rescale(ts.attrs['baselineskip']) + def renderLrs(self, lrsFile, encoding="UTF-8"): if isinstance(lrsFile, basestring): @@ -1603,6 +1594,8 @@ class Paragraph(LrsContainer): LrsContainer.__init__(self, [Text, CR, DropCaps, CharButton, LrsSimpleChar1, basestring]) if text is not None: + if isinstance(text, basestring): + text = Text(text) self.append(text) def CR(self): @@ -1923,6 +1916,8 @@ class Span(LrsSimpleChar1, LrsContainer): def __init__(self, text=None, **attrs): LrsContainer.__init__(self, [LrsSimpleChar1, Text, basestring]) if text is not None: + if isinstance(text, basestring): + text = Text(text) self.append(text) for attrname in attrs.keys(): diff --git a/src/calibre/gui2/__init__.py b/src/calibre/gui2/__init__.py index 78015dcd91..91ae9f0d57 100644 --- a/src/calibre/gui2/__init__.py +++ b/src/calibre/gui2/__init__.py @@ -83,17 +83,12 @@ class TableView(QTableView): def read_settings(self): - self.cw = str(Settings().value(self.__class__.__name__ + ' column widths', QVariant('')).toString()) - try: - self.cw = tuple(int(i) for i in self.cw.split(',')) - except ValueError: - self.cw = None + self.cw = Settings().get(self.__class__.__name__ + ' column widths') def write_settings(self): settings = Settings() - settings.setValue(self.__class__.__name__ + ' column widths', - QVariant(','.join(str(self.columnWidth(i)) - for i in range(self.model().columnCount(None))))) + settings.set(self.__class__.__name__ + ' column widths', + tuple([int(self.columnWidth(i)) for i in range(self.model().columnCount(None))])) def restore_column_widths(self): if self.cw and len(self.cw): @@ -107,14 +102,10 @@ class TableView(QTableView): is hidden, if True it is shown. ''' if cols: - Settings().setValue(self.__class__.__name__ + ' visible columns', - QVariant(repr(cols))) + Settings().set(self.__class__.__name__ + ' visible columns', cols) else: - cols = qstring_to_unicode(Settings().value(self.__class__.__name__ + ' visible columns', - QVariant('')).toString()) - if cols: - cols = eval(cols) - else: + cols = Settings().get(self.__class__.__name__ + ' visible columns') + if not cols: cols = [True for i in range(self.model().columnCount(self))] for i in range(len(cols)): @@ -217,7 +208,7 @@ _sidebar_directories = [] def set_sidebar_directories(dirs): global _sidebar_directories if dirs is None: - dirs = Settings().value('frequently used directories', QVariant([])).toStringList() + dirs = Settings().get('frequently used directories', []) _sidebar_directories = [QUrl.fromLocalFile(i) for i in dirs] class FileDialog(QObject): @@ -251,7 +242,7 @@ class FileDialog(QObject): self.fd.setModal(modal) self.fd.setFilter(ftext) self.fd.setWindowTitle(title) - state = settings.value(name, QVariant()).toByteArray() + state = settings.get(self.dialog_name, QByteArray()) if not self.fd.restoreState(state): self.fd.setDirectory(os.path.expanduser('~')) osu = [i for i in self.fd.sidebarUrls()] @@ -259,7 +250,7 @@ class FileDialog(QObject): QObject.connect(self.fd, SIGNAL('accepted()'), self.save_dir) self.accepted = self.fd.exec_() == QFileDialog.Accepted else: - dir = settings.value(self.dialog_name, QVariant(os.path.expanduser('~'))).toString() + dir = settings.get(self.dialog_name, os.path.expanduser('~')) self.selected_files = [] if mode == QFileDialog.AnyFile: f = qstring_to_unicode( @@ -284,7 +275,7 @@ class FileDialog(QObject): self.selected_files.append(f) if self.selected_files: self.selected_files = [qstring_to_unicode(q) for q in self.selected_files] - settings.setValue(self.dialog_name, QVariant(os.path.dirname(self.selected_files[0]))) + settings.set(self.dialog_name, os.path.dirname(self.selected_files[0])) self.accepted = bool(self.selected_files) @@ -299,7 +290,7 @@ class FileDialog(QObject): def save_dir(self): if self.fd: settings = Settings() - settings.setValue(self.dialog_name, QVariant(self.fd.saveState())) + settings.set(self.dialog_name, self.fd.saveState()) def choose_dir(window, name, title): diff --git a/src/calibre/gui2/dialogs/config.py b/src/calibre/gui2/dialogs/config.py index 6248144683..0af4f14d35 100644 --- a/src/calibre/gui2/dialogs/config.py +++ b/src/calibre/gui2/dialogs/config.py @@ -3,7 +3,7 @@ __copyright__ = '2008, Kovid Goyal ' import os from PyQt4.QtGui import QDialog, QMessageBox, QListWidgetItem, QIcon -from PyQt4.QtCore import QVariant, SIGNAL, QStringList, QTimer, Qt, QSize +from PyQt4.QtCore import SIGNAL, QTimer, Qt, QSize from calibre import islinux, Settings from calibre.gui2.dialogs.config_ui import Ui_Dialog @@ -13,7 +13,7 @@ from calibre.gui2.widgets import FilenamePattern class ConfigDialog(QDialog, Ui_Dialog): - + def __init__(self, window, db, columns): QDialog.__init__(self, window) Ui_Dialog.__init__(self) @@ -28,11 +28,10 @@ class ConfigDialog(QDialog, Ui_Dialog): self.location.setText(path) self.connect(self.browse_button, SIGNAL('clicked(bool)'), self.browse) self.connect(self.compact_button, SIGNAL('clicked(bool)'), self.compact) - - dirs = settings.value('frequently used directories', QVariant(QStringList())).toStringList() - rn = bool(settings.value('use roman numerals for series number', - QVariant(True)).toBool()) - self.timeout.setValue(settings.value('network timeout', QVariant(5)).toInt()[0]) + + dirs = settings.get('frequently used directories', []) + rn = settings.get('use roman numerals for series number', True) + self.timeout.setValue(settings.get('network timeout', 5)) self.roman_numerals.setChecked(rn) self.directory_list.addItems(dirs) self.connect(self.add_button, SIGNAL('clicked(bool)'), self.add_dir) @@ -43,7 +42,7 @@ class ConfigDialog(QDialog, Ui_Dialog): self.priority.addItem('Idle') if not islinux: self.dirs_box.setVisible(False) - + for hidden, hdr in self.current_cols: item = QListWidgetItem(hdr, self.columns) item.setFlags(Qt.ItemIsEnabled|Qt.ItemIsUserCheckable) @@ -51,68 +50,68 @@ class ConfigDialog(QDialog, Ui_Dialog): item.setCheckState(Qt.Unchecked) else: item.setCheckState(Qt.Checked) - + self.filename_pattern = FilenamePattern(self) self.metadata_box.layout().insertWidget(0, self.filename_pattern) - - icons = settings.value('toolbar icon size', QVariant(self.ICON_SIZES[0])).toSize() + + icons = settings.get('toolbar icon size', self.ICON_SIZES[0]) self.toolbar_button_size.setCurrentIndex(0 if icons == self.ICON_SIZES[0] else 1 if icons == self.ICON_SIZES[1] else 2) self.show_toolbar_text.setChecked(settings.get('show text in toolbar', True)) - - + + def compact(self, toggled): d = Vacuum(self, self.db) d.exec_() - + def browse(self): dir = choose_dir(self, 'database location dialog', 'Select database location') if dir: self.location.setText(dir) - + def add_dir(self): dir = choose_dir(self, 'Add freq dir dialog', 'select directory') if dir: self.directory_list.addItem(dir) - + def remove_dir(self): idx = self.directory_list.currentRow() if idx >= 0: self.directory_list.takeItem(idx) - + def accept(self): - settings = Settings() - settings.setValue('use roman numerals for series number', QVariant(self.roman_numerals.isChecked())) - settings.setValue('network timeout', QVariant(self.timeout.value())) + settings = Settings() + settings.set('use roman numerals for series number', bool(self.roman_numerals.isChecked())) + settings.set('network timeout', int(self.timeout.value())) path = qstring_to_unicode(self.location.text()) self.final_columns = [self.columns.item(i).checkState() == Qt.Checked for i in range(self.columns.count())] - settings.setValue('toolbar icon size', QVariant(self.ICON_SIZES[self.toolbar_button_size.currentIndex()])) + settings.set('toolbar icon size', self.ICON_SIZES[self.toolbar_button_size.currentIndex()]) settings.set('show text in toolbar', bool(self.show_toolbar_text.isChecked())) pattern = self.filename_pattern.commit() - settings.setValue('filename pattern', QVariant(pattern)) - + settings.set('filename pattern', pattern) + if not path or not os.path.exists(path) or not os.path.isdir(path): - d = error_dialog(self, _('Invalid database location'), + d = error_dialog(self, _('Invalid database location'), _('Invalid database location ')+path+_('
Must be a directory.')) d.exec_() elif not os.access(path, os.W_OK): - d = error_dialog(self, _('Invalid database location'), + d = error_dialog(self, _('Invalid database location'), _('Invalid database location.
Cannot write to ')+path) d.exec_() else: self.database_location = os.path.abspath(path) self.directories = [qstring_to_unicode(self.directory_list.item(i).text()) for i in range(self.directory_list.count())] - settings.setValue('frequently used directories', QVariant(self.directories)) + settings.set('frequently used directories', self.directories) QDialog.accept(self) class Vacuum(QMessageBox): - + def __init__(self, parent, db): self.db = db - QMessageBox.__init__(self, QMessageBox.Information, _('Compacting...'), _('Compacting database. This may take a while.'), + QMessageBox.__init__(self, QMessageBox.Information, _('Compacting...'), _('Compacting database. This may take a while.'), QMessageBox.NoButton, parent) QTimer.singleShot(200, self.vacuum) - + def vacuum(self): self.db.vacuum() self.accept() - + diff --git a/src/calibre/gui2/dialogs/fetch_metadata.py b/src/calibre/gui2/dialogs/fetch_metadata.py index 0967a91bb8..9a64c8dc42 100644 --- a/src/calibre/gui2/dialogs/fetch_metadata.py +++ b/src/calibre/gui2/dialogs/fetch_metadata.py @@ -76,7 +76,7 @@ class FetchMetadata(QDialog, Ui_FetchMetadata): self.timeout = timeout QObject.connect(self.fetch, SIGNAL('clicked()'), self.fetch_metadata) - self.key.setText(Settings().value('isbndb.com key', QVariant('')).toString()) + self.key.setText(Settings().get('isbndb.com key', '')) self.setWindowTitle(title if title else 'Unknown') self.tlabel.setText(self.tlabel.text().arg(title if title else 'Unknown')) @@ -106,7 +106,7 @@ class FetchMetadata(QDialog, Ui_FetchMetadata): _('You must specify a valid access key for isbndb.com')) return else: - Settings().setValue('isbndb.com key', QVariant(self.key.text())) + Settings().set('isbndb.com key', str(self.key.text())) args = ['isbndb'] if self.isbn: diff --git a/src/calibre/gui2/dialogs/lrf_single.py b/src/calibre/gui2/dialogs/lrf_single.py index 1929282770..e5df43a673 100644 --- a/src/calibre/gui2/dialogs/lrf_single.py +++ b/src/calibre/gui2/dialogs/lrf_single.py @@ -106,9 +106,8 @@ class LRFSingleDialog(QDialog, Ui_LRFSingleDialog): def load_saved_global_defaults(self): - cmdline = Settings().value('LRF conversion defaults', QVariant(QByteArray(''))).toByteArray().data() + cmdline = Settings().get('LRF conversion defaults') if cmdline: - cmdline = cPickle.loads(cmdline) self.set_options_from_cmdline(cmdline) def set_options_from_cmdline(self, cmdline): @@ -382,7 +381,7 @@ class LRFSingleDialog(QDialog, Ui_LRFSingleDialog): cmdline.extend([u'--cover', self.cover_file.name]) self.cmdline = [unicode(i) for i in cmdline] else: - Settings().setValue('LRF conversion defaults', QVariant(QByteArray(cPickle.dumps(cmdline)))) + Settings().set('LRF conversion defaults', cmdline) QDialog.accept(self) class LRFBulkDialog(LRFSingleDialog): diff --git a/src/calibre/gui2/dialogs/lrf_single.ui b/src/calibre/gui2/dialogs/lrf_single.ui index cf27b2fd7d..9fd3bee155 100644 --- a/src/calibre/gui2/dialogs/lrf_single.ui +++ b/src/calibre/gui2/dialogs/lrf_single.ui @@ -122,8 +122,8 @@ 0 0 - 622 - 454 + 642 + 458 @@ -435,8 +435,8 @@ 0 0 - 622 - 454 + 642 + 458 @@ -701,8 +701,8 @@ 0 0 - 622 - 454 + 642 + 458 @@ -825,8 +825,8 @@ 0 0 - 622 - 454 + 642 + 458 @@ -923,16 +923,6 @@ - - - - 0 - 0 - 100 - 30 - - - @@ -981,7 +971,7 @@ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'DejaVu Sans'; font-size:10pt; font-weight:400; font-style:normal;"> +</style></head><body style=" font-family:'Candara'; font-size:11pt; font-weight:400; font-style:normal;"> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Sans Serif'; font-size:9pt;"></p></body></html> diff --git a/src/calibre/gui2/dialogs/metadata_single.py b/src/calibre/gui2/dialogs/metadata_single.py index 26e2058a96..af2c97a3be 100644 --- a/src/calibre/gui2/dialogs/metadata_single.py +++ b/src/calibre/gui2/dialogs/metadata_single.py @@ -144,7 +144,7 @@ class MetadataSingleDialog(QDialog, Ui_MetadataSingleDialog): self.edit_tags) QObject.connect(self.remove_series_button, SIGNAL('clicked()'), self.remove_unused_series) - self.timeout = float(Settings().value('network timeout', QVariant(5)).toInt()[0]) + self.timeout = float(Settings().get('network timeout', 5)) self.title.setText(db.title(row)) isbn = db.isbn(self.id, index_is_id=True) if not isbn: diff --git a/src/calibre/gui2/dialogs/password.py b/src/calibre/gui2/dialogs/password.py index 2710834a57..9248f313d4 100644 --- a/src/calibre/gui2/dialogs/password.py +++ b/src/calibre/gui2/dialogs/password.py @@ -16,8 +16,8 @@ class PasswordDialog(QDialog, Ui_Dialog): self.setupUi(self) settings = Settings() - un = settings.value(name+': un', QVariant('')).toString() - pw = settings.value(name+': pw', QVariant('')).toString() + un = settings.get(name+': un', u'') + pw = settings.get(name+': pw', u'') self.gui_username.setText(un) self.gui_password.setText(pw) self.sname = name @@ -38,6 +38,6 @@ class PasswordDialog(QDialog, Ui_Dialog): def accept(self): settings = Settings() - settings.setValue(self.sname+': un', QVariant(self.gui_username.text())) - settings.setValue(self.sname+': pw', QVariant(self.gui_password.text())) + settings.set(self.sname+': un', unicode(self.gui_username.text())) + settings.set(self.sname+': pw', unicode(self.gui_password.text())) QDialog.accept(self) diff --git a/src/calibre/gui2/jobs.py b/src/calibre/gui2/jobs.py index da9c9fa36a..d135b7d6cd 100644 --- a/src/calibre/gui2/jobs.py +++ b/src/calibre/gui2/jobs.py @@ -258,8 +258,7 @@ class JobManager(QAbstractTableModel): desc = kwargs.pop('job_description', '') if args and hasattr(args[0], 'append') and '--verbose' not in args[0]: args[0].append('--verbose') - priority = self.PRIORITY[str(Settings().value('conversion job priority', - QVariant('Normal')).toString())] + priority = self.PRIORITY[Settings().get('conversion job priority', 'Normal')] job = self.create_job(ConversionJob, desc, slot, priority, callable, *args, **kwargs) return job.id diff --git a/src/calibre/gui2/library.py b/src/calibre/gui2/library.py index 7d8134b668..d344f3d6a9 100644 --- a/src/calibre/gui2/library.py +++ b/src/calibre/gui2/library.py @@ -5,6 +5,7 @@ from datetime import timedelta, datetime from operator import attrgetter from math import cos, sin, pi +from itertools import repeat from PyQt4.QtGui import QTableView, QProgressDialog, QAbstractItemView, QColor, \ QItemDelegate, QPainterPath, QLinearGradient, QBrush, \ QPen, QStyle, QPainter, QLineEdit, QApplication, \ @@ -110,8 +111,7 @@ class BooksModel(QAbstractTableModel): self.cover_cache.clear_cache() def read_config(self): - self.use_roman_numbers = bool(Settings().value('use roman numerals for series number', - QVariant(True)).toBool()) + self.use_roman_numbers = Settings().get('use roman numerals for series number', True) def set_database(self, db): @@ -591,7 +591,7 @@ class DeviceBooksModel(BooksModel): base = self.map if refinement else self.sorted_map result = [] for i in base: - q = ['', self.db[i].title, self.db[i].authors, '', ', '.join(self.db[i].tags)] + ['' for j in range(10)] + q = ['', self.db[i].title, self.db[i].authors, '', ', '.join(self.db[i].tags)] + list(repeat('', 10)) if OR: add = False for token in tokens: diff --git a/src/calibre/gui2/lrf_renderer/main.py b/src/calibre/gui2/lrf_renderer/main.py index 2d927242cc..edab6b0c1b 100644 --- a/src/calibre/gui2/lrf_renderer/main.py +++ b/src/calibre/gui2/lrf_renderer/main.py @@ -103,13 +103,13 @@ class Main(MainWindow, Ui_MainWindow): def configure(self, triggered): - opts = cPickle.loads(str(Settings().value('ebook viewer options', QVariant(cPickle.dumps(self.opts))).toString())) + opts = Settings().get('LRF ebook viewer options', self.opts) d = Config(self, opts) d.exec_() if d.result() == QDialog.Accepted: opts.white_background = bool(d.white_background.isChecked()) opts.hyphenate = bool(d.hyphenate.isChecked()) - Settings().setValue('ebook viewer options', QVariant(cPickle.dumps(opts))) + Settings().set('LRF ebook viewer options', opts) def set_ebook(self, stream): self.progress_bar.setMinimum(0) @@ -279,8 +279,7 @@ def option_parser(): return parser def normalize_settings(parser, opts): - settings = Settings() - saved_opts = cPickle.loads(str(settings.value('ebook viewer options', QVariant(cPickle.dumps(opts))).toString())) + saved_opts = Settings().get('LRF ebook viewer options', opts) for opt in parser.option_list: if not opt.dest: continue diff --git a/src/calibre/gui2/main.py b/src/calibre/gui2/main.py index c80247f815..e5e2bb4b98 100644 --- a/src/calibre/gui2/main.py +++ b/src/calibre/gui2/main.py @@ -4,7 +4,7 @@ import os, sys, textwrap, collections, traceback, time from xml.parsers.expat import ExpatError from functools import partial from PyQt4.QtCore import Qt, SIGNAL, QObject, QCoreApplication, \ - QVariant, QThread, QString, QSize, QUrl + QVariant, QThread, QSize, QUrl from PyQt4.QtGui import QPixmap, QColor, QPainter, QMenu, QIcon, QMessageBox, \ QToolButton, QDialog, QDesktopServices from PyQt4.QtSvg import QSvgRenderer @@ -18,7 +18,7 @@ from calibre.devices.interface import Device from calibre.gui2 import APP_UID, warning_dialog, choose_files, error_dialog, \ initialize_file_icon_provider, question_dialog,\ pixmap_to_data, choose_dir, ORG_NAME, \ - qstring_to_unicode, set_sidebar_directories, \ + set_sidebar_directories, \ SingleApplication, Application, available_height from calibre.gui2.cover_flow import CoverFlow, DatabaseImages from calibre.library.database import LibraryDatabase @@ -48,7 +48,7 @@ from calibre.library.database2 import LibraryDatabase2, CoverCache class Main(MainWindow, Ui_MainWindow): - + def set_default_thumbnail(self, height): r = QSvgRenderer(':/images/book.svg') pixmap = QPixmap(height, height) @@ -57,14 +57,14 @@ class Main(MainWindow, Ui_MainWindow): r.render(p) p.end() self.default_thumbnail = (pixmap.width(), pixmap.height(), pixmap_to_data(pixmap)) - + def __init__(self, single_instance, parent=None): MainWindow.__init__(self, parent) self.single_instance = single_instance if self.single_instance is not None: self.connect(self.single_instance, SIGNAL('message_received(PyQt_PyObject)'), self.another_instance_wants_to_talk) - + Ui_MainWindow.__init__(self) self.setupUi(self) self.setWindowTitle(__appname__) @@ -84,16 +84,16 @@ class Main(MainWindow, Ui_MainWindow): self.tb_wrapper = textwrap.TextWrapper(width=40) self.device_connected = False self.viewers = collections.deque() - + ####################### Location View ######################## QObject.connect(self.location_view, SIGNAL('location_selected(PyQt_PyObject)'), self.location_selected) - QObject.connect(self.stack, SIGNAL('currentChanged(int)'), + QObject.connect(self.stack, SIGNAL('currentChanged(int)'), self.location_view.location_changed) - + ####################### Vanity ######################## self.vanity_template = _('

For help visit %s.kovidgoyal.net
')%(__appname__, __appname__) - self.vanity_template += _('%s: %s by Kovid Goyal %%(version)s
%%(device)s

')%(__appname__, __version__) + self.vanity_template += _('%s: %s by Kovid Goyal %%(version)s
%%(device)s

')%(__appname__, __version__) self.latest_version = ' ' self.vanity.setText(self.vanity_template%dict(version=' ', device=' ')) self.device_info = ' ' @@ -131,13 +131,13 @@ class Main(MainWindow, Ui_MainWindow): QObject.connect(self.action_edit, SIGNAL("triggered(bool)"), self.edit_metadata) QObject.connect(md.actions()[0], SIGNAL('triggered(bool)'), self.edit_metadata) QObject.connect(md.actions()[1], SIGNAL('triggered(bool)'), self.edit_bulk_metadata) - QObject.connect(self.action_sync, SIGNAL("triggered(bool)"), self.sync_to_main_memory) + QObject.connect(self.action_sync, SIGNAL("triggered(bool)"), self.sync_to_main_memory) QObject.connect(sm.actions()[0], SIGNAL('triggered(bool)'), self.sync_to_main_memory) QObject.connect(sm.actions()[1], SIGNAL('triggered(bool)'), self.sync_to_card) self.save_menu = QMenu() self.save_menu.addAction(_('Save to disk')) self.save_menu.addAction(_('Save to disk in a single directory')) - + self.view_menu = QMenu() self.view_menu.addAction(_('View')) self.view_menu.addAction(_('View specific format')) @@ -163,7 +163,7 @@ class Main(MainWindow, Ui_MainWindow): QObject.connect(cm.actions()[0], SIGNAL('triggered(bool)'), self.convert_single) QObject.connect(cm.actions()[1], SIGNAL('triggered(bool)'), self.convert_bulk) QObject.connect(cm.actions()[3], SIGNAL('triggered(bool)'), self.set_conversion_defaults) - QObject.connect(self.action_convert, SIGNAL('triggered(bool)'), self.convert_single) + QObject.connect(self.action_convert, SIGNAL('triggered(bool)'), self.convert_single) self.convert_menu = cm self.tool_bar.widgetForAction(self.action_news).setPopupMode(QToolButton.InstantPopup) self.tool_bar.widgetForAction(self.action_edit).setPopupMode(QToolButton.MenuButtonPopup) @@ -173,10 +173,10 @@ class Main(MainWindow, Ui_MainWindow): self.tool_bar.widgetForAction(self.action_add).setPopupMode(QToolButton.MenuButtonPopup) self.tool_bar.widgetForAction(self.action_view).setPopupMode(QToolButton.MenuButtonPopup) self.tool_bar.setContextMenuPolicy(Qt.PreventContextMenu) - + QObject.connect(self.config_button, SIGNAL('clicked(bool)'), self.do_config) QObject.connect(self.advanced_search_button, SIGNAL('clicked(bool)'), self.do_advanced_search) - + ####################### Library view ######################## QObject.connect(self.library_view, SIGNAL('files_dropped(PyQt_PyObject)'), self.files_dropped) @@ -186,10 +186,10 @@ class Main(MainWindow, Ui_MainWindow): ]: for view in (self.library_view, self.memory_view, self.card_view): getattr(view, func)(target) - + self.memory_view.connect_dirtied_signal(self.upload_booklists) self.card_view.connect_dirtied_signal(self.upload_booklists) - + self.show() self.stack.setCurrentIndex(0) db = LibraryDatabase2(self.library_path) @@ -228,18 +228,18 @@ class Main(MainWindow, Ui_MainWindow): self.cover_flow.setImages(self.db_images) else: self.status_bar.cover_flow_button.disable(pictureflowerror) - - + + self.setMaximumHeight(available_height()) - + ####################### Setup device detection ######################## self.detector = DeviceDetector(sleep_time=2000) - QObject.connect(self.detector, SIGNAL('connected(PyQt_PyObject, PyQt_PyObject)'), + QObject.connect(self.detector, SIGNAL('connected(PyQt_PyObject, PyQt_PyObject)'), self.device_detected, Qt.QueuedConnection) self.detector.start(QThread.InheritPriority) - + self.news_menu.set_custom_feeds(self.library_view.model().db.get_feeds()) - + def toggle_cover_flow(self, show): if show: self.library_view.setCurrentIndex(self.library_view.currentIndex()) @@ -252,17 +252,17 @@ class Main(MainWindow, Ui_MainWindow): self.cover_flow.setVisible(False) self.status_bar.book_info.book_data.setMaximumHeight(1000) self.setMaximumHeight(available_height()) - - - - + + + + def sync_cf_to_listview(self, index, *args): if not hasattr(index, 'row') and self.library_view.currentIndex().row() != index: 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()) - + def another_instance_wants_to_talk(self, msg): if msg.startswith('launched:'): argv = eval(msg[len('launched:'):]) @@ -287,8 +287,8 @@ class Main(MainWindow, Ui_MainWindow): pass else: print msg - - + + def current_view(self): '''Convenience method that returns the currently visible view ''' idx = self.stack.currentIndex() @@ -298,12 +298,12 @@ class Main(MainWindow, Ui_MainWindow): return self.memory_view if idx == 2: return self.card_view - + def booklists(self): return self.memory_view.model().db, self.card_view.model().db - - - + + + ########################## Connect to device ############################## def device_detected(self, device, connected): ''' @@ -328,7 +328,7 @@ class Main(MainWindow, Ui_MainWindow): if self.current_view() != self.library_view: self.status_bar.reset_info() self.location_selected('library') - + def info_read(self, id, description, result, exception, formatted_traceback): ''' Called once device information has been read. @@ -342,7 +342,7 @@ class Main(MainWindow, Ui_MainWindow): self.vanity.setText(self.vanity_template%dict(version=self.latest_version, device=self.device_info)) func = self.device_manager.books_func() self.job_manager.run_device_job(self.metadata_downloaded, func) - + def metadata_downloaded(self, id, description, result, exception, formatted_traceback): ''' Called once metadata has been read for all books on the device. @@ -366,22 +366,22 @@ class Main(MainWindow, Ui_MainWindow): self.card_view.set_database(cardlist) for view in (self.memory_view, self.card_view): view.sortByColumn(3, Qt.DescendingOrder) - if not view.restore_column_widths(): + if not view.restore_column_widths(): view.resizeColumnsToContents() view.resizeRowsToContents() view.resize_on_select = not view.isVisible() ############################################################################ - - + + ############################# Upload booklists ############################# def upload_booklists(self): ''' Upload metadata to device. ''' - self.job_manager.run_device_job(self.metadata_synced, + self.job_manager.run_device_job(self.metadata_synced, self.device_manager.sync_booklists_func(), self.booklists()) - + def metadata_synced(self, id, description, result, exception, formatted_traceback): ''' Called once metadata has been uploaded. @@ -392,16 +392,16 @@ class Main(MainWindow, Ui_MainWindow): cp, fs = result self.location_view.model().update_devices(cp, fs) ############################################################################ - - + + ################################# Add books ################################ - + def add_recursive(self, single): root = choose_dir(self, 'recursive book import root dir dialog', 'Select root folder') if not root: return duplicates = self.library_view.model().db.recursive_import(root, single) - + if duplicates: files = _('

Books with the same title as the following already exist in the database. Add them anyway?