Privacy Policy

Blog italiano

Clicca qui se vuoi andare al blog italiano su Lazarus e il pascal.

Forum ufficiale

Se non siete riusciti a reperire l'informazione che cercavate nei nostri articoli o sul nostro forum vi consiglio di visitare il
Forum ufficiale di Lazarus in lingua inglese.

Lazarus 1.0

TDD o Test-Driven Development
immagine docker per lavorare con lazarus e free pascal
Indice
Installare lazarus su mac osx sierra
Un dialog per la connessione al database:TfmSimpleDbConnectionDialog
Superclasse form base per programmi gestionali (e non) #4 - wait animation
Superclasse form base per programmi gestionali (e non) #3 - traduzione delle form
Superclasse form base per programmi gestionali (e non) #2 - log, exception call stack, application toolbox
Superclasse form base per programmi gestionali (e non)
Advanced Record Syntax: un esempio pratico
DB concetti fondamentali e TDBEdit, TDBMemo e TDBText
DB concetti fondamentali e DBGrid
Advanced Record Syntax
DB concetti fondamentali e ZeosLib
DB concetti fondamentali e connessioni standard
guida all'installazione di fpc trunk da subversion in linux gentoo
Creare un server multithread
Cifratura: breve guida all'uso dell'algoritmo BlowFish con lazarus e free pascal.
Free Pascal e Lazarus sul Raspberry Pi
fpcup: installazioni parallele di lazarus e fpc
Ubuntu - aggiornare free pascal e lazarus
Come interfacciare periferica twain
Lazarus 1.0
Aspetto retrò (Cmd Line)
Disporre le finestre in un blocco unico (come Delphi)
Zeos ... dietro le quinte (prima parte)
Lavorare con file in formato JSON con Lazarus
Overload di operatori
Dataset virtuale
Linux + Zeos + Firebird
Pascal Script
La gestione degli errori in Lazarus
Indy 10 - Invio email con allegati
Brute force
Le permutazioni
Da DataSource a Excel
StatusBar - aggirare l'errore variabile duplicata
Visualizzare un file Html esterno
Evidenziare voci TreeView
Toolbar
Direttiva $macro
Indy gestiore server FTP lato Client
TImage su Form e Panel
PopUpMenu sotto Pulsante (TSpeedButton)
Conversione numerica
Proprietà
Funzioni a parametri variabili
Overload di funzioni
Liste e puntatori
Conferma di chiusura di un applicazione
Il file: questo sconosciuto
UIB: Unified Interbase
System Tray con Lazarus
Intercettare tasti premuti
LazReport, PDF ed immagini
Lazarus e la crittografia
Ampliare Lazarus
Generare controlli RUN-TIME
TTreeview e Menu
TTreeView
Utilizzare Unità esterne
Lavorare con le directory
Realizzare programmi multilingua
Codice di attivazione
File INI
Esempio lista in pascal
Stabilire il sistema operativo
Recuperare codice HTML da pagina web
Trascinare un file nel programma
Mandare mail con Lazarus
Welcome to Italian community of Lazarus and Free Pascal. Please login or sign up.

Recenti

How To

Utenti
Stats
  • Messaggi totali: 20.582
  • Totale discussioni: 2.471
  • Online today: 25
  • Online ever: 5.949 (Luglio 30, 2026, 09:49:59 PM)
Utenti online
  • Users: 0
  • Guests: 20
  • Total: 20
20 Visitatori, 0 Utenti

[Risolto] formkeydown non rileva la combinazione tasti Shift+Tab

Aperto da petrusic, Maggio 01, 2026, 05:49:10 PM

Discussione precedente - Discussione successiva

petrusic

Pensavìo di avere superato l'ostacolo, invece, oggi ripercorrendo, in prova, il tentativo di riconoscere la presisione combinata dei tasti |SHIFT|+|TAB|, mi sono ritrovato ad arrampicarmi sugli specchi.

Aggiungo che ho attivato, per la Form, la proprietà KeyPreview.

ecco il codice interno alla FormKeyDown:

