Skip to content

Commit

Permalink
Merge pull request #741 from gaphor/gaphas-4
Browse files Browse the repository at this point in the history
Gaphas 4.0
  • Loading branch information
danyeaw authored Dec 29, 2023
2 parents 30ececc + c2fa990 commit e22548e
Show file tree
Hide file tree
Showing 29 changed files with 257 additions and 608 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ jobs:
strategy:
matrix:
python-version: ['3.9', '3.10', '3.11', '3.12']
gtk-version: ['3.0', '4.0']
gtk-version: ['4.0']
name: build (python ${{ matrix.python-version }}, gtk ${{ matrix.gtk-version }})
outputs:
targz: gaphas-${{ steps.meta.outputs.version }}.tar.gz
Expand Down
4 changes: 2 additions & 2 deletions docs/guide.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ Guides consist of a couple of elements: aspects that hook into the item-drag cyc
... .append(HandlePainter(view))
... .append(GuidePainter(view))
... )
>>> view.add_controller(item_tool(view))
>>> view.add_controller(zoom_tool(view))
>>> view.add_controller(item_tool())
>>> view.add_controller(zoom_tool())

You need to hook up the ``GuidePainter``. The aspect are loaded as soon as the module is loaded.
4 changes: 2 additions & 2 deletions docs/segment.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,5 @@ to actually use it, the ``segment`` module needs to be imported.
... .append(HandlePainter(view))
... .append(LineSegmentPainter(view.selection))
... )
>>> view.add_controller(item_tool(view))
>>> view.add_controller(zoom_tool(view))
>>> view.add_controller(item_tool())
>>> view.add_controller(zoom_tool())
99 changes: 56 additions & 43 deletions examples/demo.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,16 @@
import gi

# fmt: off
GTK3 = "-3" in sys.argv
gi.require_version("Gtk", "3.0" if GTK3 else "4.0")
gi.require_version("Gtk", "4.0")
from gi.repository import Gtk
# fmt: on

from examples.exampleitems import Box, Circle, Text
from gaphas import Canvas
from gaphas.geometry import Rectangle
from gaphas.guide import GuidePainter
from gaphas.item import Line
from gaphas.painter import (
BoundingBoxPainter,
FreeHandPainter,
HandlePainter,
ItemPainter,
Expand All @@ -44,7 +43,6 @@
from gaphas.tool.itemtool import Segment
from gaphas.tool.rubberband import RubberbandPainter, RubberbandState, rubberband_tool
from gaphas.tool.scroll import pan_tool
from gaphas.util import text_extents, text_underline
from gaphas.view import GtkView

# Global undo list
Expand Down Expand Up @@ -111,6 +109,35 @@ def draw(self, context):
text_underline(cr, 0, 0, "Some text(y)")


def text_extents(cr, text, multiline=False):
"""Simple way to determine the size of a piece of text."""
if not text:
return 0, 0

if multiline:
width, height = 0, 0
for line in text.split("\n"):
_x_bear, _y_bear, w, h, _x_adv, _y_adv = cr.text_extents(line)
width = max(width, w)
height += h
else:
_x_bear, _y_bear, width, height, _x_adv, _y_adv = cr.text_extents(text)
# width, height = width + x_bearing, height + y_bearing

return width, height


def text_underline(cr, x, y, text, offset=1.5):
"""Draw text with underline."""
x_bear, y_bear, w, h, x_adv, y_adv = cr.text_extents(text)
cr.move_to(x, y - y_bear)
cr.show_text(text)
cr.move_to(x, y - y_bear + offset)
cr.set_line_width(1.0)
cr.rel_line_to(x_adv, 0)
cr.stroke()


def rubberband_state(view):
try:
return view.rubberband_state
Expand All @@ -121,14 +148,14 @@ def rubberband_state(view):

def apply_default_tool_set(view):
view.remove_all_controllers()
view.add_controller(item_tool(view))
for tool in zoom_tools(view):
view.add_controller(item_tool())
for tool in zoom_tools():
view.add_controller(tool)
view.add_controller(pan_tool(view))
view.add_controller(view_focus_tool(view))
view.add_controller(pan_tool())
view.add_controller(view_focus_tool())

view.add_controller(rubberband_tool(view, rubberband_state(view)))
view.add_controller(hover_tool(view))
view.add_controller(rubberband_tool(rubberband_state(view)))
view.add_controller(hover_tool())
return rubberband_state


Expand All @@ -137,12 +164,12 @@ def unset_placement_tool(gesture, offset_x, offset_y):
apply_default_tool_set(view)

view.remove_all_controllers()
tool = placement_tool(view, factory(view, item_type), handle_index)
tool = placement_tool(factory(view, item_type), handle_index)
tool.connect("drag-end", unset_placement_tool)
for tool in zoom_tools(view):
view.add_controller(tool)
view.add_controller(view_focus_tool(view))
view.add_controller(tool)
for tool in zoom_tools():
view.add_controller(tool)
view.add_controller(view_focus_tool())


def apply_painters(view):
Expand All @@ -158,6 +185,13 @@ def apply_painters(view):
view.bounding_box_painter = painter


def calculate_bounding_box(painter, items):
surface = cairo.RecordingSurface(cairo.Content.COLOR_ALPHA, None)
cr = cairo.Context(surface)
painter.paint(items, cr)
return Rectangle(*surface.ink_extents())


def create_window(canvas, title, zoom=1.0): # noqa too complex
view = GtkView()

Expand All @@ -170,18 +204,18 @@ def create_window(canvas, title, zoom=1.0): # noqa too complex
h = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 6)

