Link to home
Start Free TrialLog in
Avatar of HenryM2
HenryM2

asked on

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
Avatar of SteveBay
SteveBay
Flag of United States of America image

Avatar of HenryM2
HenryM2

ASKER

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
actually, I've written a better bit of code that more closely matches what you want, I'll try to dig it up...
Avatar of HenryM2

ASKER

Thanks, I am looking forward to that.  HenryM2
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...
Avatar of HenryM2

ASKER

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

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.

Avatar of HenryM2

ASKER

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.
yes, just so if you want to have more shapes than you can fit on the 1 screen, you can scroll to them
Avatar of HenryM2

ASKER

OK Thanks I will continue so long.
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
ASKER CERTIFIED SOLUTION
Avatar of TheRealLoki
TheRealLoki
Flag of New Zealand image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of HenryM2

ASKER

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
Avatar of HenryM2

ASKER

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.
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
Avatar of HenryM2

ASKER

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

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
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

Avatar of HenryM2

ASKER

Hi Sorry for only responding now, I was away since Friday.  Thanks for this, I will work this in and let you know.