unit Frm2;    // Form2 - Inserimento Movimenti

{$mode objfpc}{$H+}

interface
. . .
implementation
uses
  FrmMain, Frm7;

{$R *.lfm}

{ TForm2 }
type
  TtbcausaliGio = array of String;
. . .

var
. . .
  swTabIndietro: Boolean = False;
. . .

procedure TForm2.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState
  );
begin
  swTabIndietro:= False;
  if ((ssShift in Shift) AND (Key = VK_TAB)) then
  begin
    swTabIndietro:= True;
  end;
end;                                                   


Imposto qui la variabile booleana  "swTabIndietro:= True;" , dichiarata nella sezione Implementation, per ritrovarla  tale e quale nella

procedure TForm2.CBvoceSotCtoCasKeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
var
  striMia: String = '';

begin
  if (key = 16) or  (key = 9) then
  begin
    WriteLn('Key= ', IntToStr(Key));
  end;
  if (key <> VK_LEFT) and (key <> VK_BACK) and (key <> VK_CANCEL) and (key <> VK_END) and (key <> VK_HOME) and (key <> VK_PRIOR) and (key <> VK_RIGHT)  and (key <> VK_TAB) then
  begin
    case swTabIndietro of
      True:
        begin
          key:= VK_TAB;
        end;
    end;
    case Key of
      VK_DOWN, VK_UP, VK_NEXT, VK_PRIOR, VK_TAB:     // Freccia-giù, Freccia-su, Pagina-giù, Pagina-su
        begin
          swFineDigit:= True;
          voceSotCtoCasDig:= CBvoceSotCtoCas.Text;
          CarListCBvoceSotCtoCas;   // Carica le voci contabili di sottoconto in base al contenuto della casella CBvoceSotCtoCas.Text
        end;
    end;


Ho riportato anche il codice relativo all'evento KeyDown della ComboBox, solamente per completezza, ma il problema sorge perchè la procedura FormKeyDown NON intercetta la pressione combinata dei tasti  |SHIFT|+|TAB| e, pertanto l'istruzione
    swTabIndietro:= True; Non viene eseguita.

Ho controllato anche con un BreakPoint posto su quella riga.

Aggiungo ancora che quando viene eseguita la
    procedure TForm2.CBvoceSotCtoCasKeyDown
il campo Key contiene il valore 16, corrispondente al tasto |SHIFT|.

Io non so cosa dovrebbe contenere , dopo la pressione di |SHIFT|+|TAB|, ma sicuramente non 16.

Dopo avere riscontrato il contenuto di Key, ho perciò deciso di aggiungere la variabile swTabIndietro,  impostandola a TRUE, in modo da riconoscerla  e di proseguire diversamente da come procede ora:  Tutto inutile.

Confido nella vostra maggiore conoscenza ed esperienza.
ciao ciao

DragoRosso

#1
Il problema è che continui a mettere a false la "swTabIndietro" nella FormKeyDown.

In pratica qualla variabile rimane TRUE solo in una condizione e finchè non premi altri tasti, peccato che nella combobox tu non esegui il check di quella variabile proprio in quella condizione ... perchè escludi il check quando premi "VK_TAB" ?

    if (key <> VK_LEFT) and (key <> VK_BACK) and (key <> VK_CANCEL) and ............  (key <> VK_TAB) then // QUESTA VK_TAB VA' TOLTA e vedrai che funziona.


Il fatto che il FORMKEYDOWN non ti stia funzionando ... non è possibile, a meno che tu non stia usando in qualche altro punto del programma la cattura di un evento della tastiera (altri onkeydown, onkeypress o onkeyup) e sostituisca o cancelli il valore.

P.S.: stò parlando di Windows ovviamente non di Linux. Sotto Linux non sò se c'è qualche inghippo particolare su ciò.

[EDIT]: ovviamente come dice il nome "KeyPreview", il primo evento Onkeyxxxx deve avvenire sulla Form e non sul componente. In Windows accade ciò, spero sia così anche in Linux.
:) Ogni alba è un regalo, ogni tramonto è una conquista :)

