• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 520
  • Last Modified:

How do I create a Graphic with moveable objects link by lines (wires) glued to the objects

Using Delphi 7, I need to create an application that can be used to show wiring between objects, thus a graphic application to which one can add as many as required graphic objects represented in the attached diagram example as Objects A, B and C.  Each object, has a variable number of connection points, blocks shown as 1 to x as per the example.  Thus by changing a variable an object with the appropriate number of connector blocks is created.

Once the Object blocks with their connector blocks have been created on the graphic, the idea would be to click on, for example, Object As connector block 6 and drag a line to Object Bs connector block 2, and so connect the two points with a line.  This process is then repeated for each required connection.

The connector blocks should be attached to the Object blocks and should it be necessary to move an Object block to another place on the graphic, then it should be able to drag the Object block together with its connector blocks while the lines should remain glued to the connector blocks and be repainted to show the connections to the same connector blocks of the Object in its new position.
Graphic-object-link-example.JPG
0
HenryM2
Asked:
HenryM2
  • 9
  • 9
1 Solution
 
SteveBayCommented:
0
 
HenryM2Author Commented:
The solution offered only addresses part of the problem, namely dragging a line between lebels.  I need to have the lines glued to the labels / objects when they are dragged
0
 
TheRealLokiSenior DeveloperCommented:
actually, I've written a better bit of code that more closely matches what you want, I'll try to dig it up...
0
Free Tool: Path Explorer

An intuitive utility to help find the CSS path to UI elements on a webpage. These paths are used frequently in a variety of front-end development and QA automation tasks.

One of a set of tools we're offering as a way of saying thank you for being a part of the community.

 
HenryM2Author Commented:
Thanks, I am looking forward to that.  HenryM2
0
 
TheRealLokiSenior DeveloperCommented:
Do the Object Blocks need to be resizable, or would swapping it for a bigger Object Block suffice?

When an Object Block is moved, how perfect does the line moving have to be? does it have to be completely smooth?

Do you just require "text" inside the Object Block, or is it going to be more complicated?

Do you need any special functionality, such as changing the block's colour dynamically, or flashing connectors, etc...

The reason I ask these questions, is that a near perfect way to implement this would be with OpenGL or DirectX. A less pretty, but workable solution is with canvas drawing, and another method is actually moving TPanels around...

I've done work in opengl, canvas, and tpanels, but each has its' own set of issues...
0
 
HenryM2Author Commented:
Thanks for getting back:
1 - The object blocks can be created a certain size and does not have to change thereafter.  Object blocks must have a different number of points to which the lines need to connect, but once created it does not have to change.  As per my illustration in the example, each main object block has a number of labeled blocks attached to it, each with onbe label/caption per line that need be connected to it.  This is required to identify the line connected to that specific point.

2 - While the object block is being moved, the line moving of the lines does not have to be perfect.  It will proably be good enough to even remove the lines when the object starts moving, and redraw them only once the object block is in its new position when the mouse button is released.

 3 - The object blocks and the label or line connection blocks will only contain text.

4 - Once the object block ha sbeen created in a certain color, the color does not have to change and no other special effects are required.  The only thing is that the block must be able to be dragged to a new location, and a double click event must be generated to run some other code when double click on the object which has no effect on the object itself.

5 - I trust that the above answers will help you to guide me to use the correct methods.  I know very little about canvas drawing and even less about opengl, but from the sounds of it, the canvas drawing method will probably be fine.

Thanks HenryM2

0
 
TheRealLokiSenior DeveloperCommented:
Wont be able to get this pasted until next week (but it is coming)
Hope you're not in a hurry.
Simply put, it's TShapes on a TScrollbox, and the lines are
canvas.moveto()    canvas.LineTo()  on a TPaintBox
clear the lines before painting them to simulate "moving them"
when the dragged object is stopped (i.e. MouseUp() event)
run through all the objects and do a refresh/draw of them to clean up any little paint ugly bits.

0
 
HenryM2Author Commented:
Thank you.  I am going to give it a try so long with the items you mention.  I will however pop out some short questions as I go along, if thats ok.  My first question being, what is the significance of putting the TShapes onto a TScrollbox.  Is it just to ensure that the graphic can scroll if it gets to big or is there another reason.
0
 
TheRealLokiSenior DeveloperCommented:
yes, just so if you want to have more shapes than you can fit on the 1 screen, you can scroll to them
0
 
