Draw a line with arrow from shape1 to shape2. Code didn't work.

Hi

I wrote some code to let user draw a line with arrow from shape1 to shape2.
The code has no error, but it doesn't work, I can draw nothing. So would you
please have look at my code and tell me what's wrong?

Thanks line-event.py
<http://wxpython-users.1045709.n5.nabble.com/file/n5717248/line-event.py>

···

--
View this message in context: http://wxpython-users.1045709.n5.nabble.com/Draw-a-line-with-arrow-from-shape1-to-shape2-Code-didn-t-work-tp5717248.html
Sent from the wxPython-users mailing list archive at Nabble.com.

Gabrielle wrote:

I wrote some code to let user draw a line with arrow from shape1 to shape2.
The code has no error, but it doesn't work, I can draw nothing. So would you
please have look at my code and tell me what's wrong?

You have quite a number of problems.

You do this during the event handler initialization:
        self.ms = wx.MouseState()
then you refer to self.ms several times later. What this does is grab a
snapshot of the mouse state at the time the event handler is created.
At that time, the mouse buttons are all up and the mouse location is
0,0, and that's what you were using all throughout your code. That
object is not automatically updated with the latest information.
However, you don't even need this object; each of your drag routines is
handed the current x,y as parameters.

Next, you need to add control points to your line before you starting
changing the ends, using self.line.MakeLineControlPoints(2).

Next, you never added the lines to your canvas. You had a line object,
but it was not connected to any canvas.

Next, after changing the line positions, you never asked for the window
to be redrawn so nothing was ever shown.

I would argue that the HitTest calls in your code are completely
unnecessary. The event handler would not have been called unless the
mouse was impinging on this object. However, they don't hurt anything.

You could have learned a lot of this yourself by doing some very basic
debugging. If you had added print statements in your handler to print
out the values of the variables you were using, you would have seen
right away that the calls to self.ms.GetX() and self.ms.GetY() were not
doing what you thought they were.

This seems to do what you want:

class MyEvtHandler(ogl.ShapeEvtHandler):
   
    def __init__(self, frame,shape):
        ogl.ShapeEvtHandler.__init__(self,frame,shape)
        self.shape = self.GetShape()
        self.shape.SetAttachmentMode(True)
        self.line = ogl.LineShape()
        self.line.MakeLineControlPoints(2)
        self.shape.GetCanvas().diagram.AddShape(self.line)

    def OnBeginDragLeft(self, x,y, keys=0, attachment=0):
        print "OnBeginDragLeft", x, y
        if self.shape.HitTest(x, y):
            self.line.AddArrow(ogl.ARROW_ARROW)
            self.line.SetFrom(self.shape)
            self.line.Show(True)
            self.shape.GetCanvas().Refresh()
     
    def OnDragLeft(self,draw,x,y,keys,attachment):
        x1 = self.shape.GetX()
        y1 = self.shape.GetY()
        print "OnDragLeft", x, y, x1, y1
        self.line.SetEnds(x,y,x1,y1)
        self.line.Show(True)
        self.shape.GetCanvas().Refresh()
           
    def OnEndDragLeft(self,x,y,keys,attachment):
        print "OnEndDragLeft", x, y
        if self.shape.HitTest(x, y):
            self.line.SetTo(self.shape)
            self.line.Show(True)
            self.shape.GetCanvas().Refresh()

    def OnMoveLink(self,dc,moveControlPoints):
        pass

···

--
Tim Roberts, timr@probo.com
Providenza & Boekelheide, Inc.

Hi Tim

Thanks for your helping.Your code did work.
And then, I added a event for shapes and there came other problems.

Q1: How could I put the end point of line onto the edge of second shape?
      You see, what I can do now, is to draw a line from the edge of a
shape. But the end of line can be any where. Even though the end point of
line is on the other shape, the end point can't attach the edge of second
shape automatically. Please see the 'line test new' demo, and 'line control
point' demo.

Q2: What I want to do is to draw a line from the edge of a shape, to
realize this, I coded
        x0 = self.shape.GetX()
        y0 = self.shape.GetY()
        x1,y1 = self.shape.GetPerimeterPoint(x0,y0,x,y)
        print "OnDragLeft", x, y, x1, y1
        self.line.SetEnds(x1,y1,x,y)

    but, I can also draw line inside the shape. I really don't want that. Do
you know how to solve this?

