Evitar repetición de la pulsación de una tecla

Iniciado por adla, 7 Noviembre 2021, 19:09 PM

0 Miembros y 1 Visitante están viendo este tema.

adla

hola,

edito el mensaje, lo pregunto mejor:

¿hay alguna forma de evitar la repetición de una tecla o varias en el evento keydown? es decir que si mantengo pulsado el 1, no aparezca 11111111111111111111111111.....

gracias

Serapis

No existe un modo desde vb6, para evitarlo directamente.
Y no esoty seguro que puedas evitarlo con código (por ejemplo añadiendo varias variables buleanas en el keydown activarla a true si era false y evitar el cambio en el evento change, luego ponerlo en false en el evento Up, etc... creo que es complcado si no imposible sin el uso de un timer (por ejemplo).

Recuerda que la sucesión de eventos es: (Down change)+ up
Es decir si hay repetición Down y change se alternan y cuando se suelte ocurre un Up.
El problema de todo esto es que el evento Change no contiene parámetros que puedan controlarse para esto.

Es más sencillo, que filtres el contenido en el evento validate.
Si no te vale, la mejor opción es escribir tu propio control 'textbox'. En él tu pudes crear un evento 'change' que reciba como parámetro la tecla recibida y otro que señale si ya ha existido un evento down, antes de otro Up... con lo que al usar dicho control, sería extremadamente sencillo evitarlo.

En cualquier caso, es responsabilidad del usuario establecer el 'Repeat Delay' y en 'Repeat Rate' en las propiedades del teclado. No es tu problema que el usuario se quede 'pegado' a la tecla. Si es un usuario con capacidades mermadas, seguramente usará (o debiera usar) 'opciones de accesibilidad' adicionales que satisfagan sus necesidades y qu él habrá elegido o elegirá según sus necesidades.

...todavía a través de la API devicecaps, podrías leer y modificar dichos valores, pero siempre está el volver a restablecerlo a  sus valores previos... y no es buena idea, si el programa se cuelga, no podrás establecer los valores previos, si otra aplicación  (imagina otras 20) haciendo lo mismo, si no se 'resetean' en el mismo orden en que fueron tomados, se restablecerá un valor errado. En general las opciones dle usuairo, no hay que tocarlas, es el cosa dle usuario y no hay nada más irritante que una aplicación toque los valores que has establecido (entiendase resolución, tamaño de fuente, orientación de la pantalla (esta todavía vale si está a pantalla completa, porque se restablece al salir y mientras no hay otras aplicaciones que afecten), y los valores del ratón, teclado, cursor, idioma, etc...

Si insistes en necesitar dicha funcionalidad, recuerda lo que te he mencionado más arriba, podrás lograrlo a base de escribir tu propio control textbox, donde tu controles todo. Ahora, ¿merece el esfuerzo a dedicar escribir un control de usuario solo para poder disponer de esa funcionalidad?. Es mucho trabajo solo para eso (te obliga a implementar mucha funcionalidad ya existente en el textbox, que no incorpora el usercontrol), ...pero si lo haces incorpora otras funcionalidades útiles, todo junto al menos justifican abordarlo...

Serapis

#2
adla,  Tengo una buena noticia para tí...

Me picaba el gusanillo de probar que de alguna manera fuera posible y simple lograr lo que reclamas y lo he conseguido.
Que conste que no hay funcionalidad documentada al respecto, a sí que ha sido tirar de intuición y pruebas.

VB6 suele pasar sus parámetros por referencia... en general esto es más rápido que copiar el valor a una nueva variable y es una razón de peso (velocidad), por la que muchos lenguajes por defecto, han pasado (y siguen pasando) parámetros por referencia...
No obstante esto mismo permite modificar el valor en origen, por lo que ha sido cuestión de probar ciertos casos para jugar con tal eventualidad.

Con el evento keydown, el problema es que te pasa el 'keycode' que es la tecla pulsada, esto es muy útil, ya que te salta cuando pulsas la 'A' pero también cuand pulsas las teclas de cursor, muy útil si tratas de controlar por ejemplo el reposicionamiento del cursor en un control diseñado por tí.
Tu caso es no permitir repetición de teclas pero solo en el caso de las teclas imprimibles, lógicamente que alguien quiera mover el cursor x posiciones a derecha o izquierda (arriba y abajo suelen comportarse traducido a derecha e izquierda), no debiera ser filtrado. Es cómodo dejar el la tecla del cursor pulsada cuando hay que avanzar bastantes caracteres, en ves de pulsar una vez por cada movimiento...
Entonces, filtrar una funcionalidad y no la otra... exige crear un array de teclas, poniendo a 1 (por ejemplo) las teclas imprimibles y a 0 las que no para filtrar solo las imprimibles... el resultado es que no funciona.

Luego he recordado que está también el evento keypress, que también sucede antes del evento change (pero detrás del keydown) y que el parametro que envía no es 'keycode', si no KeyAscii', aquí si funciona, pues todo lo que hay que hacer es anular la tecla pulsada cuando se dé la condición indicada.
Se ve que el evento change es disparado justo a la devolució de este teniendo el cuenta el valor actual de 'KeyAscii' y no el del 'Keycode'.

Sin más te pongo el código.
De ejemplo se supone un formulario con al menos un textbox (llamado al caso text1) y ya:
Código (vb) [Seleccionar]

Private KeyIsDown As Boolean


Private Sub Text1_KeyPress(KeyAscii As Integer)
   If (KeyIsDown = False) Then
       KeyIsDown = True
   Else
       KeyAscii = 0
   End If
   
   Debug.Print KeyAscii    ' con CTRL +G abre la ventana de debug, para ver los resultados filtrados...
End Sub

Private Sub Text1_KeyUp(KeyCode As Integer, Shift As Integer)
   KeyIsDown = False  ' sea o no true.
   
End Sub

Ahora cuando se pulsa una tecla, solo se acepta como una sola pulsación, sin importar el tiempo que se deje pulsada la tecla, es decir elimina la repetición.

Si deseas que otras teclas (como la de cursor) tampoco se repitan, implementa el array comentado... pero ahora en el evento Keydown.
Nota la diferencia entre una tecla de cursor a izquierda keycode=36, es ditintito de Keypress= 36 que corresponde con el carácter imprimible '$'
...se modifica como aquí consta:
Código (vb) [Seleccionar]


Private KeyIsDown As Boolean
Private CodeIsDown As Boolean   ' ojo, esta opera con keydown, la otra con KeyPress...
private charsImprimibles(0 to 255)  as byte

Private Sub Form_Load()
   dim k as integer

   charsImprimibles(13) = 1
   for k= 32 to 255
        charsImprimibles(k) = 1
   next

   ' FALTA: poner a 0 las 'keycode' que quieras que si se repitan (pulsa F2 y busca KeyCodeConstants).
    charsImprimibles(vbKeyUp)= 0
    charsImprimibles(vbKeyDown)= 0
    charsImprimibles(vbKeyLeft)= 1      ' prueba, verás que cursor a derecha exige una pulsación por cada desplazamiento.
    charsImprimibles(vbKeyRight)= 0
    ' etc...
End Sub


Private Sub Text1_KeyDown(KeyCode As Integer)
   If (charsImprimibles(KeyCode ) > 0) Then
       If (CodeIsDown = False) Then
           CodeIsDown = True
       Else
           KeyCode = 0
       End If
   End If
   
   Debug.Print KeyCode
End Sub

Private Sub Text1_KeyUp(KeyCode As Integer, Shift As Integer)
    KeyIsDown = False  ' sea o no true.
    CodeIsDown = False

End Sub



p.d: Nota que si el textbox no es multiline, el salto de línea se ignora, pero no si el textbox es multiline.

adla

Cita de: Serapis en 10 Noviembre 2021, 20:22 PM
adla,  Tengo una buena noticia para tí...

Me picaba el gusanillo de probar que de alguna manera fuera posible y simple lograr lo que reclamas y lo he conseguido.
...

muchisimas gracias Serapis, te lo has trabajado, no he conseguido que me funcione porque quizas tenia que haber dicho que tengo el keypreview en true para que sea el formulario el que tome la pulsación, no es una caja de texto y por tanto no necesito mostrar el valor del keyascii.. la idea es o era, no es tan fácil, quiero hacer que una serie de luces se enciendan al ritmo de una melodía, para ello uso un arduino, reles, etc, y lo que hago es poner la canción 2 o 3 segundos, escribo el código para que se enciendan o apaguen, luego vuelvo al inicio de la cancion oigo otros 2 o 3 segundos más es decir 4 o 6, escribo el código para que se enciendan o apaguen, luego vuelvo al inicio de la cancion oigo otros 2 o 3 segundos más es decir entre 6 y 8 segundos, te puedes imaginar las veces que tengo que oir el tema, cuando llevo un minuto la he oido 30 o 40 veces y ya tengo las luces des-sincronizadas totalmente y soy incapaz de pillar el hilo, hacer esto me lleva semanas o meses,

[youtube=640,360]https://youtu.be/6iSElfabvC8[/youtube]

asi que pensé en asignar un relé a cada tecla y luego intentar seguir el ritmo de la música pulsando las teclas que se corresponderían con cada luz, por eso necesito saber cuanto tiempo permanece pulsada cada tecla para poder darle el tiempo a la luz encendida.. no se si lo he conseguido entender.. bueno sea como sea también es muy dificil conseguir sincronizar las luces así..

he conseguido solucionar el tema de esta forma y puesto que solo tenemos 10 dedos, esas serán las teclas que podamos pulsar a la vez, así que metemos las teclas pulsadas en un cadena y para cada repetición comprobamos si ya existe esa tecla en la cadena, si no existe la añadimos, al soltar la tecla borramos el código de la cadena..




Dim teclasPulsadas as String

Private Sub Form_KeyDown(KeyCode As Integer, Shift As Integer)
   If InStr(teclasPulsadas, "(" & Str(KeyCode) & ")") = 0 Then

'---- lo que queramos hacer ----

       teclasPulsadas = teclasPulsadas & "(" & Str(KeyCode) & ")"
   End If
End Sub



Private Sub Form_KeyUp(KeyCode As Integer, Shift As Integer)
   teclasPulsadas = Replace(teclasPulsadas, "(" & Str(KeyCode) & ")", "")
End Sub


Serapis

Si, cuando falta contexto no se puede aportar más que lo específicamente solicitado.

Aún así, recuerda que el formulario tambien posee el evento keypress (no solo el textbox, aunque es el prototipo de control cuando se habla de texto).
Citar
por eso necesito saber cuanto tiempo permanece pulsada cada tecla para poder darle el tiempo a la luz encendida..
La duración de cada tecla, como te decía al comienzo, viene definida en las propiedades del teclado. Puedes cambiarlo si los valores no se adecúan a tus necesidades aunque sea provisionalmente: 'Repeat Delay' y en 'Repeat Rate'.
La lástima es que en la interfaz son sendos 'sliders' pero sin mostrar valores numéricos. Así que hay que tirar de código para obtenerlos...


De todos modos El retraso de repetición es elegible entre 0 y 3 segundos 0-3, sin intervalos intermedios entre segundos.
Y la cadencia de repetición es elegible entre: 0-31, aunque experimentalmente si pongo 0, observo que se repiten 2 caracteres por segundo.
Estos valores podrían estar alterados o ignorados si hay establecidas opciones de accesibilidad (Filterkeys). No lo he investigado, ya que nunca lo he necesitado ni personal ni profesionalmente.

Código (vb) [Seleccionar]

Const GETKEYBOARD_RATE      As Long = 10
Const GETKEYBOARD_DELAY     As Long = 22

Private Declare Function KeyboardRepeat Lib "user32" Alias "SystemParametersInfoA" (ByVal Accion As Long, ByVal Param1 As Long, Param2 As Any, ByVal Param3 As Long) As Long

Private Sub Form_Load()
    Dim kbState As Long
   
    Call KeyboardRepeat(GETKEYBOARD_DELAY, 0, kbState, 0)
    Debug.Print "Retraso de repeticion del teclado: " & kbState & " Sg." 
   
    Call KeyboardRepeat(GETKEYBOARD_RATE, 0, kbState, 0)
    Debug.Print "Cadencia de repeticion del teclado: " & kbState & " Caracteres por segundo"
End Sub


Tal como lo explicas, es un trabajo de chinos, pero en el vídeo se ve que te ha quedado bien...

adla

Cita de: Serapis en 13 Noviembre 2021, 00:36 AM
Si, cuando falta contexto no ...

hola, no sé por que no recibo avisos de nuevos mensajes en el foro y lo tengo activado..

bueno, ahora estoy a tope con el curro y me queda poco tiempo para las aficiones, he pensado en un programilla que reprodujera un mp3 y diese opción a 'pintar' los encendidos de las luces pero no tengo ni idea de por donde empezar, mis conocimientos son muy básico.. dejo la idea por alguien tiene algo parecido.

gracias por todo Serapis, eres una máquina, ojalá yo dominase esto como tú.