Exemplo n.º 1
0
 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()
Exemplo n.º 2
0
    def drawContents(self, painter):
        self.drawn_once = True
        painter.save()
        painter.setRenderHint(QPainter.RenderHint.TextAntialiasing, True)
        painter.setRenderHint(QPainter.RenderHint.Antialiasing, True)
        pw = self.LOGO_SIZE
        height = max(pw, self.total_height)
        width = self.width()

        # Draw frame
        y = (self.height() - height) // 2
        bottom = y + height
        painter.fillRect(0, y, width, height, self.light_brush)
        painter.fillRect(0, y, width, self.title_height, self.dark_brush)
        painter.fillRect(0, y, pw, height, self.dark_brush)
        dy = (height - self.LOGO_SIZE) // 2
        painter.drawPixmap(0, y + dy, self.pmap)

        # Draw number
        painter.setFont(self.num_font)
        num_width = painter.boundingRect(
            0, 0, 0, 0, Qt.AlignmentFlag.AlignCenter
            | Qt.TextFlag.TextSingleLine, self.num_ch).width() + 12
        num_x = width - num_width
        painter.setPen(QPen(QColor('#d6b865')))
        painter.drawText(
            num_x, y, num_width, height,
            Qt.AlignmentFlag.AlignCenter | Qt.TextFlag.TextSingleLine,
            self.num_ch)

        # Draw title
        x = pw + 10
        width -= num_width + 5 + x
        painter.setFont(self.title_font)
        painter.drawText(
            x, y, width, self.title_height, Qt.AlignmentFlag.AlignLeft
            | Qt.AlignmentFlag.AlignVCenter | Qt.TextFlag.TextSingleLine,
            "CALIBRE")

        # Draw starting up message
        y += self.title_height + 5
        painter.setPen(QPen(self.dark_brush.color()))
        painter.setFont(self.body_font)
        br = painter.drawText(
            x, y, width, self.line_height, Qt.AlignmentFlag.AlignLeft
            | Qt.AlignmentFlag.AlignVCenter | Qt.TextFlag.TextSingleLine,
            _('Starting up, please wait...'))
        starting_up_bottom = br.bottom()

        # Draw footer
        m = self.message()
        if m and m.strip():
            painter.setFont(self.footer_font)
            b = max(starting_up_bottom + 5, bottom - self.line_height)
            painter.drawText(
                x, b, width, self.line_height, Qt.AlignmentFlag.AlignLeft
                | Qt.AlignmentFlag.AlignTop | Qt.TextFlag.TextSingleLine, m)

        painter.restore()
Exemplo n.º 3
0
    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)
Exemplo n.º 4
0
 def paint(self, painter, option, index):
     name = index.data(Qt.ItemDataRole.DisplayRole)
     sz = human_readable(index.data(Qt.ItemDataRole.UserRole))
     pmap = index.data(Qt.ItemDataRole.UserRole + 1)
     irect = option.rect.adjusted(0, 5, 0, -5)
     irect.setRight(irect.left() + 70)
     if pmap is None:
         pmap = QPixmap(
             current_container().get_file_path_for_processing(name))
         scaled, nwidth, nheight = fit_image(pmap.width(), pmap.height(),
                                             irect.width(), irect.height())
         if scaled:
             pmap = pmap.scaled(
                 nwidth,
                 nheight,
                 transformMode=Qt.TransformationMode.SmoothTransformation)
         index.model().setData(index, pmap, Qt.ItemDataRole.UserRole + 1)
     x, y = (irect.width() - pmap.width()) // 2, (irect.height() -
                                                  pmap.height()) // 2
     r = irect.adjusted(x, y, -x, -y)
     QStyledItemDelegate.paint(self, painter, option, empty_index)
     painter.drawPixmap(r, pmap)
     trect = irect.adjusted(irect.width() + 10, 0, 0, 0)
     trect.setRight(option.rect.right())
     painter.save()
     if option.state & QStyle.StateFlag.State_Selected:
         painter.setPen(
             QPen(option.palette.color(QPalette.ColorRole.HighlightedText)))
     painter.drawText(
         trect, Qt.AlignmentFlag.AlignVCenter | Qt.AlignmentFlag.AlignLeft,
         name + '\n' + sz)
     painter.restore()
Exemplo n.º 5
0
 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()
Exemplo n.º 6
0
 def __init__(self, font_loader, footer, page_style, logger, opts,
              ruby_tags, link_activated):
     Canvas.__init__(self, font_loader, footer, logger, opts, ruby_tags,
                     link_activated, page_style.textwidth,
                     page_style.footheight)
     if opts.visual_debug:
         self.setPen(QPen(Qt.GlobalColor.blue, 1, Qt.PenStyle.DashLine))
Exemplo n.º 7
0
 def __init__(self, rl):
     QGraphicsLineItem.__init__(self, 0, 0, rl.linelength, 0)
     ContentObject.__init__(self)
     self.setPen(QPen(
         COLOR(rl.linecolor, None),
         rl.linewidth,
     ))