HenryM2Author Commented:
OK Thanks I will continue so long.
0
 
TheRealLokiSenior DeveloperCommented:
Hi, I've got my source now, but it will need tweaking for your situation, mainly just adding a label to the shapes ("Object A", etc) but you could use an image instead (this is already catered for)
I will just create a quick screen layout sample for you, and upload it as a zip here today.
I'll explain more about it then
0
 
TheRealLokiSenior DeveloperCommented:
I have uploaded the source demo for you. It should have everything you need
Take what you need from it, or use the whole thing
https://filedb.experts-exchange.com/incoming/ee-stuff/7331-movable-shapes-and-lines.zip
0
 
HenryM2Author Commented:
Thanks, this solution is excellent, does exactly what I needed.  I am currently working through it and learnig as I am combinding it with the rest of my code.  I will probably have more questions later which I will then have to post as separate questions as the system appears to want me to make a conclusion on the question.  Regards HenryM2
0
 
HenryM2Author Commented:
I now need to put the scroll bar function onto a TScrollBox where I can preset the size of the scroll box area.  I tried this with just the TScrollBox and with a TPanel ontop of the scrollbox, then trying to make the TScrollBox or the panel the parent for World.
Below, is whaI did using a TPanel named FloorPlanPnl in the main form's On Create
World := TVCLTopography.Create(FloorPlanPnl);
World.Parent := FloorPlanPnl;
However, this crashes my whole application beyond repair with a Debugger Exception Notification, with an Exception Class EReadError, with message Error Reading MainForm.OnDestroy Invalid Property Value.  Changing back the On Create to what it was does not take the error away.  I obviously have to make more changes elsewhere, can you help please.
0
 
TheRealLokiSenior DeveloperCommented:
I'm not sure I understand.

TVCLTopography already is a TScrollbox
you just put it on anything, such as a TPanel in your example
you can set its' width and height, or just set its' align to alClient

Then set the WorkAreaWidth and WorkAreaHeight properties (how big the "map" inside it can be)

The TVCLTopography will show the scrollbars and scroll as per a normal TScrollbar
0
 
HenryM2Author Commented:
OK solved.  my Form create is included below.
I then set the WorkAreaWidth and WorkAreaHeight properties to
  World.WorkAreaWidth := FloorPlanPnl.Width * 2;
  World.WorkAreaHeight := FloorPlanPnl.Height * 2;

I add objects (boxes). When I scroll so that the objects go off the screen and later return to them, upon moving the mouse onto them, they sometimes seem to separate from their vertexes.

One will also have to be able to select a line start at a vertex and then move to another vertex by scroling to it as it may be off the visible part of the screen.  So it will have to remeber that a start of a line has been made when dragging the scroll bar and then conclude the line when the end vertex is visible.

I am busy looking at this but if you have an idea to correct, it will be welcome.  Thanks.  
procedure TMainFrm.FormCreate(Sender: TObject);
begin
  World := TVCLTopography.Create(FloorPlanPnl);
  World.Align := alClient;
  World.Parent := FloorPlanPnl;
// temp for debugging
  World.fPaintBox.OnMouseMove := PaintBoxMouseMove;
end;

Open in new window

0
 
TheRealLokiSenior DeveloperCommented:
the issue with the objects moving happens with all the objects, shapes, vertex, or anchor. they all do the same if you hover over them.
It comes down to me not handling the control's ClientOrigin, which I forgot to put in (oops)

as for drawing a line. Currently, you right click the first "point" and then you can scroll wherever to the next point, and right click it to finish the line
I've made that code pretty basic, but it can be expanded on
0
 
TheRealLokiSenior DeveloperCommented:
Sorry for leaving you in the lurch there.
All you need to do is subtract the scrollbox's scrollbar position
Edit the VCLAnchor, VCLVertex and VCLPlanet's "RenderSelf' routines and change them like this

procedure TVCLVertex.RenderSelf;
begin
  if self.Highlighted then
  begin
    Shape.Shape := stRoundRect;
    Shape.Brush.Color := clBlue;
    Shape.SetBounds(self.Origin.X-10, self.Origin.Y-10, 21, 21);
    Shape.BringToFront;
  end
  else
  begin
    Shape.Shape := stCircle;
    Shape.Brush.Color := clBlack;
    Shape.SetBounds(self.Origin.X-2, self.Origin.Y-2, 5, 5);
    Shape.BringToFront;
  end;
  if not assigned(Shape.Parent) then Shape.Parent := fVCLTopography; // VCLTopography_ is derived from a TScrollbox
