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

View File

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

View File

@ -98,10 +98,21 @@ class BoundingBoxPainter:
def __init__( def __init__(
self, self,
item_painter: ItemPainterType, 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.item_painter = item_painter
if bounding_box_updater:
self.bounding_box_updater = 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): def paint_item(self, item, cairo):
cairo = CairoBoundingBoxContext(cairo) cairo = CairoBoundingBoxContext(cairo)
@ -118,7 +129,8 @@ class BoundingBoxPainter:
bounds.expand(1) bounds.expand(1)
self.bounding_box_updater(item, bounds) self.bounding_box_updater(item, bounds)
def paint(self, items: Sequence[Item], cairo): def paint(self, items: Sequence[Item], cairo) -> BoundingBoxPainter:
"""Draw the items.""" """Draw the items."""
for item in items: for item in items:
self.paint_item(item, cairo) 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 cairo import LINE_JOIN_ROUND
from gaphas.canvas import Context from gaphas.canvas import Context
from gaphas.item import Item from gaphas.item import Item
from gaphas.view.selection import Selection
# The tolerance for Cairo. Bigger values increase speed and reduce accuracy # The tolerance for Cairo. Bigger values increase speed and reduce accuracy
# (default: 0.1) # (default: 0.1)
@ -25,25 +26,26 @@ class ItemPainter:
draw_all = False draw_all = False
def __init__(self, view=None): def __init__(self, selection: Optional[Selection] = None):
assert view self.selection = selection
self.view = view
def paint_item(self, item, cairo): def paint_item(self, item, cairo):
view = self.view
cairo.save() cairo.save()
try: try:
cairo.transform(item.matrix_i2c.to_cairo()) cairo.transform(item.matrix_i2c.to_cairo())
selection = self.selection
if not selection:
selection = Selection()
item.draw( item.draw(
DrawContext( DrawContext(
painter=self, painter=self,
cairo=cairo, cairo=cairo,
_item=item, _item=item,
selected=(item in view.selection.selected_items), selected=(item in selection.selected_items),
focused=(item is view.selection.focused_item), focused=(item is selection.focused_item),
hovered=(item is view.selection.hovered_item), hovered=(item is selection.hovered_item),
dropzone=(item is view.selection.dropzone_item), dropzone=(item is selection.dropzone_item),
draw_all=self.draw_all, draw_all=self.draw_all,
) )
) )

View File

@ -15,7 +15,7 @@ class View:
self._matrix = Matrix() self._matrix = Matrix()
self._painter: Painter = DefaultPainter(self) self._painter: Painter = DefaultPainter(self)
self._bounding_box_painter: Painter = BoundingBoxPainter( 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() 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.canvas import Canvas
from gaphas.item import Item from gaphas.item import Item
from gaphas.segment import HandleFinder, Line, Segment, SegmentHandleFinder from gaphas.segment import HandleFinder, Line, Segment, SegmentHandleFinder
from gaphas.view import View from gaphas.view import GtkView
class SegmentFixture: class SegmentFixture:
@ -12,7 +12,7 @@ class SegmentFixture:
self.canvas = Canvas() self.canvas = Canvas()
self.line = Line() self.line = Line()
self.canvas.add(self.line) self.canvas.add(self.line)
self.view = View(self.canvas) self.view = GtkView(self.canvas)
self.item = Item() self.item = Item()

View File

@ -6,7 +6,7 @@ from gi.repository import Gtk
from gaphas.canvas import Canvas from gaphas.canvas import Canvas
from gaphas.item import Element as Box from gaphas.item import Element as Box
from gaphas.view import GtkView, View from gaphas.view import GtkView
class ViewFixture: class ViewFixture:
@ -91,14 +91,6 @@ def test_item_removal(view_fixture):
def test_view_registration(view_fixture): def test_view_registration(view_fixture):
canvas = Canvas() 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 # GTK view does register for updates though
view = GtkView(canvas) view = GtkView(canvas)