Exemplo n.º 8
0
 def paint_non_printing(self, painter, option, charcode):
     text = self.np_pat.sub(r'\n\1', non_printing[charcode])
     painter.drawText(
         option.rect, Qt.AlignmentFlag.AlignCenter
         | Qt.TextFlag.TextWordWrap | Qt.TextFlag.TextWrapAnywhere, text)
     painter.setPen(QPen(Qt.PenStyle.DashLine))
     painter.drawRect(option.rect.adjusted(1, 1, -1, -1))
Exemplo n.º 9
0
    def do_paint(self, painter, option, index):
        text = str(index.data(Qt.ItemDataRole.DisplayRole) or '')
        font = QFont(option.font)
        font.setPointSize(QFontInfo(font).pointSize() * 1.5)
        font2 = QFont(font)
        font2.setFamily(text)

        system, has_latin = writing_system_for_font(font2)
        if has_latin:
            font = font2

        r = option.rect

        if option.state & QStyle.StateFlag.State_Selected:
            painter.setPen(QPen(option.palette.highlightedText(), 0))

        if (option.direction == Qt.LayoutDirection.RightToLeft):
            r.setRight(r.right() - 4)
        else:
            r.setLeft(r.left() + 4)

        painter.setFont(font)
        painter.drawText(r, Qt.AlignmentFlag.AlignVCenter|Qt.AlignmentFlag.AlignLeading|Qt.TextFlag.TextSingleLine, text)

        if (system != QFontDatabase.WritingSystem.Any):
            w = painter.fontMetrics().width(text + "  ")
            painter.setFont(font2)
            sample = QFontDatabase().writingSystemSample(system)
            if (option.direction == Qt.LayoutDirection.RightToLeft):
                r.setRight(r.right() - w)
            else:
                r.setLeft(r.left() + w)
            painter.drawText(r, Qt.AlignmentFlag.AlignVCenter|Qt.AlignmentFlag.AlignLeading|Qt.TextFlag.TextSingleLine, sample)
