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 paintEvent(self, event): 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, width, height) p = QPainter(self) p.setRenderHints(QPainter.RenderHint.Antialiasing | QPainter.RenderHint.SmoothPixmapTransform) try: dpr = self.devicePixelRatioF() except AttributeError: dpr = self.devicePixelRatio() spmap = self.pixmap.scaled(target.size() * dpr, Qt.AspectRatioMode.KeepAspectRatio, Qt.TransformationMode.SmoothTransformation) spmap.setDevicePixelRatio(dpr) p.drawPixmap(target, spmap) if gprefs['bd_overlay_cover_size']: sztgt = target.adjusted(0, 0, 0, -4) f = p.font() f.setBold(True) p.setFont(f) sz = '\u00a0%d x %d\u00a0'%(self.pixmap.width(), self.pixmap.height()) flags = Qt.AlignmentFlag.AlignBottom|Qt.AlignmentFlag.AlignRight|Qt.TextFlag.TextSingleLine szrect = p.boundingRect(sztgt, flags, sz) p.fillRect(szrect.adjusted(0, 0, 0, 4), QColor(0, 0, 0, 200)) p.setPen(QPen(QColor(255,255,255))) p.drawText(sztgt, flags, sz) p.end()
def resizeEvent(self, ev): PlainTextEdit.resizeEvent(self, ev) cr = self.contentsRect() if self.right: self.line_number_area.setGeometry(QRect(cr.right() - self.line_number_area_width(), cr.top(), cr.right(), cr.height())) else: self.line_number_area.setGeometry(QRect(cr.left(), cr.top(), self.line_number_area_width(), cr.height())) self.resized.emit()
def do_layout(self): fm = self.fontMetrics() bounding_rect = lambda text: fm.boundingRect(0, 0, 10000, 10000, Cell. FLAGS, text) line_spacing = 2 side_margin = Cell.SIDE_MARGIN self.rows = [] ypos = line_spacing + (1 if self.is_first else 0) if 'href' in self.data: name = self.data['href'] if isinstance(name, list): name = self.html_name br1 = bounding_rect(name) sel = self.data['selector'] or '' if self.data['type'] == 'inline': sel = 'style=""' br2 = bounding_rect(sel) self.hyperlink_rect = QRect(side_margin, ypos, br1.width(), br1.height()) self.rows.append([ Cell(name, self.hyperlink_rect, color_role=QPalette.ColorRole.Link), Cell(sel, QRect(br1.right() + side_margin, ypos, br2.width(), br2.height()), right_align=True) ]) ypos += max(br1.height(), br2.height()) + 2 * line_spacing self.lines_for_copy.append(name + ' ' + sel) for prop in self.data['properties']: text = prop.name + ':\xa0' br1 = bounding_rect(text) vtext = prop.value + '\xa0' + ('!' if prop.important else '') + prop.important br2 = bounding_rect(vtext) self.rows.append([ Cell(text, QRect(side_margin, ypos, br1.width(), br1.height()), color_role=QPalette.ColorRole.LinkVisited, is_overriden=prop.is_overriden), Cell(vtext, QRect(br1.right() + side_margin, ypos, br2.width(), br2.height()), swatch=prop.color, is_overriden=prop.is_overriden) ]) self.lines_for_copy.append(text + vtext) if prop.is_overriden: self.lines_for_copy[-1] += ' [overridden]' ypos += max(br1.height(), br2.height()) + line_spacing self.lines_for_copy.append('--------------------------\n') self.height_hint = ypos + line_spacing self.width_hint = max(row[-1].rect.right() + side_margin for row in self.rows) if self.rows else 0
def paint(self, painter, option, index): QStyledItemDelegate.paint(self, painter, option, index) style = QApplication.style() waiting = self.animator.is_running() and bool(index.data(Qt.ItemDataRole.UserRole)) if waiting: rect = QRect(0, 0, self.spinner_width, self.spinner_width) rect.moveCenter(option.rect.center()) self.animator.draw(painter, rect, self.color) else: # Ensure the cover is rendered over any selection rect style.drawItemPixmap(painter, option.rect, Qt.AlignmentFlag.AlignTop|Qt.AlignmentFlag.AlignHCenter, QPixmap(index.data(Qt.ItemDataRole.DecorationRole)))
def grid_bounds(self): #return QRect(-100, -100, 200, 200) it = iter(self.grid) try: minx, miny = next(it) maxx = minx maxy = miny except StopIteration: return QRect() for x, y in it: if x < minx: minx = x elif x > maxx: maxx = x if y < miny: miny = y elif y > maxy: maxy = y return QRect(minx, miny, maxx-minx+1, maxy-miny+1)
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()
def generate_cover(mi, prefs=None, as_qimage=False): init_environment() prefs = prefs or cprefs prefs = {k: prefs.get(k) for k in cprefs.defaults} prefs = Prefs(**prefs) color_theme = random.choice(load_color_themes(prefs)) style = random.choice(load_styles(prefs))(color_theme, prefs) title, subtitle, footer = format_text(mi, prefs) img = QImage(prefs.cover_width, prefs.cover_height, QImage.Format.Format_ARGB32) title_block, subtitle_block, footer_block = layout_text( prefs, img, title, subtitle, footer, img.height() // 3, style) p = QPainter(img) rect = QRect(0, 0, img.width(), img.height()) colors = style(p, rect, color_theme, title_block, subtitle_block, footer_block) for block, color in zip((title_block, subtitle_block, footer_block), colors): p.setPen(color) block.draw(p) p.end() img.setText('Generated cover', '{} {}'.format(__appname__, __version__)) if as_qimage: return img return pixmap_to_data(img)
def paint(self, painter, option, index): QStyledItemDelegate.paint( self, painter, option, empty_index) # draw the hover and selection highlights name = str(index.data(Qt.ItemDataRole.DisplayRole) or '') cover = self.cover_cache.get(name, None) if cover is None: cover = self.cover_cache[name] = QPixmap() try: raw = current_container().raw_data(name, decode=False) except: pass else: try: dpr = painter.device().devicePixelRatioF() except AttributeError: dpr = painter.device().devicePixelRatio() cover.loadFromData(raw) cover.setDevicePixelRatio(dpr) if not cover.isNull(): scaled, width, height = fit_image(cover.width(), cover.height(), self.cover_size.width(), self.cover_size.height()) if scaled: cover = self.cover_cache[name] = cover.scaled( int(dpr * width), int(dpr * height), transformMode=Qt.TransformationMode. SmoothTransformation) painter.save() try: rect = option.rect rect.adjust(self.MARGIN, self.MARGIN, -self.MARGIN, -self.MARGIN) trect = QRect(rect) rect.setBottom(rect.bottom() - self.title_height) if not cover.isNull(): dx = max( 0, int((rect.width() - int(cover.width() / cover.devicePixelRatio())) / 2.0)) dy = max( 0, rect.height() - int(cover.height() / cover.devicePixelRatio())) rect.adjust(dx, dy, -dx, 0) painter.drawPixmap(rect, cover) rect = trect rect.setTop(rect.bottom() - self.title_height + 5) painter.setRenderHint(QPainter.RenderHint.TextAntialiasing, True) metrics = painter.fontMetrics() painter.drawText( rect, Qt.AlignmentFlag.AlignCenter | Qt.TextFlag.TextSingleLine, metrics.elidedText(name, Qt.TextElideMode.ElideLeft, rect.width())) finally: painter.restore()
def __init__(self, parent=None, icon_size=18): super().__init__(parent) self.icon_size = QSize(icon_size, icon_size) self.setSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Preferred) self.items = [] self.button_map = {} self.applied_geometry = QRect(0, 0, 0, 0)
def __call__(self, painter, rect, color_theme, title_block, subtitle_block, footer_block): painter.fillRect(rect, self.color1) y = rect.height() - rect.height() // 3 r = QRect(rect) r.setBottom(y) painter.fillRect(rect, self.color1) r = QRect(rect) r.setTop(y) painter.fillRect(r, self.color2) return self.ccolor1, self.ccolor1, self.ccolor2
def calibre_cover2(title, author_string='', series_string='', prefs=None, as_qimage=False, logo_path=None): init_environment() title, subtitle, footer = '<b>' + escape_formatting( title), '<i>' + escape_formatting( series_string), '<b>' + escape_formatting(author_string) prefs = prefs or cprefs prefs = {k: prefs.get(k) for k in cprefs.defaults} scale = 800. / prefs['cover_height'] scale_cover(prefs, scale) prefs = Prefs(**prefs) img = QImage(prefs.cover_width, prefs.cover_height, QImage.Format.Format_ARGB32) img.fill(Qt.GlobalColor.white) # colors = to_theme('ffffff ffffff 000000 000000') color_theme = theme_to_colors(fallback_colors) class CalibeLogoStyle(Style): NAME = GUI_NAME = 'calibre' def __call__(self, painter, rect, color_theme, title_block, subtitle_block, footer_block): top = title_block.position.y + 10 extra_spacing = subtitle_block.line_spacing // 2 if subtitle_block.line_spacing else title_block.line_spacing // 3 height = title_block.height + subtitle_block.height + extra_spacing + title_block.leading top += height + 25 bottom = footer_block.position.y - 50 logo = QImage(logo_path or I('library.png')) pwidth, pheight = rect.width(), bottom - top scaled, width, height = fit_image(logo.width(), logo.height(), pwidth, pheight) x, y = (pwidth - width) // 2, (pheight - height) // 2 rect = QRect(x, top + y, width, height) painter.setRenderHint(QPainter.RenderHint.SmoothPixmapTransform) painter.drawImage(rect, logo) return self.ccolor1, self.ccolor1, self.ccolor1 style = CalibeLogoStyle(color_theme, prefs) title_block, subtitle_block, footer_block = layout_text( prefs, img, title, subtitle, footer, img.height() // 3, style) p = QPainter(img) rect = QRect(0, 0, img.width(), img.height()) colors = style(p, rect, color_theme, title_block, subtitle_block, footer_block) for block, color in zip((title_block, subtitle_block, footer_block), colors): p.setPen(color) block.draw(p) p.end() img.setText('Generated cover', '{} {}'.format(__appname__, __version__)) if as_qimage: return img return pixmap_to_data(img)
def paintEvent(self, event): w = self.viewport().rect().width() painter = QPainter(self.viewport()) painter.setClipRect(event.rect()) painter.setRenderHint(QPainter.RenderHint.SmoothPixmapTransform, True) floor = event.rect().bottom() ceiling = event.rect().top() fv = self.firstVisibleBlock().blockNumber() origin = self.contentOffset() doc = self.document() lines = [] for num, text in self.headers: top, bot = num, num + 3 if bot < fv: continue y_top = self.blockBoundingGeometry(doc.findBlockByNumber(top)).translated(origin).y() y_bot = self.blockBoundingGeometry(doc.findBlockByNumber(bot)).translated(origin).y() if max(y_top, y_bot) < ceiling: continue if min(y_top, y_bot) > floor: break painter.setFont(self.heading_font) br = painter.drawText(3, y_top, w, y_bot - y_top - 5, Qt.TextFlag.TextSingleLine, text) painter.setPen(QPen(self.palette().text(), 2)) painter.drawLine(0, br.bottom()+3, w, br.bottom()+3) for top, bot, kind in self.changes: if bot < fv: continue y_top = self.blockBoundingGeometry(doc.findBlockByNumber(top)).translated(origin).y() y_bot = self.blockBoundingGeometry(doc.findBlockByNumber(bot)).translated(origin).y() if max(y_top, y_bot) < ceiling: continue if min(y_top, y_bot) > floor: break if y_top != y_bot: painter.fillRect(0, y_top, w, y_bot - y_top, self.diff_backgrounds[kind]) lines.append((y_top, y_bot, kind)) if top in self.images: img, maxw = self.images[top][:2] if bot > top + 1 and not img.isNull(): y_top = self.blockBoundingGeometry(doc.findBlockByNumber(top+1)).translated(origin).y() + 3 y_bot -= 3 scaled, imgw, imgh = fit_image(int(img.width()/img.devicePixelRatio()), int(img.height()/img.devicePixelRatio()), w - 3, y_bot - y_top) painter.drawPixmap(QRect(3, y_top, imgw, imgh), img) painter.end() PlainTextEdit.paintEvent(self, event) painter = QPainter(self.viewport()) painter.setClipRect(event.rect()) for top, bottom, kind in sorted(lines, key=lambda t_b_k:{'replace':0}.get(t_b_k[2], 1)): painter.setPen(QPen(self.diff_foregrounds[kind], 1)) painter.drawLine(0, top, w, top) painter.drawLine(0, bottom - 1, w, bottom - 1)
def do_layout(self, rect): if len(self._children) != 2: return left, top, right, bottom = self.getContentsMargins() r = rect.adjusted(+left, +top, -right, -bottom) x = r.x() y = r.y() cover, details = self._children if self.vertical: ch = self.cover_height(r) cover.setGeometry(QRect(x, y, r.width(), ch)) cover.widget().do_layout() y += ch + 5 details.setGeometry(QRect(x, y, r.width(), r.height()-ch-5)) else: cw = self.cover_width(r) cover.setGeometry(QRect(x, y, cw, r.height())) cover.widget().do_layout() x += cw + 5 details.setGeometry(QRect(x, y, r.width() - cw - 5, r.height()))
def paintEvent(self, ev): br = ev.region().boundingRect() QWidget.paintEvent(self, ev) p = QPainter(self) p.setClipRect(br) f = p.font() f.setBold(True) f.setPointSize(20) p.setFont(f) p.setPen(Qt.PenStyle.SolidLine) r = QRect(0, self.dummy.geometry().top() + 10, self.geometry().width(), 150) p.drawText(r, Qt.AlignmentFlag.AlignHCenter | Qt.AlignmentFlag.AlignTop | Qt.TextFlag.TextSingleLine, self.text) p.end()
def __call__(self, painter, rect, color_theme, title_block, subtitle_block, footer_block): top = title_block.position.y + 10 extra_spacing = subtitle_block.line_spacing // 2 if subtitle_block.line_spacing else title_block.line_spacing // 3 height = title_block.height + subtitle_block.height + extra_spacing + title_block.leading top += height + 25 bottom = footer_block.position.y - 50 logo = QImage(logo_path or I('library.png')) pwidth, pheight = rect.width(), bottom - top scaled, width, height = fit_image(logo.width(), logo.height(), pwidth, pheight) x, y = (pwidth - width) // 2, (pheight - height) // 2 rect = QRect(x, top + y, width, height) painter.setRenderHint(QPainter.RenderHint.SmoothPixmapTransform) painter.drawImage(rect, logo) return self.ccolor1, self.ccolor1, self.ccolor1
def do_layout(self, rect, apply_geometry=False): left, top, right, bottom = self.getContentsMargins() erect = rect.adjusted(left, top, -right, -bottom) x, y = erect.x(), erect.y() line_height = 0 def layout_spacing(wid, horizontal=True): ans = self.smart_spacing(horizontal) if ans != -1: return ans if wid is None: return 0 return wid.style().layoutSpacing( QSizePolicy.ControlType.PushButton, QSizePolicy.ControlType.PushButton, Qt.Orientation.Horizontal if horizontal else Qt.Orientation.Vertical) lines, current_line = [], [] gmap = {} for item in self.items: isz, wid = item.sizeHint(), item.widget() hs, vs = layout_spacing(wid), layout_spacing(wid, False) next_x = x + isz.width() + hs if next_x - hs > erect.right() and line_height > 0: x = erect.x() y = y + line_height + vs next_x = x + isz.width() + hs lines.append((line_height, current_line)) current_line = [] line_height = 0 if apply_geometry: gmap[item] = x, y, isz x = next_x line_height = max(line_height, isz.height()) current_line.append((item, isz.height())) lines.append((line_height, current_line)) if apply_geometry: for line_height, items in lines: for item, item_height in items: x, wy, isz = gmap[item] if item_height < line_height: wy += (line_height - item_height) // 2 item.setGeometry(QRect(QPoint(x, wy), isz)) return y + line_height - rect.y() + bottom
def draw(self, painter, width, palette): flags = self.FLAGS | (Qt.AlignmentFlag.AlignRight if self.right_align else Qt.AlignmentFlag.AlignLeft) rect = QRect(self.rect) if self.right_align: rect.setRight(width - self.SIDE_MARGIN) painter.setPen(palette.color(self.color_role) if self.override_color is None else self.override_color) br = painter.drawText(rect, flags, self.text) if self.swatch is not None: r = QRect(br.right() + self.SIDE_MARGIN // 2, br.top() + 2, br.height() - 4, br.height() - 4) painter.fillRect(r, self.swatch) br.setRight(r.right()) if self.is_overriden: painter.setPen(palette.color(QPalette.ColorRole.WindowText)) painter.drawLine(br.left(), br.top() + br.height() // 2, br.right(), br.top() + br.height() // 2)
def __call__(self, painter, rect, color_theme, title_block, subtitle_block, footer_block): painter.fillRect(rect, self.color1) r = QRect(0, int(title_block.position.y), rect.width(), title_block.height + subtitle_block.height + subtitle_block.line_spacing // 2 + title_block.leading) painter.save() p = QPainterPath() p.addRoundedRect(QRectF(r), 10, 10 * r.width()/r.height(), Qt.SizeMode.RelativeSize) painter.setClipPath(p) painter.setRenderHint(QPainter.RenderHint.Antialiasing) painter.fillRect(r, self.color2) painter.restore() r = QRect(0, 0, int(title_block.position.x), rect.height()) painter.fillRect(r, self.color2) return self.ccolor2, self.ccolor2, self.ccolor1
def paintEvent(self, ev): painter = QStylePainter(self) opt = QStyleOptionToolButton() self.initStyleOption(opt) text = opt.text opt.text = '' opt.icon = QIcon() s = painter.style() painter.drawComplexControl(QStyle.ComplexControl.CC_ToolButton, opt) if s.styleHint(QStyle.StyleHint.SH_UnderlineShortcut, opt, self): flags = self.text_flags | Qt.TextFlag.TextShowMnemonic else: flags = self.text_flags | Qt.TextFlag.TextHideMnemonic fw = s.pixelMetric(QStyle.PixelMetric.PM_DefaultFrameWidth, opt, self) opt.rect.adjust(fw, fw, -fw, -fw) w = opt.iconSize.width() text_rect = opt.rect.adjusted(w, 0, 0, 0) painter.drawItemText(text_rect, flags, opt.palette, self.isEnabled(), text) fm = QFontMetrics(opt.font) text_rect = s.itemTextRect(fm, text_rect, flags, self.isEnabled(), text) left = text_rect.left() - w - 4 pixmap_rect = QRect(left, opt.rect.top(), opt.iconSize.width(), opt.rect.height()) painter.drawItemPixmap(pixmap_rect, Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter, self.icon().pixmap(opt.iconSize))
def do_layout(self, rect, apply_geometry=False): x, y = rect.x(), rect.y() line_height = 0 def layout_spacing(wid, horizontal=True): ans = self.smart_spacing(horizontal) if ans != -1: return ans return wid.style().layoutSpacing( QSizePolicy.ControlType.ToolButton, QSizePolicy.ControlType.ToolButton, Qt.Orientation.Horizontal if horizontal else Qt.Orientation.Vertical) lines, current_line = [], [] gmap = {} if apply_geometry: for item in self.items: if isinstance(item, Separator): item.setGeometry(0, 0, 0, 0) def commit_line(): while current_line and isinstance(current_line[-1], Separator): current_line.pop() if current_line: lines.append((line_height, current_line)) for wid in self.items: if not wid.isVisible() or (not current_line and isinstance(wid, Separator)): continue isz = wid.sizeHint() hs, vs = layout_spacing(wid), layout_spacing(wid, False) next_x = x + isz.width() + hs if next_x - hs > rect.right() and line_height > 0: if isinstance(wid, Separator): continue x = rect.x() y = y + line_height + vs next_x = x + isz.width() + hs commit_line() current_line = [] line_height = 0 if apply_geometry: gmap[wid] = x, y, isz x = next_x line_height = max(line_height, isz.height()) current_line.append(wid) commit_line() if apply_geometry: self.applied_geometry = rect for line_height, items in lines: for wid in items: x, wy, isz = gmap[wid] if isz.height() < line_height: wy += (line_height - isz.height()) // 2 if wid.isVisible(): wid.setGeometry(QRect(QPoint(x, wy), isz)) return y + line_height - rect.y()
def heightForWidth(self, width): return self.do_layout(QRect(0, 0, width, 0), apply_geometry=False)
def rect_at(self, frac): return QRect(self.point_at(frac), self.size())
def resizeEvent(self, ev): QPlainTextEdit.resizeEvent(self, ev) cr = self.contentsRect() self.line_number_area.setGeometry(QRect(cr.left(), cr.top(), self.line_number_area_width(), cr.height()))
def paint_embossed_emblem(self, pixmap, painter, orect, right_adjust, left=True): drect = QRect(orect) pw = int(pixmap.width() / pixmap.devicePixelRatio()) ph = int(pixmap.height() / pixmap.devicePixelRatio()) if left: drect.setLeft(drect.left() + right_adjust) drect.setRight(drect.left() + pw) else: drect.setRight(drect.right() - right_adjust) drect.setLeft(drect.right() - pw + 1) drect.setBottom(drect.bottom() - self.title_height) drect.setTop(drect.bottom() - ph) painter.drawPixmap(drect, pixmap)
def paint_emblems(self, painter, rect, emblems): gutter = self.emblem_size + self.MARGIN grect = QRect(rect) gpos = self.gutter_position if gpos is self.TOP: grect.setBottom(grect.top() + gutter) rect.setTop(rect.top() + gutter) elif gpos is self.BOTTOM: grect.setTop(grect.bottom() - gutter + self.MARGIN) rect.setBottom(rect.bottom() - gutter) elif gpos is self.LEFT: grect.setRight(grect.left() + gutter) rect.setLeft(rect.left() + gutter) else: grect.setLeft(grect.right() - gutter + self.MARGIN) rect.setRight(rect.right() - gutter) horizontal = gpos in (self.TOP, self.BOTTOM) painter.save() painter.setClipRect(grect) try: for i, emblem in enumerate(emblems): delta = 0 if i == 0 else self.emblem_size + self.MARGIN grect.moveLeft(grect.left() + delta) if horizontal else grect.moveTop( grect.top() + delta) rect = QRect(grect) rect.setWidth(int(emblem.width() / emblem.devicePixelRatio())), rect.setHeight( int(emblem.height() / emblem.devicePixelRatio())) painter.drawPixmap(rect, emblem) finally: painter.restore()
def paint(self, painter, option, index): QStyledItemDelegate.paint( self, painter, option, empty_index) # draw the hover and selection highlights m = index.model() db = m.db try: book_id = db.id(index.row()) except (ValueError, IndexError, KeyError): return if book_id in m.ids_to_highlight_set: painter.save() try: painter.setPen(self.highlight_color) painter.setRenderHint(QPainter.RenderHint.Antialiasing, True) painter.drawRoundedRect(option.rect, 10, 10, Qt.SizeMode.RelativeSize) finally: painter.restore() marked = db.data.get_marked(book_id) db = db.new_api cdata = self.cover_cache[book_id] device_connected = self.parent().gui.device_connected is not None on_device = device_connected and db.field_for('ondevice', book_id) emblem_rules = db.pref('cover_grid_icon_rules', default=()) emblems = [] if self.emblem_size > 0: mi = None for i, (kind, column, rule) in enumerate(emblem_rules): icon_name, mi = self.render_emblem(book_id, rule, i, m.cover_grid_emblem_cache, mi, db, m.formatter, m.cover_grid_template_cache) if icon_name is not None: for one_icon in filter(None, (i.strip() for i in icon_name.split(':'))): pixmap = self.cached_emblem(m.cover_grid_bitmap_cache, one_icon) if pixmap is not None: emblems.append(pixmap) if marked: emblems.insert( 0, self.cached_emblem(m.cover_grid_bitmap_cache, ':marked', m.marked_icon)) if on_device: emblems.insert( 0, self.cached_emblem(m.cover_grid_bitmap_cache, ':ondevice')) painter.save() right_adjust = 0 try: rect = option.rect rect.adjust(self.MARGIN, self.MARGIN, -self.MARGIN, -self.MARGIN) if self.emblem_size > 0: self.paint_emblems(painter, rect, emblems) orect = QRect(rect) trect = QRect(rect) if self.title_height != 0: rect.setBottom(rect.bottom() - self.title_height) trect.setTop(trect.bottom() - self.title_height + 5) if cdata is None or cdata is False: title = db.field_for('title', book_id, default_value='') authors = ' & '.join( db.field_for('authors', book_id, default_value=())) painter.setRenderHint(QPainter.RenderHint.TextAntialiasing, True) painter.drawText( rect, Qt.AlignmentFlag.AlignCenter | Qt.TextFlag.TextWordWrap, '%s\n\n%s' % (title, authors)) if cdata is False: self.render_queue.put(book_id) if self.title_height != 0: self.paint_title(painter, trect, db, book_id) else: if self.animating is not None and self.animating.row( ) == index.row(): cdata = cdata.scaled(cdata.size() * self._animated_size) dpr = cdata.devicePixelRatio() cw, ch = int(cdata.width() / dpr), int(cdata.height() / dpr) dx = max(0, int((rect.width() - cw) / 2.0)) dy = max(0, int((rect.height() - ch) / 2.0)) right_adjust = dx rect.adjust(dx, dy, -dx, -dy) painter.drawPixmap(rect, cdata) if self.title_height != 0: self.paint_title(painter, trect, db, book_id) if self.emblem_size > 0: return # We dont draw embossed emblems as the ondevice/marked emblems are drawn in the gutter if marked: try: p = self.marked_emblem except AttributeError: p = self.marked_emblem = m.marked_icon.pixmap(48, 48) self.paint_embossed_emblem(p, painter, orect, right_adjust) if on_device: try: p = self.on_device_emblem except AttributeError: p = self.on_device_emblem = QIcon(I('ok.png')).pixmap( 48, 48) self.paint_embossed_emblem(p, painter, orect, right_adjust, left=False) finally: painter.restore()
def drag_icon(self, cover, multiple): cover = cover.scaledToHeight(120, Qt.TransformationMode.SmoothTransformation) if multiple: base_width = cover.width() base_height = cover.height() base = QImage(base_width + 21, base_height + 21, QImage.Format.Format_ARGB32_Premultiplied) base.fill(QColor(255, 255, 255, 0).rgba()) p = QPainter(base) rect = QRect(20, 0, base_width, base_height) p.fillRect(rect, QColor('white')) p.drawRect(rect) rect.moveLeft(10) rect.moveTop(10) p.fillRect(rect, QColor('white')) p.drawRect(rect) rect.moveLeft(0) rect.moveTop(20) p.fillRect(rect, QColor('white')) p.save() p.setCompositionMode( QPainter.CompositionMode.CompositionMode_SourceAtop) p.drawImage(rect.topLeft(), cover) p.restore() p.drawRect(rect) p.end() cover = base return QPixmap.fromImage(cover)
def decoration_for_style(palette, style, icon_size, device_pixel_ratio, is_dark): style_key = (is_dark, icon_size, device_pixel_ratio, tuple((k, style[k]) for k in sorted(style))) sentinel = object() ans = decoration_cache.get(style_key, sentinel) if ans is not sentinel: return ans ans = None kind = style.get('kind') if kind == 'color': key = 'dark' if is_dark else 'light' val = style.get(key) if val is None: which = style.get('which') val = (builtin_colors_dark if is_dark else builtin_colors_light).get(which) if val is None: val = style.get('background-color') if val is not None: ans = QColor(val) elif kind == 'decoration': which = style.get('which') if which is not None: q = builtin_decorations.get(which) if q is not None: style = q sz = int(math.ceil(icon_size * device_pixel_ratio)) canvas = QImage(sz, sz, QImage.Format.Format_ARGB32) canvas.fill(Qt.GlobalColor.transparent) canvas.setDevicePixelRatio(device_pixel_ratio) p = QPainter(canvas) p.setRenderHint(QPainter.RenderHint.Antialiasing, True) p.setPen(palette.color(QPalette.ColorRole.WindowText)) irect = QRect(0, 0, icon_size, icon_size) adjust = -2 text_rect = p.drawText(irect.adjusted(0, adjust, 0, adjust), Qt.AlignmentFlag.AlignHCenter| Qt.AlignmentFlag.AlignTop, 'a') p.drawRect(irect) fm = p.fontMetrics() pen = p.pen() if 'text-decoration-color' in style: pen.setColor(QColor(style['text-decoration-color'])) lstyle = style.get('text-decoration-style') or 'solid' q = {'dotted': Qt.PenStyle.DotLine, 'dashed': Qt.PenStyle.DashLine, }.get(lstyle) if q is not None: pen.setStyle(q) lw = fm.lineWidth() if lstyle == 'double': lw * 2 pen.setWidth(fm.lineWidth()) q = style.get('text-decoration-line') or 'underline' pos = text_rect.bottom() height = irect.bottom() - pos if q == 'overline': pos = height elif q == 'line-through': pos = text_rect.center().y() - adjust - lw // 2 p.setPen(pen) if lstyle == 'wavy': p.drawPath(wavy_path(icon_size, height, pos)) else: p.drawLine(0, pos, irect.right(), pos) p.end() ans = QPixmap.fromImage(canvas) elif 'background-color' in style: ans = QColor(style['background-color']) decoration_cache[style_key] = ans return ans
class Declaration(QWidget): hyperlink_activated = pyqtSignal(object) context_menu_requested = pyqtSignal(object, object) def __init__(self, html_name, data, is_first=False, parent=None): QWidget.__init__(self, parent) self.setSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Minimum) self.data = data self.is_first = is_first self.html_name = html_name self.lines_for_copy = [] self.do_layout() self.setMouseTracking(True) def do_layout(self): fm = self.fontMetrics() bounding_rect = lambda text: fm.boundingRect(0, 0, 10000, 10000, Cell. FLAGS, text) line_spacing = 2 side_margin = Cell.SIDE_MARGIN self.rows = [] ypos = line_spacing + (1 if self.is_first else 0) if 'href' in self.data: name = self.data['href'] if isinstance(name, list): name = self.html_name br1 = bounding_rect(name) sel = self.data['selector'] or '' if self.data['type'] == 'inline': sel = 'style=""' br2 = bounding_rect(sel) self.hyperlink_rect = QRect(side_margin, ypos, br1.width(), br1.height()) self.rows.append([ Cell(name, self.hyperlink_rect, color_role=QPalette.ColorRole.Link), Cell(sel, QRect(br1.right() + side_margin, ypos, br2.width(), br2.height()), right_align=True) ]) ypos += max(br1.height(), br2.height()) + 2 * line_spacing self.lines_for_copy.append(name + ' ' + sel) for prop in self.data['properties']: text = prop.name + ':\xa0' br1 = bounding_rect(text) vtext = prop.value + '\xa0' + ('!' if prop.important else '') + prop.important br2 = bounding_rect(vtext) self.rows.append([ Cell(text, QRect(side_margin, ypos, br1.width(), br1.height()), color_role=QPalette.ColorRole.LinkVisited, is_overriden=prop.is_overriden), Cell(vtext, QRect(br1.right() + side_margin, ypos, br2.width(), br2.height()), swatch=prop.color, is_overriden=prop.is_overriden) ]) self.lines_for_copy.append(text + vtext) if prop.is_overriden: self.lines_for_copy[-1] += ' [overridden]' ypos += max(br1.height(), br2.height()) + line_spacing self.lines_for_copy.append('--------------------------\n') self.height_hint = ypos + line_spacing self.width_hint = max(row[-1].rect.right() + side_margin for row in self.rows) if self.rows else 0 def sizeHint(self): return QSize(self.width_hint, self.height_hint) def paintEvent(self, ev): p = QPainter(self) p.setClipRect(ev.rect()) palette = self.palette() p.setPen(palette.color(QPalette.ColorRole.WindowText)) if not self.is_first: p.drawLine(0, 0, self.width(), 0) try: for row in self.rows: for cell in row: p.save() try: cell.draw(p, self.width(), palette) finally: p.restore() finally: p.end() def mouseMoveEvent(self, ev): if hasattr(self, 'hyperlink_rect'): pos = ev.pos() hovering = self.hyperlink_rect.contains(pos) self.update_hover(hovering) cursor = Qt.CursorShape.ArrowCursor for r, row in enumerate(self.rows): for cell in row: if cell.rect.contains(pos): cursor = Qt.CursorShape.PointingHandCursor if cell.rect is self.hyperlink_rect else Qt.CursorShape.IBeamCursor if r == 0: break if cursor != Qt.CursorShape.ArrowCursor: break self.setCursor(cursor) return QWidget.mouseMoveEvent(self, ev) def mousePressEvent(self, ev): if hasattr( self, 'hyperlink_rect') and ev.button() == Qt.MouseButton.LeftButton: pos = ev.pos() if self.hyperlink_rect.contains(pos): self.emit_hyperlink_activated() return QWidget.mousePressEvent(self, ev) def emit_hyperlink_activated(self): dt = self.data['type'] data = {'type': dt, 'name': self.html_name, 'syntax': 'html'} if dt == 'inline': # style attribute data['sourceline_address'] = self.data['href'] elif dt == 'elem': # <style> tag data['sourceline_address'] = self.data['href'] data['rule_address'] = self.data['rule_address'] else: # stylesheet data['name'] = self.data['href'] data['rule_address'] = self.data['rule_address'] data['syntax'] = 'css' self.hyperlink_activated.emit(data) def leaveEvent(self, ev): self.update_hover(False) self.setCursor(Qt.CursorShape.ArrowCursor) return QWidget.leaveEvent(self, ev) def update_hover(self, hovering): cell = self.rows[0][0] if (hovering and cell.override_color is None) or ( not hovering and cell.override_color is not None): cell.override_color = QColor( Qt.GlobalColor.red) if hovering else None self.update() def contextMenuEvent(self, ev): self.context_menu_requested.emit(self, ev)