Ir al contenido


Foto

Unir 2 botones con una linea y mover los botones y que linea se mueva.


  • Por favor identifícate para responder
9 respuestas en este tema

#1 martinartaza

martinartaza

    Advanced Member

  • Miembros
  • PipPipPip
  • 159 mensajes
  • LocationArgentina, Tucuman

Escrito 13 octubre 2011 - 09:43

Hola querida comunidad, cada vez mis titulos son más raros. Siguiendo con el tema de hacer un software para diseñar grafo me queda ahora unir los nodos (que por el momento son botones). Eh creado un simple programita para crear botones en tiempo de ejecución, poder moverlo, pasar al frente, o enviarlo al fondo, eliminarlo si es necesario y (cuando llegue a casa, implementare para cambiarles las propiedades). Le muestro como queda el programa y subo su código fuente (por si alguien le interesa, aunque no hace mucho que digamos).
Imagen Enviada
Y el codigo fuente  http://www.alertaene...ursos/mover.rar

En definitiva, estoy tratando de crear 2 botones (en tiempo de ejecución), luego apretar en el boton unir (creado en tiempo de diseño que está arriba) y que me permita graficar una linea que una 2 botones, está linea tiene que tener las siguiente propiedades:

1 - Al mover los botones (de a uno) la linea se acomode o sea se mueva para que los botones sigan unidos)
2 - Que está linea se pueda poner un punto intermedio y a este punto se lo pueda mover entonces la linea tendria un Inicio, punto intermedio, fin .  Y al mover el boton, ligado al inicio, se mueve redibuje el primer tramo, si se mueve el punto intermedio se redibuje los dos tramos y si se mueve el boton ligado al fin, se redibuje el segundo plano.

3 - Además que se guarda de alguna forma, que el primer boton está unido al segundo.

Bueno se que estoy pidiendo cosas complicadas y aún más debido a que no estoy usando componentes de tercero, sino usando solo código de pascal, esto se debe que deseo poder aprender para lazarus esto también.

Todo esto es con la idea de poder aprender está esquema de programación y empezar a desarrollar programas simples que tengo en la cabeza hace más de 10 años ( o más tal vez.)

Mis preguntas:

Como hago lo anterior, creando en tiempo de ejecución un tshape y dibujar adentro las lineas ,o uso la api de windows (sera esto portable). O que otra alternativa hay, la verdad es que no sé por donde encararlo.

Desde ya muchas gracias y bueno sigo con esto hasta que aparezca algo urgentisimo.

  • 0

#2 felipe

felipe

    Advanced Member

  • Administrador
  • 3.283 mensajes
  • LocationColombia

Escrito 13 octubre 2011 - 01:28

No se si me queda muy clara la idea, pero me preguntaba si no puedes usar un panel y un split.


Saludos!
  • 0

#3 martinartaza

martinartaza

    Advanced Member

  • Miembros
  • PipPipPip
  • 159 mensajes
  • LocationArgentina, Tucuman

Escrito 13 octubre 2011 - 02:13

Ehh, creo que no te entiendo, creiria que un objeto de la clase TPanel y otro de la clase TSplitter no se puede hacer lo que quiero.


Lo que yo quiero es (en tiempo de ejecución) crear un boton, digamos boton1, con coordenadas top y left (10, 10) y despues crear un boton 2 con coordernadas (50, 10), luego crear un linea que una los botones, que iria (en principio, después se lo mejora)    desde (10, 10) hasta (50,10).  Que luego si uno mueve uno de los botones se mueva la linea.

Hoy cuando llegue a mi casa envió imágenes aclarando la idea.




  • 0

#4 Desart

Desart

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 715 mensajes
  • LocationEspaña

Escrito 14 octubre 2011 - 01:01

Holamartinartaza, pregunto no seria más lógico, saber 0º Saber si uno de los botones se ha movido de la posición inicial.

1º que botón esta más alejado (left+ alto)2º que botón esta más alto (Top+bajo)

Sabiendo estos dos parametros yo dibujaria una linea desde el botón que este más alto, desde una de las esquinas inferiores o superiores, pongamos la derecha, a la  esquina inferior, o superior izquierda del botón más alejado, todo esto unido a uno de los eventos del ratón.

  • 0

#5 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6.295 mensajes
  • LocationArgentina

Escrito 14 octubre 2011 - 06:48

Hola,
A como yo lo veo es cosa de "jugar" con los eventos OnMouseUp, OnMouseDown, OnMouseMove, OnClick del botón para desplazarlo.
Una vez que se suelte el botón, simplemente se ha leer el valor de la propiedades Left, Top y sus respectivos Heigth, y Width para determinar las coordenadas iniciales y finales para trazar la línea.