Exemplo n.º 10
0
    def paintEvent(self, event):
        pmap = self.blank if self.pixmap is None or self.pixmap.isNull(
        ) else self.pixmap
        target = self.rect()
        scaled, width, height = fit_image(pmap.width(), pmap.height(),
                                          target.width(), target.height())
        target.setRect(target.x(), target.y(), width, height)
        p = QPainter(self)
        p.setRenderHints(QPainter.RenderHint.Antialiasing
                         | QPainter.RenderHint.SmoothPixmapTransform)
        p.drawPixmap(target, pmap)

        if self.pixmap is not None and not self.pixmap.isNull():
            sztgt = target.adjusted(0, 0, 0, -4)
            f = p.font()
            f.setBold(True)
            p.setFont(f)
            sz = u'\u00a0%d x %d\u00a0' % (self.pixmap.width(),
                                           self.pixmap.height())
            flags = int(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()
Exemplo n.º 11
0
 def __init__(self, parent, start, stop, refobj, slot):
     QGraphicsRectItem.__init__(self, start, 0, stop-start, parent.height, parent)
     self.refobj = refobj
     self.slot = slot
     self.brush = self.__class__.inactive_brush
     self.setPen(QPen(Qt.PenStyle.NoPen))
     self.setCursor(Qt.CursorShape.PointingHandCursor)
     self.setAcceptHoverEvents(True)
Exemplo n.º 12
0
 def __init__(self,
              font_loader,
              logger,
              opts,
              width=0,
              height=0,
              parent=None,
              x=0,
              y=0):
     QGraphicsRectItem.__init__(self, x, y, width, height, parent)
     self.font_loader, self.logger, self.opts = font_loader, logger, opts
     self.current_y, self.max_y, self.max_x = 0, height, width
     self.is_full = False
     pen = QPen()
     pen.setStyle(Qt.PenStyle.NoPen)
     self.setPen(pen)
     if not hasattr(self, 'children'):
         self.children = self.childItems
Exemplo n.º 13
0
 def copy(self):
     ans = GraphicsState()
     ans.fill = QBrush(self.fill)
     ans.stroke = QPen(self.stroke)
     ans.opacity = self.opacity
     ans.transform = self.transform * QTransform()
     ans.brush_origin = QPointF(self.brush_origin)
     ans.clip_updated = self.clip_updated
     ans.do_fill, ans.do_stroke = self.do_fill, self.do_stroke
     return ans
Exemplo n.º 14
0
 def __init__(self):
     self.fill = QBrush(Qt.GlobalColor.white)
     self.stroke = QPen()
     self.opacity = 1.0
     self.transform = QTransform()
     self.brush_origin = QPointF()
     self.clip_updated = False
     self.do_fill = False
     self.do_stroke = True
     self.qt_pattern_cache = {}
Exemplo n.º 15
0
def draw_size(p, rect, w, h):
    rect = rect.adjusted(0, 0, 0, -4)
    f = p.font()
    f.setBold(True)
    p.setFont(f)
    sz = '\u00a0%d x %d\u00a0' % (w, h)
    flags = Qt.AlignmentFlag.AlignBottom | Qt.AlignmentFlag.AlignRight | Qt.TextFlag.TextSingleLine
    szrect = p.boundingRect(rect, flags, sz)
    p.fillRect(szrect.adjusted(0, 0, 0, 4), QColor(0, 0, 0, 200))
    p.setPen(QPen(QColor(255, 255, 255)))
    p.drawText(rect, flags, sz)
Exemplo n.º 16
0
 def paint(self, painter, option, widget):
     x, y = 0, 0 + self.height - self.descent
     if self.vdebug:
         painter.save()
         painter.setPen(QPen(Qt.GlobalColor.yellow, 1, Qt.PenStyle.DotLine))
         painter.drawRect(self.boundingRect())
         painter.restore()
     painter.save()
     painter.setPen(QPen(Qt.PenStyle.NoPen))
     for c in self.children():
         painter.setBrush(c.brush)
         painter.drawRect(c.boundingRect())
     painter.restore()
     painter.save()
     for tok in self.tokens:
         if isinstance(tok, numbers.Number):
             x += tok
         elif isinstance(tok, Word):
             painter.setFont(tok.font)
             if tok.highlight:
                 painter.save()
                 painter.setPen(QPen(Qt.PenStyle.NoPen))
                 painter.setBrush(QBrush(Qt.GlobalColor.yellow))
                 painter.drawRect(int(x), 0, tok.width, tok.height)
                 painter.restore()
             painter.setPen(QPen(tok.text_color))
             if tok.valign is None:
                 painter.drawText(int(x), int(y), tok.string)
             elif tok.valign == 'Sub':
                 painter.drawText(int(x + 1), int(y + self.descent / 1.5),
                                  tok.string)
             elif tok.valign == 'Sup':
                 painter.drawText(int(x + 1), int(y - 2. * self.descent),
                                  tok.string)
             x += tok.width
         else:
             painter.drawPixmap(int(x), 0, tok.pixmap())
             x += tok.width
     painter.restore()
Exemplo n.º 17
0
 def failed_img(self):
     if self._failed_img is None:
         try:
             dpr = self.devicePixelRatioF()
         except AttributeError:
             dpr = self.devicePixelRatio()
         i = QImage(200, 150, QImage.Format.Format_ARGB32)
         i.setDevicePixelRatio(dpr)
         i.fill(Qt.GlobalColor.white)
         p = QPainter(i)
         r = i.rect().adjusted(10, 10, -10, -10)
         n = QPen(Qt.PenStyle.DashLine)
         n.setColor(Qt.GlobalColor.black)
         p.setPen(n)
         p.drawRect(r)
         p.setPen(Qt.GlobalColor.black)
         f = self.font()
         f.setPixelSize(20)
         p.setFont(f)
         p.drawText(r.adjusted(10, 0, -10, 0), Qt.AlignmentFlag.AlignCenter | Qt.TextFlag.TextWordWrap, _('Image could not be rendered'))
         p.end()
         self._failed_img = QPixmap.fromImage(i)
     return self._failed_img
Exemplo n.º 18
0
    def __call__(self, painter, rect, color_theme, title_block, subtitle_block, footer_block):
        painter.fillRect(rect, self.color1)
        top = title_block.position.y + 2
        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
        right = rect.right() - self.hmargin
        width = right - self.hmargin

        # Draw main banner
        p = main = QPainterPath(QPointF(self.hmargin, top))
        draw_curved_line(p, rect.width() - 2 * self.hmargin, 0, 0.1, -0.1, 0.9, -0.1)
        deltax = self.GRADE * height
        p.lineTo(right + deltax, top + height)
        right_corner = p.currentPosition()
        draw_curved_line(p, - width - 2 * deltax, 0, 0.1, 0.05, 0.9, 0.05)
        left_corner = p.currentPosition()
        p.closeSubpath()

        # Draw fold rectangles
        rwidth = self.fold_width
        yfrac = 0.1
        width23 = int(0.67 * rwidth)
        rtop = top + height * yfrac

        def draw_fold(x, m=1, corner=left_corner):
            ans = p = QPainterPath(QPointF(x, rtop))
            draw_curved_line(p, rwidth*m, 0, 0.1, 0.1*m, 0.5, -0.2*m)
            fold_upper = p.currentPosition()
            p.lineTo(p.currentPosition() + QPointF(-deltax*m, height))
            fold_corner = p.currentPosition()
            draw_curved_line(p, -rwidth*m, 0, 0.2, -0.1*m, 0.8, -0.1*m)
            draw_curved_line(p, deltax*m, -height, 0.2, 0.1*m, 0.8, 0.1*m)
            p = inner_fold = QPainterPath(corner)
            dp = fold_corner - p.currentPosition()
            draw_curved_line(p, dp.x(), dp.y(), 0.5, 0.3*m, 1, 0*m)
            p.lineTo(fold_upper), p.closeSubpath()
            return ans, inner_fold

        left_fold, left_inner = draw_fold(self.hmargin - width23)
        right_fold, right_inner = draw_fold(right + width23, m=-1, corner=right_corner)

        painter.save()
        painter.setRenderHint(QPainter.RenderHint.Antialiasing)
        pen = QPen(self.ccolor2)
        pen.setWidth(3)
        pen.setJoinStyle(Qt.PenJoinStyle.RoundJoin)
        painter.setPen(pen)
        for r in (left_fold, right_fold):
            painter.fillPath(r, QBrush(self.color2))
            painter.drawPath(r)
        for r in (left_inner, right_inner):
            painter.fillPath(r, QBrush(self.color2.darker()))
            painter.drawPath(r)
        painter.fillPath(main, QBrush(self.color2))
        painter.drawPath(main)
        painter.restore()
        return self.ccolor2, self.ccolor2, self.ccolor1
Exemplo n.º 19
0
    def __init__(self, font_loader, chapter, odd, logger, opts, ruby_tags,
                 link_activated):
        self.logger, self.opts = logger, opts
        page_style = chapter.style
        sidemargin = page_style.oddsidemargin if odd else page_style.evensidemargin
        width = 2 * sidemargin + page_style.textwidth
        self.content_x = 0 + sidemargin
        self.text_width = page_style.textwidth
        self.header_y = page_style.topmargin

        self.text_y = self.header_y + page_style.headheight + page_style.headsep
        self.text_height = page_style.textheight
        self.footer_y = self.text_y + self.text_height + (
            page_style.footspace - page_style.footheight)

        _Canvas.__init__(self,
                         font_loader,
                         logger,
                         opts,
                         width=width,
                         height=self.footer_y + page_style.footheight)
        if opts.visual_debug:
            self.setPen(QPen(Qt.GlobalColor.red, 1, Qt.PenStyle.SolidLine))
        header = footer = None
        if page_style.headheight > 0:
            try:
                header = chapter.oddheader if odd else chapter.evenheader
            except AttributeError:
                pass
        if page_style.footheight > 0:
            try:
                footer = chapter.oddfooter if odd else chapter.evenfooter
            except AttributeError:
                pass
        if header:
            header = Header(font_loader, header, page_style, logger, opts,
                            ruby_tags, link_activated)
            self.layout_canvas(header, self.content_x, self.header_y)
        if footer:
            footer = Footer(font_loader, footer, page_style, logger, opts,
                            ruby_tags, link_activated)
            self.layout_canvas(footer, self.content_x, self.header_y)

        self.page = None
Exemplo n.º 20
0
 def paint(self, painter, option, index):
     QStyledItemDelegate.paint(self, painter, option, empty_index)
     theme = index.data(Qt.ItemDataRole.UserRole)
     if not theme:
         return
     painter.save()
     pixmap = index.data(Qt.ItemDataRole.DecorationRole)
     if pixmap and not pixmap.isNull():
         rect = option.rect.adjusted(0, self.SPACING,
                                     COVER_SIZE[0] - option.rect.width(),
                                     -self.SPACING)
         painter.drawPixmap(rect, pixmap)
     if option.state & QStyle.StateFlag.State_Selected:
         painter.setPen(
             QPen(QApplication.instance().palette().highlightedText().color(
             )))
     bottom = option.rect.bottom() - 2
     painter.drawLine(0, bottom, option.rect.right(), bottom)
     if 'static-text' not in theme:
         theme['static-text'] = QStaticText(
             _('''
         <h1>{title}</h1>
         <p>by <i>{author}</i> with <b>{number}</b> icons [{size}]</p>
         <p>{description}</p>
         <p>Version: {version} Number of users: {usage}</p>
         <p><i>Right click to visit theme homepage</i></p>
         '''.format(
                 title=theme.get('title', _('Unknown')),
                 author=theme.get('author', _('Unknown')),
                 number=theme.get('number', 0),
                 description=theme.get('description', ''),
                 size=human_readable(theme.get('compressed-size', 0)),
                 version=theme.get('version', 1),
                 usage=theme.get('usage', 0),
             )))
     painter.drawStaticText(COVER_SIZE[0] + self.SPACING,
                            option.rect.top() + self.SPACING,
                            theme['static-text'])
     painter.restore()
Exemplo n.º 21
0
 def __init__(self, font_loader, logger, opts, width, height):
     _Canvas.__init__(self, font_loader, logger, opts, width, height)
     if opts.visual_debug:
         self.setPen(QPen(Qt.GlobalColor.cyan, 1, Qt.PenStyle.DashLine))
Exemplo n.º 22
0
 def __init__(self, color, width):
     QPen.__init__(
         self, QBrush(Color(color)), width,
         (Qt.PenStyle.SolidLine if width > 0 else Qt.PenStyle.NoPen))
Exemplo n.º 23
0
    def paintEvent(self, event):
        QSplitterHandle.paintEvent(self, event)
        left, right = self.parent().left, self.parent().right
        painter = QPainter(self)
        painter.setClipRect(event.rect())
        w = self.width()
        h = self.height()
        painter.setRenderHints(QPainter.RenderHint.Antialiasing, True)

        C = 16  # Curve factor.

        def create_line(ly, ry, right_to_left=False):
            ' Create path that represents upper or lower line of change marker '
            line = QPainterPath()
            if not right_to_left:
                line.moveTo(0, ly)
                line.cubicTo(C, ly, w - C, ry, w, ry)
            else:
                line.moveTo(w, ry)
                line.cubicTo(w - C, ry, C, ly, 0, ly)
            return line

        ldoc, rdoc = left.document(), right.document()
        lorigin, rorigin = left.contentOffset(), right.contentOffset()
        lfv, rfv = left.firstVisibleBlock().blockNumber(), right.firstVisibleBlock().blockNumber()
        lines = []

        for (ltop, lbot, kind), (rtop, rbot, kind) in zip(left.changes, right.changes):
            if lbot < lfv and rbot < rfv:
                continue
            ly_top = left.blockBoundingGeometry(ldoc.findBlockByNumber(ltop)).translated(lorigin).y()
            ly_bot = left.blockBoundingGeometry(ldoc.findBlockByNumber(lbot)).translated(lorigin).y()
            ry_top = right.blockBoundingGeometry(rdoc.findBlockByNumber(rtop)).translated(rorigin).y()
            ry_bot = right.blockBoundingGeometry(rdoc.findBlockByNumber(rbot)).translated(rorigin).y()
            if max(ly_top, ly_bot, ry_top, ry_bot) < 0:
                continue
            if min(ly_top, ly_bot, ry_top, ry_bot) > h:
                break

            upper_line = create_line(ly_top, ry_top)
            lower_line = create_line(ly_bot, ry_bot, True)

            region = QPainterPath()
            region.moveTo(0, ly_top)
            region.connectPath(upper_line)
            region.lineTo(w, ry_bot)
            region.connectPath(lower_line)
            region.closeSubpath()

            painter.fillPath(region, left.diff_backgrounds[kind])
            for path, aa in zip((upper_line, lower_line), (ly_top != ry_top, ly_bot != ry_bot)):
                lines.append((kind, path, aa))

        for kind, path, aa in sorted(lines, key=lambda x:{'replace':0}.get(x[0], 1)):
            painter.setPen(left.diff_foregrounds[kind])
            painter.setRenderHints(QPainter.RenderHint.Antialiasing, aa)
            painter.drawPath(path)

        painter.setFont(left.heading_font)
        for (lnum, text), (rnum, text) in zip(left.headers, right.headers):
            ltop, lbot, rtop, rbot = lnum, lnum + 3, rnum, rnum + 3
            if lbot < lfv and rbot < rfv:
                continue
            ly_top = left.blockBoundingGeometry(ldoc.findBlockByNumber(ltop)).translated(lorigin).y()
            ly_bot = left.blockBoundingGeometry(ldoc.findBlockByNumber(lbot)).translated(lorigin).y()
            ry_top = right.blockBoundingGeometry(rdoc.findBlockByNumber(rtop)).translated(rorigin).y()
            ry_bot = right.blockBoundingGeometry(rdoc.findBlockByNumber(rbot)).translated(rorigin).y()
            if max(ly_top, ly_bot, ry_top, ry_bot) < 0:
                continue
            if min(ly_top, ly_bot, ry_top, ry_bot) > h:
                break
            ly = painter.boundingRect(3, ly_top, left.width(), ly_bot - ly_top - 5, Qt.TextFlag.TextSingleLine, text).bottom() + 3
            ry = painter.boundingRect(3, ry_top, right.width(), ry_bot - ry_top - 5, Qt.TextFlag.TextSingleLine, text).bottom() + 3
            line = create_line(ly, ry)
            painter.setPen(QPen(left.palette().text(), 2))
            painter.setRenderHints(QPainter.RenderHint.Antialiasing, ly != ry)
            painter.drawPath(line)

        painter.end()
        # Paint the splitter without the change lines if the mouse is over the
        # splitter
        if getattr(self, 'hover', False):
            QSplitterHandle.paintEvent(self, event)
Exemplo n.º 24
0
class Canvas(QWidget):

    BACKGROUND = QColor(60, 60, 60)
    SHADE_COLOR = QColor(0, 0, 0, 180)
    SELECT_PEN = QPen(QColor(Qt.GlobalColor.white))

    selection_state_changed = pyqtSignal(object)
    selection_area_changed = pyqtSignal(object)
    undo_redo_state_changed = pyqtSignal(object, object)
    image_changed = pyqtSignal(object)

    @property
    def has_selection(self):
        return self.selection_state.current_mode == 'selected'

    @property
    def is_modified(self):
        return self.current_image is not self.original_image

    # Drag 'n drop {{{

    def dragEnterEvent(self, event):
        md = event.mimeData()
        if dnd_has_extension(md, image_extensions()) or dnd_has_image(md):
            event.acceptProposedAction()

    def dropEvent(self, event):
        event.setDropAction(Qt.DropAction.CopyAction)
        md = event.mimeData()

        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.undo_stack.push(
                    Replace(x.toImage(), _('Drop image'), self))
            else:
                d = DownloadDialog(x, y, self.gui)
                d.start_download()
                if d.err is None:
                    with open(d.fpath, 'rb') as f:
                        img = QImage()
                        img.loadFromData(f.read())
                    if not img.isNull():
                        self.undo_stack.push(
                            Replace(img, _('Drop image'), self))

        event.accept()

    def dragMoveEvent(self, event):
        event.acceptProposedAction()

    # }}}

    def __init__(self, parent=None):
        QWidget.__init__(self, parent)
        self.setAcceptDrops(True)
        self.setMouseTracking(True)
        self.setFocusPolicy(Qt.FocusPolicy.ClickFocus)
        self.selection_state = SelectionState()
        self.undo_stack = u = QUndoStack()
        u.setUndoLimit(10)
        u.canUndoChanged.connect(self.emit_undo_redo_state)
        u.canRedoChanged.connect(self.emit_undo_redo_state)

        self.original_image_data = None
        self.is_valid = False
        self.original_image_format = None
        self.current_image = None
        self.current_scaled_pixmap = None
        self.last_canvas_size = None
        self.target = QRectF(0, 0, 0, 0)

        self.undo_action = a = self.undo_stack.createUndoAction(
            self,
            _('Undo') + ' ')
        a.setIcon(QIcon(I('edit-undo.png')))
        self.redo_action = a = self.undo_stack.createRedoAction(
            self,
            _('Redo') + ' ')
        a.setIcon(QIcon(I('edit-redo.png')))

    def load_image(self, data):
        self.is_valid = False
        try:
            fmt = identify(data)[0].encode('ascii')
        except Exception:
            fmt = b''
        self.original_image_format = fmt.decode('ascii').lower()
        self.selection_state.reset()
        self.original_image_data = data
        self.current_image = i = self.original_image = (QImage.fromData(
            data, format=fmt) if fmt else QImage.fromData(data))
        self.is_valid = not i.isNull()
        self.current_scaled_pixmap = None
        self.update()
        self.image_changed.emit(self.current_image)

    def set_image(self, qimage):
        self.selection_state.reset()
        self.current_scaled_pixmap = None
        self.current_image = qimage
        self.is_valid = not qimage.isNull()
        self.update()
        self.image_changed.emit(self.current_image)

    def get_image_data(self, quality=90):
        if not self.is_modified:
            return self.original_image_data
        fmt = self.original_image_format or 'JPEG'
        if fmt.lower() not in {
                x.data().decode('utf-8')
                for x in QImageWriter.supportedImageFormats()
        }:
            if fmt.lower() == 'gif':
                data = image_to_data(self.current_image,
                                     fmt='PNG',
                                     png_compression_level=0)
                from PIL import Image
                i = Image.open(BytesIO(data))
                buf = BytesIO()
                i.save(buf, 'gif')
                return buf.getvalue()
            else:
                raise ValueError('Cannot save %s format images' % fmt)
        return pixmap_to_data(self.current_image, format=fmt, quality=90)

    def copy(self):
        if not self.is_valid:
            return
        clipboard = QApplication.clipboard()
        if not self.has_selection or self.selection_state.rect is None:
            clipboard.setImage(self.current_image)
        else:
            trim = Trim(self)
            clipboard.setImage(trim.after_image)
            trim.before_image = trim.after_image = None

    def paste(self):
        clipboard = QApplication.clipboard()
        md = clipboard.mimeData()
        if md.hasImage():
            img = QImage(md.imageData())
            if not img.isNull():
                self.undo_stack.push(Replace(img, _('Paste image'), self))
        else:
            error_dialog(self,
                         _('No image'),
                         _('No image available in the clipboard'),
                         show=True)

    def break_cycles(self):
        self.undo_stack.clear()
        self.original_image_data = self.current_image = self.current_scaled_pixmap = None

    def emit_undo_redo_state(self):
        self.undo_redo_state_changed.emit(self.undo_action.isEnabled(),
                                          self.redo_action.isEnabled())

    @imageop
    def trim_image(self):
        if self.selection_state.rect is None:
            error_dialog(
                self,
                _('No selection'),
                _('No active selection, first select a region in the image, by dragging with your mouse'
                  ),
                show=True)
            return False
        self.undo_stack.push(Trim(self))
        return True

    @imageop
    def autotrim_image(self):
        self.undo_stack.push(AutoTrim(self))
        return True

    @imageop
    def rotate_image(self):
        self.undo_stack.push(Rotate(self))
        return True

    @imageop
    def resize_image(self, width, height):
        self.undo_stack.push(Scale(width, height, self))
        return True

    @imageop
    def sharpen_image(self, sigma=3.0):
        self.undo_stack.push(Sharpen(sigma, self))
        return True

    @imageop
    def blur_image(self, sigma=3.0):
        self.undo_stack.push(Blur(sigma, self))
        return True

    @imageop
    def despeckle_image(self):
        self.undo_stack.push(Despeckle(self))
        return True

    @imageop
    def normalize_image(self):
        self.undo_stack.push(Normalize(self))
        return True

    @imageop
    def oilify_image(self, radius=4.0):
        self.undo_stack.push(Oilify(radius, self))
        return True

    # The selection rectangle {{{
    @property
    def dc_size(self):
        sr = self.selection_state.rect
        dx = min(75, sr.width() / 4)
        dy = min(75, sr.height() / 4)
        return dx, dy

    def get_drag_corner(self, pos):
        dx, dy = self.dc_size
        sr = self.selection_state.rect
        x, y = pos.x(), pos.y()
        hedge = 'left' if x < sr.x() + dx else 'right' if x > sr.right(
        ) - dx else None
        vedge = 'top' if y < sr.y() + dy else 'bottom' if y > sr.bottom(
        ) - dy else None
        return (hedge, vedge) if hedge or vedge else None

    def get_drag_rect(self):
        sr = self.selection_state.rect
        dc = self.selection_state.drag_corner
        if None in (sr, dc):
            return
        dx, dy = self.dc_size
        if None in dc:
            # An edge
            if dc[0] is None:
                top = sr.top() if dc[1] == 'top' else sr.bottom() - dy
                ans = QRectF(sr.left() + dx, top, sr.width() - 2 * dx, dy)
            else:
                left = sr.left() if dc[0] == 'left' else sr.right() - dx
                ans = QRectF(left, sr.top() + dy, dx, sr.height() - 2 * dy)
        else:
            # A corner
            left = sr.left() if dc[0] == 'left' else sr.right() - dx
            top = sr.top() if dc[1] == 'top' else sr.bottom() - dy
            ans = QRectF(left, top, dx, dy)
        return ans

    def get_cursor(self):
        dc = self.selection_state.drag_corner
        if dc is None:
            ans = Qt.CursorShape.OpenHandCursor if self.selection_state.last_drag_pos is None else Qt.CursorShape.ClosedHandCursor
        elif None in dc:
            ans = Qt.CursorShape.SizeVerCursor if dc[
                0] is None else Qt.CursorShape.SizeHorCursor
        else:
            ans = Qt.CursorShape.SizeBDiagCursor if dc in {
                ('left', 'bottom'), ('right', 'top')
            } else Qt.CursorShape.SizeFDiagCursor
        return ans

    def update(self):
        super().update()
        self.selection_area_changed.emit(self.selection_state.rect)

    def move_edge(self, edge, dp):
        sr = self.selection_state.rect
        horiz = edge in {'left', 'right'}
        func = getattr(sr, 'set' + capitalize(edge))
        delta = getattr(dp, 'x' if horiz else 'y')()
        buf = 50
        if horiz:
            minv = self.target.left() if edge == 'left' else sr.left() + buf
            maxv = sr.right() - buf if edge == 'left' else self.target.right()
        else:
            minv = self.target.top() if edge == 'top' else sr.top() + buf
            maxv = sr.bottom() - buf if edge == 'top' else self.target.bottom()
        func(max(minv, min(maxv, delta + getattr(sr, edge)())))

    def move_selection_rect(self, x, y):
        sr = self.selection_state.rect
        half_width = sr.width() / 2.0
        half_height = sr.height() / 2.0
        c = sr.center()
        nx = c.x() + x
        ny = c.y() + y
        minx = self.target.left() + half_width
        maxx = self.target.right() - half_width
        miny, maxy = self.target.top() + half_height, self.target.bottom(
        ) - half_height
        nx = max(minx, min(maxx, nx))
        ny = max(miny, min(maxy, ny))
        sr.moveCenter(QPointF(nx, ny))

    def move_selection(self, dp):
        dm = self.selection_state.dragging
        if dm is None:
            self.move_selection_rect(dp.x(), dp.y())
        else:
            for edge in dm:
                if edge is not None:
                    self.move_edge(edge, dp)

    def mousePressEvent(self, ev):
        if ev.button() == Qt.MouseButton.LeftButton and self.target.contains(
                ev.pos()):
            pos = ev.pos()
            self.selection_state.last_press_point = pos
            if self.selection_state.current_mode is None:
                self.selection_state.current_mode = 'select'

            elif self.selection_state.current_mode == 'selected':
                if self.selection_state.rect is not None and self.selection_state.rect.contains(
                        pos):
                    self.selection_state.drag_corner = self.selection_state.dragging = self.get_drag_corner(
                        pos)
                    self.selection_state.last_drag_pos = pos
                    self.setCursor(self.get_cursor())
                else:
                    self.selection_state.current_mode = 'select'
                    self.selection_state.rect = None
                    self.selection_state_changed.emit(False)

    def mouseMoveEvent(self, ev):
        changed = False
        if self.selection_state.in_selection:
            changed = True
        self.selection_state.in_selection = False
        self.selection_state.drag_corner = None
        pos = ev.pos()
        cursor = Qt.CursorShape.ArrowCursor
        try:
            if not self.target.contains(pos):
                return
            if ev.buttons() & Qt.MouseButton.LeftButton:
                if self.selection_state.last_press_point is not None and self.selection_state.current_mode is not None:
                    if self.selection_state.current_mode == 'select':
                        self.selection_state.rect = QRectF(
                            self.selection_state.last_press_point,
                            pos).normalized()
                        changed = True
                    elif self.selection_state.last_drag_pos is not None:
                        self.selection_state.in_selection = True
                        self.selection_state.drag_corner = self.selection_state.dragging
                        dp = pos - self.selection_state.last_drag_pos
                        self.selection_state.last_drag_pos = pos
                        self.move_selection(dp)
                        cursor = self.get_cursor()
                        changed = True
            else:
                if self.selection_state.rect is None or not self.selection_state.rect.contains(
                        pos):
                    return
                if self.selection_state.current_mode == 'selected':
                    if self.selection_state.rect is not None and self.selection_state.rect.contains(
                            pos):
                        self.selection_state.drag_corner = self.get_drag_corner(
                            pos)
                        self.selection_state.in_selection = True
                        cursor = self.get_cursor()
                        changed = True
        finally:
            if changed:
                self.update()
            self.setCursor(cursor)

    def mouseReleaseEvent(self, ev):
        if ev.button() == Qt.MouseButton.LeftButton:
            self.selection_state.dragging = self.selection_state.last_drag_pos = None
            if self.selection_state.current_mode == 'select':
                r = self.selection_state.rect
                if r is None or max(r.width(), r.height()) < 3:
                    self.selection_state.reset()
                else:
                    self.selection_state.current_mode = 'selected'
                self.selection_state_changed.emit(self.has_selection)
            elif self.selection_state.current_mode == 'selected' and self.selection_state.rect is not None and self.selection_state.rect.contains(
                    ev.pos()):
                self.setCursor(self.get_cursor())
            self.update()

    def keyPressEvent(self, ev):
        k = ev.key()
        if k in (
                Qt.Key.Key_Left, Qt.Key.Key_Right, Qt.Key.Key_Up,
                Qt.Key.Key_Down
        ) and self.selection_state.rect is not None and self.has_selection:
            ev.accept()
            delta = 10 if ev.modifiers(
            ) & Qt.KeyboardModifier.ShiftModifier else 1
            x = y = 0
            if k in (Qt.Key.Key_Left, Qt.Key.Key_Right):
                x = delta * (-1 if k == Qt.Key.Key_Left else 1)
            else:
                y = delta * (-1 if k == Qt.Key.Key_Up else 1)
            self.move_selection_rect(x, y)
            self.update()
        else:
            return QWidget.keyPressEvent(self, ev)

    # }}}

    # Painting {{{
    @painter
    def draw_background(self, painter):
        painter.fillRect(self.rect(), self.BACKGROUND)

    @painter
    def draw_image_error(self, painter):
        font = painter.font()
        font.setPointSize(3 * font.pointSize())
        font.setBold(True)
        painter.setFont(font)
        painter.setPen(QColor(Qt.GlobalColor.black))
        painter.drawText(self.rect(), Qt.AlignmentFlag.AlignCenter,
                         _('Not a valid image'))

    def load_pixmap(self):
        canvas_size = self.rect().width(), self.rect().height()
        if self.last_canvas_size != canvas_size:
            if self.last_canvas_size is not None and self.selection_state.rect is not None:
                self.selection_state.reset()
                # TODO: Migrate the selection rect
            self.last_canvas_size = canvas_size
            self.current_scaled_pixmap = None
        if self.current_scaled_pixmap is None:
            pwidth, pheight = self.last_canvas_size
            i = self.current_image
            width, height = i.width(), i.height()
            scaled, width, height = fit_image(width, height, pwidth, pheight)
            try:
                dpr = self.devicePixelRatioF()
            except AttributeError:
                dpr = self.devicePixelRatio()
            if scaled:
                i = self.current_image.scaled(
                    int(dpr * width),
                    int(dpr * height),
                    transformMode=Qt.TransformationMode.SmoothTransformation)
            self.current_scaled_pixmap = QPixmap.fromImage(i)
            self.current_scaled_pixmap.setDevicePixelRatio(dpr)

    @painter
    def draw_pixmap(self, painter):
        p = self.current_scaled_pixmap
        try:
            dpr = self.devicePixelRatioF()
        except AttributeError:
            dpr = self.devicePixelRatio()
        width, height = int(p.width() / dpr), int(p.height() / dpr)
        pwidth, pheight = self.last_canvas_size
        x = int(abs(pwidth - width) / 2.)
        y = int(abs(pheight - height) / 2.)
        self.target = QRectF(x, y, width, height)
        painter.drawPixmap(self.target, p, QRectF(p.rect()))

    @painter
    def draw_selection_rect(self, painter):
        cr, sr = self.target, self.selection_state.rect
        painter.setPen(self.SELECT_PEN)
        painter.setRenderHint(QPainter.RenderHint.Antialiasing, False)
        if self.selection_state.current_mode == 'selected':
            # Shade out areas outside the selection rect
            for r in (
                    QRectF(cr.topLeft(), QPointF(sr.left(),
                                                 cr.bottom())),  # left
                    QRectF(QPointF(sr.left(), cr.top()), sr.topRight()),  # top
                    QRectF(QPointF(sr.right(), cr.top()),
                           cr.bottomRight()),  # right
                    QRectF(sr.bottomLeft(), QPointF(sr.right(),
                                                    cr.bottom())),  # bottom
            ):
                painter.fillRect(r, self.SHADE_COLOR)

            dr = self.get_drag_rect()
            if self.selection_state.in_selection and dr is not None:
                # Draw the resize rectangle
                painter.save()
                painter.setCompositionMode(
                    QPainter.CompositionMode.RasterOp_SourceAndNotDestination)
                painter.setClipRect(sr.adjusted(1, 1, -1, -1))
                painter.drawRect(dr)
                painter.restore()

        # Draw the selection rectangle
        painter.setCompositionMode(
            QPainter.CompositionMode.RasterOp_SourceAndNotDestination)
        painter.drawRect(sr)

    def paintEvent(self, event):
        QWidget.paintEvent(self, event)
        p = QPainter(self)
        p.setRenderHints(QPainter.RenderHint.Antialiasing
                         | QPainter.RenderHint.SmoothPixmapTransform)
        try:
            self.draw_background(p)
            if self.original_image_data is None:
                return
            if not self.is_valid:
                return self.draw_image_error(p)
            self.load_pixmap()
            self.draw_pixmap(p)
            if self.selection_state.rect is not None:
                self.draw_selection_rect(p)
        finally:
            p.end()