petrusic

#2
@ DragoRosso

Mi rendo conto che, a volte, andare troppo dentro nei dettagli può essere controproducente. Lo dico perchè, a causa della mia esposizione, accompagnata dal codice di due procedure, una succedanea all'altra, tu non hai colto l'anomalia che pensavo di avere evidenziato.

Intanto confermo che FORMKEYDOWN, anche in Linux viene schedulata dall'evento Onkeyxxxxprima prima della "ControlloXXXKetDown".
Ed è proprio dentro la FORMKEYDOWN che la pressione combinata dei tasti  |SHIFT|+|TAB| non viene riconosciuta. Riporto qui sotto la mia segnalazione originaria:
Citazione
procedure TForm2.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState
  );
begin
  swTabIndietro:= False;
  if ((ssShift in Shift) AND (Key = VK_TAB)) then
  begin
    swTabIndietro:= True;      <---- Riga sottoposto a BreakPoint
  end;
Come è visibile e comprensibile, io interrogo lì dentro l'avvenuta pressione dei due tasti suiccitati e l'esito della "IF" termina sempre per "<>" (NON uguale).  Lo affermo con sicurezza, perhè come detto sempre nel mio post di apertura, ho posto un BreakPoint:
Citazione
la procedura FormKeyDown NON intercetta la pressione combinata dei tasti  |SHIFT|+|TAB| e, pertanto l'istruzione
    swTabIndietro:= True; Non viene eseguita.

Ho controllato anche con un BreakPoint posto su quella riga.

Ora forse risulkta più chiaro quello che ho cercato di rappresentare all'inizio.
???
ciao ciao

DragoRosso

@petrusic
Si, ho compreso il problema, e ti ho risposto che non è possibile che non venga generato quell'evento, a meno di intercettazioni da parte del tuo codice o da parte del sistema operativo.
Anche se funzionasse, comunque avresti l'altra situazione che ti ho segnalato.

Ribadisco che <SHIFT> <TAB> deve essere intercettato dal codice che hai proposto (fatto salvo quanto detto sopra). In Windows funziona perfettamente (e io lo uso normalmente quel tipo di codice per una svariata di motivi) e quindi non saprei come aiutarti.

.... AGG. ... Mentre in Lazarus x WIn64 la cattura dei tasti <SHIFT> <TAB> mi funziona, ho provato per curiosità in Delphi e ... non mi funziona in alcun modo. Il tasto TAB non viene mai intercettato dal programma ....  :o :(

Quindi voglio indagare ... perchè forse @petrusic hai ragione da vendere.
:) Ogni alba è un regalo, ogni tramonto è una conquista :)

petrusic

Trovaaaatoo!
;D
Ho apportato nella FORMKEYDOWN la seguente modifica:

procedure TForm2.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState
  );
begin
  WriteLn('procedure TForm2.FormKeyDown');
  swTabIndietro:= False;
  (Key = VK_TAB) and (ssShift in Shift)
//  if ((ssShift in Shift) AND (Key = VK_TAB)) then
  if ((Key = VK_TAB) and (ssShift in Shift)) then
  begin
    swTabIndietro:= True;
  end;
end;

e . . . Udite, Udite:
F U N Z I O N A
;D ;)
Alè,  Alé, Alé
ciao ciao

DragoRosso

Secondo me ti funziona per caso. Ti confermo che il tasto TAB è usato dai sistemi operativi o dai loro "sottoprogrammi" per vari scopi. E' possibile che se "parte" un servizio X (che ora non è attivo) il tasto TAB cessi di funzionare.

Prova questo:

uses LMessages;
type
  TForm1 = class(TForm)
     ..........
  protected
     function IsShortCut(var Msg: TLMKey): Boolean; override; //AGGIUNGI QUESTA RIGA
    ...........
  end;