Lo que yo haría es obtener la coordenada centro de cada botón:


delphi
  1. Xi = (Left_i + Heigth_i) div 2;
  2. Yi = (Top_i + Width_i) div 2;
  3.  
  4.  
  5.  
  6. Xf = (Left_f + Heigth_f) div 2;
  7. Yf = (Top_f + Width_f) div 2;




Siendo "i" el botón de punto de partida, y "f" el botón de punto de llegada.
Ahora con estas 2 coordenadas (Xi, Yi) (Xf, Yf) simplemente aprovecho el objeto Canvas y los método MoveTo(Xi, Yi) y LineTo(Xf, Yf) para trazar una recta:




delphi
  1. Canvas.MoveTo(Xi, Yi); // nos ponemos en el centro del botón inicial
  2. Canvas.LineTo(Xf, Yf); // trazamos una línea hasta centro del botón final




No interesa si uno está más alto que otro, en diagonal... o el lugar. Dejo que Canvas se encargue... Después de todo, se supone que "él" ya sabe como y que hacer. ¿no? 

Claro está, esto es sólo un bosquejo, una orientación. Para algo más elaborado hay que evaluar otra serie de cosas. Lo que te recomiendo seriamente es que empieces a separar lo que es lógica de lo que es interfaz. Justamente por lo que comentas del punto 2 y 3.


Una posible manera de verlo es que dispongas de un vector que tenga almacenado los elementos que vas a pintar con la información de sus posiciones, colores, formas y si están conectados cómo.


Luego la interfaz simplemente hace uso de esta estructura, la lee y la representa en pantalla. Aprovechando esta estructura se puede evaluar y analizar la forma de cómo llevarlo a archivo para guardarlo y posteriormente recuperarlo.
Lamentablemente esto dependerá de las posibilidades de diseño y su complejidad.


Saludos,
  • 0

#6 Sergio

Sergio

    Advanced Member

  • Moderadores
  • PipPipPip
  • 1.092 mensajes
  • LocationMurcia, España

Escrito 14 octubre 2011 - 08:56

Yo hago algo similar poniendo los botones sobre un TImage que ocupa todo, y tras mover un boton, borro el tiimage dibujando en su canvas un rectangle blanco relleno de blanco, y dibujo la linea que une ambos botones usando sus centros, como en el ejemplo de delphius que usa canvas.moveto() y canvas.lineto() solo que usando un timage encima del form, y encima de el, los botones (paniendole el owner:= Image1 al crearlo).

Si necesitas algun detalle de como hacer algo de esto, comentalo y te pego algo de código.
  • 0

#7 martinartaza

martinartaza

    Advanced Member

  • Miembros
  • PipPipPip
  • 159 mensajes
  • LocationArgentina, Tucuman

Escrito 14 octubre 2011 - 02:28

Muchas gracias a todos por sus respuestas, no pude responder antes porque fue un día movido de trabajo.
Creo que combinare lo mejor de lo que me dijeron:

Una posible manera de verlo es que dispongas de un vector que tenga almacenado los elementos que vas a pintar con la información de sus posiciones, colores, formas y si están conectados cómo.


Luego la interfaz simplemente hace uso de esta estructura, la lee y la representa en pantalla. Aprovechando esta estructura se puede evaluar y analizar la forma de cómo llevarlo a archivo para guardarlo y posteriormente recuperarlo.


Yo hago algo similar poniendo los botones sobre un TImage que ocupa todo, y tras mover un boton, borro el tiimage dibujando en su canvas un rectangle blanco relleno de blanco, y dibujo la linea que une ambos botones usando sus centros



Sobre código si no es mucho pedir no hay problema.

Desde ya muchas gracias por sus respuesta, espero aprender está tema y no postergar  más las ideas que tengo en la cabeza.

  • 0

#8 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4.107 mensajes
  • LocationMadrid - España

Escrito 14 octubre 2011 - 05:27

Considero que la forma mas eficaz de conseguir lo que quieres es realizar un control que una graficamente los botones.

He preparado un prototipo de control a modo de ejemplo que une un nodo con dos hojas. Puede ser el comienzo para solucionar tu problema.

El control sólo tiene tres propiedades añadidas que corresponden a cada uno de los botones y responde a los movimientos de cada botón manteniendo la unión gráfica con una línea.

Este es el código de la Unit:


