diff --git a/src/calibre/gui2/metadata/diff.py b/src/calibre/gui2/metadata/diff.py index ad9c5d42fc..9c54bbe8b1 100644 --- a/src/calibre/gui2/metadata/diff.py +++ b/src/calibre/gui2/metadata/diff.py @@ -5,28 +5,29 @@ __license__ = 'GPL v3' __copyright__ = '2013, Kovid Goyal ' -import os, weakref +import os +import weakref from collections import OrderedDict, namedtuple from functools import partial -from polyglot.builtins import iteritems, itervalues, zip, unicode_type, range, map - from qt.core import ( - QDialog, QWidget, QGridLayout, QLabel, QToolButton, QIcon, - QVBoxLayout, QDialogButtonBox, QApplication, pyqtSignal, QFont, QPixmap, - QSize, QPainter, Qt, QColor, QPen, QSizePolicy, QScrollArea, - QKeySequence, QAction, QMenu, QHBoxLayout, QCheckBox) + QAction, QApplication, QCheckBox, QColor, QDialog, QDialogButtonBox, QFont, + QGridLayout, QHBoxLayout, QIcon, QKeySequence, QLabel, QMenu, QPainter, QPen, + QPixmap, QScrollArea, QSize, QSizePolicy, QStackedLayout, Qt, QToolButton, + QVBoxLayout, QWidget, pyqtSignal +) from calibre import fit_image -from calibre.ebooks.metadata import title_sort, authors_to_sort_string, fmt_sidx -from calibre.gui2 import pixmap_to_data, gprefs -from calibre.gui2.complete2 import LineEdit as EditWithComplete +from calibre.ebooks.metadata import authors_to_sort_string, fmt_sidx, title_sort +from calibre.gui2 import gprefs, pixmap_to_data from calibre.gui2.comments_editor import Editor +from calibre.gui2.complete2 import LineEdit as EditWithComplete from calibre.gui2.dialogs.confirm_delete import confirm from calibre.gui2.languages import LanguagesEdit as LE -from calibre.gui2.widgets2 import RightClickButton from calibre.gui2.metadata.basic_widgets import PubdateEdit, RatingEdit +from calibre.gui2.widgets2 import RightClickButton from calibre.ptempfile import PersistentTemporaryFile from calibre.utils.date import UNDEFINED_DATE +from polyglot.builtins import iteritems, itervalues, map, range, unicode_type, zip Widgets = namedtuple('Widgets', 'new old label button') @@ -310,6 +311,7 @@ class CommentsEdit(Editor): class CoverView(QWidget): changed = pyqtSignal() + zoom_requested = pyqtSignal(object) def __init__(self, field, is_new, parent, metadata, extra): QWidget.__init__(self, parent) @@ -321,6 +323,10 @@ class CoverView(QWidget): self.setSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.PolicyFlag.GrowFlag|QSizePolicy.PolicyFlag.ExpandFlag) self.sizePolicy().setHeightForWidth(True) + def mouseDoubleClickEvent(self, ev): + if self.pixmap and not self.pixmap.isNull(): + self.zoom_requested.emit(self.pixmap) + @property def is_blank(self): return self.pixmap is None @@ -398,6 +404,8 @@ class CoverView(QWidget): class CompareSingle(QWidget): + zoom_requested = pyqtSignal(object) + def __init__( self, field_metadata, parent=None, revert_tooltip=None, datetime_fmt='MMMM yyyy', blank_as_equal=True, @@ -474,6 +482,9 @@ class CompareSingle(QWidget): m.addAction(_('Merge tags')).triggered.connect(self.merge_tags) m.actions()[1].setIcon(QIcon(I('merge.png'))) + if cls is CoverView: + neww.zoom_requested.connect(self.zoom_requested) + oldw.zoom_requested.connect(self.zoom_requested) self.widgets[field] = Widgets(neww, oldw, newl, button) for i, w in enumerate((newl, neww, button, oldw)): c = i if i < 2 else i + 1 @@ -540,6 +551,45 @@ class CompareSingle(QWidget): return changed +class ZoomedCover(QWidget): + pixmap = None + + def paintEvent(self, event): + pmap = self.pixmap + if pmap is None: + return + target = self.rect() + scaled, width, height = fit_image(pmap.width(), pmap.height(), target.width(), target.height()) + dx = 0 + if target.width() > width + 1: + dx += (target.width() - width) // 2 + target.setRect(target.x() + dx, target.y(), width, height) + p = QPainter(self) + p.setRenderHints(QPainter.RenderHint.Antialiasing | QPainter.RenderHint.SmoothPixmapTransform) + p.drawPixmap(target, pmap) + + +class CoverZoom(QWidget): + + def __init__(self, parent): + QWidget.__init__(self, parent) + self.l = l = QVBoxLayout(self) + self.cover = ZoomedCover(self) + l.addWidget(self.cover) + self.h = QHBoxLayout() + l.addLayout(self.h) + self.bb = QDialogButtonBox(QDialogButtonBox.StandardButton.Close, self) + self.size_label = QLabel(self) + self.h.addWidget(self.size_label) + self.h.addStretch(10) + self.h.addWidget(self.bb) + + def set_pixmap(self, pixmap): + self.cover.pixmap = pixmap + self.size_label.setText(_('Cover size: {0}x{1}').format(pixmap.width(), pixmap.height())) + self.cover.update() + + class CompareMany(QDialog): def __init__(self, ids, get_metadata, field_metadata, parent=None, @@ -552,9 +602,11 @@ class CompareMany(QDialog): action_button=None, **kwargs): QDialog.__init__(self, parent) - self.l = l = QVBoxLayout() + self.stack = s = QStackedLayout(self) + self.w = w = QWidget(self) + self.l = l = QVBoxLayout(w) + s.addWidget(w) self.next_called = False - self.setLayout(l) self.setWindowIcon(QIcon(I('auto_author_sort.png'))) self.get_metadata = get_metadata self.ids = list(ids) @@ -573,6 +625,10 @@ class CompareMany(QDialog): l.addWidget(sa) sa.setWidget(self.compare_widget) sa.setWidgetResizable(True) + self.cover_zoom = cz = CoverZoom(self) + cz.bb.rejected.connect(self.reject) + s.addWidget(cz) + self.compare_widget.zoom_requested.connect(self.show_zoomed_cover) self.bb = bb = QDialogButtonBox(QDialogButtonBox.StandardButton.Cancel) bb.button(QDialogButtonBox.StandardButton.Cancel).setAutoDefault(False) @@ -630,6 +686,10 @@ class CompareMany(QDialog): b.setFocus(Qt.FocusReason.OtherFocusReason) self.next_called = False + def show_zoomed_cover(self, pixmap): + self.cover_zoom.set_pixmap(pixmap) + self.stack.setCurrentIndex(1) + @property def mark_rejected(self): return self.markq.isChecked() @@ -643,6 +703,9 @@ class CompareMany(QDialog): super(CompareMany, self).accept() def reject(self): + if self.stack.currentIndex() == 1: + self.stack.setCurrentIndex(0) + return if self.next_called and not confirm(_( 'All reviewed changes will be lost! Are you sure you want to Cancel?'), 'confirm-metadata-diff-dialog-cancel'):