def files_dropped_on_book(self, event, paths, cid=None, do_confirm=True): accept = False if self.gui.current_view() is not self.gui.library_view: return db = self.gui.library_view.model().db cover_changed = False current_idx = self.gui.library_view.currentIndex() if cid is None: if not current_idx.isValid(): return cid = db.id(current_idx.row()) if cid is None else cid formats = [] from calibre.gui2.dnd import image_extensions image_exts = set(image_extensions()) - set( tweaks['cover_drop_exclude']) if iswindows: from calibre.gui2.add import resolve_windows_links paths = list( resolve_windows_links(paths, hwnd=int(self.gui.effectiveWinId()))) for path in paths: ext = os.path.splitext(path)[1].lower() if ext: ext = ext[1:] if ext in image_exts: pmap = QPixmap() pmap.load(path) if not pmap.isNull(): accept = True db.set_cover(cid, pmap) cover_changed = True else: formats.append((ext, path)) accept = True if accept and event is not None: event.accept() add_as_book = False if do_confirm and formats: ok, add_as_book = confirm(_( 'You have dropped some files onto the book <b>%s</b>. This will' ' add or replace the files for this book. Do you want to proceed?' ) % db.title(cid, index_is_id=True), 'confirm_drop_on_book', parent=self.gui, extra_button=ngettext( 'Add as new book', 'Add as new books', len(formats))) if ok and add_as_book: add_as_book = [path for ext, path in formats] if not ok or add_as_book: formats = [] for ext, path in formats: db.add_format_with_hooks(cid, ext, path, index_is_id=True) if current_idx.isValid(): self.gui.library_view.model().current_changed( current_idx, current_idx) if cover_changed: self.gui.refresh_cover_browser() if add_as_book: self.files_dropped(add_as_book)
def dropEvent(self, event): event.setDropAction(Qt.DropAction.CopyAction) md = event.mimeData() pmap, data = dnd_get_local_image_and_pixmap(md) if pmap is not None: self.handle_image_drop(pmap, data) return x, y = dnd_get_image(md) if x is not None: # We have an image, set cover event.accept() if y is None: # Local image self.handle_image_drop(x) else: # Remote files, use the first file d = DownloadDialog(x, y, self) d.start_download() if d.err is None: pmap = QPixmap() with lopen(d.fpath, 'rb') as f: data = f.read() pmap.loadFromData(data) if not pmap.isNull(): self.handle_image_drop(pmap, data=data)
def load_icon_resource_as_pixmap(icon_resource, size=ICON_SIZE): if not icon_resource: return parts = tuple(filter(None, re.split(r',([-0-9]+$)', icon_resource))) if len(parts) != 2: return module, index = parts index = int(index) if module.startswith('"') and module.endswith('"'): module = split_commandline(module)[0] hmodule = winutil.load_library( module, winutil.LOAD_LIBRARY_AS_DATAFILE | winutil.LOAD_LIBRARY_AS_IMAGE_RESOURCE) icons = winutil.load_icons(hmodule, index) pixmaps = [] must_use_qt() for icon_data, icon_handle in icons: pixmap = QPixmap() pixmap.loadFromData(icon_data) if pixmap.isNull() and bool(icon_handle): pixmap = hicon_to_pixmap(icon_handle) if pixmap.isNull(): continue pixmaps.append(pixmap) if not pixmaps: return def area(p): return p.width() * p.height() pixmaps.sort(key=area) q = size * size for pmap in pixmaps: if area(pmap) >= q: if area(pmap) == q: return pmap return pmap.scaled( size, size, aspectRatioMode=Qt.AspectRatioMode.KeepAspectRatio, transformMode=Qt.TransformationMode.SmoothTransformation) return pixmaps[-1].scaled( size, size, aspectRatioMode=Qt.AspectRatioMode.KeepAspectRatio, transformMode=Qt.TransformationMode.SmoothTransformation)
def load(data): p = QPixmap() p.loadFromData(as_bytes(data)) try: dpr = self.devicePixelRatioF() except AttributeError: dpr = self.devicePixelRatio() p.setDevicePixelRatio(dpr) if data and p.isNull(): p = self.failed_img return p
def dnd_get_local_image_and_pixmap(md, image_exts=None): if md.hasImage(): for x in md.formats(): x = str(x) if x.startswith('image/'): cdata = bytes(md.data(x)) pmap = QPixmap() pmap.loadFromData(cdata) if not pmap.isNull(): return pmap, cdata if md.hasFormat('application/octet-stream'): cdata = bytes(md.data('application/octet-stream')) pmap = QPixmap() pmap.loadFromData(cdata) if not pmap.isNull(): return pmap, cdata if image_exts is None: image_exts = image_extensions() # No image, look for an URL pointing to an image urls = urls_from_md(md) paths = [path_from_qurl(u) for u in urls] # Look for a local file images = [xi for xi in paths if extension(xi) in image_exts] images = [xi for xi in images if os.path.exists(xi)] for path in images: try: with open(path, 'rb') as f: cdata = f.read() except Exception: continue p = QPixmap() p.loadFromData(cdata) if not p.isNull(): return p, cdata return None, None
def from_mi(self, mi): p = getattr(mi, 'cover', None) if p and os.path.exists(p): pmap = QPixmap() with open(p, 'rb') as f: pmap.loadFromData(f.read()) if not pmap.isNull(): self.pixmap = pmap self.update() self.changed.emit() return cd = getattr(mi, 'cover_data', (None, None)) if cd and cd[1]: pmap = QPixmap() pmap.loadFromData(cd[1]) if not pmap.isNull(): self.pixmap = pmap self.update() self.changed.emit() return self.pixmap = None self.update() self.changed.emit()
def update_cover(self, pmap=None, cdata=None): if pmap is None: pmap = QPixmap() pmap.loadFromData(cdata) if pmap.isNull(): return if pmap.hasAlphaChannel(): pmap = QPixmap.fromImage(blend_image(image_from_x(pmap))) self.pixmap = pmap self.do_layout() self.update() self.update_tooltip(getattr(self.parent(), 'current_path', '')) if not config['disable_animations']: self.animation.start() id_ = self.data.get('id', None) if id_ is not None: self.cover_changed.emit(id_, cdata or pixmap_to_data(pmap))
def entry_to_icon_text(entry, only_text=False): if only_text: return entry.get('name', entry.get('Name')) or _('Unknown') data = entry.get('icon_data') if isinstance(data, str): with suppress(Exception): from base64 import standard_b64decode data = bytearray(standard_b64decode(data)) if not isinstance(data, (bytearray, bytes)): icon = QIcon(I('blank.png')) else: pmap = QPixmap() pmap.loadFromData(bytes(data)) if pmap.isNull(): icon = QIcon(I('blank.png')) else: icon = QIcon(pmap) return icon, entry.get('name', entry.get('Name')) or _('Unknown')
def initialize_metadata_options(self): self.initialize_combos() self.author.editTextChanged.connect(self.deduce_author_sort) mi = self.db.get_metadata(self.book_id, index_is_id=True) self.title.setText(mi.title), self.title.home(False) self.publisher.show_initial_value(mi.publisher if mi.publisher else '') self.publisher.home(False) self.author_sort.setText(mi.author_sort if mi.author_sort else '') self.author_sort.home(False) self.tags.setText(', '.join(mi.tags if mi.tags else [])) self.tags.update_items_cache(self.db.new_api.all_field_names('tags')) self.tags.home(False) self.comment.html = comments_to_html( mi.comments) if mi.comments else '' self.series.show_initial_value(mi.series if mi.series else '') self.series.home(False) if mi.series_index is not None: try: self.series_index.setValue(mi.series_index) except: self.series_index.setValue(1.0) cover = self.db.cover(self.book_id, index_is_id=True) if cover: pm = QPixmap() pm.loadFromData(cover) if not pm.isNull(): pm.setDevicePixelRatio( getattr(self, 'devicePixelRatioF', self.devicePixelRatio)()) self.cover.setPixmap(pm) self.cover_data = cover self.set_cover_tooltip(pm) else: pm = QPixmap(I('default_cover.png')) pm.setDevicePixelRatio( getattr(self, 'devicePixelRatioF', self.devicePixelRatio)()) self.cover.setPixmap(pm) self.cover.setToolTip(_('This book has no cover')) for x in ('author', 'series', 'publisher'): x = getattr(self, x) x.lineEdit().deselect() self.series_changed()
def select_cover(self): files = choose_images( self, 'change cover dialog', _('Choose cover for ') + unicode_type(self.title.text())) if not files: return _file = files[0] if _file: _file = os.path.abspath(_file) if not os.access(_file, os.R_OK): d = error_dialog( self.parent(), _('Cannot read'), _('You do not have permission to read the file: ') + _file) d.exec_() return cover = None try: with open(_file, "rb") as f: cover = f.read() except IOError as e: d = error_dialog( self.parent(), _('Error reading file'), _("<p>There was an error reading from file: <br /><b>") + _file + "</b></p><br />" + unicode_type(e)) d.exec_() if cover: pix = QPixmap() pix.loadFromData(cover) pix.setDevicePixelRatio( getattr(self, 'devicePixelRatioF', self.devicePixelRatio)()) if pix.isNull(): d = error_dialog(self.parent(), _('Error reading file'), _file + _(" is not a valid picture")) d.exec_() else: self.cover_path.setText(_file) self.set_cover_tooltip(pix) self.cover.setPixmap(pix) self.cover_changed = True self.cpixmap = pix self.cover_data = cover
class ImagePopup(object): def __init__(self, parent): self.current_img = QPixmap() self.current_url = QUrl() self.parent = parent self.dialogs = [] def __call__(self): if self.current_img.isNull(): return d = ImageView(self.parent, self.current_img, self.current_url) self.dialogs.append(d) d.finished.connect(self.cleanup, type=Qt.ConnectionType.QueuedConnection) d() def cleanup(self): for d in tuple(self.dialogs): if not d.isVisible(): self.dialogs.remove(d)
def data(self, role): if role == Qt.ItemDataRole.DisplayRole: return (self.title) if role == Qt.ItemDataRole.DecorationRole: if self.icon is None: icon = '%s.png' % self.urn[8:] p = QPixmap() if icon in self.favicons: try: with zipfile.ZipFile(self.zf, 'r') as zf: p.loadFromData(zf.read(self.favicons[icon])) except Exception: pass if not p.isNull(): self.icon = (QIcon(p)) else: self.icon = self.default_icon return self.icon if role == Qt.ItemDataRole.UserRole: return self.urn
def bd_open_cover_with(self, book_id, entry): cpath = self.current_db.new_api.format_abspath(book_id, '__COVER_INTERNAL__') if cpath: if entry is None: pm = QPixmap() pm.load(cpath) pm.setDevicePixelRatio(self.devicePixelRatioF()) if pm.isNull(): open_local_file(cpath) else: from calibre.gui2.image_popup import ImageView iv = ImageView(QApplication.instance().focusWindow(), pm, QUrl.fromLocalFile(cpath), geom_name='book_details_image_view') iv(use_exec=True) return from calibre.gui2.open_with import run_program run_program(entry, cpath, self)
class CoverView(QWidget): def __init__(self, parent=None): QWidget.__init__(self, parent) self.current_pixmap_size = QSize(0, 0) self.pixmap = QPixmap() self.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding) def set_pixmap(self, data): self.pixmap.loadFromData(data) self.current_pixmap_size = self.pixmap.size() self.update() def paintEvent(self, event): if self.pixmap.isNull(): return canvas_size = self.rect() width = self.current_pixmap_size.width() extrax = canvas_size.width() - width if extrax < 0: extrax = 0 x = int(extrax / 2.) height = self.current_pixmap_size.height() extray = canvas_size.height() - height if extray < 0: extray = 0 y = int(extray / 2.) target = QRect(x, y, min(canvas_size.width(), width), min(canvas_size.height(), height)) p = QPainter(self) p.setRenderHints(QPainter.RenderHint.Antialiasing | QPainter.RenderHint.SmoothPixmapTransform) p.drawPixmap( target, self.pixmap.scaled(target.size(), Qt.AspectRatioMode.KeepAspectRatio, Qt.TransformationMode.SmoothTransformation)) p.end() def sizeHint(self): return QSize(300, 400)
def set_color(self): r, g, b = gprefs['cover_grid_color'] tex = gprefs['cover_grid_texture'] pal = self.palette() pal.setColor(QPalette.ColorRole.Base, QColor(r, g, b)) self.setPalette(pal) ss = '' if tex: from calibre.gui2.preferences.texture_chooser import texture_path path = texture_path(tex) if path: path = os.path.abspath(path).replace(os.sep, '/') ss += 'background-image: url({});'.format(path) ss += 'background-attachment: fixed;' pm = QPixmap(path) if not pm.isNull(): val = pm.scaled(1, 1).toImage().pixel(0, 0) r, g, b = qRed(val), qGreen(val), qBlue(val) dark = max(r, g, b) < 115 col = '#eee' if dark else '#111' ss += 'color: {};'.format(col) self.delegate.highlight_color = QColor(col) self.setStyleSheet('QListView {{ {} }}'.format(ss))
class ImageView(QWidget, ImageDropMixin): BORDER_WIDTH = 1 cover_changed = pyqtSignal(object) def __init__(self, parent=None, show_size_pref_name=None, default_show_size=False): QWidget.__init__(self, parent) self.show_size_pref_name = ( 'show_size_on_cover_' + show_size_pref_name) if show_size_pref_name else None self._pixmap = QPixmap() self.setMinimumSize(QSize(150, 200)) ImageDropMixin.__init__(self) self.draw_border = True self.show_size = False if self.show_size_pref_name: self.show_size = gprefs.get(self.show_size_pref_name, default_show_size) def setPixmap(self, pixmap): if not isinstance(pixmap, QPixmap): raise TypeError('Must use a QPixmap') self._pixmap = pixmap self.updateGeometry() self.update() def build_context_menu(self): m = ImageDropMixin.build_context_menu(self) if self.show_size_pref_name: text = _('Hide size in corner') if self.show_size else _( 'Show size in corner') m.addAction(text, self.toggle_show_size) return m def toggle_show_size(self): self.show_size ^= True if self.show_size_pref_name: gprefs[self.show_size_pref_name] = self.show_size self.update() def pixmap(self): return self._pixmap def sizeHint(self): if self._pixmap.isNull(): return self.minimumSize() return self._pixmap.size() def paintEvent(self, event): QWidget.paintEvent(self, event) pmap = self._pixmap if pmap.isNull(): return w, h = pmap.width(), pmap.height() ow, oh = w, h cw, ch = self.rect().width(), self.rect().height() scaled, nw, nh = fit_image(w, h, cw, ch) if scaled: pmap = pmap.scaled(int(nw * pmap.devicePixelRatio()), int(nh * pmap.devicePixelRatio()), Qt.AspectRatioMode.IgnoreAspectRatio, Qt.TransformationMode.SmoothTransformation) w, h = int(pmap.width() / pmap.devicePixelRatio()), int( pmap.height() / pmap.devicePixelRatio()) x = int(abs(cw - w) / 2) y = int(abs(ch - h) / 2) target = QRect(x, y, w, h) p = QPainter(self) p.setRenderHints(QPainter.RenderHint.Antialiasing | QPainter.RenderHint.SmoothPixmapTransform) p.drawPixmap(target, pmap) if self.draw_border: pen = QPen() pen.setWidth(self.BORDER_WIDTH) p.setPen(pen) p.drawRect(target) if self.show_size: draw_size(p, target, ow, oh) p.end()