end;

becomes

procedure TVCLVertex.RenderSelf;
begin
  if self.Highlighted then
  begin
    Shape.Shape := stRoundRect;
    Shape.Brush.Color := clBlue;
    Shape.SetBounds(self.Origin.X-10-self.fVCLTopography.HorzScrollBar.Position, self.Origin.Y-10-self.fVCLTopography.VertScrollBar.Position, 21, 21);
    Shape.BringToFront;
  end
  else
  begin
    Shape.Shape := stCircle;
    Shape.Brush.Color := clBlack;
    Shape.SetBounds(self.Origin.X-2-self.fVCLTopography.HorzScrollBar.Position, self.Origin.Y-2-self.fVCLTopography.VertScrollBar.Position, 5, 5);
    Shape.BringToFront;
  end;
  if not assigned(Shape.Parent) then Shape.Parent := fVCLTopography; // VCLTopography_ is derived from a TScrollbox
end;

I will paste all 3 adjusted "RenderSelf" routines below

procedure TVCLAnchor.RenderSelf;
begin
  if self.Highlighted then
  begin
    Shape.Shape := stRoundRect;
    Shape.Brush.Color := clRed;
    Shape.SetBounds(self.Origin.X-10-self.fVCLTopography.HorzScrollBar.Position, self.Origin.Y-10-self.fVCLTopography.VertScrollBar.Position, 21, 21);
    Shape.BringToFront;
  end
  else
  begin
    Shape.Shape := stCircle;
    Shape.Brush.Color := clBlue;
    Shape.SetBounds(self.Origin.X-3-self.fVCLTopography.HorzScrollBar.Position, self.Origin.Y-3-self.fVCLTopography.VertScrollBar.Position, 7, 7);
    Shape.BringToFront;
  end;
  if not assigned(Shape.Parent) then Shape.Parent := fVCLTopography; // VCLTopography_ is derived from a TScrollbox
end;
 
 
procedure TVCLVertex.RenderSelf;
begin
  if self.Highlighted then
  begin
    Shape.Shape := stRoundRect;
    Shape.Brush.Color := clBlue;
    Shape.SetBounds(self.Origin.X-10-self.fVCLTopography.HorzScrollBar.Position, self.Origin.Y-10-self.fVCLTopography.VertScrollBar.Position, 21, 21);
    Shape.BringToFront;
  end
  else
  begin
    Shape.Shape := stCircle;
    Shape.Brush.Color := clBlack;
    Shape.SetBounds(self.Origin.X-2-self.fVCLTopography.HorzScrollBar.Position, self.Origin.Y-2-self.fVCLTopography.VertScrollBar.Position, 5, 5);
    Shape.BringToFront;
  end;
  if not assigned(Shape.Parent) then Shape.Parent := fVCLTopography; // VCLTopography_ is derived from a TScrollbox
end;
 
procedure TVCLPlanet.RenderSelf;
begin
  if self.Highlighted then
  begin
    Image.SetBounds(self.Origin.X-(self.Image.Width div 2)-self.fVCLTopography.HorzScrollBar.Position, self.Origin.Y-(self.Image.Height div 2)-self.fVCLTopography.VertScrollBar.Position, self.Image.Width, self.Image.Height);
  end
  else
  begin
    Image.SetBounds(self.Origin.X-(self.Image.Width div 2)-self.fVCLTopography.HorzScrollBar.Position, self.Origin.Y-(self.Image.Height div 2)-self.fVCLTopography.VertScrollBar.Position, self.Image.Width, self.Image.Height);
  end;
  if not assigned(Image.Parent) then Image.Parent := fVCLTopography; // VCLTopography_ is derived from a TScrollbox
end;

Open in new window

0
 
HenryM2Author Commented:
Hi Sorry for only responding now, I was away since Friday.  Thanks for this, I will work this in and let you know.
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

Join & Write a Comment

Featured Post

Free Tool: Site Down Detector

Helpful to verify reports of your own downtime, or to double check a downed website you are trying to access.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

  • 9
  • 9
Tackle projects and never again get stuck behind a technical roadblock.
Join Now