delphi
  1. unit UnionControl;
  2.  
  3. interface
  4.  
  5. uses
  6.   Windows, Messages, Forms, Math, ExtCtrls, Graphics, SysUtils, Classes, Controls;
  7.  
  8. type
  9.   TDefault = class(TControl);
  10.   TUnionControl = class(TWinControl)
  11.   private
  12.   FControl1: TControl;
  13.   FControl2: TControl;
  14.   FNodo: TControl;
  15.   P1, P2, N: TPoint;
  16.   protected
  17.     procedure HookWindowProc1(var Message: TMessage);
  18.     procedure HookWindowProc2(var Message: TMessage);
  19.     procedure HookWindowProc3(var Message: TMessage);
  20.     procedure SetControl1(Value: TControl);
  21.     procedure SetControl2(Value: TControl);
  22.     procedure SetNodo(Value: TControl);
  23.     procedure WMPaint(var Message: TWMPaint); message WM_PAINT;
  24.     procedure UpdateBoundsRect;
  25.   public
  26.     constructor Create(AOwner: TComponent); override;
  27.   published
  28.     Property Control1: TControl read FControl1 write SetControl1;
  29.     Property Control2: TControl read FControl2 write SetControl2;
  30.     Property Nodo: TControl read FNodo write SetNodo;
  31.   end;
  32.  
  33.  
  34. implementation
  35.  
  36.  
  37. constructor TUnionControl.Create(AOwner: TComponent);
  38. begin
  39.   inherited Create(AOwner);
  40.   Left:= 20;
  41.   Top:= 20;
  42.   Width:= 0;
  43.   Height:= 0;
  44.   Control1:= nil;
  45.   Control2:= nil;
  46.   Nodo:= nil;
  47.   P1.X:= 0; P1.Y:=0;
  48.   P2:= P1;
  49.   N:= P1;
  50.   OnMouseDown:= TDefault(AOwner).OnMouseDown;
  51. end;
  52.  
  53. procedure TUnionControl.SetControl1(Value: TControl);
  54. begin
  55.   if Value <> FControl1 then
  56.   begin
  57.     FControl1 := Value;
  58.     Control1.WindowProc:= HookWindowProc1;
  59.     P1.X:= Control1.Left + Control1.Width div 2;
  60.     P1.Y:= Control1.Top + Control1.Height div 2;
  61.     UpdateBoundsRect;
  62.   end;
  63. end;
  64.  
  65. procedure TUnionControl.SetControl2(Value: TControl);
  66. begin
  67.   if Value <> FControl2 then
  68.   begin
  69.     FControl2 := Value;
  70.     Control2.WindowProc:= HookWindowProc2;
  71.     P2.X:= Control2.Left + Control2.Width div 2;
  72.     P2.Y:= Control2.Top + Control2.Height div 2;
  73.     UpdateBoundsRect;
  74.   end;
  75. end;
  76.  
  77. procedure TUnionControl.SetNodo(Value: TControl);
  78. begin
  79.   if Value <> FNodo then
  80.   begin
  81.     FNodo := Value;
  82.     Nodo.WindowProc:= HookWindowProc3;
  83.     N.X:= Nodo.Left + Nodo.Width div 2;
  84.     N.Y:= Nodo.Top + Nodo.Height div 2;
  85.     UpdateBoundsRect;
  86.   end;
  87. end;
  88.  
  89. procedure TUnionControl.WMPaint(var Message: TWMPaint);
  90. var
  91.   DC: HDC;
  92. begin
  93.   DC:= GetDC(Handle);
  94.   if Control1 <> nil then MoveToEx(DC, P1.X-Left, P1.Y - Top, nil);
  95.   if Nodo <> nil then    LineTo(DC, N.X-Left, N.Y-Top);
  96.   if Control2 <> nil then LineTo(DC, P2.X-Left, P2.Y - Top);
  97.   ReleaseDC(Handle, DC);
  98. end;
  99.  
  100. procedure TUnionControl.HookWindowProc1(var Message: TMessage);
  101. begin
  102.   case Message.Msg of
  103.     WM_MOVE:
  104.     begin
  105.       P1.X:= Control1.Left + Control1.Width div 2;
  106.       P1.Y:= Control1.Top + Control1.Height div 2;
  107.       UpdateBoundsRect;
  108.     end;
  109.   end;
  110.   TDefault(Control1).WndProc(Message);
  111. end;
  112.  
  113. procedure TUnionControl.HookWindowProc2(var Message: TMessage);
  114. begin
  115.   case Message.Msg of
  116.     WM_MOVE:
  117.     begin
  118.       P2.X:= Control2.Left + Control2.Width div 2;
  119.       P2.Y:= Control2.Top + Control2.Height div 2;
  120.       UpdateBoundsRect;
  121.     end;
  122.   end;
  123.   TDefault(Control2).WndProc(Message);
  124. end;
  125.  
  126. procedure TUnionControl.HookWindowProc3(var Message: TMessage);
  127. begin
  128.   case Message.Msg of
  129.     WM_MOVE:
  130.     begin
  131.       N.X:= Nodo.Left + Nodo.Width div 2;
  132.       N.Y:= Nodo.Top + Nodo.Height div 2;
  133.       UpdateBoundsRect;
  134.     end;
  135.   end;
  136.   TDefault(Nodo).WndProc(Message);
  137. end;
  138.  
  139. procedure TUnionControl.UpdateBoundsRect;
  140. var
  141.   RB: TRect;
  142. begin
  143.   RB.Left:=  Min(Min(N.X, P1.X), P2.X);
  144.   RB.Top :=  Min(Min(N.Y, P1.Y), P2.Y);
  145.   RB.Right:= Max(Max(N.X, P1.X), P2.X);
  146.   RB.Bottom:= Max(Max(N.Y, P1.Y), P2.Y);
  147.   InflateRect(RB, 2, 2);
  148.   BoundsRect:= RB;
  149.  
  150.   SendToBack;
  151. end;
  152.  
  153. end.