function TForm1.IsShortCut(var Msg: TLMKey): Boolean;
begin
  if (Msg.CharCode = VK_TAB) and (GetKeyState(VK_SHIFT) < 0) then
    begin
      writeln('Shift+Tab intercettato !!! YAHOO');
      Result := True;
    end
  else
    Result := inherited;
end;
:) Ogni alba è un regalo, ogni tramonto è una conquista :)

petrusic

Citazione di: DragoRosso il Maggio 02, 2026, 12:13:26 PM
Secondo me ti funziona per caso. Ti confermo che il tasto TAB è usato dai sistemi operativi o dai loro "sottoprogrammi" per vari scopi. E' possibile che se "parte" un servizio X (che ora non è attivo) il tasto TAB cessi di funzionare.
Ahimè si. Ha funzionato solamente per pura casualità. Poi  ho ripovato ancora e non ha funzionato  più.
Ho perciò apportato le correzioni immettendo il codice che mi hai suggerito, ma la funzione  GetKeyState non
viene riconosciuta.
Ecco le modifiche apprtate:

unit Frm2;    // Form2 - Inserimento Movimenti

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, Forms, Controls, DateUtils, Graphics, Dialogs, StdCtrls,
MaskEdit, ExtCtrls, Buttons, ComCtrls, Grids, DBGrids, LCLType, Math, ActnList,
LMessages, ButtonPanel, Menus, Frm4, UModuloDatiDB, utilmie, Types;

type

  { TForm2 }

  TForm2 = class(TForm)
    BpulsCancTutto: TButton;
    CBDescr: TComboBox;

. . .
procedure RBCauGioClick(Sender: TObject);
    procedure RBCauPreMomClick(Sender: TObject);
    procedure RBCauVecClick(Sender: TObject);

protected
   function IsShortCut(var Msg: TLMKey): Boolean; override;
var
    tipoVistaPreMom: String;

//    tbPreMomVivi: TtbPreMomVivi;
end;


var
  Form2: TForm2;

  statoGridDraw: TGridDrawState;


implementation
uses
  FrmMain, Frm7;

{$R *.lfm}

{ TForm2 }
type
  TtbcausaliGio = array of String;             
. . .
function TForm2.IsShortCut(var Msg: TLMKey): Boolean;
begin
  if (Msg.CharCode = VK_TAB) and (GetKeyState(VK_SHIFT) < 0) then
  begin
    writeln('Shift+Tab intercettato !!! YAHOO');
    Result := True;
  end
  else begin
    Result := inherited;
  end;
end;                                                                     


In compilazione viene emesso il seguenmte Errore:
Citazionefrm2.pas(6994,35) Error: Identifier not found "GetKeyState"

Ho allora cercate di capire perchè e accedendo qui ho trovato la risposta: GetKeyState è una funzione specifica di Windows

Ho cercato  altro per capire come supereare l'ostacolo, ma fiuno ad ora NON ho trovato niente

ciao ciao

DragoRosso

Non posso aiutarti su ciò, in Delphi per Linux c'è una libreria apposita (di cui non c'è il sorgente) che gestisce questa funzione.

Dovresti provare a chiedere a qualche AI la cosa tipo:

"In Lazarus (pascal) per Linux come faccio a rilevare se il tasto shift è premuto a runtime, puoi generarmi il codice ?"
:) Ogni alba è un regalo, ogni tramonto è una conquista :)

petrusic

Citazione di: DragoRosso il Maggio 03, 2026, 04:01:08 PM
Dovresti provare a chiedere a qualche AI la cosa tipo:

"In Lazarus (pascal) per Linux come faccio a rilevare se il tasto shift è premuto a runtime, puoi generarmi il codice ?"


Ti ringrazio moltissimo per il suggerimento e sulla domanda da formulare, ma scusa la domanda: Chi sono gli Al?
Purtroppo non ho mai incontrato tale termine prima d'ora  :-[
ciao ciao

DragoRosso

 ;D ;D

