Newbie question with Gaphas

6 views
Skip to first unread message

John Pye

unread,
May 18, 2008, 4:28:05 AM5/18/08
to gapho...@googlegroups.com
Hi all

I've recently discovered Gaphas and thought I might be able to use it to
implement a process-flow diagram editor, for diagrams of the sort shown
here:
http://www.cocosimulator.org/picscoco/scr0.gif

To get me started, I wonder if anyone might be able to point me in the
right direction with the following.

Firstly, I think I will need a class that is a refinement of something
like gaphas.examples.Box, except that instead of being able to connect a
line to any point on its perimeterr, connectors should be limited to a
discrete set of 'ports' at predefined local (x,y) locations.

Secondly, I will need a line class that only permits orthogonal lines,
to be started from an unused 'port'. When the originating point has been
selected, all the available as-yet-unconnected connector points
elsewhere in the diagram should become lit up in some way.

If someone were able to sketch out for me the sensible approach to this
problem, it would be a really great help and very much appreciated.

As far as I can tell, Gaphas provides the closest FOSS codebase for
performing this task, and with its implementation in pure python on top
of Cairo, it looks like a good cross-platform solution as well.

Cheers
JP

FYI my aim is to work out a 'canvas based' graphical modeller for
ASCEND, the FOSS mathematical modelling environment. This work will be
GPL. http://ascend.cheme.cmu.edu/

Arjan Molenaar

unread,
May 18, 2008, 8:27:31 AM5/18/08
to gapho...@googlegroups.com
Hi John,

On Sun, May 18, 2008 at 10:28 AM, John Pye <jo...@curioussymbols.com> wrote:
>
> Hi all
>
> I've recently discovered Gaphas and thought I might be able to use it to
> implement a process-flow diagram editor, for diagrams of the sort shown
> here:
> http://www.cocosimulator.org/picscoco/scr0.gif

This looks like something that can be done with Gaphas.

> To get me started, I wonder if anyone might be able to point me in the
> right direction with the following.
>
> Firstly, I think I will need a class that is a refinement of something
> like gaphas.examples.Box, except that instead of being able to connect a
> line to any point on its perimeterr, connectors should be limited to a
> discrete set of 'ports' at predefined local (x,y) locations.

You can create box-like elements directly from gaphor.item.Element, as Box does.

to connect items, you'll need to extend the HandleTool. It already has
some hooks defined to implement a "connection protocol", but no
default implementation (apart from the examples, but I'd advise you to
implement your own instead) is available. This is because the actions
that should be performed depend highly on the application. E.g. Gaphor
(the UML tool) requires all sorts of updates to be performed, as items
(shown in diagrams) are backed by a data object.

> Secondly, I will need a line class that only permits orthogonal lines,
> to be started from an unused 'port'. When the originating point has been
> selected, all the available as-yet-unconnected connector points
> elsewhere in the diagram should become lit up in some way.

Orthogonal lines can easily be made by setting the right properties on
a line ("orthogonal" and perhaps "horizontal" to switch the alignment
of the first line segment).

The concept of ports is interesting and may be something you want in
Gaphas. A Port is basically a Handle: it is used to connect a line
with an Element. However, Ports are different since they are not used
to resize the Element, but instead will create a new line on the
canvas if not connected.

I think I would solve this by creating a special Port class (you can
borrow some code from Handle (or should they need a common superclass
:) ) ). There's no need to inherit from Handle IMHO. A PortPainter
should be created that draws the ports (see HandlerPiainter in
painter.py). Also a PortTool should be created, that kinda behaves
like a PlacementTool.

The HandleTool's glue(), connect() and disconnect() methods should be
implemented in such a way that they try to connect a handle to a port.

> If someone were able to sketch out for me the sensible approach to this
> problem, it would be a really great help and very much appreciated.

I hope the above makes sense to you. The port concept as you describe
it is very simple and usable. It fits in the design of Gaphas and
would make a nice extension to the current functionality.

> As far as I can tell, Gaphas provides the closest FOSS codebase for
> performing this task, and with its implementation in pure python on top
> of Cairo, it looks like a good cross-platform solution as well.