Q3: I added an event for shapes so that we can select the shape and change
the size of shape. But after changing the size of shape, the line can't
change. The start point will be in or out of the shape. What I want is to
make the start point always stay on the edge of shape, no matter how the
shape is changed.
Do you know how to do this? It's hard to find a demo which can realize these
functions.

I have some ideas, but I didn't work it out.
There is a method shape.AddLine(self, line, other, attachFrom, attachTo,
positionFrom, positionTo) . This method can make a line attach to the edge
of two shapes. If I draw a line with code, this method works well. But if I
draw a line with mouse , it would be difficult to realize. I don't know how
to do that.

All the questions above could be solved if there is a way to make the start
point and end point of line attach to the edge of shapes. And there are some
attachment methods but I really don't understand how to make them work.

I will appreciate it if you can help me to solve these problems. Even some
ideas would be helpful.

The attachments are two demos.

line_test_new.py
<http://wxpython-users.1045709.n5.nabble.com/file/n5717269/line_test_new.py>
line_control_point.py
<http://wxpython-users.1045709.n5.nabble.com/file/n5717269/line_control_point.py>

···

--
View this message in context: http://wxpython-users.1045709.n5.nabble.com/Draw-a-line-with-arrow-from-shape1-to-shape2-Code-didn-t-work-tp5717248p5717269.html
Sent from the wxPython-users mailing list archive at Nabble.com.

Gabrielle wrote:

Q1: How could I put the end point of line onto the edge of second shape?
      You see, what I can do now, is to draw a line from the edge of a
shape. But the end of line can be any where. Even though the end point of
line is on the other shape, the end point can't attach the edge of second
shape automatically.

Right. The library doesn't know what effect you're trying to achieve.
This is why objects in applications like Visio and Illustrator have
specific attachment points on their edges -- points on each edge where
the object would prefer to have connections made. Then, when the user
makes a connection, the application figures out which of the "preferred"
attachment points is closest to the mouse, and "snaps" the endpoint to
that position.

Q2: What I want to do is to draw a line from the edge of a shape, to
realize this, I coded
        x0 = self.shape.GetX()
        y0 = self.shape.GetY()
        x1,y1 = self.shape.GetPerimeterPoint(x0,y0,x,y)
        print "OnDragLeft", x, y, x1, y1
        self.line.SetEnds(x1,y1,x,y)

but, I can also draw line inside the shape. I really don't want that. Do
you know how to solve this?

I don't know what you mean here. If the user attaches a line to the
opposite side of the object and drags the line across the object, why
should you stop him from doing that? If you really want the connector
to have the shortest possible line, then you will need to change the
endpoints after you know BOTH ends, by determining the points on each
object that are closest to each other.

Q3: I added an event for shapes so that we can select the shape and change
the size of shape. But after changing the size of shape, the line can't
change. The start point will be in or out of the shape. What I want is to
make the start point always stay on the edge of shape, no matter how the
shape is changed.
Do you know how to do this? It's hard to find a demo which can realize these
functions.

There's not going to be a demo for this kind of thing. To solve
problems like this, you need to think about how YOU, as a human, would
solve it, and you're going to need to use some geometry to so so. What
you want to do is move the endpoint to the closest position that is on
the perimeter of the new object. It's the same problem as the
"snapping" described above. You might consider writing a general
purpose function that says "given this object and this point, find the
closest point on the perimeter of the object".

All the questions above could be solved if there is a way to make the start
point and end point of line attach to the edge of shapes. And there are some
attachment methods but I really don't understand how to make them work.

It's a complicated problem, and you're going to find when you are done
that there are special cases it won't handle. What do you do when lines
cross? What if I drag one object to the other side? Should I move the
point? For a square object, should the attachment only be at the center
of a side, or can it be anywhere along the perimeter? For a circle,
should it only attach at 90 degree increments? 45 degree increments?
Arbitrarily?

I hope you weren't thinking you could write Inkscape in 30 lines of
code. This is complicated.

···

--
Tim Roberts, timr@probo.com
Providenza & Boekelheide, Inc.

Hi Tim

I think I didn't make the right way to draw a line to realize what I want.
There are some methods realated to attachment, I think these are the methods
I need to use.But I didn't find any demo to use them. Do you have some ideas
on using these methods?

Thanks

···

--
View this message in context: http://wxpython-users.1045709.n5.nabble.com/Draw-a-line-with-arrow-from-shape1-to-shape2-Code-didn-t-work-tp5717248p5717288.html
Sent from the wxPython-users mailing list archive at Nabble.com.