diff --git a/src/calibre/ebooks/chm/reader.py b/src/calibre/ebooks/chm/reader.py index 04ce6d5efe..fe8b4fdbde 100644 --- a/src/calibre/ebooks/chm/reader.py +++ b/src/calibre/ebooks/chm/reader.py @@ -54,8 +54,12 @@ class CHMReader(CHMFile): self._extracted = False # location of '.hhc' file, which is the CHM TOC. - self.root, ext = os.path.splitext(self.topics.lstrip('/')) - self.hhc_path = self.root + ".hhc" + if self.topics is None: + self.root, ext = os.path.splitext(self.home.lstrip('/')) + self.hhc_path = self.root + ".hhc" + else: + self.root, ext = os.path.splitext(self.topics.lstrip('/')) + self.hhc_path = self.root + ".hhc" def _parse_toc(self, ul, basedir=os.getcwdu()): toc = TOC(play_order=self._playorder, base_path=basedir, text='') diff --git a/src/calibre/ebooks/metadata/book/json_codec.py b/src/calibre/ebooks/metadata/book/json_codec.py index f434800edf..1d93b5dece 100644 --- a/src/calibre/ebooks/metadata/book/json_codec.py +++ b/src/calibre/ebooks/metadata/book/json_codec.py @@ -119,12 +119,12 @@ class JsonCodec(object): for item in js: book = book_class(prefix, item.get('lpath', None)) for key in item.keys(): - if key == 'classifiers': - key = 'identifiers' meta = self.decode_metadata(key, item[key]) if key == 'user_metadata': book.set_all_user_metadata(meta) else: + if key == 'classifiers': + key = 'identifiers' setattr(book, key, meta) booklist.append(book) except: @@ -132,6 +132,8 @@ class JsonCodec(object): traceback.print_exc() def decode_metadata(self, key, value): + if key == 'classifiers': + key = 'identifiers' if key == 'user_metadata': for k in value: if value[k]['datatype'] == 'datetime': diff --git a/src/calibre/gui2/book_details.py b/src/calibre/gui2/book_details.py index 2f7892692c..63deccb2f0 100644 --- a/src/calibre/gui2/book_details.py +++ b/src/calibre/gui2/book_details.py @@ -10,7 +10,7 @@ from Queue import Queue from PyQt4.Qt import QPixmap, QSize, QWidget, Qt, pyqtSignal, QUrl, \ QPropertyAnimation, QEasingCurve, QThread, QApplication, QFontInfo, \ - QSizePolicy, QPainter, QRect, pyqtProperty, QLayout, QPalette + QSizePolicy, QPainter, QRect, pyqtProperty, QLayout, QPalette, QMenu from PyQt4.QtWebKit import QWebView from calibre import fit_image, prepare_string_for_xml @@ -18,7 +18,7 @@ from calibre.gui2.widgets import IMAGE_EXTENSIONS from calibre.ebooks import BOOK_EXTENSIONS from calibre.constants import preferred_encoding from calibre.library.comments import comments_to_html -from calibre.gui2 import config, open_local_file, open_url +from calibre.gui2 import config, open_local_file, open_url, pixmap_to_data from calibre.utils.icu import sort_key # render_rows(data) {{{ @@ -70,6 +70,7 @@ def render_rows(data): class CoverView(QWidget): # {{{ + cover_changed = pyqtSignal(object, object) def __init__(self, vertical, parent=None): QWidget.__init__(self, parent) @@ -151,6 +152,35 @@ class CoverView(QWidget): # {{{ fset=setCurrentPixmapSize ) + def contextMenuEvent(self, ev): + cm = QMenu(self) + paste = cm.addAction(_('Paste Cover')) + copy = cm.addAction(_('Copy Cover')) + if not QApplication.instance().clipboard().mimeData().hasImage(): + paste.setEnabled(False) + copy.triggered.connect(self.copy_to_clipboard) + paste.triggered.connect(self.paste_from_clipboard) + cm.exec_(ev.globalPos()) + + def copy_to_clipboard(self): + QApplication.instance().clipboard().setPixmap(self.pixmap) + + def paste_from_clipboard(self): + cb = QApplication.instance().clipboard() + pmap = cb.pixmap() + if pmap.isNull() and cb.supportsSelection(): + pmap = cb.pixmap(cb.Selection) + if not pmap.isNull(): + self.pixmap = pmap + self.do_layout() + self.update() + if not config['disable_animations']: + self.animation.start() + id_ = self.data.get('id', None) + if id_ is not None: + self.cover_changed.emit(id_, + pixmap_to_data(pmap)) + # }}} @@ -362,7 +392,9 @@ class BookDetails(QWidget): # {{{ # Drag 'n drop {{{ DROPABBLE_EXTENSIONS = IMAGE_EXTENSIONS+BOOK_EXTENSIONS files_dropped = pyqtSignal(object, object) + cover_changed = pyqtSignal(object, object) + # application/x-moz-file-promise-url @classmethod def paths_from_event(cls, event): ''' @@ -399,6 +431,7 @@ class BookDetails(QWidget): # {{{ self.setLayout(self._layout) self.cover_view = CoverView(vertical, self) + self.cover_view.cover_changed.connect(self.cover_changed.emit) self._layout.addWidget(self.cover_view) self.book_info = BookInfo(vertical, self) self._layout.addWidget(self.book_info) diff --git a/src/calibre/gui2/dialogs/scheduler.py b/src/calibre/gui2/dialogs/scheduler.py index 48d0d67255..460e1d9fdb 100644 --- a/src/calibre/gui2/dialogs/scheduler.py +++ b/src/calibre/gui2/dialogs/scheduler.py @@ -264,10 +264,8 @@ class Scheduler(QObject): ids = list(self.recipe_model.db.tags_older_than(_('News'), delta)) except: - # Should never happen + # Happens if library is being switched ids = [] - import traceback - traceback.print_exc() if ids: if ids: self.delete_old_news.emit(ids) diff --git a/src/calibre/gui2/init.py b/src/calibre/gui2/init.py index e7abbddd2a..e8c2712c83 100644 --- a/src/calibre/gui2/init.py +++ b/src/calibre/gui2/init.py @@ -262,6 +262,8 @@ class LayoutMixin(object): # {{{ self.status_bar.initialize(self.system_tray_icon) self.book_details.show_book_info.connect(self.iactions['Show Book Details'].show_book_info) self.book_details.files_dropped.connect(self.iactions['Add Books'].files_dropped_on_book) + self.book_details.cover_changed.connect(self.bd_cover_changed, + type=Qt.QueuedConnection) self.book_details.open_containing_folder.connect(self.iactions['View'].view_folder_for_id) self.book_details.view_specific_format.connect(self.iactions['View'].view_format_by_id) @@ -272,6 +274,10 @@ class LayoutMixin(object): # {{{ self.library_view.currentIndex()) self.library_view.setFocus(Qt.OtherFocusReason) + def bd_cover_changed(self, id_, cdata): + self.library_view.model().db.set_cover(id_, cdata) + if self.cover_flow: + self.cover_flow.dataChanged() def save_layout_state(self): for x in ('library', 'memory', 'card_a', 'card_b'): diff --git a/src/calibre/gui2/tag_view.py b/src/calibre/gui2/tag_view.py index 0a65b8da67..3f347069a9 100644 --- a/src/calibre/gui2/tag_view.py +++ b/src/calibre/gui2/tag_view.py @@ -1135,7 +1135,7 @@ class TagsModel(QAbstractItemModel): # {{{ collapse_model = 'partition' collapse_template = tweaks['categories_collapsed_popularity_template'] - def process_one_node(category, state_map): + def process_one_node(category, state_map): # {{{ collapse_letter = None category_index = self.createIndex(category.row(), 0, category) category_node = category_index.internalPointer() @@ -1277,6 +1277,7 @@ class TagsModel(QAbstractItemModel): # {{{ # This id_set must not be None node_parent.id_set |= tag.id_set return + # }}} for category in self.category_nodes: if len(category.children) > 0: @@ -2079,6 +2080,10 @@ class TagBrowserWidget(QWidget): # {{{ _('Add your own categories to the Tag Browser')) parent.edit_categories.setStatusTip(parent.edit_categories.toolTip()) + # self.leak_test_timer = QTimer(self) + # self.leak_test_timer.timeout.connect(self.test_for_leak) + # self.leak_test_timer.start(5000) + def set_pane_is_visible(self, to_what): self.tags_view.set_pane_is_visible(to_what) @@ -2140,5 +2145,13 @@ class TagBrowserWidget(QWidget): # {{{ def not_found_label_timer_event(self): self.not_found_label.setVisible(False) + def test_for_leak(self): + from calibre.utils.mem import memory + import gc + before = memory() + self.tags_view.recount() + for i in xrange(3): gc.collect() + print 'Used memory:', memory(before)/(1024.), 'KB' + # }}} diff --git a/src/calibre/gui2/widgets.py b/src/calibre/gui2/widgets.py index 5cacf32bb2..aa9d6c8b9f 100644 --- a/src/calibre/gui2/widgets.py +++ b/src/calibre/gui2/widgets.py @@ -236,8 +236,8 @@ class ImageDropMixin(object): # {{{ def contextMenuEvent(self, ev): cm = QMenu(self) - copy = cm.addAction(_('Copy Image')) - paste = cm.addAction(_('Paste Image')) + paste = cm.addAction(_('Paste Cover')) + copy = cm.addAction(_('Copy Cover')) if not QApplication.instance().clipboard().mimeData().hasImage(): paste.setEnabled(False) copy.triggered.connect(self.copy_to_clipboard)