Thanks. I'm very happy with the design myself. Note that this is a
re-implementation of DiaCanvas (http://diacanvas.sf.net). I learned a
lot from writing DiaCanvas and as a result Gaphas has become much more
generic and much more flexible.

Regards,

Arjan

John Pye

unread,
May 18, 2008, 8:49:38 AM5/18/08
to gapho...@googlegroups.com
Hi Arjan

Thanks very much for getting back to me so soon! Some followups below...

Arjan Molenaar wrote:
> Hi John,
>
> On Sun, May 18, 2008 at 10:28 AM, John Pye <jo...@curioussymbols.com> wrote:
>
>> Hi all
>>
>> I've recently discovered Gaphas and thought I might be able to use it to
>> implement a process-flow diagram editor, for diagrams of the sort shown
>> here:
>> http://www.cocosimulator.org/picscoco/scr0.gif
>>
>
> This looks like something that can be done with Gaphas.
>

Great!

>> To get me started, I wonder if anyone might be able to point me in the
>> right direction with the following.
>>
>> Firstly, I think I will need a class that is a refinement of something
>> like gaphas.examples.Box, except that instead of being able to connect a
>> line to any point on its perimeterr, connectors should be limited to a
>> discrete set of 'ports' at predefined local (x,y) locations.
>>
>
> You can create box-like elements directly from gaphor.item.Element, as Box does.
>
> to connect items, you'll need to extend the HandleTool. It already has
> some hooks defined to implement a "connection protocol", but no
> default implementation (apart from the examples, but I'd advise you to
> implement your own instead) is available. This is because the actions
> that should be performed depend highly on the application. E.g. Gaphor
> (the UML tool) requires all sorts of updates to be performed, as items
> (shown in diagrams) are backed by a data object.
>

OK, I figured I would start with ConnectingHandleTool and work on
changing the 'glue' method to detect only Ports as valid glue_points.

>> Secondly, I will need a line class that only permits orthogonal lines,
>> to be started from an unused 'port'. When the originating point has been
>> selected, all the available as-yet-unconnected connector points
>> elsewhere in the diagram should become lit up in some way.
>>
>
> Orthogonal lines can easily be made by setting the right properties on
> a line ("orthogonal" and perhaps "horizontal" to switch the alignment
> of the first line segment).
>
> The concept of ports is interesting and may be something you want in
> Gaphas. A Port is basically a Handle: it is used to connect a line
> with an Element. However, Ports are different since they are not used
> to resize the Element, but instead will create a new line on the
> canvas if not connected.
>
> I think I would solve this by creating a special Port class (you can
> borrow some code from Handle (or should they need a common superclass
> :) ) ). There's no need to inherit from Handle IMHO. A PortPainter
> should be created that draws the ports (see HandlerPiainter in
> painter.py). Also a PortTool should be created, that kinda behaves
> like a PlacementTool.
>

I was starting to think this way too. Originally I thought that I could
keep track of
these 'ports' in some ad hoc way within my 'Block' class, but it becomes
clear that my Block must be some sort of compound Item, containing many
Port objects to which Line objects can then be connected.

Question: what sort of things can Handles connect to? If they can only
connect to an Item, then my Port must be a subclass of Item, right? Or
is there no such restriction?

Do you think that addition of a Port class would require modification of
the Item class? I would need to add a _ports member (a list), right?

I have an additional thought for the Port class. If an unconnected port
were clicked on, the canvas should start drawing a line that is already
connected to that port. From my tinkering about with the demo.py script,
it seemed that *creating* lines that are already connected is not
possible; one must first create the line 'in space' and then afterwards
connect it to things.

> The HandleTool's glue(), connect() and disconnect() methods should be
> implemented in such a way that they try to connect a handle to a port.
>

Yes, understand that bit now.

