Canvas Render - How to?

Not perfect, but can give some Ideas.

"""
tested in Krita 4.4.8 Win 10
Notes:
    create new document before running.
    coordinates are little bit wrong. (integer vs float pixels...)
"""

from PyQt5.QtCore import (
        Qt,
        QEvent,
        QPointF,
        QRect)

from PyQt5.QtGui import (
        QTransform,
        QPainter,
        QBrush,
        QColor,
        QPolygonF,
        QInputEvent)

from PyQt5.QtWidgets import (
        QWidget,
        QMdiArea,
        QAbstractScrollArea)


def get_q_view(view):
    window = view.window()
    q_window = window.qwindow()
    q_stacked_widget = q_window.centralWidget()
    q_mdi_area = q_stacked_widget.findChild(QMdiArea)
    for v, q_mdi_view in zip(window.views(), q_mdi_area.subWindowList()):
        if v == view:
            return q_mdi_view.widget()


def get_transform(view):
    def _offset(scroller):
        mid = (scroller.minimum() + scroller.maximum()) / 2.0
        return -(scroller.value() - mid)
    canvas = view.canvas()
    document = view.document()
    q_view = get_q_view(view)
    area = q_view.findChild(QAbstractScrollArea)
    zoom = (canvas.zoomLevel() * 72.0) / document.resolution()
    transform = QTransform()
    transform.translate(
            _offset(area.horizontalScrollBar()),
            _offset(area.verticalScrollBar()))
    transform.rotate(canvas.rotation())
    transform.scale(zoom, zoom)
    return transform


class MyOverlay(QWidget):
    _degree = 6
    _colors = [
            QBrush(QColor(0, 0, 255)),
            QBrush(QColor(0, 255, 0)),
            QBrush(QColor(255, 0, 0)),
            QBrush(QColor(255, 0, 255)),
            QBrush(QColor(0, 255, 255)),
            QBrush(QColor(255, 255, 0))]

    def __init__(self, view):
        parent = get_q_view(view)
        super().__init__(parent)
        # ToDo: is there better way to ignore events?
        self.setAttribute(Qt.WA_TransparentForMouseEvents)
        self.setFocusPolicy(Qt.NoFocus)
        self._view = view
        q_canvas = parent.findChild(QAbstractScrollArea).viewport()
        size = q_canvas.size()
        self.setGeometry(QRect(0, 0, size.width(), size.height()))
        parent.installEventFilter(self)

    def paintEvent(self, e):
        document = self._view.document()
        width = float(document.width())
        height = float(document.height())
        half_width = 0.5 * width
        half_height = 0.5 * height
        triangle = (
                QPointF(-half_width, half_height),
                QPointF(0.0, -half_height),
                QPointF(half_width, half_height))
        # Turtles all the way down
        painter = QPainter(self)
        try:
            painter.setRenderHint(QPainter.Antialiasing, True)
            painter.translate(self.rect().center())
            painter.setTransform(get_transform(self._view), combine=True)
            painter.setPen(Qt.NoPen)
            self.draw_sierpinski(painter, triangle, self._degree)
        finally:
            painter.end()

    def draw_sierpinski(self, painter, triangle, degree):
        painter.setBrush(self._colors[degree % len(self._colors)])
        polygon = QPolygonF()
        polygon.append(triangle[0])
        polygon.append(triangle[1])
        polygon.append(triangle[2])
        painter.drawConvexPolygon(polygon)
        if degree > 0:
            next_degree = degree - 1
            mid_1 = 0.5 * (triangle[0] + triangle[1])
            mid_2 = 0.5 * (triangle[0] + triangle[2])
            mid_3 = 0.5 * (triangle[1] + triangle[2])
            self.draw_sierpinski(painter, (triangle[0], mid_1, mid_2), next_degree)
            self.draw_sierpinski(painter, (triangle[1], mid_1, mid_3), next_degree)
            self.draw_sierpinski(painter, (triangle[2], mid_3, mid_2), next_degree)

    def eventFilter(self, obj, e):
        if e.type() == QEvent.Resize:
            q_canvas = self.parent().findChild(QAbstractScrollArea).viewport()
            size = q_canvas.size()
            self.setGeometry(QRect(0, 0, size.width(), size.height()))
        return super().eventFilter(obj, e)


active_window = Application.activeWindow()
active_view = active_window.activeView()
if active_view.document() is None:
    raise RuntimeError('Document of active view is None!')
my_overlay = MyOverlay(active_view)
my_overlay.show()

/AkiR

3 Likes