mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Allow drag drop of images to change cover in book details window. Fixes #9226 (Drag/drop covers into book information dialog)
This commit is contained in:
parent
27412b5b5c
commit
89f2da224d
@ -6,7 +6,7 @@ __docformat__ = 'restructuredtext en'
|
|||||||
import textwrap, os, re
|
import textwrap, os, re
|
||||||
|
|
||||||
from PyQt4.Qt import QCoreApplication, SIGNAL, QModelIndex, QTimer, Qt, \
|
from PyQt4.Qt import QCoreApplication, SIGNAL, QModelIndex, QTimer, Qt, \
|
||||||
QDialog, QPixmap, QGraphicsScene, QIcon, QSize
|
QDialog, QPixmap, QIcon, QSize
|
||||||
|
|
||||||
from calibre.gui2.dialogs.book_info_ui import Ui_BookInfo
|
from calibre.gui2.dialogs.book_info_ui import Ui_BookInfo
|
||||||
from calibre.gui2 import dynamic, open_local_file, open_url
|
from calibre.gui2 import dynamic, open_local_file, open_url
|
||||||
@ -14,12 +14,14 @@ from calibre import fit_image
|
|||||||
from calibre.library.comments import comments_to_html
|
from calibre.library.comments import comments_to_html
|
||||||
from calibre.utils.icu import sort_key
|
from calibre.utils.icu import sort_key
|
||||||
|
|
||||||
|
|
||||||
class BookInfo(QDialog, Ui_BookInfo):
|
class BookInfo(QDialog, Ui_BookInfo):
|
||||||
|
|
||||||
def __init__(self, parent, view, row, view_func):
|
def __init__(self, parent, view, row, view_func):
|
||||||
QDialog.__init__(self, parent)
|
QDialog.__init__(self, parent)
|
||||||
Ui_BookInfo.__init__(self)
|
Ui_BookInfo.__init__(self)
|
||||||
self.setupUi(self)
|
self.setupUi(self)
|
||||||
|
self.gui = parent
|
||||||
self.cover_pixmap = None
|
self.cover_pixmap = None
|
||||||
self.comments.sizeHint = self.comments_size_hint
|
self.comments.sizeHint = self.comments_size_hint
|
||||||
self.comments.page().setLinkDelegationPolicy(self.comments.page().DelegateAllLinks)
|
self.comments.page().setLinkDelegationPolicy(self.comments.page().DelegateAllLinks)
|
||||||
@ -38,11 +40,22 @@ class BookInfo(QDialog, Ui_BookInfo):
|
|||||||
self.connect(self.text, SIGNAL('linkActivated(QString)'), self.open_book_path)
|
self.connect(self.text, SIGNAL('linkActivated(QString)'), self.open_book_path)
|
||||||
self.fit_cover.stateChanged.connect(self.toggle_cover_fit)
|
self.fit_cover.stateChanged.connect(self.toggle_cover_fit)
|
||||||
self.cover.resizeEvent = self.cover_view_resized
|
self.cover.resizeEvent = self.cover_view_resized
|
||||||
|
self.cover.cover_changed.connect(self.cover_changed)
|
||||||
|
|
||||||
desktop = QCoreApplication.instance().desktop()
|
desktop = QCoreApplication.instance().desktop()
|
||||||
screen_height = desktop.availableGeometry().height() - 100
|
screen_height = desktop.availableGeometry().height() - 100
|
||||||
self.resize(self.size().width(), screen_height)
|
self.resize(self.size().width(), screen_height)
|
||||||
|
|
||||||
|
def cover_changed(self, data):
|
||||||
|
if self.current_row is not None:
|
||||||
|
id_ = self.view.model().id(self.current_row)
|
||||||
|
self.view.model().db.set_cover(id_, data)
|
||||||
|
if self.gui.cover_flow:
|
||||||
|
self.gui.cover_flow.dataChanged()
|
||||||
|
ci = self.view.currentIndex()
|
||||||
|
if ci.isValid():
|
||||||
|
self.view.model().current_changed(ci, ci)
|
||||||
|
|
||||||
def link_clicked(self, url):
|
def link_clicked(self, url):
|
||||||
open_url(url)
|
open_url(url)
|
||||||
|
|
||||||
@ -83,7 +96,6 @@ class BookInfo(QDialog, Ui_BookInfo):
|
|||||||
if self.cover_pixmap is None:
|
if self.cover_pixmap is None:
|
||||||
return
|
return
|
||||||
self.setWindowIcon(QIcon(self.cover_pixmap))
|
self.setWindowIcon(QIcon(self.cover_pixmap))
|
||||||
self.scene = QGraphicsScene()
|
|
||||||
pixmap = self.cover_pixmap
|
pixmap = self.cover_pixmap
|
||||||
if self.fit_cover.isChecked():
|
if self.fit_cover.isChecked():
|
||||||
scaled, new_width, new_height = fit_image(pixmap.width(),
|
scaled, new_width, new_height = fit_image(pixmap.width(),
|
||||||
@ -92,8 +104,7 @@ class BookInfo(QDialog, Ui_BookInfo):
|
|||||||
if scaled:
|
if scaled:
|
||||||
pixmap = pixmap.scaled(new_width, new_height,
|
pixmap = pixmap.scaled(new_width, new_height,
|
||||||
Qt.KeepAspectRatio, Qt.SmoothTransformation)
|
Qt.KeepAspectRatio, Qt.SmoothTransformation)
|
||||||
self.scene.addPixmap(pixmap)
|
self.cover.set_pixmap(pixmap)
|
||||||
self.cover.setScene(self.scene)
|
|
||||||
|
|
||||||
def refresh(self, row):
|
def refresh(self, row):
|
||||||
if isinstance(row, QModelIndex):
|
if isinstance(row, QModelIndex):
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="0">
|
<item row="1" column="0">
|
||||||
<widget class="QGraphicsView" name="cover"/>
|
<widget class="CoverView" name="cover"/>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="1">
|
<item row="1" column="1">
|
||||||
<layout class="QVBoxLayout" name="verticalLayout">
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
@ -115,6 +115,11 @@
|
|||||||
<extends>QWidget</extends>
|
<extends>QWidget</extends>
|
||||||
<header>QtWebKit/QWebView</header>
|
<header>QtWebKit/QWebView</header>
|
||||||
</customwidget>
|
</customwidget>
|
||||||
|
<customwidget>
|
||||||
|
<class>CoverView</class>
|
||||||
|
<extends>QGraphicsView</extends>
|
||||||
|
<header>calibre/gui2/widgets.h</header>
|
||||||
|
</customwidget>
|
||||||
</customwidgets>
|
</customwidgets>
|
||||||
<resources>
|
<resources>
|
||||||
<include location="../../../../resources/images.qrc"/>
|
<include location="../../../../resources/images.qrc"/>
|
||||||
|
@ -11,9 +11,9 @@ from PyQt4.Qt import QIcon, QFont, QLabel, QListWidget, QAction, \
|
|||||||
QPixmap, QSplitterHandle, QToolButton, \
|
QPixmap, QSplitterHandle, QToolButton, \
|
||||||
QAbstractListModel, QVariant, Qt, SIGNAL, pyqtSignal, \
|
QAbstractListModel, QVariant, Qt, SIGNAL, pyqtSignal, \
|
||||||
QRegExp, QSettings, QSize, QSplitter, \
|
QRegExp, QSettings, QSize, QSplitter, \
|
||||||
QPainter, QLineEdit, QComboBox, QPen, \
|
QPainter, QLineEdit, QComboBox, QPen, QGraphicsScene, \
|
||||||
QMenu, QStringListModel, QCompleter, QStringList, \
|
QMenu, QStringListModel, QCompleter, QStringList, \
|
||||||
QTimer, QRect, QFontDatabase
|
QTimer, QRect, QFontDatabase, QGraphicsView
|
||||||
|
|
||||||
from calibre.gui2 import NONE, error_dialog, pixmap_to_data, gprefs
|
from calibre.gui2 import NONE, error_dialog, pixmap_to_data, gprefs
|
||||||
from calibre.gui2.filename_pattern_ui import Ui_Form
|
from calibre.gui2.filename_pattern_ui import Ui_Form
|
||||||
@ -181,22 +181,16 @@ class FormatList(QListWidget):
|
|||||||
else:
|
else:
|
||||||
return QListWidget.keyPressEvent(self, event)
|
return QListWidget.keyPressEvent(self, event)
|
||||||
|
|
||||||
|
class ImageDropMixin(object): # {{{
|
||||||
class ImageView(QWidget):
|
'''
|
||||||
|
Adds support for dropping images onto widgets and a contect menu for
|
||||||
BORDER_WIDTH = 1
|
copy/pasting images.
|
||||||
cover_changed = pyqtSignal(object)
|
'''
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
|
||||||
QWidget.__init__(self, parent)
|
|
||||||
self._pixmap = QPixmap(self)
|
|
||||||
self.setMinimumSize(QSize(150, 200))
|
|
||||||
self.setAcceptDrops(True)
|
|
||||||
self.draw_border = True
|
|
||||||
|
|
||||||
# Drag 'n drop {{{
|
|
||||||
DROPABBLE_EXTENSIONS = IMAGE_EXTENSIONS
|
DROPABBLE_EXTENSIONS = IMAGE_EXTENSIONS
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.setAcceptDrops(True)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def paths_from_event(cls, event):
|
def paths_from_event(cls, event):
|
||||||
'''
|
'''
|
||||||
@ -223,14 +217,58 @@ class ImageView(QWidget):
|
|||||||
pmap = QPixmap()
|
pmap = QPixmap()
|
||||||
pmap.load(path)
|
pmap.load(path)
|
||||||
if not pmap.isNull():
|
if not pmap.isNull():
|
||||||
self.setPixmap(pmap)
|
self.handle_image_drop(path, pmap)
|
||||||
event.accept()
|
event.accept()
|
||||||
self.cover_changed.emit(open(path, 'rb').read())
|
|
||||||
break
|
break
|
||||||
|
|
||||||
|
def handle_image_drop(self, path, pmap):
|
||||||
|
self.set_pixmap(pmap)
|
||||||
|
self.cover_changed.emit(open(path, 'rb').read())
|
||||||
|
|
||||||
def dragMoveEvent(self, event):
|
def dragMoveEvent(self, event):
|
||||||
event.acceptProposedAction()
|
event.acceptProposedAction()
|
||||||
# }}}
|
|
||||||
|
def get_pixmap(self):
|
||||||
|
return self.pixmap()
|
||||||
|
|
||||||
|
def set_pixmap(self, pmap):
|
||||||
|
self.setPixmap(pmap)
|
||||||
|
|
||||||
|
def contextMenuEvent(self, ev):
|
||||||
|
cm = QMenu(self)
|
||||||
|
copy = cm.addAction(_('Copy Image'))
|
||||||
|
paste = cm.addAction(_('Paste Image'))
|
||||||
|
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.get_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.set_pixmap(pmap)
|
||||||
|
self.cover_changed.emit(
|
||||||
|
pixmap_to_data(pmap))
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
class ImageView(QWidget, ImageDropMixin):
|
||||||
|
|
||||||
|
BORDER_WIDTH = 1
|
||||||
|
cover_changed = pyqtSignal(object)
|
||||||
|
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
QWidget.__init__(self, parent)
|
||||||
|
self._pixmap = QPixmap(self)
|
||||||
|
self.setMinimumSize(QSize(150, 200))
|
||||||
|
ImageDropMixin.__init__(self)
|
||||||
|
self.draw_border = True
|
||||||
|
|
||||||
def setPixmap(self, pixmap):
|
def setPixmap(self, pixmap):
|
||||||
if not isinstance(pixmap, QPixmap):
|
if not isinstance(pixmap, QPixmap):
|
||||||
@ -272,32 +310,23 @@ class ImageView(QWidget):
|
|||||||
p.drawRect(target)
|
p.drawRect(target)
|
||||||
p.end()
|
p.end()
|
||||||
|
|
||||||
|
class CoverView(QGraphicsView, ImageDropMixin):
|
||||||
|
|
||||||
# Clipboard copy/paste # {{{
|
cover_changed = pyqtSignal(object)
|
||||||
def contextMenuEvent(self, ev):
|
|
||||||
cm = QMenu(self)
|
|
||||||
copy = cm.addAction(_('Copy Image'))
|
|
||||||
paste = cm.addAction(_('Paste Image'))
|
|
||||||
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):
|
def __init__(self, *args, **kwargs):
|
||||||
QApplication.instance().clipboard().setPixmap(self.pixmap())
|
QGraphicsView.__init__(self, *args, **kwargs)
|
||||||
|
ImageDropMixin.__init__(self)
|
||||||
|
|
||||||
def paste_from_clipboard(self):
|
def get_pixmap(self):
|
||||||
cb = QApplication.instance().clipboard()
|
for item in self.scene().items():
|
||||||
pmap = cb.pixmap()
|
if hasattr(item, 'pixmap'):
|
||||||
if pmap.isNull() and cb.supportsSelection():
|
return item.pixmap()
|
||||||
pmap = cb.pixmap(cb.Selection)
|
|
||||||
if not pmap.isNull():
|
|
||||||
self.setPixmap(pmap)
|
|
||||||
self.cover_changed.emit(
|
|
||||||
pixmap_to_data(pmap))
|
|
||||||
# }}}
|
|
||||||
|
|
||||||
|
def set_pixmap(self, pmap):
|
||||||
|
self.scene = QGraphicsScene()
|
||||||
|
self.scene.addPixmap(pmap)
|
||||||
|
self.setScene(self.scene)
|
||||||
|
|
||||||
class FontFamilyModel(QAbstractListModel):
|
class FontFamilyModel(QAbstractListModel):
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user