Can now render a canvas without the need for a View

This commit is contained in:
Arjan Molenaar 2020-10-28 22:50:12 +01:00
parent 5d0ccf5c76
commit 7b96d45e08
7 changed files with 56 additions and 45 deletions

View File

@ -17,7 +17,7 @@ import cairo
import gi
from examples.exampleitems import Box, Circle, FatLine, PortoBox, Text
from gaphas import Canvas, GtkView, View, state
from gaphas import Canvas, GtkView, state
from gaphas.canvas import Context
from gaphas.item import Line
from gaphas.painter import (
@ -112,13 +112,13 @@ def create_window(canvas, title, zoom=1.0): # noqa too complex
view = GtkView()
view.painter = (
PainterChain()
.append(FreeHandPainter(ItemPainter(view)))
.append(FreeHandPainter(ItemPainter(view.selection)))
.append(HandlePainter(view))
.append(FocusedItemPainter(view))
.append(ToolPainter(view))
)
view.bounding_box_painter = BoundingBoxPainter(
FreeHandPainter(ItemPainter(view)), view.bounding_box_updater
FreeHandPainter(ItemPainter(view.selection)), view.bounding_box_updater
)
w = Gtk.Window()
w.set_title(title)
@ -236,25 +236,26 @@ def create_window(canvas, title, zoom=1.0): # noqa too complex
b = Gtk.Button.new_with_label("Write demo.png")
def on_write_demo_png_clicked(button):
svgview = View(view.canvas)
svgview.painter = ItemPainter(svgview)
painter = ItemPainter()
# Update bounding boxes with a temporary CairoContext
# (used for stuff like calculating font metrics)
tmpsurface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 0, 0)
tmpcr = cairo.Context(tmpsurface)
svgview.update_bounding_box(tmpcr)
bounding_box = (
BoundingBoxPainter(painter)
.paint(canvas.get_all_items(), tmpcr)
.bounding_box
)
tmpcr.show_page()
tmpsurface.flush()
w, h = svgview.bounding_box.width, svgview.bounding_box.height
surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, int(w), int(h))
surface = cairo.ImageSurface(
cairo.FORMAT_ARGB32, int(bounding_box.width), int(bounding_box.height)
)
cr = cairo.Context(surface)
svgview.matrix.translate(-svgview.bounding_box.x, -svgview.bounding_box.y)
cr.save()
paint(svgview, cr)
cr.restore()
cr.translate(-bounding_box.x, -bounding_box.y)
painter.paint(items=view.canvas.get_all_items(), cairo=cr)
cr.show_page()
surface.write_to_png("demo.png")
@ -264,22 +265,26 @@ def create_window(canvas, title, zoom=1.0): # noqa too complex
b = Gtk.Button.new_with_label("Write demo.svg")
def on_write_demo_svg_clicked(button):
svgview = View(view.canvas)
svgview.painter = ItemPainter(svgview)
painter = ItemPainter()
# Update bounding boxes with a temporaly CairoContext
# (used for stuff like calculating font metrics)
tmpsurface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 0, 0)
tmpcr = cairo.Context(tmpsurface)
svgview.update_bounding_box(tmpcr)
bounding_box = (
BoundingBoxPainter(painter)
.paint(canvas.get_all_items(), tmpcr)
.bounding_box
)
tmpcr.show_page()
tmpsurface.flush()
w, h = svgview.bounding_box.width, svgview.bounding_box.height
surface = cairo.SVGSurface("demo.svg", w, h)
surface = cairo.SVGSurface(
"demo.svg", int(bounding_box.width), int(bounding_box.height)
)
cr = cairo.Context(surface)
svgview.matrix.translate(-svgview.bounding_box.x, -svgview.bounding_box.y)
paint(svgview, cr)
cr.translate(-bounding_box.x, -bounding_box.y)
painter.paint(items=view.canvas.get_all_items(), cairo=cr)
cr.show_page()
surface.flush()
surface.finish()

View File