>> If someone were able to sketch out for me the sensible approach to this
>> problem, it would be a really great help and very much appreciated.
>>
>
> I hope the above makes sense to you. The port concept as you describe
> it is very simple and usable. It fits in the design of Gaphas and
> would make a nice extension to the current functionality.
>
>
>> As far as I can tell, Gaphas provides the closest FOSS codebase for
>> performing this task, and with its implementation in pure python on top
>> of Cairo, it looks like a good cross-platform solution as well.
>>
>
> Thanks. I'm very happy with the design myself. Note that this is a
> re-implementation of DiaCanvas (http://diacanvas.sf.net). I learned a
> lot from writing DiaCanvas and as a result Gaphas has become much more
> generic and much more flexible.
>

I suppose you have tried using Gaphor/Gaphas on Windows already? It
works OK, I guess?

Cheers
JP

John Pye

unread,
May 18, 2008, 9:07:00 AM5/18/08
to gapho...@googlegroups.com
Me again...

Arjan Molenaar wrote:
> The concept of ports is interesting and may be something you want in
> Gaphas. A Port is basically a Handle: it is used to connect a line
> with an Element. However, Ports are different since they are not used
> to resize the Element, but instead will create a new line on the
> canvas if not connected.
>
> I think I would solve this by creating a special Port class (you can
> borrow some code from Handle (or should they need a common superclass
> :) ) ). There's no need to inherit from Handle IMHO. A PortPainter
> should be created that draws the ports (see HandlerPiainter in
> painter.py). Also a PortTool should be created, that kinda behaves
> like a PlacementTool.

I have been thinking a bit more about the Port idea. One thing that
might be a problem is the fact that there are going to be certain rules
about Ports down the track. For example some ports will be 'outputs' and
others will be 'inputs', and each kind will look a bit different,
probably. When drawing a line, it will only be possible to connect an
output to an input. Furthermore, there will be ports of different types,
probably just distinguished by a string identifier. For example one
might have ports of type 'double' and ports of type 'int' and ports of
type 'water' and ports of type 'electricity'. This would further limit
the types of ports available for connection. When drawing a line from an
'out' port, only 'in' ports of matching type should be available, and
they should somehow glow or be coloured, to make it easy to connect them.

This led me to a problem: as far as I can tell, the 'glue' method for a
Port will only know about the Port and about the Handle, but the Handle
won't know anything about the Port at the other end of the Line. How can
I use the Handle data type to restrict connections between correct types
of Ports?

I hope that this will still be something that can be done without too
much fundamental changing of Gaphas.

Also, are you sure that Port is the best approach -- that I couldn't do
it by just subclassing Item, for example?

Cheers
JP


Arjan Molenaar

unread,
May 18, 2008, 3:26:12 PM5/18/08
to gapho...@googlegroups.com
Hi John,

On Sun, May 18, 2008 at 2:49 PM, John Pye <jo...@curioussymbols.com> wrote:
>
[...]


>> to connect items, you'll need to extend the HandleTool. It already has
>> some hooks defined to implement a "connection protocol", but no
>> default implementation (apart from the examples, but I'd advise you to
>> implement your own instead) is available. This is because the actions
>> that should be performed depend highly on the application. E.g. Gaphor
>> (the UML tool) requires all sorts of updates to be performed, as items
>> (shown in diagrams) are backed by a data object.
>>
>
> OK, I figured I would start with ConnectingHandleTool and work on
> changing the 'glue' method to detect only Ports as valid glue_points.

Looks like a sound approach :)

Handle.connected_to refers to Items. It is used to refer figure out
which items are referring to the specific item too (there's a method
for this in Canvas).

> Do you think that addition of a Port class would require modification of
> the Item class? I would need to add a _ports member (a list), right?

Item should have a _ports member. A ports() method (analogue to
handles) is also desired.
A Port should refer to a Handle and/or visa versa. This could mean an
extra attribute on the Handle class.

Note that when a handle is connected a disconnect() method is defined
on the handle. That method is called when the handle is disconnected.

Also note that disconnecting a handle is a 2-phase process. First the
constraints are removed. This is because otherwise the handle could
not be moved. Second (if the handle is not reconnected to the item)
Handle.disconnect() is called.

> I have an additional thought for the Port class. If an unconnected port
> were clicked on, the canvas should start drawing a line that is already
> connected to that port. From my tinkering about with the demo.py script,
> it seemed that *creating* lines that are already connected is not
> possible; one must first create the line 'in space' and then afterwards
> connect it to things.

This is kinda the same stuff PlacementTool is doing right now. If a
new line is placed on the canvas, it will try to connect one end to
the nearest element and the other end will be moved by HandleTool.