Intellegenza Artificiale .... quelle gratuite disponibile a portata di mano sono Copilot e Gemini, poi se vai su ChatGPT puoi usarla gratuitamente, poi ce ne sono tante altre, una delle più note è Claude (nelle sue versioni Opus, Sonnet e Mythos) che peròè a pagamento.
:) Ogni alba è un regalo, ogni tramonto è una conquista :)

petrusic

@ DragoRosso
Ho seguito il tuo consiglio ed ho chiesto alla AI Gemini il codice per rilevale il tasto shift.
Eccoi la risposta:
Citazione
Rilevare la pressione del tasto Shift in Lazarus su Linux è un'operazione piuttosto semplice, ma l'approccio cambia leggermente a seconda che tu voglia intercettarlo durante un evento specifico (come il click di un pulsante) o in qualsiasi momento tramite un timer.In ambiente LCL (Lazarus Component Library), lo stato dei tasti modificatori è memorizzato nella variabile globale ssShift all'interno del set GetKeyShiftState.Ecco i due metodi principali:1. Verificare Shift durante un Evento (es. OnClick)Se vuoi che il programma faccia qualcosa di diverso quando un utente clicca su un pulsante tenendo premuto Shift, puoi usare la funzione GetCompositionState o più semplicemente controllare lo stato globale.Delphiuses
  LCLIntf, LCLType, Classes;

procedure TForm1.Button1Click(Sender: TObject);
begin
  // Verifichiamo se lo Shift è nel set dei tasti premuti attualmente
  if (GetKeyState(VK_SHIFT) < 0) then
    ShowMessage('Hai cliccato il pulsante premendo SHIFT!')
  else
    ShowMessage('Hai cliccato il pulsante senza SHIFT.');
end;
2. Rilevamento in Tempo Reale (con un TTimer)Se hai bisogno di monitorare il tasto Shift costantemente (anche se l'utente non sta cliccando nulla), puoi usare un componente TTimer impostando un intervallo breve (es. 50ms).Delphiprocedure TForm1.Timer1Timer(Sender: TObject);
begin
  // GetKeyShiftState restituisce un set (TShiftState)
  if ssShift in GetKeyShiftState then
    Label1.Caption := 'Stato: SHIFT PREMUTO'
  else
    Label1.Caption := 'Stato: Libero';
end;
Dettagli Tecnici ImportantiCross-platform: Il codice sopra funziona bene su Linux (sia con widgetset GTK2 che Qt5) e anche su Windows, rendendo il tuo software portabile.Differenza tra Left e Right: Se hai bisogno di distinguere tra lo Shift sinistro e quello destro, su Linux la gestione può essere più complessa a seconda del server grafico (X11 o Wayland). In genere, VK_LSHIFT e VK_RSHIFT sono le costanti da passare a GetKeyState.Focus: Ricorda che GetKeyState o GetKeyShiftState rilevano lo stato del tasto nel contesto dell'applicazione. Se l'applicazione perde il focus (non è la finestra attiva), il comportamento potrebbe variare in base alle politiche di sicurezza del Desktop Environment.Quale funzione usare?FunzioneDescrizioneGetKeyState(VK_SHIFT)Restituisce un valore negativo se il tasto è premuto. Utile per controlli rapidi.GetKeyShiftStateRestituisce un set di tipo TShiftState. È il modo più "stile Pascal" per gestire i modificatori.OnKeyDownEvento della Form che passa direttamente il parametro Shift. (Assicurati che KeyPreview := True nella Form).

Ho provato, interrogando GetKeyState direttamente nella TForm2.CBvoceSotCtoCasKeyDown(

procedure TForm2.CBvoceSotCtoCasKeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
var
. . .
begin
      if GetKeyState(VK_SHIFT) < 0 then
      begin
          ShowMessage('Il tasto Shift è premuto!')
      end
      else begin
          ShowMessage('Il tasto Shift NON è premuto.');
      end;
. . .
end;

Effettivamente ha funzionato, ma così non riesco a capire se è stato premuto anche il taso TAB.
Allora hop chiesto anche come agire se voglio rilevare la pressione combinata dei due tasti e questa volta la risposta ricevuta è quella di interrogare tale evenioenza nella FormKeydown con l'istruzione che ho già impiegato prima e cioè:
if ((Key = VK_TAB) and (ssShift in Shift)) then

Quindi, alla fine sono tornato al punto di prima: <niente di fatto>. perchè così facendo il tasto TAB non viene rilevato.
:o

ciao ciao

DragoRosso

Ma scusa ... GeTKeyState quindi funziona su Linux ? Allora dovrebbe funzionare anche con il codice "IsShortCut" che ti avevo passato ... :o ???
:) Ogni alba è un regalo, ogni tramonto è una conquista :)

