Provide connection to lines

This commit is contained in:
Arjan Molenaar 2020-11-01 22:43:58 +01:00
parent 8173c47dab
commit 9ea1407955
12 changed files with 91 additions and 91 deletions

View File

@ -270,7 +270,9 @@ item.py: Element
An element has ``min_height`` and ``min_width`` properties.
>>> from gaphas import Element
>>> e = Element()
>>> from gaphas.connections import Connections
>>> from gaphas.solver import Solver
>>> e = Element(Connections(Solver()))
>>> e.min_height, e.min_width
(Variable(10, 100), Variable(10, 100))
>>> e.min_height, e.min_width = 30, 40
@ -293,7 +295,7 @@ A line has the following properties: ``line_width``, ``fuzziness``,
>>> from gaphas import Line
>>> from gaphas.segment import Segment
>>> l = Line()
>>> l = Line(Connections(Solver()))
Let's first add a segment to the line, to test orthogonal lines as well.
@ -350,7 +352,7 @@ Also creation and removal of connected lines is recorded and can be undone:
>>> canvas.add(b0)
>>> b1 = Item()
>>> canvas.add(b1)
>>> l = Line()
>>> l = Line(Connections(Solver()))
>>> canvas.add(l)
>>> real_connect(l, l.handles()[0], b0)
>>> real_connect(l, l.handles()[1], b1)

View File

@ -32,9 +32,12 @@ class Connections:
solver = property(lambda s: s._solver)
def add_constraint(self, item, constraint):
self._solver.add_constraint(constraint)
self._connections.insert(item, None, None, None, constraint, None)
return constraint
def remove_constraint(self, item, constraint):
self._solver.remove_constraint(constraint)
self._connections.delete(item, None, None, None, constraint, None)
@observed

View File