>> The HandleTool's glue(), connect() and disconnect() methods should be
>> implemented in such a way that they try to connect a handle to a port.
>>
>
> Yes, understand that bit now.
>
>>> If someone were able to sketch out for me the sensible approach to this
>>> problem, it would be a really great help and very much appreciated.
>>>
>>
>> I hope the above makes sense to you. The port concept as you describe
>> it is very simple and usable. It fits in the design of Gaphas and
>> would make a nice extension to the current functionality.
>>
>>
>>> As far as I can tell, Gaphas provides the closest FOSS codebase for
>>> performing this task, and with its implementation in pure python on top
>>> of Cairo, it looks like a good cross-platform solution as well.
>>>
>>
>> Thanks. I'm very happy with the design myself. Note that this is a
>> re-implementation of DiaCanvas (http://diacanvas.sf.net). I learned a
>> lot from writing DiaCanvas and as a result Gaphas has become much more
>> generic and much more flexible.
>>
>
> I suppose you have tried using Gaphor/Gaphas on Windows already? It
> works OK, I guess?

I use it at work on windows. Check out the gaphor-win32-libs tree in subversion.

Regards,

Arjan

Arjan Molenaar

unread,
May 18, 2008, 3:34:57 PM5/18/08
to gapho...@googlegroups.com

This looks like something that has to be solved in the HandleTool (the
controller).

> This led me to a problem: as far as I can tell, the 'glue' method for a
> Port will only know about the Port and about the Handle, but the Handle
> won't know anything about the Port at the other end of the Line. How can
> I use the Handle data type to restrict connections between correct types
> of Ports?

Glue() and connect know about the handle and the item owning the
handle (item and handle parameters). The item the handle should
connect to is not given as parameter.
(the docstrings are a bit misleading in this case).

> I hope that this will still be something that can be done without too
> much fundamental changing of Gaphas.

I think it can be implemented quite easily. Maybe Handle needs to have
a special port property, but that's all.

> Also, are you sure that Port is the best approach -- that I couldn't do
> it by just subclassing Item, for example?

Well, Port is a new concept and my first hunch was that is would
require a new class to resemble that concept. It's not an item for
sure ;)

Regards,

Arjan

arjan

unread,
May 19, 2008, 3:03:02 AM5/19/08
to gaphor-dev
Hi,

I created a ticket: http://gaphor.devjavu.com/ticket/121.

John Pye

unread,
May 19, 2008, 8:58:02 AM5/19/08
to gapho...@googlegroups.com
Hi Arjan

arjan wrote:
> Hi,
>
> I created a ticket: http://gaphor.devjavu.com/ticket/121.

Great!

I've had a bit of a go at writing the associated classes for this Port
thing. I wonder if you might be able able to have a look at it. I'm not
sure what to do with the 'connect' method in the
PortConnectingHandleTool. I don't really understand the
'CanvasProjection' thing yet -- do I need to use that here or not?

See attached port.py.

Cheers
JP


port.py

Arjan Molenaar

unread,
May 19, 2008, 10:41:04 AM5/19/08
to gapho...@googlegroups.com
On Mon, May 19, 2008 at 2:58 PM, John Pye <jo...@curioussymbols.com> wrote:
> Hi Arjan
>
> arjan wrote:
>> Hi,
>>
>> I created a ticket: http://gaphor.devjavu.com/ticket/121.
> Great!
>
> I've had a bit of a go at writing the associated classes for this Port
> thing. I wonder if you might be able able to have a look at it. I'm not
> sure what to do with the 'connect' method in the
> PortConnectingHandleTool.

connect() should do the same thing glue() does (so the handle is
properly placed over the port) and then a constraint has to be
created. An equals constraint should do (keeping in mind the handle
and port coordinates are translated to canvas space).

Note that Handle.connection_data can be managed by the undo system :).


> I don't really understand the
> 'CanvasProjection' thing yet -- do I need to use that here or not?

Projections are a nice feature ;). Every Item on the canvas has it's
own coordinate system (origin), relative to it's parent item. This is
done by setting the Item's matrix.

Of course, when connecting a line with a box, you'll have a problem,
since both items live in their own coordinate system. This is where
Projections come into play. A projection simply translates a Variable
(solvable value) from one coordinate system to another. In the case of
CanvasProjection, a point (x, y) is translated from the item space to
canvas space. At that point it's simple to create the constraints
required to keep both items connected.

I hope this helps,

Regards,

Arjan

> See attached port.py.

Looks good. Maybe you want to let a special PortPainter take care of
drawing the port?

Regards,

Arjan

Reply all
Reply to author
Forward
0 new messages