def h_append(b):
h.add(b) if GTK3 else h.append(b)
h.append(b)

w.add(h) if GTK3 else w.set_child(h)
w.set_child(h)

# VBox contains buttons that can be used to manipulate the canvas:
v = Gtk.Box.new(Gtk.Orientation.VERTICAL, 6)

def v_append(b):
v.add(b) if GTK3 else v.append(b)
v.append(b)

f = Gtk.Frame()
f.add(v) if GTK3 else f.set_child(v)
f.set_child(v)
h_append(f)

v_append(Gtk.Label.new("Item placement:"))
Expand Down Expand Up @@ -250,15 +284,7 @@ def on_write_demo_png_clicked(_button):
assert view.model
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)
bounding_box = BoundingBoxPainter(painter).bounding_box(
canvas.get_all_items(), tmpcr
)
tmpcr.show_page()
tmpsurface.flush()
bounding_box = calculate_bounding_box(painter, canvas.get_all_items())

surface = cairo.ImageSurface(
cairo.FORMAT_ARGB32, int(bounding_box.width), int(bounding_box.height)
Expand All @@ -278,15 +304,7 @@ def on_write_demo_svg_clicked(button):
assert view.model
painter = ItemPainter()

# Update bounding boxes with a temporarily CairoContext
# (used for stuff like calculating font metrics)
tmpsurface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 0, 0)
tmpcr = cairo.Context(tmpsurface)
bounding_box = BoundingBoxPainter(painter).bounding_box(
canvas.get_all_items(), tmpcr
)
tmpcr.show_page()
tmpsurface.flush()
bounding_box = calculate_bounding_box(painter, canvas.get_all_items())

