I'm working on this Block and Port implementation again. I am working on
a 'basic' block design now, which would be a rectangular box with a
specified number of input ports on the left and a number of output ports
on the right.
My issue is that it would be nice to inherit from Element, which does
the job of adding resize handles to the corners of the box, but I would
also like to inherit from Block, which does the job of keeping track of
Ports.
Down the track I will want to be able to create blocks that are not
inheriting from Element, eg round shapes, SVG shapes, etc.
So it's a multiple inheritance situation, right? What do you think I
should do here?
Cheers
JP
On Thu, May 22, 2008 at 9:16 AM, John Pye <jo...@curioussymbols.com> wrote:
>
> Hi Arjan
>
> I'm working on this Block and Port implementation again. I am working on
> a 'basic' block design now, which would be a rectangular box with a
> specified number of input ports on the left and a number of output ports
> on the right.
>
> My issue is that it would be nice to inherit from Element, which does
> the job of adding resize handles to the corners of the box, but I would
> also like to inherit from Block, which does the job of keeping track of
> Ports.
>
> Down the track I will want to be able to create blocks that are not
> inheriting from Element, eg round shapes, SVG shapes, etc.
Is it not nice to be able to resize round shaped elements and SVG elements too?
That means you can just inherit from Element.
> So it's a multiple inheritance situation, right? What do you think I
> should do here?
Hmm.. Basically all that Block adds are ports, so there should be no
problem. If you definitely want to use the Ports with other existing
element types you may want to consider a PortSupport class you can add
to any Item on an as-needed basis. That's the trick I use in Gaphor
(e.g. both element and line may contain editable text).
Regards,
Arjan
i wonder if we can make gaphas more pluggable.
let's say we introduce class attribute gaphas.item.Item._extension.
it would be a dictionary specyfing item extension with extension
name as a key (i.e. 'ports') and extension data (i.e. ports data providing
information for ports related tools, painters, etc).
imho, it would be also useful in case of other concepts like item align,
item annotation with images or adding editable text support to items.
regards,
wrobell <wro...@pld-linux.org>
Can't we solve it better with Mixins then?
regards,
Arjan
I've taken your advice and made Block a subclass of Element. I've got
something now that draws simple DefaultBlock objects with two inputs
ports and two output ports.
You can see the code so far here:
http://ascendcode.cheme.cmu.edu/viewvc.cgi/code/branches/extfn/pygtk/canvas/
I need your advice on the next bits though:
1. How can I make the ports scale their location correctly as the
block is resize? Would this be best done using constraints, or
would it be better to just calculate it manually in terms of the
known object width and height? Or does the local item coordinate
system change as the object is scaled/resized, and should I use
i2v or something like that?
2. Should the Port object have its own draw method, or is drawing
these objects best done in the Block class?
Basically I have currently got the lines snapping to the Port locations,
but I haven't got them connecting correctly yet, and the Port locations
don't scale properly as the Block is resized.
Cheers
JP
Any suggestions, Arjan?
Code is here:
http://ascendcode.cheme.cmu.edu/viewvc.cgi/code/branches/extfn/pygtk/canvas/
You can test it with 'python gaphas-test.py'. Draw a 'block' then a
'line' then attempt to connect the line to one of the yellow ports. I
get the error:
Found glue point Variable(146, 20) Variable(-70.5, 20)
Gluing to port <port.Port object at 0x83da1ac>
p1 = <gaphas.canvas.CanvasProjection object at 0x83daecc>
p1[0] = Variable(146, 20)
Traceback (most recent call last):
File "/var/lib/python-support/python2.5/gaphas/view.py", line 718, in
do_event
return getattr(self._tool, handler)(ToolContext(view=self), event)
and True or False
File "/var/lib/python-support/python2.5/gaphas/tool.py", line 228, in
on_button_release
self._handle('on_button_release', context, event)
File "/var/lib/python-support/python2.5/gaphas/tool.py", line 215, in
_handle
return getattr(self._grabbed_tool, func)(context, event)
File "/var/lib/python-support/python2.5/gaphas/tool.py", line 463, in
on_button_release
self.connect(view, self._grabbed_item, self._grabbed_handle,
event.x, event.y)
File "/home/john/ascend/pygtk/canvas/port.py", line 322, in connect
,p2=CanvasProjection(glue_port.pos, glue_port.block)
File "/home/john/ascend/pygtk/canvas/port.py", line 126, in __init__
super(PointConstraint,
self).__init__(p1[0].variable(),p1[1].variable(),p2[0].variable(),p2[1].variable())
File "/var/lib/python-support/python2.5/gaphas/constraint.py", line
67, in __init__
self.create_weakest_list()
File "/var/lib/python-support/python2.5/gaphas/constraint.py", line
74, in create_weakest_list
strength = min(v.strength for v in self._variables)
File "/var/lib/python-support/python2.5/gaphas/constraint.py", line
74, in <genexpr>
strength = min(v.strength for v in self._variables)
AttributeError: 'int' object has no attribute 'strength'
Cheers
JP
On Thu, May 29, 2008 at 4:03 PM, John Pye <jo...@curioussymbols.com> wrote:
>
> A bit more to report on this. I tried to add a new 'PointConstraint'
> class (in port.py) to ensure that a Handle stays in the same place as a
> Port when the Block is moved. But I can't make it work. I think there is
> something strange happening with the CanvasProjection class that I don't
> understand.
>
> Any suggestions, Arjan?
I haven't tried the code yet, but doesn't the EqualsConstraint do the
job (of course you'll need one for x and one for y)?
I'll get back to you about this.
Regards,
Arjan
On Thu, May 29, 2008 at 1:13 PM, John Pye <jo...@curioussymbols.com> wrote:
>
> Hi Arjan
>
> I've taken your advice and made Block a subclass of Element. I've got
> something now that draws simple DefaultBlock objects with two inputs
> ports and two output ports.
>
> You can see the code so far here:
> http://ascendcode.cheme.cmu.edu/viewvc.cgi/code/branches/extfn/pygtk/canvas/
>
> I need your advice on the next bits though:
>
> 1. How can I make the ports scale their location correctly as the
> block is resize? Would this be best done using constraints, or
> would it be better to just calculate it manually in terms of the
> known object width and height? Or does the local item coordinate
> system change as the object is scaled/resized, and should I use
> i2v or something like that?
You may want to look at how Element does it. Element keeps its handles
aligned using
constraints (item.py line 309).
> 2. Should the Port object have its own draw method, or is drawing
> these objects best done in the Block class?
I'd suggest creating a special Painter for drawing the ports. That way
ports can be drawn on to of the items (always, even if there's an item
overlaying the one being drawn (huh? ;) )) . Have a look at
HandlePainter, it does almost what you want (just replace Handle with
Port).
> Basically I have currently got the lines snapping to the Port locations,
> but I haven't got them connecting correctly yet, and the Port locations
> don't scale properly as the Block is resized.
Regards,
Arjan
Arjan Molenaar wrote:
> Hi John,
>
> On Thu, May 29, 2008 at 4:03 PM, John Pye <jo...@curioussymbols.com> wrote:
>
>> A bit more to report on this. I tried to add a new 'PointConstraint'
>> class (in port.py) to ensure that a Handle stays in the same place as a
>> Port when the Block is moved. But I can't make it work. I think there is
>> something strange happening with the CanvasProjection class that I don't
>> understand.
>>
>> Any suggestions, Arjan?
>>
>
> I haven't tried the code yet, but doesn't the EqualsConstraint do the
> job (of course you'll need one for x and one for y)?
>
> I'll get back to you about this.
>
I needed to create a single PointConstraint class so that I could assign
the constraint to the handle.connection_data property for the purpose of
later removing the constraint.
I have narrowed the problem down to something going wrong inside
PointConstraint; I'll keep working on it now. It's probably a silly
mistake with var references and equality checks.
Cheers
JP
I made a bit more progess. I changed the PointConstraint class so that
it pretty much ignores the 'var' parameter in the 'solve_for' method, in
the same way that the LineConstraint does. Changes here:
http://ascendcode.cheme.cmu.edu/viewvc.cgi/code/branches/extfn/pygtk/canvas/
Now I'm stuck again. When I create a connection between two Blocks, the
connecting lines moves correctly when I resize the blocks, but not when
I *move* one of the blocks.
Also, when I resize a block using a handle on the left-hand side of the
Block, the Ports on the right edge of the block remains fixed in the
local coordinate system (relative to the top-left of the block) rather
than being constrained to remain on the right-hand edge of the block as
specified by the ports' constraints. This doesn't happen when I resize a
block using a handle on the right-hand side, so I can't tell what's
wrong there.\
Any suggestions?
Cheers
JP
You can also add a tuple or list to connection_data. That works as well (IIRC).
John Pye wrote:
> Now I'm stuck again. When I create a connection between two Blocks, the
> connecting lines moves correctly when I resize the blocks, but not when
> I *move* one of the blocks.
>
> Also, when I resize a block using a handle on the left-hand side of the
> Block, the Ports on the right edge of the block remains fixed in the
> local coordinate system (relative to the top-left of the block) rather
> than being constrained to remain on the right-hand edge of the block as
> specified by the ports' constraints. This doesn't happen when I resize a
> block using a handle on the right-hand side, so I can't tell what's
> wrong there.
>
To try to get the bottom of this stuff, perhaps you could describe to me
the whole chain of events that leads to lines being moved when a block
is resized. Then, again, the chain of events that leads to lines being
moved when a block is *resized*. How could it be that in one case my
code does the right thing, but in the other it does not?
Cheers
JP
Okay. sounds reasonable, since the port coordinates should not change
when a handle changes.
> Now I'm stuck again. When I create a connection between two Blocks, the
> connecting lines moves correctly when I resize the blocks, but not when
> I *move* one of the blocks.
Moving a block does not automatically mark the port's variables as dirty.
For handles this happens in canvas.py line 436.
Hmm.. The fact that this is not happening for ports is not good, you
may consider this a bug: if you move an item, all variables that are
projected using CanvasProjection should be marked as dirty. This has
to be done before the solver does its thing (hence in pre-update or
something.
If you look at the code, the following is happening:
- Items marked for update have their pre_update() method called
- matrices for those items are updated, child items are also updated
(if one item moves, it's child items do too)
- for this full set of items the handle variables are marked as dirty
if they are used through a Projection (it implies a constraint uses
variables owned by different items).
So, marking items as dirty is possible in the item's pre_update(), or
I should introduce a special method in Canvas that can be overruled
(this is probably nicer).
> Also, when I resize a block using a handle on the left-hand side of the
> Block, the Ports on the right edge of the block remains fixed in the
> local coordinate system (relative to the top-left of the block) rather
> than being constrained to remain on the right-hand edge of the block as
> specified by the ports' constraints. This doesn't happen when I resize a
> block using a handle on the right-hand side, so I can't tell what's
> wrong there.\
This looks like the same issue: when you move the top-left, the matrix
is updated and in this case the constraints based on Port variables
are not recalculated.
> Any suggestions?
BTW. What's the URL I can use to checkout your code? That way I can
play with it and see what's happening.
Regards,
Arjan
I hope my previous mail explained some of it.
When you move a top or left handle, in addition to a resize also a
translation (move) is performed on the matrix (so the top-left remains
(0, 0)).
> Cheers
> JP
>
>
> >
>
Regards,
Arjan
OK, I'll try your new code.
Our code can be checked out via
svn co
http://ascendsvn.cheme.cmu.edu/ascend/code/branches/extfn/pygtk/canvas
canvas
BTW I wrote a page with some thoughts on the process of adding a
'canvas-based modeller' to ASCEND, which you might want to look at or
comment on:
http://ascendwiki.cheme.cmu.edu/Canvas-based_modeller_for_ASCEND
Currently the server seems to be down though! I'll go and chase down the
reason why, maybe you could try a bit later on.
Cheers
JP
i hope that i won't add more confusion with my post :) it is quite long
and it does not explain everything but it should help.
as you probably know at the moment, there are several coordinate systems
in gaphas:
- canvas item's coordinate system (it is internal to canvas item; other
items know nothing about other items at this stage)
- canvas coordinate system
- multiple views coordinate systems to make things bit more flexible
what you are interested in are two first coordinate systems - canvas item's
and canvas, therefore let's completely skip views coordinate system for now.
unfortunately i cannot see your code now, but i assume that Block derives
from gaphas.item.Element, which means that probably you have 4 handles
(the only important thing is that there is at least one handle).
now, let's assume that you want just to draw a Block. we speak in terms of
canvas item's coordinate system, which means that first handle is always in
(0, 0) position (this is top-left handle). first and the rest of the handles
create a rectangle.
let's assume that bottom-right handles is at position (20, 10) - in terms
of Block item coordinate system. what does it mean in terms of canvas
coordinate system? if top-left handle is at position (100, 100) of canvas,
then bottom-right handle is in position (120, 110) of canvas.
if you move bottom-right handle, for example to position (130, 120)
in terms of canvas coordinate system, then
- handle position in terms of Block's coordinate system changes to (30, 20)
- top-left handle is unchanged, nothing happened for it
if you move top-left handle, let's say to position (90, 90) of canvas,
then it is bit more tricky as it is (-10, -10) in terms of canvas item
coordinate system. few things are done by Gaphas
- first handle position is changed to (0, 0) - item's coordinate system
- position (item's coordinate system) of other handles is updated to keep
the handles in the same position (canvas coordinate system) on canvas;
this means, that bottom-right handle position is changed to (40, 30),
which is (130, 120) on canvas
if you move the canvas item itself, then all handles are moved, first
handle is updated to be in (0, 0) and the rest of handles are updated as
well using the same procedure as above.
i hope that it is clear so far.
now, a bit about constraints. constraints operate only in terms of item's
coordinate system. it works very well in case of Element, where constraints
are used to maintain shape of rectangle made with 4 handles...
... and the tricky part comes. if constraint variables are kept in item's
coordinate system, then how to keep handle position of a line on a Block?
it is tricky, because a line has its own coordinate system and a Block
has other one, both are independent of each other.
to fix that, we introduced canvas projection. canvas projection translates
variables of constraints into canvas coordinate system. i will talk about
it below.
how i would solve problem of connecting a handle to a port? let's assume
that port is a point at (5, 2) - Block's coordinate system of course (later
it will be really easy to make it much more flexible)
if handle of a line is moved near a Block and it is detected that actually
a handle should be connected to a port, therefore you need to create a pair
of constraints
- "equals" constraint to keep Handle.x on position 5
- "equals" constraint to keep Handle.y on position 2
please, keep in mind that we use Block's coordinate system. as simple like
that.
what happens when top-left handle of a Block is moved (or when whole Block
is moved)? Gaphas will perform all the calculations (keep top-left handle
at (0, 0), constraint solving, etc.) and _connected_ handle of a line will
follow. this is fully automated by Gaphas.
however, please note that moment of glueing to a port is not automated,
because you need to
- get (x, y) of line's handle (the handle which connects to a Block's port)
- convert (x, y) from line's coordinate system into _Block's_ coordinate
system
- check if handle should be connected to a port (i.e. if it is close enough
to position of a port, this is (5, 2) in our case)
also, the moment of connection is not automated
- you need to create the constraints
- also constraint solver needs to know that constraints should be solved
using canvas projection (CanvasProjection class)
- constraints needs to be added to constraint solver
gaphas/examples.py provides an example how to do glueing and connecting.
if you need more help, then please let us know. imho, Gaphas fails here
a bit as it could be more automated, but hopefully, we will improve it
soon.
speaking about port flexibility. imho, port is always a point. you can
draw a rectangle/circle/whatever to indicate that there is a port, but
when line's handle is connected, then only 2 constraints are required to
keep handle in one place (one for x and one for y). above, i used "equals"
constraints. of course you can use more flexible constraints (see
gaphas.constraint module) realized by classes
- CenterConstraint
- BalanceConstraint
please provide us a svn link, so we can checkout the code. sending you
a patch to "visualise" above description would probably help a lot, too :)
best regards,
wrobell <wro...@pld-linux.org>
wrobell wrote:
> please provide us a svn link, so we can checkout the code. sending you
> a patch to "visualise" above description would probably help a lot, too :)
>
Thanks for the detailed description of the coordinate transformations. I
guess I had it roughly like that in my mind, but a clear description was
very helpful, especially as relates the CanvasProjection stuff.
Due to servers being relocated our SVN is offline this week (yes, all
week), our svn URL is not much use just now. I did send it in a
previously email though.
Instead, I've attached a tarball containing the current files that I
have on my machine. You can unpack them to their own directory, then so
long as you have gaphas installed on your PYTHONPATH, you can just run
the gaphas-test.py script.
Hope this will serve the purpose for now.
Cheers
JP
I thought I should respond in detail to this email of yours, so that you
know what I was thinking as your read the code that I sent earlier.
wrobell wrote:
> ...
> i hope that it is clear so far.
>
The behaviour of handles as far as resizing goes appears to be working
but there is the problem of the ports not being correctly 'triggered' to
relocate.
I suspect that the problem here is the code in the canvas.py file. I
think that the new 'update_constraints' method is a step in the right
direction, but instead of looping through the handles belonging to each
item, it should just loop through each item and call an
'update_constraints' method. Then, it can be up to the Item how it wants
to deal with its constraints. This would allow me to add some code for
Blocks so that they can update their Ports as well as just their Handles.
> now, a bit about constraints. constraints operate only in terms of item's
> coordinate system. it works very well in case of Element, where constraints
> are used to maintain shape of rectangle made with 4 handles...
>
> ... and the tricky part comes. if constraint variables are kept in item's
> coordinate system, then how to keep handle position of a line on a Block?
> it is tricky, because a line has its own coordinate system and a Block
> has other one, both are independent of each other.
>
> to fix that, we introduced canvas projection. canvas projection translates
> variables of constraints into canvas coordinate system. i will talk about
> it below.
>
> how i would solve problem of connecting a handle to a port? let's assume
> that port is a point at (5, 2) - Block's coordinate system of course (later
> it will be really easy to make it much more flexible)
>
> if handle of a line is moved near a Block and it is detected that actually
> a handle should be connected to a port, therefore you need to create a pair
> of constraints
> - "equals" constraint to keep Handle.x on position 5
> - "equals" constraint to keep Handle.y on position 2
> please, keep in mind that we use Block's coordinate system. as simple like
> that.
>
In my 'port.py' file, I have added a PointConstraint that does a similar
job to a pair of EqualsConstraints, except that it makes the assumption
that the Handle location is always made to comply with the Port
location, and never the other way around.
I'm not sure how elaborate the constraint solver in Gaphas is; I wonder
if there could possibly be some issues with implementing a constraint in
this manner. However, a similar trick is used in LineConstraint and that
seems to work OK.
> what happens when top-left handle of a Block is moved (or when whole Block
> is moved)? Gaphas will perform all the calculations (keep top-left handle
> at (0, 0), constraint solving, etc.) and _connected_ handle of a line will
> follow. this is fully automated by Gaphas.
>
> however, please note that moment of glueing to a port is not automated,
> because you need to
> - get (x, y) of line's handle (the handle which connects to a Block's port)
> - convert (x, y) from line's coordinate system into _Block's_ coordinate
> system
> - check if handle should be connected to a port (i.e. if it is close enough
> to position of a port, this is (5, 2) in our case)
>
> also, the moment of connection is not automated
> - you need to create the constraints
> - also constraint solver needs to know that constraints should be solved
> using canvas projection (CanvasProjection class)
> - constraints needs to be added to constraint solver
>
These things are all implemented in port.py as previously attached.
> gaphas/examples.py provides an example how to do glueing and connecting.
> if you need more help, then please let us know. imho, Gaphas fails here
> a bit as it could be more automated, but hopefully, we will improve it
> soon.
>
> speaking about port flexibility. imho, port is always a point. you can
> draw a rectangle/circle/whatever to indicate that there is a port, but
> when line's handle is connected, then only 2 constraints are required to
> keep handle in one place (one for x and one for y). above, i used "equals"
> constraints. of course you can use more flexible constraints (see
> gaphas.constraint module) realized by classes
> - CenterConstraint
> - BalanceConstraint
>
Currently, the job of drawing Ports is left to the Block. Arjan
suggested that I might want to have a Port 'layer' via the Painter
classes, but my view is that that might not be necessary and that for a
port to be obscured behind another thing wouldn't be a problem.
> please provide us a svn link, so we can checkout the code. sending you
> a patch to "visualise" above description would probably help a lot, too :)
>
I'll be very keen to hear your suggestions for this Port/Block stuff if
you can manage it.
Cheers
JP
well, i think i have a solution. please update gaphas from subversion.
i've changed the handle connection tool, which is in gaphas.examples
to allow to connect a line to any element.
i am also attaching a patch against your code - attaching to a port works
but without bells & whistles :)
looking at your code i see that you managed to use constraints in your
favour... i am talking about keeping ports on the edge of Block. it is
really cool to see constraints in real action (besides gaphor of course) :]
also, i was wrong above. see below...
speaking about PointConstraint. imho, it is completely unnecessary. you
just need to connect to particular point on a Block. this is realized with
Block.glue (please note my change to return a point instead of port) and
gaphas.examples.ConnectingHandleTool (which uses LineConstraint).
i've left PortConnectingHandleTool to indicate that you can code some
additional behaviour in this class, i.e. connecting only to input ports,
etc.
[...]
> Currently, the job of drawing Ports is left to the Block. Arjan
> suggested that I might want to have a Port 'layer' via the Painter
> classes, but my view is that that might not be necessary and that for a
> port to be obscured behind another thing wouldn't be a problem.
i tend to agree with Arjan. you can have few different types of ports,
drawn different way, which could be "attached" to any diagram item. but
maybe we are wrong, so i would left things in Block.draw for now.
also, i see a possibility to abstract ports idea from Block class (which
derives from Element), i.e. by using painters, mixins, etc... imho, such
idea would be very useful as part of gaphas. we could also reuse it in
gaphor for... UML Ports :)
> > please provide us a svn link, so we can checkout the code. sending you
> > a patch to "visualise" above description would probably help a lot, too :)
> >
>
> I'll be very keen to hear your suggestions for this Port/Block stuff if
> you can manage it.
here you go :)
best regards,
wrobell <wro...@pld-linux.org>
On Thu, Jun 5, 2008 at 12:03 AM, wrobell <wro...@pld-linux.org> wrote:
> On Wed, Jun 04, 2008 at 01:14:56PM +1000, John Pye wrote:
> [...]
>>
>> wrobell wrote:
>> > ...
>> > i hope that it is clear so far.
>> >
>>
>> The behaviour of handles as far as resizing goes appears to be working
>> but there is the problem of the ports not being correctly 'triggered' to
>> relocate.
>>
>> I suspect that the problem here is the code in the canvas.py file. I
>> think that the new 'update_constraints' method is a step in the right
>> direction, but instead of looping through the handles belonging to each
>> item, it should just loop through each item and call an
>> 'update_constraints' method. Then, it can be up to the Item how it wants
>> to deal with its constraints. This would allow me to add some code for
>> Blocks so that they can update their Ports as well as just their Handles.
Delegating the update_constraints() to the dirty items... That means
another update method in Item.
I realize that the Canvas.update_constraints() is not ideal either,
since it makes you to subclass Canvas in order to update ports.
> well, i think i have a solution. please update gaphas from subversion.
> i've changed the handle connection tool, which is in gaphas.examples
> to allow to connect a line to any element.
>
> i am also attaching a patch against your code - attaching to a port works
> but without bells & whistles :)
>
[...]
I was thinking of Ports like the crosses used in Visio and Dia. Those
are drawn on top of the items. Of course the Item may draw a somewhat
more fancy kind of port :).
> also, i see a possibility to abstract ports idea from Block class (which
> derives from Element), i.e. by using painters, mixins, etc... imho, such
> idea would be very useful as part of gaphas. we could also reuse it in
> gaphor for... UML Ports :)
Indeed. I think then we can also abstract the Handle stuff into a
HandleMixin. We already have the Painter and tools.
Regards,
Arjan
Arjan Molenaar wrote:
> Hi,
>
> On Thu, Jun 5, 2008 at 12:03 AM, wrobell <wro...@pld-linux.org> wrote:
>
>> On Wed, Jun 04, 2008 at 01:14:56PM +1000, John Pye wrote:
>> [...]
>>
>>> wrobell wrote:
>>>
>>>> ...
>>>> i hope that it is clear so far.
>>>>
>>>>
>>> The behaviour of handles as far as resizing goes appears to be working
>>> but there is the problem of the ports not being correctly 'triggered' to
>>> relocate.
>>>
>>> I suspect that the problem here is the code in the canvas.py file. I
>>> think that the new 'update_constraints' method is a step in the right
>>> direction, but instead of looping through the handles belonging to each
>>> item, it should just loop through each item and call an
>>> 'update_constraints' method. Then, it can be up to the Item how it wants
>>> to deal with its constraints. This would allow me to add some code for
>>> Blocks so that they can update their Ports as well as just their Handles.
>>>
>
> Delegating the update_constraints() to the dirty items... That means
> another update method in Item.
>
> I realize that the Canvas.update_constraints() is not ideal either,
> since it makes you to subclass Canvas in order to update ports.
>
What do you see as the problem with adding Item.update_constraints()?
What about the idea of Port and Handle being a subclass of some new
superclass that can have its list kept in the Item, and iterated as current.
How about another kind of interactive thing that could be potentially
added to an Item, for example a toggle button or slider etc -- how would
that work?
For the sake of seeing if this update_constraints thing works, I might
give it a try, anyway.
OK, I guess that's a reason thought, maybe we should have a painter
layer for Ports.
>> also, i see a possibility to abstract ports idea from Block class (which
>> derives from Element), i.e. by using painters, mixins, etc... imho, such
>> idea would be very useful as part of gaphas. we could also reuse it in
>> gaphor for... UML Ports :)
>>
>
> Indeed. I think then we can also abstract the Handle stuff into a
> HandleMixin. We already have the Painter and tools.
>
I will have a look at this.
Cheers
JP
if i can answer that... it is possible to change a lot by diagram item in
Item.pre_update method, for example see Interface.pre_update_icon method
(which is called in Interface.pre_update) in Gaphor (line 102, currently)
http://gaphor.devjavu.com/browser/gaphor/trunk/gaphor/diagram/classes/interface.py
please also read gaphas.item.Item.pre_update method documentation.
imho, adding Item.update_constraints is not good idea. some constraints are
specific to the given diagram item, but a lot of them affect also other
diagram items (or subitems to make things more complicated). therefore it
is controlled by canvas, which "knows" about all items.
> What about the idea of Port and Handle being a subclass of some new
> superclass that can have its list kept in the Item, and iterated as current.
>
> How about another kind of interactive thing that could be potentially
> added to an Item, for example a toggle button or slider etc -- how would
> that work?
well, we already handle such things i think... for example in Gaphor
lifeline diagram item has lifetime line realized with additional handle
and constraints - that would be good start for slider :}
> For the sake of seeing if this update_constraints thing works, I might
> give it a try, anyway.
imho, you do not need to do that. please take a look at my patch i have
sent yesterday evening.
[...]
best regards,
wrobell <wro...@pld-linux.org>
It's getting interesting :)
On Thu, Jun 5, 2008 at 3:51 PM, wrobell <wro...@pld-linux.org> wrote:
>
> On Thu, Jun 05, 2008 at 09:54:52PM +1000, John Pye wrote:
>>
>> Hi Arjan
>>
>> Arjan Molenaar wrote:
>> > Hi,
[...]
>> > Delegating the update_constraints() to the dirty items... That means
>> > another update method in Item.
>> >
>> > I realize that the Canvas.update_constraints() is not ideal either,
>> > since it makes you to subclass Canvas in order to update ports.
>> >
>>
>> What do you see as the problem with adding Item.update_constraints()?
>
> if i can answer that... it is possible to change a lot by diagram item in
> Item.pre_update method, for example see Interface.pre_update_icon method
> (which is called in Interface.pre_update) in Gaphor (line 102, currently)
>
> http://gaphor.devjavu.com/browser/gaphor/trunk/gaphor/diagram/classes/interface.py
>
> please also read gaphas.item.Item.pre_update method documentation.
>
> imho, adding Item.update_constraints is not good idea. some constraints are
> specific to the given diagram item, but a lot of them affect also other
> diagram items (or subitems to make things more complicated). therefore it
> is controlled by canvas, which "knows" about all items.
Yep, that's basically it. We want to keep Item clean (as it's the
"model" in mvc).
>> What about the idea of Port and Handle being a subclass of some new
>> superclass that can have its list kept in the Item, and iterated as current.
The thought had crossed my mind. Intentionally they're the same: a
point. Difference is that Handles are used to interact with the item
(resize/scale) while ports are more static.
>> How about another kind of interactive thing that could be potentially
>> added to an Item, for example a toggle button or slider etc -- how would
>> that work?
>
> well, we already handle such things i think... for example in Gaphor
> lifeline diagram item has lifetime line realized with additional handle
> and constraints - that would be good start for slider :}
You'll probably create a Tool (controller) that handles the
button/slider behaviour for such a widget. It may be interesting to
create an example for this.
>> For the sake of seeing if this update_constraints thing works, I might
>> give it a try, anyway.
You can delegate in your own code, yes.
Regards,
Arjan
wrobell wrote:
>
> well, i think i have a solution. please update gaphas from subversion.
> i've changed the handle connection tool, which is in gaphas.examples
> to allow to connect a line to any element.
>
> i am also attaching a patch against your code - attaching to a port works
> but without bells & whistles :)
>
I had a look at your patch and I'm not sure it's going to meet my needs.
Firstly, you removed the storage of the handle->port connection in,
which means that I can't traverse the data structures to work out the
topology of the system that the user has created (I need to be able to
recover the port-to-port connections, so that I can build up the
computational model of the system being represented).
Secondly, you have added constraints to fix the positions of the line's
handle relative to the Block, but this results in a set of constraints
that simply duplicates the same constraints that I have implemented in
order to fix the positions of the ports.The constraints you created are
really redundant, so I think it's possibly that this isn't the best design.
I think that the reason your approach worked was by making the line
handles directly connected to the Block, it somehow makes the 'dirty'
markers work properly, and somehow that isn't working correctly for my
implementation of Ports and handles connected to ports.
I don't think this is the right approach. It leads to redundant
constraints, since I already have constraints in place to position the
port relative to the block, and your approach just duplicates this
constraints in order to place the handle relative to the block in the
exact same place
> i've left PortConnectingHandleTool to indicate that you can code some
> additional behaviour in this class, i.e. connecting only to input ports,
> etc.
>
Given that there is no way of determining from the Line object what sort
of Port the other end of the Line is connected to (or even which
specific Port), I can't see how this would work in your approach.
> [...]
>
>
>> Currently, the job of drawing Ports is left to the Block. Arjan
>> suggested that I might want to have a Port 'layer' via the Painter
>> classes, but my view is that that might not be necessary and that for a
>> port to be obscured behind another thing wouldn't be a problem.
>>
>
> i tend to agree with Arjan. you can have few different types of ports,
> drawn different way, which could be "attached" to any diagram item. but
> maybe we are wrong, so i would left things in Block.draw for now.
>
> also, i see a possibility to abstract ports idea from Block class (which
> derives from Element), i.e. by using painters, mixins, etc... imho, such
> idea would be very useful as part of gaphas. we could also reuse it in
> gaphor for... UML Ports :)
>
I am thinking that there is something important missing here in the way
that constraints are solved and/or 'dirtiness' is propagated through the
canvas.
When I move a Block, I would expect something like the following to occur:
The Block position is updated, its item matrix is updated, which via the
projections for the ports and handles results in the variables for those
items being marked dirty.
When the solver comes along it should look through a list of dirty
*variables* and, using the constraints associated with those variables,
will find some more dirty variables, namely the positions of the handles
on the lines that connect to the ports on the block being moved.
When a variable is marked dirty, it should cause the item containing it
to also be marked dirty, so the lines will also be marked dirty.
Once the solver has done its thing, it the view should be redrawn with
all items from the canvas that are currently in view.
I suspect that somehow the part about the line being dirty is not
working. How does that work in the current code, and what needs to
change for it to work with my Ports?
Cheers
JP
OK, that's fair enough. If that's the case then the canvas needs to
generalise its understanding about what things an item might have that
might need to be updated.
>>> What about the idea of Port and Handle being a subclass of some new
>>> superclass that can have its list kept in the Item, and iterated as current.
>>>
>
> The thought had crossed my mind. Intentionally they're the same: a
> point. Difference is that Handles are used to interact with the item
> (resize/scale) while ports are more static.
>
I can't quite manage to locate where the 'gap' is here, but when I move
a Block, it is not causing a signal to get through to the line so that
it knows that it needs to be updated. Somehow the constraint solver is
not working as I would have expected it to.
My suspicion is that the constraint solver or the canvas has an implicit
assumption that the only variables present in an Item are those relating
to the position of its Handles, or something like that, so my variables
belonging to my Ports are being ignored. Maybe my port variables need to
be explicitly added to the solver, or something?
Cheers
JP
but you can build appropriate data structures and traverse them in
independent way from ports using connection tool (this is the reason
i left it in your code). i would treat ports as an entity, which
forces handle to be connected to particular position under some conditions
(i.e. input/output) - nothing else nothing more. i would put additional
structures somewhere else, which separates model (data structures) from
view (ports).
> Secondly, you have added constraints to fix the positions of the line's
> handle relative to the Block, but this results in a set of constraints
> that simply duplicates the same constraints that I have implemented in
> order to fix the positions of the ports.The constraints you created are
> really redundant, so I think it's possibly that this isn't the best design.
no. as i see it, you need two sets of constraints
- first set keeps position of ports on a block, i.e. ports are distributed
evenly, it is created at diagram item creation
- second set is created, when you connect a handle to an item. these constraints
keep line's handle on an diagram item - it happens that it is on a port
position thanks to Block.glue
both sets are independent from each other. what's nice about such approach
is that you couple ports, item, tools in very loosely way, which allows for
further reuse.
[...]
no, i just reuse existing constraints. just compare your and mine versions.
reuse of a connection tool, line constraints and... less code. which works
straight away. and if you need to build data structures, then just put
more code to the port connection tool. as simple as that.
in your case, you try to use port constraints for two purposes
- keep ports on an diagram item
- maintain handle-port connection relationship
maybe it is possible to make it work, but... for me it is more complicated
thing, which does not reuse existing code.
> > i've left PortConnectingHandleTool to indicate that you can code some
> > additional behaviour in this class, i.e. connecting only to input ports,
> > etc.
> >
>
> Given that there is no way of determining from the Line object what sort
> of Port the other end of the Line is connected to (or even which
> specific Port), I can't see how this would work in your approach.
just override PortConnectingHandleTool.glue method. you can check there
if a handle is allowed to glue to a specific port. if it is not allowed,
then return None. it won't be connected then. and if it is bit more
complicated, then it is still related to the tool, imho.
may i ask you to provide some example of data structures you want to build?
(let's involve also input/output ports into that).
> > [...]
> >
> >
> >> Currently, the job of drawing Ports is left to the Block. Arjan
> >> suggested that I might want to have a Port 'layer' via the Painter
> >> classes, but my view is that that might not be necessary and that for a
> >> port to be obscured behind another thing wouldn't be a problem.
> >>
> >
> > i tend to agree with Arjan. you can have few different types of ports,
> > drawn different way, which could be "attached" to any diagram item. but
> > maybe we are wrong, so i would left things in Block.draw for now.
> >
> > also, i see a possibility to abstract ports idea from Block class (which
> > derives from Element), i.e. by using painters, mixins, etc... imho, such
> > idea would be very useful as part of gaphas. we could also reuse it in
> > gaphor for... UML Ports :)
> >
>
> I am thinking that there is something important missing here in the way
> that constraints are solved and/or 'dirtiness' is propagated through the
> canvas.
well, it works for line constraints, then it should work for "port
constraints", imho. let's discuss whole thing a bit more, find some
consesus and if you really need port constraints, then i promise to
look at port constraints from your point of view.
On Sat, Jun 7, 2008 at 7:22 AM, John Pye <jo...@curioussymbols.com> wrote:
[...]
> I am thinking that there is something important missing here in the way
> that constraints are solved and/or 'dirtiness' is propagated through the
> canvas.
>
> When I move a Block, I would expect something like the following to occur:
>
> The Block position is updated, its item matrix is updated, which via the
> projections for the ports and handles results in the variables for those
> items being marked dirty.
>
> When the solver comes along it should look through a list of dirty
> *variables* and, using the constraints associated with those variables,
> will find some more dirty variables, namely the positions of the handles
> on the lines that connect to the ports on the block being moved.
>
> When a variable is marked dirty, it should cause the item containing it
> to also be marked dirty, so the lines will also be marked dirty.
>
> Once the solver has done its thing, it the view should be redrawn with
> all items from the canvas that are currently in view.
>
> I suspect that somehow the part about the line being dirty is not
> working. How does that work in the current code, and what needs to
> change for it to work with my Ports?
Items that are marked as dirty get their pre_update() method called.
Items that are marked dirty during constraint solving get only their
post_update() method called (as well as the items that have initially
been marked dirty).
There is also a category of items who's matrix has just been marked as
dirty (e.g. when moving the item). Those items get their matrix
updated and their handles are marked for constraint solving (which may
eventually lead to their post_update() method being called).
Regards,
Arjan
Please check the latest copy of port.py and gaphas-test.py from the svn
URL I sent earlier. It's now doing the right thing as far as updating
the lines that are connected to the ports.
There is a small problem with what happens to the ports when a block is
resized on the left-hand-side.
Can you give me your thoughts on this? What do you think is failing to
solve?
Cheers
JP
:) I see.
> Can you give me your thoughts on this? What do you think is failing to
> solve?
Everything solved correctly. The problem is normalization
(gaphas.canvas.Canvas._normalize()). After the constraints are
resolved, the canvas tries to "normalize" the items on the canvas by
moving the item origin (0, 0) to the place where the first handle is
located. As a result the matrix changes, and a correction is done on
the handle positions. This does not trigger the constraint solver,
however.
I fixed in in applied patch. I am aware that this solution is not
ideal: the handle code is mangled to much in the Canvas code.
Regards,
Arjan
Index: port.py
===================================================================
--- port.py (revision 2020)
+++ port.py (working copy)
@@ -400,7 +400,30 @@
super(BlockCanvas,self).update_constraints(items)
+ def _normalize(self, items):
+ """
+ Correct offset of ports due to movement of left-side handles.
+ """
+ dirty_matrix_items = set()
+ for item in items:
+ if not hasattr(item, 'ports'):
+ continue
+ handles = item.handles()
+ ports = item.ports
+ if not handles or not ports:
+ continue
+ x, y = map(float, handles[0].pos)
+ # Dirty marking is done by the superclass' method
+ if x:
+ for p in ports:
+ p.x._value -= x
+ if y:
+ for p in ports:
+ p.y._value -= y
+ dirty_matrix_items.update(super(BlockCanvas, self)._normalize(items))
+ return dirty_matrix_items
+
Cheers
JP
I see this as a workaround. The real solution would be to simplify the
way you can add arbitrary points (be it handles or ports or something
else) to your application.
Regards,
Arjan
> Cheers
> JP
>
>
> >
>
I agree, there is the possibility of generalising the idea of 'points
associated with an item' such that the constraint solver can more easily
iterate through all such points.
Cheers
JP