Te muestro como lo he intercalado en tu código para usar sólo tres botones. Para usar mas deberás seguir creandolos en tiempo de ejecución.



delphi
  1. procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
  2.   Shift: TShiftState; X, Y: Integer);
  3. var
  4.   B: TButton;
  5. begin
  6.   B:= TButton.Create(self);
  7.   if BanNodo then
  8.   begin
  9.     with B do
  10.     begin
  11.     Parent := self;
  12.     Left := X;
  13.     Top := Y;
  14.     Inc(UltimoID);
  15.     Tag := UltimoID;
  16.     Caption := 'Nodo' + IntToStr(UltimoID);
  17.     Name := 'BNodo' + IntToStr(UltimoID);
  18.     BanNodo:= False;
  19. //    PopupMenu:= PopMenuNodo;
  20.     OnMouseDown:= ControlMouseDown;
  21.     OnMouseMove:= ControlMouseMove;
  22.     OnMouseUp:= ControlMouseUp;
  23.     end;
  24.     UnionControl1.Nodo:= B;
  25.   end;
  26. if BanHoja then
  27.   begin
  28.   with B do
  29.   begin
  30.     Parent := self;
  31.     Left := X;
  32.     Top := Y;
  33.     Inc(UltimoID);
  34.     Tag := UltimoID;
  35.     Caption := 'Hoja' + IntToStr(UltimoID);
  36.     Name := 'BHoja' + IntToStr(UltimoID);
  37.     BanHoja:= False;
  38. //    PopupMenu:= PopMenuNodo;
  39.     OnMouseDown:= ControlMouseDown;
  40.     OnMouseMove:= ControlMouseMove;
  41.     OnMouseUp:= ControlMouseUp;
  42.     end;
  43.     if UnionControl1.Control1 = nil then UnionControl1.Control1:= B
  44.     else  UnionControl1.Control2:= B;
  45.   end;
  46. end;



Se trata de una prueba de concepto, ahora se debe desarrollar.

Espero que te sirva de ayuda.


PD. Si lo que pretendes es hacer un árbol, la implementación debe ser distinta. En este caso debes crear un control nodo gráfico que representará a los nodos y hojas. Los botones simples no te servirán...


Saludos.

Archivos adjuntos


  • 0

#9 martinartaza

martinartaza

    Advanced Member

  • Miembros
  • PipPipPip
  • 159 mensajes
  • LocationArgentina, Tucuman

Escrito 18 octubre 2011 - 10:27

Muchas gracias a todos, sigo trabajando en las ideas que me dieron, cuando tenga avances, lo posteo . Pensaba que ya lo iba a tener, pero bueno bien los tenga lo pongo acá.

Cuando llegue a casa vere si puedo hacer andar el ejemplo de escafandra en lazarus en ubuntu, pero antes de eso, eh creado un programa que dibuja en un timage una grilla, ya se puede hacer ZOOM o alejarse y ahora estoy viendo como poner los botones en los puntos de la grilla. Bueno cuando lo termine lo voy a postear y sera más claro.



  • 0

#10 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4.107 mensajes
  • LocationMadrid - España

Escrito 18 octubre 2011 - 03:30

...vere si puedo hacer andar el ejemplo de escafandra en lazarus en ubuntu...


Pues pe parece que no vas a poder. Mi ejemplo, tal como está, es muy cercano a la API de Windows.

Saludos. 
  • 0




IP.Board spam blocked by CleanTalk.