surface = cairo.SVGSurface(
"demo.svg", int(bounding_box.width), int(bounding_box.height)
Expand Down Expand Up @@ -325,13 +343,10 @@ def on_reattach_clicked(_button, li):
view.set_size_request(150, 120)
s = Gtk.ScrolledWindow.new()
s.set_hexpand(True)
s.add(view) if GTK3 else s.set_child(view)
s.set_child(view)
h_append(s)

if GTK3:
w.show_all()
else:
w.show()
w.present()

w.connect("destroy", lambda w: app.quit())

Expand Down Expand Up @@ -406,8 +421,6 @@ def activate(app):


if __name__ == "__main__":
import sys

if "-p" in sys.argv:
print("Profiling...")
import hotshot
Expand Down
70 changes: 68 additions & 2 deletions examples/exampleitems.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@
These items are used in various tests.
"""
from math import pi

from gaphas.connector import Handle
from gaphas.item import NW, Element, Matrices
from gaphas.util import path_ellipse, text_align, text_multiline


class Box(Element):
Expand Down Expand Up @@ -34,7 +35,7 @@ class Text(Matrices):

def __init__(self, text=None, plain=False, multiline=False, align_x=1, align_y=-1):
super().__init__()
self.text = text is None and "Hello" or text
self.text = "Hello" if text is None else text
self.plain = plain
self.multiline = multiline
self.align_x = align_x
Expand Down Expand Up @@ -94,3 +95,68 @@ def draw(self, context):
cr = context.cairo
path_ellipse(cr, 0, 0, 2 * self.radius, 2 * self.radius)
cr.stroke()


def text_align(cr, x, y, text, align_x=0, align_y=0, padding_x=0, padding_y=0):
"""Draw text relative to (x, y).
x, y - coordinates
text - text to print (utf8)
align_x - -1 (top), 0 (middle), 1 (bottom)
align_y - -1 (left), 0 (center), 1 (right)
padding_x - padding (extra offset), always > 0
padding_y - padding (extra offset), always > 0
"""
if not text:
return

x_bear, y_bear, w, h, _x_adv, _y_adv = cr.text_extents(text)
if align_x == 0:
x = 0.5 - (w / 2 + x_bear) + x
elif align_x < 0:
x = -(w + x_bear) + x - padding_x
else:
x = x + padding_x
if align_y == 0:
y = 0.5 - (h / 2 + y_bear) + y
elif align_y < 0:
y = -(h + y_bear) + y - padding_y
else:
y = -y_bear + y + padding_y
cr.move_to(x, y)
cr.show_text(text)


def text_multiline(cr, x, y, text):
"""Draw a string of text with embedded newlines.
cr - cairo context
x - leftmost x
y - topmost y
text - text to draw
"""
if not text:
return
for line in text.split("\n"):
_x_bear, _y_bear, _w, h, _x_adv, _y_adv = cr.text_extents(text)
y += h
cr.move_to(x, y)
cr.show_text(line)


def path_ellipse(cr, x, y, width, height, angle=0):
"""Draw an ellipse.
x - center x
y - center y
width - width of ellipse (in x direction when angle=0)
height - height of ellipse (in y direction when angle=0)
angle - angle in radians to rotate, clockwise
"""
cr.save()
cr.translate(x, y)
cr.rotate(angle)
cr.scale(width / 2.0, height / 2.0)
cr.move_to(1.0, 0.0)
cr.arc(0.0, 0.0, 1.0, 0.0, 2.0 * pi)
cr.restore()
8 changes: 4 additions & 4 deletions examples/simple-box.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@

def apply_default_tool_set(view):
view.remove_all_controllers()
view.add_controller(item_tool(view))
view.add_controller(zoom_tool(view))
view.add_controller(view_focus_tool(view))
view.add_controller(hover_tool(view))
view.add_controller(item_tool())
view.add_controller(zoom_tool())
view.add_controller(view_focus_tool())
view.add_controller(hover_tool())


def create_canvas(canvas, title):
Expand Down
12 changes: 0 additions & 12 deletions gaphas/aspect/__init__.py

This file was deleted.

10 changes: 0 additions & 10 deletions gaphas/aspect/connector.py

This file was deleted.

3 changes: 0 additions & 3 deletions gaphas/aspect/handlemove.py

This file was deleted.

2 changes: 0 additions & 2 deletions gaphas/aspect/move.py

This file was deleted.

Loading

0 comments on commit e22548e

Please sign in to comment.