petrusic

Citazione di: DragoRosso il Maggio 05, 2026, 11:01:36 PM
Ma scusa ... GeTKeyState quindi funziona su Linux ? Allora dovrebbe funzionare anche con il codice "IsShortCut" che ti avevo passato ... :o ???
Si, l'ho provato ieri pomeriggio e funziona. La spiegazione rispetto alla prova fatta subito dopo il codice da te suggerito e quela fatta ieri  è data dalla NON presenza / presenza della libreria LCLIntf fra gli uses.
Per cui, inserendo fra gli uses del file frmxx.pas:

unit Frm2;    // Form2

{$mode objfpc}{$H+}

interface

uses
. . .
. . . LCLIntf,LCLType, LMessages
. . .


"IsShortCut"  funziona benissimo.

Ora devo solo adeguarne la gestione per le mie necessità

Grazie, grazie , grazie! :D
ciao ciao

petrusic

Ho risposto troppo presto.
É  verso che la IsShortCut funziona, ma non ncome mi aspettavo. Non viene eseguita prima della TForm2.CBvoceSotCtoCasKeyDown, bensì dopo detta procedura:
procedure TForm2.FormKeyDown
procedure TForm2.CBvoceSotCtoCasKeyDown
function TForm2.IsShortCut
function TForm2.IsShortCut


Ma, a quel punto il codice che avrebbe dovuto eseguire dentro la  TForm2.CBvoceSotCtoCasKeyDown, nella condzione SHIFT+TAB, non può essere più eseguito.
Perciò ho dovuto ripetere all'interno della TForm2.IsShortCut il codice previsto nella TForm2.CBvoceSotCtoCasKeyDown, ma non solo, bensì anche parte di quello contenuto nella TForm2.CBvoceSotCtoCasChange, perchè quest'ultima non viene richiamata ed a me serve che venga richiamata.
É venuta fuori, in pratica una seconda  CBvoceSotCtoCasKeyDown.
Non è una bella soluzione, ma, per quanto visto e vissuto, meglio non riesco a fare.

Data la nuova realtà, mi viene in mente che potrei anche eliminare del tutto la CBvoceSotCtoCasKeyDown.
:(

ciao ciao

DragoRosso

Gli eventi in generale, a parte quelli legati ai DB o quelli grafici (come OnPaint e affini), normalmente gestiscono soprattutto delle chiamate: è normale quindi che nell'evento tu chiami un'altra funzione.
Per quello che riguarda la gestione della tastiera, il tuo uso del TAB mi ha spiazzato perchè non uso mai combinazioni di sistema o simili (tipo TAB o SHIFT TAB appunto) per gestire
l'input sulle mie applicazioni. TAB e SHIFT TAB hanno un loro ruolo già prestabilito e quindi usarle per altro è controproducente.

Per evitare situazioni simili sarebbe necessaria una progettazione preliminare, che indichi scopo, strumenti e risorse per ogni singola attività per ogni singola parte del programma.

Capisco e comprendo che ovviamente portando avanti un applicativo per diletto (anche se estremamente utile) ovviamente segua percorsi totalmente differenti da quelli necessari.

Buon divertimento.
:) Ogni alba è un regalo, ogni tramonto è una conquista :)

Disclaimer:

Questo blog non rappresenta una testata giornalistica poiché viene aggiornato senza alcuna periodicità. Non può pertanto considerarsi un prodotto editoriale ai sensi della legge n. 62/2001.