@ -192,8 +192,9 @@ class Element(Item):
min_width = variable(strength=REQUIRED, varname="_min_width")
min_height = variable(strength=REQUIRED, varname="_min_height")
def __init__(self, width=10, height=10):
def __init__(self, connections, width=10, height=10):
super().__init__()
self._connections = connections
self._handles = [h(strength=VERY_STRONG) for h in [Handle] * 4]
handles = self._handles
@ -213,21 +214,19 @@ class Element(Item):
# initialize min_x variables
self.min_width, self.min_height = 10, 10
self.constraint(horizontal=(h_nw.pos, h_ne.pos))
self.constraint(horizontal=(h_sw.pos, h_se.pos))
self.constraint(vertical=(h_nw.pos, h_sw.pos))
self.constraint(vertical=(h_ne.pos, h_se.pos))
add = connections.add_constraint
add(self, constraint(horizontal=(h_nw.pos, h_ne.pos)))
add(self, constraint(horizontal=(h_sw.pos, h_se.pos)))
add(self, constraint(vertical=(h_nw.pos, h_sw.pos)))
add(self, constraint(vertical=(h_ne.pos, h_se.pos)))
# create minimal size constraints
self.constraint(left_of=(h_nw.pos, h_se.pos), delta=self.min_width)
self.constraint(above=(h_nw.pos, h_se.pos), delta=self.min_height)
add(self, constraint(left_of=(h_nw.pos, h_se.pos), delta=self.min_width))
add(self, constraint(above=(h_nw.pos, h_se.pos), delta=self.min_height))
self.width = width
self.height = height
# TODO: constraints that calculate width and height based on handle pos
# self.constraints.append(EqualsConstraint(p1[1], p2[1], delta))
def setup_canvas(self):
super().setup_canvas()
@ -315,8 +314,9 @@ class Line(Item):
draw an arrow point).
"""
def __init__(self):
def __init__(self, connections):
super().__init__()
self._connections = connections
self._handles = [Handle(connectable=True), Handle((10, 10), connectable=True)]
self._ports = []
self._update_ports()
@ -351,7 +351,7 @@ class Line(Item):
return
for c in self._orthogonal_constraints:
self._canvas.solver.remove_constraint(c)
self._connections.remove_constraint(self, c)
del self._orthogonal_constraints[:]
if not orthogonal:
@ -361,16 +361,16 @@ class Line(Item):
# if len(h) < 3:
# self.split_segment(0)
eq = EqualsConstraint # lambda a, b: a - b
add = self._canvas.solver.add_constraint
add = self._connections.add_constraint
cons = []
rest = self._horizontal and 1 or 0
for pos, (h0, h1) in enumerate(zip(h, h[1:])):
p0 = h0.pos
p1 = h1.pos
if pos % 2 == rest: # odd
cons.append(add(eq(a=p0.x, b=p1.x)))
cons.append(add(self, eq(a=p0.x, b=p1.x)))
else:
cons.append(add(eq(a=p0.y, b=p1.y)))
cons.append(add(self, eq(a=p0.y, b=p1.y)))
p1.x.notify()
p1.y.notify()
self._set_orthogonal_constraints(cons)
@ -427,21 +427,6 @@ class Line(Item):
horizontal = reversible_property(lambda s: s._horizontal, _set_horizontal)
def setup_canvas(self):
"""Setup constraints.
In this case orthogonal.
"""
super().setup_canvas()
self._update_orthogonal_constraints(self.orthogonal)
def teardown_canvas(self):
"""Remove constraints created in setup_canvas()."""
super().teardown_canvas()
assert self._canvas
for c in self._orthogonal_constraints:
self._canvas.solver.remove_constraint(c)
@observed
def _reversible_insert_handle(self, index, handle):
self._handles.insert(index, handle)

View File

@ -23,22 +23,23 @@ class SimpleCanvas:
def __init__(self):
self.canvas = Canvas()
self.connections = self.canvas.connections
self.box1 = Box()
self.box1 = Box(self.connections)
self.canvas.add(self.box1)
self.box1.matrix.translate(100, 50)
self.box1.width = 40
self.box1.height = 40
self.canvas.request_update(self.box1)
self.box2 = Box()
self.box2 = Box(self.connections)
self.canvas.add(self.box2)
self.box2.matrix.translate(100, 150)
self.box2.width = 50
self.box2.height = 50
self.canvas.request_update(self.box2)
self.line = Line()
self.line = Line(self.connections)
self.head = self.line.handles()[0]
self.tail = self.line.handles()[-1]
self.tail.pos = 100, 100

View File

@ -10,8 +10,8 @@ from gaphas.matrix import Matrix
def test_update_matrices():
"""Test updating of matrices."""
c = Canvas()
i = Box()
ii = Box()
i = Box(c.connections)
ii = Box(c.connections)
c.add(i)
c.add(ii, i)
@ -24,8 +24,8 @@ def test_update_matrices():
def test_reparent():
c = Canvas()
b1 = Box()
b2 = Box()
b1 = Box(c.connections)
b2 = Box(c.connections)
c.add(b1)
c.add(b2, b1)
c.reparent(b2, None)
@ -36,10 +36,10 @@ def count(i):
def test_connect_item():
b1 = Box()
b2 = Box()
line = Line()
c = Canvas()
b1 = Box(c.connections)
b2 = Box(c.connections)
line = Line(c.connections)
c.add(b1)
c.add(b2)
c.add(line)
@ -54,10 +54,10 @@ def test_connect_item():
def test_disconnect_item_with_callback():
b1 = Box()
b2 = Box()
line = Line()
c = Canvas()
b1 = Box(c.connections)
b2 = Box(c.connections)
line = Line(c.connections)
c.add(b1)
c.add(b2)
c.add(line)
@ -76,10 +76,10 @@ def test_disconnect_item_with_callback():
def test_disconnect_item_with_constraint():
b1 = Box()
b2 = Box()
line = Line()
c = Canvas()
b1 = Box(c.connections)
b2 = Box(c.connections)
line = Line(c.connections)
c.add(b1)
c.add(b2)
c.add(line)
@ -98,10 +98,10 @@ def test_disconnect_item_with_constraint():
def test_disconnect_item_by_deleting_element():
b1 = Box()
b2 = Box()
line = Line()
c = Canvas()
b1 = Box(c.connections)
b2 = Box(c.connections)
line = Line(c.connections)
c.add(b1)
c.add(b2)
c.add(line)
@ -121,10 +121,10 @@ def test_disconnect_item_by_deleting_element():
def test_disconnect_item_with_constraint_by_deleting_element():
b1 = Box()
b2 = Box()
line = Line()
c = Canvas()
b1 = Box(c.connections)
b2 = Box(c.connections)
line = Line(c.connections)
c.add(b1)
c.add(b2)
c.add(line)
@ -150,15 +150,15 @@ def test_remove_connected_item():
from gaphas.aspect import ConnectionSink, Connector
l1 = Line()
l1 = Line(canvas.connections)
canvas.add(l1)
b1 = Box()
b1 = Box(canvas.connections)
canvas.add(b1)
number_cons1 = len(canvas.solver.constraints)
b2 = Box()
b2 = Box(canvas.connections)
canvas.add(b2)
number_cons2 = len(canvas.solver.constraints)

View File

@ -12,8 +12,8 @@ def connections():
def test_reconnect_item(connections):
i = item.Line()
ii = item.Line()
i = item.Line(connections)
ii = item.Line(connections)
cons1 = EqualsConstraint(i.handles()[0].pos.x, i.handles()[0].pos.x)
cons2 = EqualsConstraint(i.handles()[0].pos.y, i.handles()[0].pos.y)
@ -30,7 +30,7 @@ def test_reconnect_item(connections):
def test_add_item_constraint(connections):
i = item.Line()
i = item.Line(connections)
c1 = EqualsConstraint(i.handles()[0].pos.x, i.handles()[0].pos.x)
connections.add_constraint(i, c1)
@ -42,7 +42,7 @@ def test_add_item_constraint(connections):
def test_remove_item_constraint(connections):
i = item.Line()
i = item.Line(connections)
c1 = EqualsConstraint(i.handles()[0].pos.x, i.handles()[0].pos.x)
connections.add_constraint(i, c1)
@ -52,7 +52,7 @@ def test_remove_item_constraint(connections):
def test_remove_item_constraint_when_item_is_disconnected(connections):
i = item.Line()
i = item.Line(connections)
c1 = EqualsConstraint(i.handles()[0].pos.x, i.handles()[0].pos.x)
connections.add_constraint(i, c1)

View File

@ -10,7 +10,7 @@ from gaphas.item import Element as Box
class CanvasBox:
def __init__(self):
self.canvas = Canvas()
self.box = Box()
self.box = Box(self.canvas.connections)
self.handles = self.box.handles()

View File

@ -2,8 +2,10 @@ import pytest
from gi.repository import Gtk
from gaphas.canvas import Canvas
from gaphas.connections import Connections
from gaphas.guide import Guide, GuidedItemInMotion
from gaphas.item import Element, Line
from gaphas.solver import Solver
from gaphas.view import GtkView
@ -14,33 +16,38 @@ class Window:
self.window = Gtk.Window()
self.window.add(self.view)
self.window.show_all()
self.line = Line()
self.line = Line(self.canvas.connections)
self.canvas.add(self.line)
self.e1 = Element()
self.e2 = Element()
self.e3 = Element()
self.e1 = Element(self.canvas.connections)
self.e2 = Element(self.canvas.connections)
self.e3 = Element(self.canvas.connections)
@pytest.fixture()
@pytest.fixture
def win():
test_window = Window()
yield test_window
test_window.window.destroy()
@pytest.fixture
def connections(win):
return win.canvas.connections
def test_find_closest(win):
"""Test find closest method."""
set1 = [0, 10, 20]
set2 = [2, 15, 30]
guider = GuidedItemInMotion(Element(), win.view)
guider = GuidedItemInMotion(Element(win.canvas.connections), win.view)
d, closest = guider.find_closest(set1, set2)
assert 2.0 == d
assert [2.0] == closest
def test_element_guide():
e1 = Element()
e1 = Element(Connections(Solver()))
assert 10 == e1.width
assert 10 == e1.height
guides = Guide(e1).horizontal()

View File

@ -5,16 +5,17 @@ from gaphas.item import Line
from gaphas.segment import Segment
def test_initial_ports(revert_undo):
def test_initial_ports():
"""Test initial ports amount."""
line = Line()
canvas = Canvas()
line = Line(canvas.connections)
assert 1 == len(line.ports())
def test_orthogonal_horizontal_undo(revert_undo, undo_fixture):
"""Test orthogonal line constraints bug (#107)."""
canvas = Canvas()
line = Line()
line = Line(canvas.connections)
canvas.add(line)
assert not line.horizontal
assert len(canvas.solver._constraints) == 0
@ -45,7 +46,7 @@ def test_orthogonal_horizontal_undo(revert_undo, undo_fixture):
def test_orthogonal_line_undo(revert_undo, undo_fixture):
"""Test orthogonal line undo."""
canvas = Canvas()
line = Line()
line = Line(canvas.connections)
canvas.add(line)
segment = Segment(line, canvas)

View File

@ -10,7 +10,7 @@ from gaphas.view import GtkView
class SegmentFixture:
def __init__(self):
self.canvas = Canvas()
self.line = Line()
self.line = Line(self.canvas.connections)
self.canvas.add(self.line)
self.view = GtkView(self.canvas)
self.item = Item()
@ -33,7 +33,7 @@ def test_segment_fails_for_item(seg):
def test_segment(seg):
"""Test add a new segment to a line."""
line = Line()
line = Line(seg.canvas.connections)
seg.canvas.add(line)
segment = Segment(line, seg.canvas)
assert 2 == len(line.handles())
@ -145,7 +145,7 @@ def test_ports_after_split(simple_canvas):
def test_constraints_after_split(simple_canvas):
"""Test if constraints are recreated after line split."""
# Connect line2 to self.line
line2 = Line()
line2 = Line(simple_canvas.connections)
simple_canvas.canvas.add(line2)
head = line2.handles()[0]
simple_canvas.tool.connect(line2, head, (25, 25))
@ -206,19 +206,19 @@ def test_orthogonal_line_split(simple_canvas):
def test_params_error_exc(simple_canvas):
"""Test parameter error exceptions."""
line = Line()
line = Line(simple_canvas.connections)
segment = Segment(line, simple_canvas.canvas)
# There is only 1 segment
with pytest.raises(ValueError):
segment.split_segment(-1)
line = Line()
line = Line(simple_canvas.connections)
segment = Segment(line, simple_canvas.canvas)
with pytest.raises(ValueError):
segment.split_segment(1)
line = Line()
line = Line(simple_canvas.connections)
# Can't split into one or less segment :)
segment = Segment(line, simple_canvas.canvas)
with pytest.raises(ValueError):
@ -264,7 +264,7 @@ def test_merge_first_single(simple_canvas):
def test_constraints_after_merge(simple_canvas):
"""Test if constraints are recreated after line merge."""
line2 = Line()
line2 = Line(simple_canvas.connections)
simple_canvas.canvas.add(line2)
head = line2.handles()[0]
@ -363,7 +363,7 @@ def test_orthogonal_line_merge(simple_canvas):
@pytest.mark.parametrize("num_segments", [-1, 2, (0, 1), 0, 1, (0, 3)])
def test_params_errors(simple_canvas, num_segments):
"""Test parameter error exceptions."""
line = Line()
line = Line(simple_canvas.connections)
simple_canvas.canvas.add(line)
segment = Segment(line, simple_canvas.canvas)
with pytest.raises(ValueError):

View File

@ -5,13 +5,13 @@ from gaphas.item import Line
def test_undo_on_delete_element(revert_undo, undo_fixture):
b1 = Box()
b2 = Box()
line = Line()
canvas = Canvas()
b1 = Box(canvas.connections)
b2 = Box(canvas.connections)
line = Line(canvas.connections)
canvas.add(b1)
assert 6 == len(canvas.solver.constraints)
assert 12 == len(canvas.solver.constraints)
canvas.add(b2)
assert 12 == len(canvas.solver.constraints)

View File

@ -17,8 +17,9 @@ class ViewFixture:
self.window.add(self.view)
self.window.show_all()
self.box = Box()
self.box = Box(self.canvas.connections)
self.canvas.add(self.box)
self.connections = self.canvas.connections
# Process pending (expose) events, which cause the canvas to be drawn.
while Gtk.events_pending():
@ -51,7 +52,7 @@ def test_get_unselected_item_at_point(view_fixture):
def test_get_handle_at_point(view_fixture):
box = Box()
box = Box(view_fixture.connections)
box.min_width = 20
box.min_height = 30
box.matrix.translate(20, 20)
@ -64,7 +65,7 @@ def test_get_handle_at_point(view_fixture):
def test_get_handle_at_point_at_pi_div_2(view_fixture):
box = Box()
box = Box(view_fixture.connections)
box.min_width = 20
box.min_height = 30
box.matrix.translate(20, 20)