Delphi teclado virtual: editar el objeto en el que me encuentro
Resuelto
duffduffduff
Mensajes publicados
15
Estado
Miembro
-
nicocorico Mensajes publicados 846 Estado Miembro -
nicocorico Mensajes publicados 846 Estado Miembro -
Hola a todos y gracias de antemano por su ayuda.
No sean muy técnicos porque estoy cerca del nivel 0 en Delphi, pero ¡me divierto! ;-)
Estoy creando un teclado virtual en mi aplicación de pedido de mercancías por correo electrónico.
Mi problema es que me gustaría que cuando haga clic en una tecla de mi teclado virtual, Delphi sepa en qué campo de edición me encuentro sin tener que hacerlo manualmente para cada campo.
PD: he hecho mi teclado con una stringgrid.
Espero haber explicado bien mi problema.
Gracias,
Céd
No sean muy técnicos porque estoy cerca del nivel 0 en Delphi, pero ¡me divierto! ;-)
Estoy creando un teclado virtual en mi aplicación de pedido de mercancías por correo electrónico.
Mi problema es que me gustaría que cuando haga clic en una tecla de mi teclado virtual, Delphi sepa en qué campo de edición me encuentro sin tener que hacerlo manualmente para cada campo.
PD: he hecho mi teclado con una stringgrid.
Espero haber explicado bien mi problema.
Gracias,
Céd
3 respuestas
-
Hola,
no entiendo exactamente la pregunta, pero la respuesta probablemente se encuentra en "Col" y "Row" del stringgrid, que dan respectivamente la columna y la fila del elemento en curso.
Por lo tanto, solo sería necesario leer estos dos valores para deducir la correspondencia de la tecla y reaccionar en consecuencia...
--
El roble también fue una bellota, antes de ser un roble-
Oh sí, después de una pequeña reflexión pedagógica, me di cuenta de que debían llenar las cadenas del teclado virtual a partir del inspector de objetos, de ahí que luego falte un vínculo claro entre la tecla y su contenido.
Dicho esto, el contenido de cada cadena es accesible en tiempo de ejecución a través de "Items" +que es el contenedor de las cadenas mostradas+ utilizando "Col" y "Row" para determinar la tecla correspondiente.
Pero para hacerlo mejor, deberían practicar llenando estas cadenas programáticamente en lugar de utilizando el inspector de objetos, es fácil y es muy útil para avanzar más.
Por mi parte, incluso habría utilizado un Drawgrid: a partir de este componente es fácil dibujar uno mismo las teclas en el método "OnDrawCell" utilizando colores y justificaciones con "DrawText" (que es una función de Windows, no de Delphi, así que en win32sdk).
Pasar por alto el uso de simples cadenas para determinar las teclas permite implementar todo tipo de comportamientos, como la tecla "enter", "esc", "volver", etc.
Lo ideal +siempre manteniendo la simplicidad+ sería crear un arreglo de dos dimensiones representando las filas y columnas del teclado, con un campo para la tecla virtual del tipo Vk_ (tecla virtual de Windows, que codifica todas las teclas del teclado), a partir de la cual es posible restar el código ascii, por lo tanto el carácter a mostrar u otro símbolo, con los bitmaps de una "ImageList" por ejemplo.
El contenido de este arreglo puede definirse simplemente en forma de constante.
Con esta base, se sabe qué carácter dibujar durante un "OnDrawCell" leyendo en este arreglo con las "Col" y "Row" presentes como parámetros.
Igualmente, es fácil reaccionar al pasar el cursor sobre una tecla o al hacer clic, recuperando el carácter correspondiente en el arreglo gracias a las propiedades "Col" y "Row" del componente. Sin embargo, estas propiedades solo reflejan el último recuadro seleccionado, así que para saber qué recuadro está siendo sobrevolado con "OnMouseMove", o más generalmente para saber qué recuadro se encuentra en una posición determinada, hay que usar "MouseToCell".
-
-
Lo siento por la respuesta tardía, me he tomado una buena semana sin programar....
Me expresé mal, mi teclado está bien "relleno" y funciona perfectamente de la siguiente manera:
Cuando "entro" en una edición, por ejemplo "edit1" (onenter)
le digo a mi variable string que se llama "focus" que focus:='edit1';
Y entonces, cuando hago clic en la tecla 'A' de mi teclado virtual, le digo:
if focus='edit1' then edit1.text:=edit1.text+(la letra A del teclado virtual).
Hasta aquí todo va bien... cuando tengo 1, 2,... 5 ediciones es bastante simple, solo tengo que hacer:
if focus='edit1' then
if focus='edit2' then
if focus='edit3' then....edit3.text:=....
Pero imaginemos que tengo más de 50 ediciones en mi programa.... se vuelve laborioso y "repetitivo" programar.
"Es ahora cuando me enredo en mi explicación"
Lo que busco es que mi aplicación recuerde sola cuál fue la última edición seleccionada (eso es bastante simple), pero me gustaría que mi teclado virtual escribiera en la edición correcta sin tener que poner una línea de código para cada edición.
Es como si pudiera programar esto:
on teclado(teclaA).click then last_focusededit.text:=.......
O decir a mi string focus en qué edición estoy y hacer
focus.text:=.... (lo que significaría: edit1.text:=.....)
Lo siento de nuevo, incluso cuando me leo, no entiendo nada ;-)-
Hola,
¡Ah, sí lo veo! Entonces, en programación, cuando hay repeticiones de código generalmente hay una manera de hacerlo mucho más simple y, por lo tanto, mucho mejor.
Aquí, la variable que retiene el Edit relacionado con la pulsación de la tecla es la que falla, es decir, no es una cadena lo que se necesita, sino un TEdit o una clase padre.
De hecho, TEdit es un tipo de clase, y es posible declarar una variable de este tipo sin necesidad de crear el objeto, ya que esta variable en todos los casos es solo un puntero a un objeto, y no el objeto en sí mismo.
Así que declaramos una variable "Focus: TEdit" o "Focus: TObject" y luego podemos asignarle un puntero al Edit que está enfocado, es decir, no con el nombre del Edit, sino con su puntero: "Focus:= Edit1".
Luego, podemos acceder directamente al Edit enfocado a través de la variable: "Focus.text:= Focus.Text + (Letra del teclado virtual)", como si Focus fuera el Edit mismo, lo que nos da efectivamente una formulación de este tipo:
en teclado(teclaA).click entonces focus.text:=.......
Por lo tanto, para definir el contenido de "Focus", el uso del evento OnEnter de los TEdit es adecuado, y es posible compartirlo entre todos los Edit (declarando para uno de ellos y copiando el nombre del evento para los demás), y luego emplear "Sender": "If sender is TEdit then Focus:= Sender as TEdit". -
¡MERCIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII
Sospechaba que eso debía existir, ya había visto "sender" y cosas así
pero no sabía cómo usarlos..... no es tan complicado como eso....
¡especialmente cuando te dan la respuesta! ;-)
¡Gracias de nuevo!!!! -
¡De nada, fue un placer!
Y la explicación sobre el "Sender" es que los eventos están previstos para ser llamados por un componente externo, como en el caso del TEdit: Es decir, el TEdit u otro "TControl" recibe el evento de Windows y desencadena el evento que se ha declarado en el Form a través del inspector de objetos; Pero para saber quién es el origen del evento, siempre hay el parámetro "Sender" que especifica el emisor, así que el TEdit en este caso.
Concretamente, un evento es un campo que contiene un puntero a un objeto específico así como uno de los métodos declarados de ese objeto; El evento es, por lo tanto, la llamada a un método de una instancia de un objeto externo.
Así, cuando se declara un evento "OnEnter" de un TEdit en el inspector de objetos, Delphi agrega un método tipado en el Form, Edit1OnEnter por ejemplo, tal como OnEnter está declarado en el TEdit, y luego conecta el evento correspondiente del TEdit a dicho método.
El TEdit luego llama al evento contenido en su campo "FOnEnter", dándose a sí mismo como Sender, lo que provoca la llamada al método Edit1OnEnter contenido en el Form, ¡y eso es todo! -
-
-
Bien, aquí es donde es importante comprender bien el principio de herencia: todos los objetos tienen al menos un ancestro común (TObject), del cual heredan propiedades y código; Sabiendo esto, para almacenar un puntero de objeto más flexible, basta con tiparlo con su ancestro común, sobre todo si incluye los elementos que nos interesan;
Observamos que TDbEdit y TEdit tienen como ancestro común TCustomControl, por lo que basta declarar:
Focus: TCustomControl
así en esta variable podemos almacenar tanto DbEdit como TEdit, mientras tenemos acceso a la propiedad Text que pertenece a su ancestro común TCustomControl.
Dicho esto, no hay nada obligatorio en todo esto, y además nos encontramos rápidamente con definiciones de punteros mucho más difusas, así que se definen en TObject.
Podemos declarar:
Focus: TObject;
Como TObject, podemos colocar un objeto de cualquier clase en esta variable:
Focus:= Sender;
Focus:= Edit1;
Focus:= DbEdit1;
etc...
Luego, para saber de qué tipo es esta variable sin tipo, basta con probarlo:
Si Focus es TCustomControl entonces (Focus como TCustomControl).Text:= '';
Y así:
Si Focus es TCustomControl entonces Con (Focus como TCustomControl) haz Text:= Text + letra del teclado;
El operador "is" permite saber si el objeto es compatible con la clase especificada, sabiendo que responde verdadero si es un descendiente aunque sea lejano;
Así TEdit is TCustomControl es verdadero,
TDbEdit is TCustomControl también,
TDbEdit is TObject igualmente.
El operador "as" es el equivalente de "is", permite decir que usamos el objeto en el tipo especificado.
Por lo tanto, "is" permite probar la compatibilidad del objeto con tal clase, y "as" permite usarlo como tal clase, siempre que descienda de la clase dada.
En lugar de "as", también podemos hacer TCustomEdit(Focus).Text;
De hecho, la variable Focus solo contiene un puntero a un objeto, y darle un tipo solo sirve para especificar al compilador lo que podemos esperar de esta variable, evitando así tener que probar más adelante. Así que cuando el tipo es conocido, o cuando hay una clase común más allá del TObject que corresponde a los diferentes tipos que colocaremos allí, es mejor en este caso declarar la variable con el tipo más evolutivo posible, de este modo el código es más claro (además del nombre de la variable, el tipo da información sobre su contenido) y evitamos pruebas y otros casteos.
Como muchos lenguajes, Delphi está completamente estructurado en torno al objeto, y te sugiero que amplíes tu conocimiento al respecto para entender mejor los mecanismos involucrados:
Este tutorial me parece bien construido:
https://fbeaulieu.developpez.com/guide/?page=page_16
El roble también fue una bellota, antes de ser un roble-
Genial, nada es imposible, de hecho ;-) pero todavía tengo una duda
var Focus: TObject; //esto está bien
edit1onenter --> Focus:= Sender; //todo va bien
para lo siguiente me estoy bloqueando!
If Focus is TCustomControl then With (Focus as TCustomControl) do Text:= Text + letra del teclado;
la aplicación se compila pero no pasa nada. ¿Me he perdido un episodio? -
Como que demasiado control mata el control, pensaba que les hacía un favor al indicarles la versión bien estructurada que yo nunca utilizo por mi parte, pero funciona un poco diferente a lo que creía hasta ahora, ya que descubro que los operadores "is" y "as" solo funcionan si el tipo coincide estrictamente, y sin duda es por eso que abandoné su uso hace tiempo, ¡de hecho! Entonces, para el funcionamiento tal como lo describía, hay que usar el método InheritsFrom en lugar de "is", y el casteo forzado en lugar de "as":
If (Focus <> nil) and Focus.InheritsFrom(TCustomControl) then With TCustomControl(Focus) do Text:= Text + letra del teclado;
Así que, comenzamos por probar la validez (puntero no nulo) de Focus porque llamamos a un método a partir de su puntero;
"InheritsFrom" devuelve Verdadero si la clase especificada coincide con la del objeto o con uno de sus ancestros;
TCustomControl(Focus) permite forzar al compilador a usar el objeto como...
Por otro lado, para saber qué funciona mal, nada como el depurador integrado. La tecla F4 permite ejecutar hasta la línea del cursor y detenerse allí, F7 avanza paso a paso al ritmo de las llamadas de funciones, F8 es un paso a paso superficial, y F9 ejecuta hasta el final.
Cuando el programa se detiene en una línea, es posible inspeccionar el valor de ciertas variables y seguirlas, lo que es extremadamente valioso para verificar la conformidad del desarrollo del código...
El depurador funciona línea por línea únicamente, así que a veces es necesario dividir las líneas en varias partes:
If (Focus <> nil) and Focus.InheritsFrom(TCustomControl) then
With TCustomControl(Focus) do
Text:= Text + letra del teclado; -
-
Haa baah, es mi culpa, es que escribí TCustomControl en lugar de TCustomEdit, ¡TCustomControl no siendo un ancestro de los controles de tipo Edit! ¡Estoy trabajando demasiado en este momento! Y estoy trabajando demasiado con TCustomControl!
TCustomEdit es el ancestro más cercano en común entre TEdit y TDbEdit, y contiene la propiedad "Text", mientras que el hecho de poner "With TCustomControl(Focus) do" no genera un error porque el compilador encuentra la propiedad "Text" en "Form1"...
Y el "is" y "as" funcionan bien en los ancestros, así que soy yo quien comete el error desde el principio, ¡lo siento!
Prometido, esa línea funciona, y esta vez la probé antes!
Si Focus es TCustomEdit, entonces Con (Focus como TCustomEdit) do Text:= Text + letra del teclado;
Eso muestra la dificultad de identificar errores en programación, ya que aunque este es evidente, cuando uno se ha convencido de que es tal nombre y no otro, ¡no lo ve más! Otros errores son mucho más sutiles, y la utilidad del depurador integrado se siente rápidamente para detectarlos. -
GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS GRACIAS
Si el tiempo lo permite... tengo una pregunta que ya he hecho en un foro y a la que nadie ha sabido responder. Un conocido, "un pequeño genio autodidacta en informática", creó para las versiones a partir de D3 un componente "AUTOTABLE" en table Ado que se supone que actualiza automáticamente una tabla de una base de datos en red cuando se realiza una modificación.
El componente funciona como una tabla Ado, pero el problema es que no consigo encontrar la sutileza de por qué se inventó... en otras palabras, su auto-actualización
He intentado contactar a este señor, pero tiene mucho trabajo y no tiene tiempo para preocuparse por un pequeño componente (hoy seguramente sin internet para él) que creó hace más de 18 años....
Aquí está el enlace donde puedes encontrarlo: https://torry.net/authorsmore.php?id=1241
Pero como he dicho, no hay prisa, solo mucha curiosidad.
PD: lo he probado en D6 con Access y Paradox
@+
-