Can now render a canvas without the need for a View
This commit is contained in:
parent
5d0ccf5c76
commit
7b96d45e08
@ -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()
|
||||||
|
@ -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))
|
||||||
|
@ -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
|
||||||
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):
|
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
|
||||||
|
@ -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,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -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()
|
||||||
|
@ -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()
|
||||||
|
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user