Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Gaphas 4.0 #741

Merged
merged 10 commits into from
Dec 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading