PDA

Ver la Versión Completa : Interconexion de bloques


astwin
24-02-2009, 10:07:48
Hola, deseo realizar una aplicacion grafica para interconectar bloques.
Seleccionare desde un treeview un bloque y lo arrastraré hasta una zona de dibujo donde se deberá crear y se debera poder mover. Todos los bloques poseen entradas y salidas (seran unas bolitas que deberan tener) y se deberá poder conectar unos con otros (se deberá dibujar una linea que indique la conexion).

No tengo ni idea de como realizar esto. Ruego si me puedan dar ideas de como es la mejor forma de programar algo asi. Si pudieran decirme si existe algun componente o libreria para realizar aplicaciones asi, tambien les estaria muy agradecido.

duilioisola
24-02-2009, 10:57:10
Busca información sobre Drag&Drop para arrastrar objetos.
Por ejemplo esto (http://www.clubdelphi.com/foros/showthread.php?t=57499&highlight=drag+drop).

Busca información sobre Canvas para dibujar objetos, líneas, etc.

Neftali [Germán.Estévez]
24-02-2009, 12:02:04
Para crear los objetos visuales, y moverlos y arrastrarlos con el ratón, puedes revisar estos dos ejemplos que hay en mi página Web.


Seleccionar Shapes visualmente (http://neftali.clubdelphi.com/?p=122)
Crear/destruir comp. en Runtime y moverlas con el ratón (http://neftali.clubdelphi.com/?p=121)Están con el código fuente, así que puedes revisarlo para ver si te aclaras.

Si necesitas algo más complejo, puedes dar un paso más y revisar el componente TSimpleGraph (http://www.delphiarea.com/products/delphi-components/simplegraph/)(ya hemos hablado otras veces aquí de él, así que puedes buscar en los foros) que implemente multitud de métodos para trabajar con objetos gráficos.

astwin
25-02-2009, 12:54:16
Muchas gracias Neftali (http://www.clubdelphi.com/foros/member.php?u=3561) , pero no puedo descargarme los ejemplos que me has dicho. Creo que si podran serme útiles. Si pudieras mandarmelos de alguna manera (o arreglar la pagina web para que se puedan descargar) te estaria muy agradecido.
Un saludo.

Neftali [Germán.Estévez]
25-02-2009, 13:07:53
.pero no puedo descargarme los ejemplos que me has dicho. Creo que si podran serme útiles. Si pudieras mandarmelos de alguna manera (o arreglar la pagina web para que se puedan descargar) te estaria muy agradecido.


Perdón, perdón,...:o:o:o
Justo estos días estoy "remodelando" la página y me has pillado dos enlaces que están mal.
Los he corregido; Ahora deberías descargar los ejemplos sin problemas. Cualquier otro problema dímelo.

Un saludo.

astwin
27-02-2009, 08:48:39
Pues muchas gracias!!!! ya he conseguido descargarmelo. Me serán de utilidad tus ejemplos. otra vez, gracias.
Un saludo.

astwin
12-03-2009, 12:30:55
Hola, deseo hacer una aplicacion que se encarga de interconectar una serie de bloques con entradas y salidas. La interconexion debe realizarse mediante lineas. Hasta ahora lo he realizado dibujandolas sobre el canvas del panel donde los bloques los inserto como Timages, pero el problema que tengo es que de esa manera las lineas no se pueden seleccionar para editarlas o borrarlas. Supongo que tendre que crearme un componente Tlinea y utilizar los eventos onMouseClick... pero no se realmente como hacerlo.

He visto un post muy antiguo en el que hablaban sobre algo parecido http://www.clubdelphi.com/foros/showthread.php?t=1736

Tambien he visto un componente de dibujar polilineas que se parece mucho a lo que busco hacer http://www.rick_b.dds.nl/tools/PolyLines/

Decir que soy novato con Delphi y si pudieran orientarme un poco en como poder realizar este componente os estaria muy agradecido. Un saludo.

Neftali [Germán.Estévez]
12-03-2009, 14:02:46
Tambien he visto un componente de dibujar polilineas que se parece mucho a lo que busco hacer http://www.rick_b.dds.nl/tools/PolyLines/

Decir que soy novato con Delphi y si pudieran orientarme un poco en como poder realizar este componente os estaria muy agradecido. Un saludo.

El problema de ese componente es que es de pago.
La verdad, es que hacer un componente de ese tipo puede ser algo complejo, más cuando estás comentando que eres un poco novato.

Talvez derivando de TGraphicscontrol y mirando la implamentación de un TShape puedas conseguir algo.

¿Has mirado este componente que te propuse?
Si necesitas algo más complejo, puedes dar un paso más y revisar el componente TSimpleGraph (http://www.delphiarea.com/products/delphi-components/simplegraph/)(ya hemos hablado otras veces aquí de él, así que puedes buscar en los foros) que implemente multitud de métodos para trabajar con objetos gráficos.

astwin
13-03-2009, 08:56:53
Hola neftali, primero pedir disculpas si he abierto demasiados post distintos (pense que aunque todas mis dudas estan encaminadas hacia la misma aplicacion, cada una de ellas se centra en algo distinto ,drag and drog, dibujado de lineas seleccionables y modificables... y para alguien que tenga una duda en concreto sobre alguno de estos temas es mas facil de encontrar un post especifico de ello, no uno generico, en el que por el titulo del tema es dificil de averiguar de que temas se estan tratando).

Segundo darte las gracias por tu atencion y tu ayuda.

Miré por enciima el componente TsimpleGraph, pero la verdad que me asuste un poco, ya que a priori me parecio muy dificil de entender y de usar. Pero creo que voy a dedicarle desde ahora un tiempo a comprenderlo y a hacer pruebas con el.

La aplicacion que quiero desarrollar es muy similar a esta http://www.symetrixaudio.com/repository/snd_v801.exe. (http://www.symetrixaudio.com/repository/snd_v801.exe)
Si alguien fuera capaz de echarle un ojo y decirme que rumbo deberia tomar para realizar algo asi, le estaria muy agradecido, ya que soy muy nuevo con delphi y ando aun algo (por no decir) muy perdido.

Neftali [Germán.Estévez]
13-03-2009, 10:35:51
Miré por enciima el componente TsimpleGraph, pero la verdad que me asuste un poco, ya que a priori me parecio muy dificil de entender y de usar. Pero creo que voy a dedicarle desde ahora un tiempo a comprenderlo y a hacer pruebas con el.

La aplicacion que quiero desarrollar es muy similar a esta http://www.symetrixaudio.com/repository/snd_v801.exe.
Si alguien fuera capaz de echarle un ojo y decirme que rumbo deberia tomar para realizar algo asi, le estaria muy agradecido, ya que soy muy nuevo con delphi y ando aun algo (por no decir) muy perdido.

Hola astwin.
La verdad es que parece una contradicción. Quieres hacer un programa similar al que me propones, pero un simple componente que te facilitaría el trabajo (no te lo hará por completo) ya te parece difícil de usar y de entender. ?¿?¿?
No quiero desanimarte ni mucho menos, es un buen reto a afrontar, pero debes pensar que ese componentes es sólo una herramienta que te permitará realizar de forma más sencilla ese programa. Deberás entenderla y modificarla si quieres usarla.
Si no quieres o puedes usarla (esta u otra similar), creo que aun la cosa se torna más negra, ya que la mayoría de cosas de las que hace ese componente deberás programarlas tú.

Un componente como SimpleGraph o algun otro similar que hay ya te proveen de lo siguiente:

(1) En primer lugar de la más básico de un programa como este; Tu espacio de trabajo; La "hoja/papel" sobre el que posicionas los componentes. No es poco, ya que ese simple espacio posee muchos métodos; Todos los de movimiento (pan) y zoom; Todos los de tratamiento de los objetos; Añadir, eliminar, guardar, imprimir, exportar; Te gestiona la selección/multiselección de objetos. Te provee los métodos para recorrer los objetos y sus diferentes tipos; Gestión del Grid, bloqueo de objetos,...
A priori tal vez no lo has pensado, pero en un programa de este estilo todo eso antes o después es necesario.

(2) En segundo lugar te provee de los objetos. SimpleGraph posee programados los objetos básicos; Rectángulos, círculos, líneas,... Tu trabajo será extender esos objetos (derivarlos) para conseguir los que tú necesitas. Al igual que el punto anterior, eso no es poco. No son simple objetos, sino que ya tienen programados muchos métodos y propiedades que te harán falta. Selección, redimensionado, Añadir texto a los objetos, gestionar marcas de lección, pintado, Brush,... Para el tema de los links entre objetos también tines algo hecho, ya que programa deterinada lógica para "enganchar" diferentes objetos.

http://img6.imageshack.us/img6/3423/imagen60.png

A partir de ahí posiblemente tengas que programar el comportamiento que se ve en el ejemplo de "unir" bloques por determinados puntos.

Como ya he dicho, no me parece algo trivial, ni sencillo. Creo (y yo lo haría así) que para centrarte en la funcionalidad del programa debes usar algun componente que te evite perder tiempo y esfuerzo en cosas que a priori paracen superfluas (como las que te he comentado).

Un saludo.

astwin
13-03-2009, 10:48:55
Hola, no es que no quiera usarlo. Soy muy nuevo en esto de Delphi, y cuando me lo propusiste y empece a intentar entenderlo, pues claro, se me hizo un mundo. Consideré mas adecuado empezar a realizar una version muy simplificada por mi cuenta y asi poder coger soltura y aprender mucho mas. Ten en cuenta que partia casi de cero y enfrentarse asi a un componente asi de potente, pues daba impresion. Ahora creo que ya tengo muchos mas conceptos claros y que soy capaz de enfrentarme a la comprension, utilizacion y ampliacion de este componente. Desde ya me pondré con el. Espero no haber sido muy pesado y agradecerte toda la ayuda y consejos que me estas brindando.
Un saludo.

astwin
23-03-2009, 10:10:55
Hola, ya me he puesto a fondo con TsimpleGraph. Voy poco a poco comprendiendo como funciona.

El problema que tengo es que el linkado entre objetos se realiza mediante lineas que parten desde el centro del objeto, no pudiendose definir un punto fijo del objeto para linkarlo a otros.

Mi solucion ha sido crear cada bloque como un rectangulo, y las entradas y salidas tambien como rectangulos mas pequeños. El problema ahora lo tngo a la hora de mover el bloque grande, que no se como hacer que se puedan mover a la vez los pequeños (ademas el bloque grande esta alineado a la cuadricula y los pequeños no). Haber si alguien puede echarme un cable.


type
// Rectangulo que servirá como entrada
TGraphIn=class(TRectangularNode)
public
procedure init();
end;
// Rectangulo que servirá como salida
TGraphOut=class(TRectangularNode)
procedure init();
end;
// Rectangulo que será el cuerpo del bloque
TGraphBlock=class(TRectangularNode)
public
nIn,nOut: integer ; //numero de entradas y de salidas
vIn: array of TGraphNode;
vOut: array of TGraphNode;
IOHeigth: Integer;
//GraphIORadious: integer; // Altura de las entradas y salidas
InSelected:integer;
OutSelected:integer;
procedure init();
//function QueryHitTest(const Pt: TPoint): DWORD; override;
//procedure Draw(Canvas: TCanvas); override;
//procedure MouseDown(Button: TMouseButton; Shift: TShiftState; const Pt: TPoint);override;
end;

Type
TGraphLine=class(TGraphLink)

end;

const
// ForEachObject Actions
FEO_DesactiveIO = 00;


implementation
uses
unit1,unit2;





procedure TGraphBlock.init();
var
I: Integer;
inc: Integer;
bound:Trect;
pt: Tpoint;
begin
// Opciones del nodo;
self.NodeOptions:=[gnoMovable,gnoShowBackGround];
// Imagen del nodo;
self.Background.LoadFromFile('./images/sos.jpg');
// Numero de entradas / salidas
self.Nin:=3; self.NOut:=4;
// Creamos las entradas / salidas graficas
self.IOHeigth:=8;
setlength(vIn,nIn);
setlength(vOut,nOut);
// Entradas
inc:=trunc((self.height-NIn*IOHeigth)/(Nin+1)); // Espacio entre entradas
for I := 0 to nIn - 1 do
begin
pt.X:=left; pt.y:= Top+(i+1)*inc+i*IOHeigth;
bound:= Rect(pt, Point(pt.X+IOHeigth, pt.Y+IOHeigth));
form2.simpleGraph1.SnapToGrid:=False;
vIn[I]:=form2.simpleGraph1.InsertNode(bound,TGraphIn);
form2.simpleGraph1.SnapToGrid:=True;
TGraphIn(vIn[I]).init;
end;
// Entradas
inc:=trunc((self.height-NOut*IOHeigth)/(NOut+1)); // Espacio entre entradas
for I := 0 to nOut - 1 do
begin
pt.X:=left+width-IOHeigth; pt.y:= Top+(i+1)*inc+i*IOHeigth;
bound:= Rect(pt, Point(pt.X+IOHeigth, pt.Y+IOHeigth));
form2.simpleGraph1.SnapToGrid:=False;
vOut[I]:=form2.simpleGraph1.InsertNode(bound,TGraphIn);
form2.simpleGraph1.SnapToGrid:=True;
TGraphIn(vOut[I]).init;
end;

self.InSelected:=-1;
self.OutSelected:=-1;
end;

procedure TGraphIn.init;
begin
NodeOptions:=[gnoShowBackGround];
end;

procedure TGraphOut.init;
begin
NodeOptions:=[gnoShowBackGround];
end;

Neftali [Germán.Estévez]
23-03-2009, 10:45:42
Mi solucion ha sido crear cada bloque como un rectangulo, y las entradas y salidas tambien como rectangulos mas pequeños. El problema ahora lo tngo a la hora de mover el bloque grande, que no se como hacer que se puedan mover a la vez los pequeños (ademas el bloque grande esta alineado a la cuadricula y los pequeños no). Haber si alguien puede echarme un cable.


Me parece una buena solución. Si fuera mi caso posiblemente hubiera optado por la misma, ya que los puntos de entrada debes comportarse como elementos individuales, aunque en que los contenga sea el bloque externo.

Para el tema de movimiento, lo que hago yo, es detectar el movimiento del bloque exterior y "mover al mismo tiempo los bloques interiores". En tu caso, tú sabes en qué posición está cada entrada/salida, por lo tanto cuando mueves el bloque exterior lo único que debes hacer el aplicar ese mismo desplazamiento a los bloques interiores.

astwin
23-03-2009, 13:47:04
el problema es que no comprendo muy bien desde que funcion puedo sobreescribir del bloque 'grande' para que las entradas y salidas sigan el movimiento. mousemove, begindrag,... vamos, que no me aclaro muy bien como hacerlo. Haber si puedes orientarme.

Neftali [Germán.Estévez]
23-03-2009, 15:57:57
Para que te hagas una idea de cómo lo hago yo...


procedure TLeyendaNode.DrawBody(Canvas: TCanvas);
var
i:Integer;
obj:TGraphNode;
_MinWidth:Integer;
begin
inherited;

// controlar el estado
if (Self.State = osDestroying) then begin
Exit;
end;

//- Owner.BeginUpdate;
// proteccion
try

// AutoDimensionar
if (FAutoSizeHeight) then begin
Self.Height := DEFAULT_HEIGHT_TITLE +
(FTextNodes.Count * DEFAULT_HEIGHT_ELEMENT) +
(DEFAULT_HEIGHT_ELEMENT DIV 2);
// Objeto en sí
Self.Width := Max(Self.Width, DEFAULT_HEIGHT_ELEMENT);
end;

_MinWidth := 0;


// Recolocar
for i := 0 to (FTextNodes.Count - 1) do begin
// Cuadro de color
obj := TGraphNode(FColorNodes.Objects[i]);
obj.OwnerNode := Self;
obj.Left := Self.Left + LEFT_SPACE;
obj.Top := Self.Top + DEFAULT_HEIGHT_TITLE + (DEFAULT_HEIGHT_ELEMENT * i);
obj.Width := DEFAULT_HEIGHT_ELEMENT;
obj.Height := DEFAULT_HEIGHT_ELEMENT - 1;
obj.Brush.Color := StrToInt(FColorNodes.Strings[i]);
obj.Pen.Color := clGray;
obj.CanSelect := False;

// Texto
obj := TGraphNode(FTextNodes.Objects[i]);
obj.Left := Self.Left + LEFT_SPACE + DEFAULT_HEIGHT_ELEMENT + LEFT_SPACE;
obj.Top := Self.Top + DEFAULT_HEIGHT_TITLE + (DEFAULT_HEIGHT_ELEMENT * i);
obj.Width := Self.Width - LEFT_SPACE - DEFAULT_HEIGHT_ELEMENT - LEFT_SPACE -
(SOMBRA_WIDTH * 2);
obj.Height := DEFAULT_HEIGHT_ELEMENT;
obj.Alignment := taLeftJustify ;
obj.Text := FTextNodes.Strings[i];

_MinWidth := Max(_MinWidth, Canvas.TextWidth(obj.Text) + (LEFT_SPACE * 2));

obj.Pen.Style := psClear;
obj.CanSelect := False;
end;

// No asignado?
if not Assigned(FTitleNode) then begin
// Crear el título
FTitleNode := Owner.InsertNode(nil, TEtiquetaNode{TRectangularNode});
FTitleNode.OwnerNode := Self;
FTitleNode.Visible := Self.Visible;
FTitleNode.Top := Top;
FTitleNode.Left := Left;
FTitleNode.Height := DEFAULT_HEIGHT_ELEMENT - (SOMBRA_WIDTH * 2);
FTitleNode.Width := Width;
FTitleNode.CanSelect := False;
FTitleNode.BringToFront;

FTitleNode.Selected := False;
FTitleNode.CanSelect := False;
FTitleNode.Font.Style := FTitleNode.Font.Style + [fsBold];
end;

// Titulo
FTitleNode.Top := Top + 1;
FTitleNode.Left := Left + (DEFAULT_HEIGHT_ELEMENT div 2);
FTitleNode.Width := Width - SOMBRA_WIDTH - DEFAULT_HEIGHT_ELEMENT;
FTitleNode.Text := FTitle;
FTitleNode.Font.Color := clBlack;

_MinWidth := Max(_MinWidth, Canvas.TextWidth(FTitleNode.Text) +
(DEFAULT_HEIGHT_ELEMENT * 2));


// AutoDimensionar
if (FAutoSizeWidth) then begin
// Objeto en sí
Self.Width := Max(_MinWidth, DEFAULT_HEIGHT_ELEMENT);
end;

finally
//- Owner.EndUpdate;
end;
end;


Es un objeto que correspomde a un cuadro de leyenda.

http://img256.imageshack.us/img256/9955/imagen70.png

Como ves es un objeto al que se le pasan un número de elementos (Texto + Color); Al mover la leyenda se posicionan de nuevo los elementos interiores.

astwin
23-03-2009, 16:36:26
Muchisimas gracias neftali, gracias a tu ayuda ya lo he conseguido hacer. Te tas ganando unas cuantas cervecillas :):):D. Muchas gracias. Espero no ser muy pesao.

astwin
08-04-2009, 18:25:53
Hola,
gracias al componente TsimpleGraph tengo el trabajo muy adelantado. Ya consigo linkar bloques con lineas, que estas conexiones de forma gráfica se traduzcan en una serie de estructuras y clases con las que realizar diversas tareas, etc.. Muchas gracias Neftali por sugerirmelo. Ahora estoy liado con el tema de copiar/cortar/pegar.

No me aclaro muy bien con el funcionamiento de esta funcionalidad en simplegraph.

Como ya postee anteriormente, para realizar el bloque cree una clase que hereda de Trectangularnode. Las entradas y salidas las cree mediante otra clase que hereda de Trectangularnode y se encuentran en una lista definida dentro de la clase de bloque. Decir además, que le he añadido en la variable 'data' referencias hacia otras clases, que no tienen nada que ver con la parte grafica.

Ahora para copiar y pegar un bloque no tengo ni idea de como proceder.


procedure TMainForm.EditCopyUpdate(Sender: TObject);
begin
EditCopy.Enabled :=(SimpleGraph.SelectedObjects.Count > 0);
end;

procedure TMainForm.EditCopyExecute(Sender: TObject);
begin
SimpleGraph.CopyToClipboard(True);
end;

procedure TMainForm.EditPasteUpdate(Sender: TObject);
begin
EditPaste.Enabled := not IsReadonly and Clipboard.HasFormat(CF_SIMPLEGRAPH);
end;

procedure TMainForm.EditPasteExecute(Sender: TObject);
begin
SimpleGraph.PasteFromClipboard;
end;


Este es el codigo de copiar y pegar que he observado en la demo del simpleGraph. Pero cuando selecciono un bloque grafico, lo copio y lo pego, al pegarlo me da error. Alguien puede orientarme un poco, si debo sobreescribir alguna funcion en mi clase de bloque para que se copie bien al portatapeles o que debo hacer o mirar.

Un saludo.

Neftali [Germán.Estévez]
08-04-2009, 23:52:11
Este es el codigo de copiar y pegar que he observado en la demo del simpleGraph. Pero cuando selecciono un bloque grafico, lo copio y lo pego, al pegarlo me da error. Alguien puede orientarme un poco, si debo sobreescribir alguna funcion en mi clase de bloque para que se copie bien al portatapeles o que debo hacer o mirar.



Supongo que SimpleGraph implementa esos métodos, así como un formato nuevo del Clipboard. Al hacer el pegar debe crear los nuevos objetos.
¿Sabes en qué punto está fallando?
¿Lo has "debuggado" paso a paso?

La verdad es que esta funcionalidad no la he utilizado nunca.

astwin
15-04-2009, 17:13:08
ya tengo arreglado lo de copiar y pegar. Al final he pasado de la funcionalidad del simplegraph y lo he realizado por mi cuenta, creando un objeto nuevo del mismo tipo que el que copie y luego copiando todas las propiedades.

Ahora tengo otro problemilla. El programa ejemplo para linkar dos nodos, tienes que pinxar sobre uno de ellos y sin soltar arrastrar hacia el otro. Yo quisiera realizarlo haciendo click sobre el primero, que la linea siguiera el raton, si haces click en cualquier sitio del canvas se añada un breakpoint y cuando hagas click sobre otro nodo se acabe la edicion de la linea, teniendo los dos nodos linkados. Toy tudiando el codigo, pero no encuentro como poder hacerlo. Neftali, haber si me puedes aconsejar algo. muxisimas gracias.

Neftali [Germán.Estévez]
15-04-2009, 17:50:31
El programa ejemplo para linkar dos nodos, tienes que pinxar sobre uno de ellos y sin soltar arrastrar hacia el otro. Yo quisiera realizarlo haciendo click sobre el primero, que la linea siguiera el raton, si haces click en cualquier sitio del canvas se añada un breakpoint y cuando hagas click sobre otro nodo se acabe la edicion de la linea, teniendo los dos nodos linkados.

Ahora mismo no tengo el código a mano, pero seguramente deberás utilizar los Links del propio SimpleGraph o un objeto similar/derivado.

Revisa los métodos de ratón del simpleGraph. El el MouseUp verás que se trabaja en base al CommandMode (al menos en la versión actual que yo tengo). Algunos de los valores que admite esta propiedad son referentes a los Links; Creo que modificando el comportamiento de ese procedimiento (y los relacionados -MouseUp y MouseMove-) podrás hacer lo que necesitas.

La idea es que si sueltas/pulsas el botón sobre un lugar dnde no hay ningun objeto se añada un nuevo punto al link actual. Cosa que en los links estandard se hace a posteriori.

Un saludo.