diff --git a/src/calibre/ebooks/fb2/fb2ml.py b/src/calibre/ebooks/fb2/fb2ml.py index 51bfaa7293..5efc360f1f 100644 --- a/src/calibre/ebooks/fb2/fb2ml.py +++ b/src/calibre/ebooks/fb2/fb2ml.py @@ -73,6 +73,10 @@ class FB2MLizer(object): text = re.sub(r'(?miu)

\s*

', '', text) text = re.sub(r'(?miu)\s+

', '

', text) text = re.sub(r'(?miu)

', '

\n\n

', text) + + if self.opts.insert_blank_line: + text = re.sub(r'(?miu)

', '

', text) + return text def fb2_header(self): @@ -293,6 +297,18 @@ class FB2MLizer(object): s_out, s_tags = self.handle_simple_tag('emphasis', tag_stack+tags) fb2_out += s_out tags += s_tags + elif tag in ('del', 'strike'): + s_out, s_tags = self.handle_simple_tag('strikethrough', tag_stack+tags) + fb2_out += s_out + tags += s_tags + elif tag == 'sub': + s_out, s_tags = self.handle_simple_tag('sub', tag_stack+tags) + fb2_out += s_out + tags += s_tags + elif tag == 'sup': + s_out, s_tags = self.handle_simple_tag('sup', tag_stack+tags) + fb2_out += s_out + tags += s_tags # Processes style information. if style['font-style'] == 'italic': @@ -303,6 +319,10 @@ class FB2MLizer(object): s_out, s_tags = self.handle_simple_tag('strong', tag_stack+tags) fb2_out += s_out tags += s_tags + elif style['text-decoration'] == 'line-through': + s_out, s_tags = self.handle_simple_tag('strikethrough', tag_stack+tags) + fb2_out += s_out + tags += s_tags # Process element text. if hasattr(elem_tree, 'text') and elem_tree.text: diff --git a/src/calibre/gui2/__init__.py b/src/calibre/gui2/__init__.py index 57ca2a1880..f96c64080d 100644 --- a/src/calibre/gui2/__init__.py +++ b/src/calibre/gui2/__init__.py @@ -9,7 +9,7 @@ from PyQt4.Qt import QVariant, QFileInfo, QObject, SIGNAL, QBuffer, Qt, \ QByteArray, QTranslator, QCoreApplication, QThread, \ QEvent, QTimer, pyqtSignal, QDate, QDesktopServices, \ QFileDialog, QMessageBox, QPixmap, QFileIconProvider, \ - QIcon, QApplication, QDialog, QPushButton, QUrl + QIcon, QApplication, QDialog, QPushButton, QUrl, QFont ORG_NAME = 'KovidsBrain' APP_UID = 'libprs500' @@ -52,6 +52,7 @@ gprefs.defaults['show_splash_screen'] = True gprefs.defaults['toolbar_icon_size'] = 'medium' gprefs.defaults['toolbar_text'] = 'auto' gprefs.defaults['show_child_bar'] = False +gprefs.defaults['font'] = None # }}} @@ -613,6 +614,10 @@ class Application(QApplication): qt_app = self self._file_open_paths = [] self._file_open_lock = RLock() + self.original_font = QFont(QApplication.font()) + fi = gprefs['font'] + if fi is not None: + QApplication.setFont(QFont(*fi)) def _send_file_open_events(self): with self._file_open_lock: diff --git a/src/calibre/gui2/actions/edit_metadata.py b/src/calibre/gui2/actions/edit_metadata.py index 60a943ccb9..11949632e9 100644 --- a/src/calibre/gui2/actions/edit_metadata.py +++ b/src/calibre/gui2/actions/edit_metadata.py @@ -154,15 +154,17 @@ class EditMetadataAction(InterfaceAction): d.view_format.connect(lambda fmt:self.gui.iactions['View'].view_format(row_list[current_row], fmt)) - if d.exec_() != d.Accepted: - d.view_format.disconnect() + ret = d.exec_() + d.break_cycles() + if ret != d.Accepted: break - d.view_format.disconnect() + changed.add(d.id) if d.row_delta == 0: break current_row += d.row_delta + if changed: self.gui.library_view.model().refresh_ids(list(changed)) current = self.gui.library_view.currentIndex() diff --git a/src/calibre/gui2/dialogs/metadata_single.py b/src/calibre/gui2/dialogs/metadata_single.py index 30c964ca49..55c147ddd4 100644 --- a/src/calibre/gui2/dialogs/metadata_single.py +++ b/src/calibre/gui2/dialogs/metadata_single.py @@ -293,7 +293,8 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog): finally: self.fetch_cover_button.setEnabled(True) self.unsetCursor() - self.pi.stop() + if self.pi is not None: + self.pi.stop() # }}} @@ -442,7 +443,6 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog): ResizableDialog.__init__(self, window) self.cover_fetcher = None self.bc_box.layout().setAlignment(self.cover, Qt.AlignCenter|Qt.AlignHCenter) - self.cancel_all = False base = unicode(self.author_sort.toolTip()) self.ok_aus_tooltip = '

' + textwrap.fill(base+'

'+ _(' The green color indicates that the current ' @@ -573,7 +573,6 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog): QObject.connect(self.series, SIGNAL('editTextChanged(QString)'), self.enable_series_index) self.series.lineEdit().editingFinished.connect(self.increment_series_index) - self.show() pm = QPixmap() if cover: pm.loadFromData(cover) @@ -593,6 +592,8 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog): self.original_author = unicode(self.authors.text()).strip() self.original_title = unicode(self.title.text()).strip() + self.show() + def create_custom_column_editors(self): w = self.central_widget.widget(1) layout = w.layout() @@ -907,3 +908,48 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog): dynamic.set('metasingle_window_geometry', bytes(self.saveGeometry())) dynamic.set('metasingle_splitter_state', bytes(self.splitter.saveState())) + + def break_cycles(self): + # Break any reference cycles that could prevent python + # from garbage collecting this dialog + def disconnect(signal): + try: + signal.disconnect() + except: + pass # Fails if view format was never connected + disconnect(self.view_format) + for b in ('next_button', 'prev_button'): + x = getattr(self, b, None) + if x is not None: + disconnect(x.clicked) + +if __name__ == '__main__': + from calibre.library import db + from PyQt4.Qt import QApplication + from calibre.utils.mem import memory + import gc + + + app = QApplication([]) + db = db() + + # Initialize all Qt Objects once + d = MetadataSingleDialog(None, 4, db) + d.break_cycles() + d.reject() + del d + + for i in range(5): + gc.collect() + before = memory() + + d = MetadataSingleDialog(None, 4, db) + d.reject() + d.break_cycles() + del d + + for i in range(5): + gc.collect() + print 'Used memory:', memory(before)/1024.**2, 'MB' + + diff --git a/src/calibre/gui2/preferences/look_feel.py b/src/calibre/gui2/preferences/look_feel.py index 10c2fcfe95..b2ba87d1e0 100644 --- a/src/calibre/gui2/preferences/look_feel.py +++ b/src/calibre/gui2/preferences/look_feel.py @@ -5,10 +5,11 @@ __license__ = 'GPL v3' __copyright__ = '2010, Kovid Goyal ' __docformat__ = 'restructuredtext en' +from PyQt4.Qt import QApplication, QFont, QFontInfo, QFontDialog from calibre.gui2.preferences import ConfigWidgetBase, test_widget from calibre.gui2.preferences.look_feel_ui import Ui_Form -from calibre.gui2 import config, gprefs +from calibre.gui2 import config, gprefs, qt_app from calibre.utils.localization import available_translations, \ get_language, get_lang from calibre.utils.config import prefs @@ -56,12 +57,64 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): (_('Never'), 'never')] r('toolbar_text', gprefs, choices=choices) + self.current_font = None + self.change_font_button.clicked.connect(self.change_font) + + + def initialize(self): + ConfigWidgetBase.initialize(self) + self.current_font = gprefs['font'] + self.update_font_display() + + def restore_defaults(self): + ConfigWidgetBase.restore_defaults(self) + ofont = self.current_font + self.current_font = None + if ofont is not None: + self.changed_signal.emit() + self.update_font_display() + + def build_font_obj(self): + font_info = self.current_font + if font_info is not None: + font = QFont(*font_info) + else: + font = qt_app.original_font + return font + + def update_font_display(self): + font = self.build_font_obj() + fi = QFontInfo(font) + name = unicode(fi.family()) + + self.font_display.setFont(font) + self.font_display.setText(_('Current font:') + ' ' + name + + ' [%dpt]'%fi.pointSize()) + + def change_font(self, *args): + fd = QFontDialog(self.build_font_obj(), self) + if fd.exec_() == fd.Accepted: + font = fd.selectedFont() + fi = QFontInfo(font) + self.current_font = (unicode(fi.family()), fi.pointSize(), + fi.weight(), fi.italic()) + self.update_font_display() + self.changed_signal.emit() + + def commit(self, *args): + rr = ConfigWidgetBase.commit(self, *args) + if self.current_font != gprefs['font']: + gprefs['font'] = self.current_font + QApplication.setFont(self.font_display.font()) + rr = True + return rr + + def refresh_gui(self, gui): gui.search.search_as_you_type(config['search_as_you_type']) - + self.update_font_display() if __name__ == '__main__': - from PyQt4.Qt import QApplication app = QApplication([]) test_widget('Interface', 'Look & Feel') diff --git a/src/calibre/gui2/preferences/look_feel.ui b/src/calibre/gui2/preferences/look_feel.ui index 1de55d51ef..91f45a155f 100644 --- a/src/calibre/gui2/preferences/look_feel.ui +++ b/src/calibre/gui2/preferences/look_feel.ui @@ -183,7 +183,7 @@ - + Qt::Vertical @@ -196,6 +196,20 @@ + + + + true + + + + + + + Change &font (needs restart) + + + diff --git a/src/calibre/library/__init__.py b/src/calibre/library/__init__.py index 8ff23c0a0a..177c5063ac 100644 --- a/src/calibre/library/__init__.py +++ b/src/calibre/library/__init__.py @@ -19,12 +19,15 @@ def generate_test_db(library_path, max_tags=10 ): import random, string, os, sys, time + from calibre.constants import preferred_encoding if not os.path.exists(library_path): os.makedirs(library_path) + letters = string.letters.decode(preferred_encoding) + def randstr(length): - return ''.join(random.choice(string.letters) for i in + return ''.join(random.choice(letters) for i in xrange(length)) all_tags = [randstr(tag_length) for j in xrange(num_of_tags)] diff --git a/src/calibre/utils/mem.py b/src/calibre/utils/mem.py new file mode 100644 index 0000000000..f48aec34c6 --- /dev/null +++ b/src/calibre/utils/mem.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python +# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai + +__license__ = 'GPL v3' +__copyright__ = '2010, Kovid Goyal ' +__docformat__ = 'restructuredtext en' + +## {{{ http://code.activestate.com/recipes/286222/ (r1) +import os + +_proc_status = '/proc/%d/status' % os.getpid() + +_scale = {'kB': 1024.0, 'mB': 1024.0*1024.0, + 'KB': 1024.0, 'MB': 1024.0*1024.0} + +def _VmB(VmKey): + '''Private. + ''' + global _proc_status, _scale + # get pseudo file /proc//status + try: + t = open(_proc_status) + v = t.read() + t.close() + except: + return 0.0 # non-Linux? + # get VmKey line e.g. 'VmRSS: 9999 kB\n ...' + i = v.index(VmKey) + v = v[i:].split(None, 3) # whitespace + if len(v) < 3: + return 0.0 # invalid format? + # convert Vm value to bytes + return float(v[1]) * _scale[v[2]] + + +def memory(since=0.0): + '''Return memory usage in bytes. + ''' + return _VmB('VmSize:') - since + + +def resident(since=0.0): + '''Return resident memory usage in bytes. + ''' + return _VmB('VmRSS:') - since + + +def stacksize(since=0.0): + '''Return stack size in bytes. + ''' + return _VmB('VmStk:') - since +## end of http://code.activestate.com/recipes/286222/ }}} + + +