@ -12,7 +12,7 @@ def DefaultPainter(view) -> Painter:
"""Default painter, containing item, handle and tool painters."""
return (
PainterChain()
.append(ItemPainter(view))
.append(ItemPainter(view.selection))
.append(HandlePainter(view))
.append(FocusedItemPainter(view))
.append(ToolPainter(view))

View File

@ -98,10 +98,21 @@ class BoundingBoxPainter:
def __init__(
self,
item_painter: ItemPainterType,
bounding_box_updater: Callable[[Item, Rectangle], None],
bounding_box_updater: Optional[Callable[[Item, Rectangle], None]] = None,
):
self.item_painter = item_painter
self.bounding_box_updater = bounding_box_updater
if bounding_box_updater:
self.bounding_box_updater = bounding_box_updater
else:
self.bounding_box = Rectangle()
def default_bounding_box_updater(item, bounds):
if not self.bounding_box:
self.bounding_box = Rectangle(*bounds)
else:
self.bounding_box += bounds
self.bounding_box_updater = default_bounding_box_updater
def paint_item(self, item, cairo):
cairo = CairoBoundingBoxContext(cairo)
@ -118,7 +129,8 @@ class BoundingBoxPainter:
bounds.expand(1)
self.bounding_box_updater(item, bounds)
def paint(self, items: Sequence[Item], cairo):
def paint(self, items: Sequence[Item], cairo) -> BoundingBoxPainter:
"""Draw the items."""
for item in items:
self.paint_item(item, cairo)
return self

View File

@ -1,9 +1,10 @@
from typing import Sequence
from typing import Optional, Sequence
from cairo import LINE_JOIN_ROUND
from gaphas.canvas import Context
from gaphas.item import Item
from gaphas.view.selection import Selection
# The tolerance for Cairo. Bigger values increase speed and reduce accuracy
# (default: 0.1)
@ -25,25 +26,26 @@ class ItemPainter:
draw_all = False
def __init__(self, view=None):
assert view
self.view = view
def __init__(self, selection: Optional[Selection] = None):
self.selection = selection
def paint_item(self, item, cairo):
view = self.view
cairo.save()
try:
cairo.transform(item.matrix_i2c.to_cairo())
selection = self.selection
if not selection:
selection = Selection()
item.draw(
DrawContext(
painter=self,
cairo=cairo,
_item=item,
selected=(item in view.selection.selected_items),
focused=(item is view.selection.focused_item),
hovered=(item is view.selection.hovered_item),
dropzone=(item is view.selection.dropzone_item),
selected=(item in selection.selected_items),
focused=(item is selection.focused_item),
hovered=(item is selection.hovered_item),
dropzone=(item is selection.dropzone_item),
draw_all=self.draw_all,
)
)

View File

@ -15,7 +15,7 @@ class View:
self._matrix = Matrix()
self._painter: Painter = DefaultPainter(self)
self._bounding_box_painter: Painter = BoundingBoxPainter(
ItemPainter(self), self.bounding_box_updater
ItemPainter(self.selection), self.bounding_box_updater # type: ignore[attr-defined]
)
self._qtree: Quadtree[Item, Tuple[float, float, float, float]] = Quadtree()

View File

@ -4,7 +4,7 @@ import pytest
from gaphas.canvas import Canvas
from gaphas.item import Item
from gaphas.segment import HandleFinder, Line, Segment, SegmentHandleFinder
from gaphas.view import View
from gaphas.view import GtkView
class SegmentFixture:
@ -12,7 +12,7 @@ class SegmentFixture:
self.canvas = Canvas()
self.line = Line()
self.canvas.add(self.line)
self.view = View(self.canvas)
self.view = GtkView(self.canvas)
self.item = Item()

View File

@ -6,7 +6,7 @@ from gi.repository import Gtk
from gaphas.canvas import Canvas
from gaphas.item import Element as Box
from gaphas.view import GtkView, View
from gaphas.view import GtkView
class ViewFixture:
@ -91,14 +91,6 @@ def test_item_removal(view_fixture):
def test_view_registration(view_fixture):
canvas = Canvas()
# Simple views do not register on the canvas
view = View(canvas)
assert len(canvas._registered_views) == 0
box = Box()
canvas.add(box)
# GTK view does register for updates though
view = GtkView(canvas)