martedì 31 dicembre 2013

FreeDrawing: disegnare e scrivere a mano libera (Android e iOS)

Una richiesta che spesso mi è stata fatta nelle ultime settimane è quella di poter far firmare un utente su un dispositivo mobile in una applicazione fatta con Delphi.
Al di là delle questioni legali relative, ho pensato che potesse essere interessante realizzare un'applicazione mobile che permettesse di disegnare a mano libera (o con un pennino).

Devo dire che è stato relativamente semplice, grazie agli strumenti messi a disposizione da FireMonkey.
L'idea di fondo è quella di sfruttare l'evento OnMouseMove di una TImage e disegnare sul canvas della Bitmap della TImage stessa.

Alcuni punti fondamentali che voglio brevemente evidenziare:
  1. grazie all'evento OnMouseMove abbiamo a disposizione le coordinate X e Y dei punti attraversati durante l'azione di disegno effettuata dall'utente con il dito o con il pennino;
  2. molte opzioni di disegno sono disponibili fra le proprietà dell'oggetto TStroke del canvas stesso;
  3. sfruttiamo i metodi DrawLine e DrawEllipse di TCanvas per realizzare la linea spezzata che congiunge i punti rilevati nell'handler di OnMouseMove e per disegnare dei punti nel caso di tocchi singoli sulla superficie di disegno (ottimo per realizzare i puntini sulle i e la punteggiatura);
  4. grazie ai componenti TColorPicker e ai TColorQuad, è facile permettere all'utente di personalizzare i colori dello sfondo e del tratto;
  5. un semplice algoritmo per ridurre "glitches" nel disegno (soprattutto nella scrittura a mano libera) è stato implementato tenendo in considerazione la distanza fra due punti da unire nelle spezzate e il gap temporale fra due tocchi distinti (anche qui, usando i componenti TTrackBar, è facile dare un minimo di personalizzazione dei parametri dell'algoritmo all'utente);
  6. l'uso di un Drawer (cfr. articolo e video tutorial di Josè Leon) ci permette di avere la massima superficie di disegno a disposizione e al contempo realizzare una GUI intuitiva per l'utente;
  7. la TToolbar superiore permette facilmente di mettere a disposizione dei comandi rapidi all'utente fra cui troviamo anche i pulsanti per utilizzare una immagine scattata dalla fotocamera (o presa dalla galleria immagini del dispositivo) come sfondo; da notare come l'implementazione di questa funzionalità (utilizzare un'immagine già scattata o scattare un'immagine dalla fotocamera) non abbia richiesto che le tre righe di codice necessarie a disegnare (in stretch mode) l'immagine selezionata sulla nostra superficie di disegno, grazie all'uso delle TTakePhotoFromCameraAction TTakePhotoFromLibraryAction a disposizione fra le standard action;
  8. Ultima ma non meno importante, una volta completato il nostro disegno, l'utente può condividere il risultato attraverso lo ShareSheet standard di sistema, che permetterà di inviare il contenuto (la Bitmap) a qualunque applicazione installata sul nostro dispositivo (ottimo per inviarla via social networks o per email / instant messaging).

Conclusioni e materiale

Realizzare una semplice applicazione per il disegno a mano libera (e scrittura) è possibile con Delphi XE5 ed è possibile, con poco sforzo, realizzare le funzionalità fondamentali per offrire all'utente una buona esperienza d'uso. Grazie a FireMonkey molte di queste funzionalità sono realizzabili con pochissime righe di codice (scattare una foto dalla camera, usare un'immagine dalla libreria, condividere il risultato del disegno con altre applicazioni).

Trovate i sorgenti completi dell'applicazione qui: Link to sources DXE5
Demo: Link to Android APK

Di seguito qualche screenshot preso con il mio Nexus 7, e con l'occasione possiamo anche farvi i migliori auguri per un felice anno nuovo!

FreeDrawing semplice (auguri!)

FreeDrawing con una foto come sfondo

FreeDrawing, opzioni di disegno nel drawer

Ed ecco uno screenshot da un iPad 4:
FreeDrawing su iPad4 (iOS 7)

lunedì 16 dicembre 2013

Gli strumenti per potenziare l'IDE di Delphi (Parte 2 - GExperts)

La seconda puntata di questa serie dedicata agli strumenti per potenziare l'IDE di Delphi prende in esame un tool storico, i GExperts.
Nome:GExperts
Sito:http://www.gexperts.org/
Download per XE5:Experimental for Delphi XE5
Licenza:Open Source
Prezzo:0€



GExperts è il tool che installo ed uso fin dalle prime versioni di Delphi, è un progetto Open Source iniziato da Erik Berry nel 1996 e poi portato avanti dal "GExperts Development Team"


Una volta installato i GExperts vedrete un nuovo menu in Delphi GExperts che contiene tutti i (numerosi) comandi e le impostazioni di questo strumento.
Come nel caso di ModelMaker Code Explorer vi mostrerò una selezione dei comandi e funzionalità che ritengo più utili e che uso più spesso mentre vi invito ad esplorare estesamente questi tool e scegliere poi quali sono le caratteristiche che a voi sembrano più interessanti.

Backup dei progetti

La prima funzionalità che vi descrivo è il backup dei progetti che, soprattutto in passato, ho usato parecchio. Il funzionamento è semplice, l'expert prende l'elenco dei file dalle impostazioni di progetto e crea un archivio compresso (zip) con un nome scelto dall'utente. Ovviamente è possibile aggiungere file che non sono del progetto (configurazione, risorse, ecc...)
Come ho accennato sopra ultimamente uso sempre di meno questa funzionalità visto che da numerosi anni uso altri strumenti per gestire il codice sorgente, ovvero i version control. Se siete interessati all'argomento i version control saranno oggetto di un prossimo articolo.

Class Browser



La finestra Class Browser è una vista utile sulla classi del progetto (anche qui viene effettuato il parsing dei file del progetto per popolare l'elenco delle classi). E' possibile navigare tra le varie classi e tra i membri delle stesse. In Delphi questa funzionalità è presente in due viste: la vista Structure e la vista Delphi Class Explorer.

Clean Directories

La funzionalità della pulitura delle directory è allo stesso tempo molto senplice e molto utile. Ormai i file di Delphi "di contorno" si sono moltiplicati, dai vecchi .dsk o .ddp ai recenti .local e .identcache, più vari file di backup ed altri ancora. La presenza di questi file (che sono solo di supporto all'IDE per vari task) rende noiosa la pulizia manuale del progetto ad esempio prima di un backup o di un'importazione in un version control. La "pulizia" diventa invece quasi obbligatoria se dobbiamo spedire il progetto ad un collega oppure renderlo aperto al pubblico.
Il comando Clean presente in questa finestra consente quindi di cancellare in un colpo solo tutti i file non richiesti con una personalizzazione molto potente.

Code Librarian


Una vera e propria gemma nascosta (che oggigiorno andrebbe ripensata in versione "remotizzata") è il tool Code Librarian, in pratica un browser di "code snippets" più utili o ricorrenti da richiamare in ogni momento con funzionalità di copia dall'IDE e incolla sull'IDE per velocizzare le operazioni. Questo tool si basa su un file in formato ClientDataSet (salvato in formato binario) che contiene i nostri pezzetti di codice con la possibilità di salvarli in folder e sotto folder. E' anche possibile mettere il file degli snippets in una directory su una share di rete in modo da condividerli tra diversi sviluppatori (ovviamente sulla stessa rete).

Components to Code

Altra funzionalità essenziale, soprattutto per chi è alle prime armi o vuole farsi aiutare nella costruzione a runtime dei componenti Delphi, è la conversione che effettua GExperts di un componente piazzato sul form o su un DataModule a Design Time nelle righe di codice necessarie a definire, creare e configurare il suddetto componente.

Nella foto sopra potete vedere un classico componente data aware (in questo caso una query UniDAC) convertito con il comando di GExperts Components to Code, a destra il risultato del comando incolla in un metodo del DataModule. Da quyesto momento potremo eliminare il componente a Design Time e tenere il codice corrispondente a Run Time.

Editor Experts

Il sotto-menu Editor Experts visualizza una serie di comando molto utili per la scrittura e la modifica del codice sorgente nell'editor di Delphi. 


Come vedete si tratta di una serie di comandi da menu con relativo shortcut per applicare alcune trasformazioni al testo del nostro codice. Alcuni sono per me irrinunciabili, vdiamoli:
  • Ctrl-Alt-Z Allinea linee diverse di codice sorgente in base ad alcuni identificatori (se avete decine di righe di lettura da field di un dataset dovete assolutamente provarlo)
  • Shift-Alt-R inverte la parte di statement a destra del := con quella a sinistra. Anche qui per intuire la comodità del comando pensate che le vostre 100 righe di lettura dai campi del dataset possono essere trasformate istantaneamente nella scrittura dei campi stessi.
  • Ctrl-Alt-Up e Ctrl-Alt-Down - Questi comandi consentono di "saltare" dalla parola su cui è posizionato il cursore alla stessa parola più avanti o più indietro nel codice. Per me indispensabili. La percentuale di utilizzo  di questi due comandi rispetto alle funzioni classiche di ricerca (F3, ecc...)  per quanto mi riguarda è di 100 a 1.

Expert Manager


La finestra Expert Manager consente di gestire gli expert installati nell'IDE in modo semplice ed intuitivo invece di giocare con le stringe nel registry in Windows. Il tool è utile soprattutto per testare Delphi senza caricare expert che possano falsare il risultato oppure per verificare che alcune instabilità dell'ambiente Delphi non siano in realtà imputabili a qualche expert in particolare.

Grep Search

Qualora le funzioni di ricerca di Delphi o di altri experts non riescono avenirvi in aiuto la funzione Grep Search dei GExperts fa al caso vostro.


Diciamo che è una funzione di ricerca del testo all'interno di file (non solo quelli Delphi) molto veloce, precisa e configurabile. Potete ricercare del testo (anche con le Regular Expression) nel file corrente, in tutti i file del progetto oppure a partire da una (o più di una) directory, è possibile escluderne alcune e provvedere alla mask per il tipo di file in cui ricercare. 
La funzionalità di search all'interno dei file è talmente utile che è possibile usare il "Grep Search" direttamente dalla shell (explorer) sostituendo così l'orribile funzionalità di ricerca di Windows.

Hide/Show non Visual

Se avete molti componenti non visuali piazzati sulle form timer, actionlist, imagelist, menu, popup, datasource ma soprattutto dataset (questi ultimi andrebbero però tolti e piazzati su datamodule) e dovete continuamente spostarli per vedere i controlli UI messi sotto questo comando è perfetto per voi, nasconde (e poi visualizza) tutti i componenti non visuali sul form corrente.

IDE Menu Shortcut



Questo tool è interessante qualora i vostri comandi preferiti (parlo di tutto l'IDE di Delphi, non solo dei GExperts) non abbiano uno shortcut associato. Con questo tool, appunto, è possibile assegnarne uno (o cambiarlo se è in conflitto oppure vi è scomodo)

Replace Components

Questo è un tool che mi ha fatto risparmiare (e ha fatto risparmiare a migliaia di programmatori Delphi) diverse ore di lavoro e possibili errori. Vi descrivo un possibile scenario: voi (il vostro capo, il vostro cliente) avete deciso che i pulsanti standard non vi vanno più bene e volete sostituirli con pulsanti molto più belli. A questo punto di solito comincia un lavoro di sostituzione immane (se il progetto è grande) che può anche portare all'introduzione di bug se no fatto in modo preciso. Se invece avete installato i GExperts potete selezionare il componente tipo che volete sostituire (un TButton) invocare il comando Replace Components impostare la configurazione appropriata... et voilà la conversione su tutto il progetto è già terminata.

Come vedete dall'immagine le opzioni sono piuttosto chiare. 


Per raffinare il processo di conversione poi, è possibile dalla voce Settings accedere ad un'altra finestra di opzioni da cui potete mappare una proprietà del componente sorgente ad una proprietà (anche con nome diverso) del componente di destinazione.

Set Tab Order



Nei programmi non c'è nulla di più fastidioso (per chi è abituato a lavorare con la tastiera) di premere Tab per spostarsi sul campo successivo ed invece saltare in modo quasi casuale a qualsiasi controllo sul form. Come ovviamente sapete è frutto di una non corretta impostazione del Tab Order su form. Con questo strumento dei GExperts potete invece tenere sotto controllo (e cambiare) l'ordine dello scorrimento dei campi nella vostra maschera.

Conclusioni

Ricordo ancora che i GExperts contengono più funzionalità rispetto a quelle presentate in questo articolo e quindi vi esorto a testare i vari tool contenuti per trovare quelli più utili a voi.

Nel prossimo articolo prenderemo in esame uno strumento che si è affacciato da alcuni anni nel panorama Delphi, CnPack.

Se avete quesiti o proposte di recensione di altri tool scrivetemi o lasciate un commento su questo blog.

giovedì 12 dicembre 2013

Delphi XE5 Update 2 disponibile!

Da ieri è disponibile il secondo aggiornamento per RAD Studio XE5 (Delphi e C++) se per gli sviluppatori C++ la novità più interessante è il supporto per iOS, per gli sviluppatori Delphi ci sono (oltre a numerosi bug-fix) alcune nuove funzionalità decisamente interessanti.

Fra queste, vi segnalo il Mobile Preview, che permette sostanzialmente di sviluppare la vostra applicazione mobile  in un ambiente Win32 quindi con tempi di compilazione ridotti ma senza rinunciare all'applicazione dello stile FireMonkey mobile, semplificando di molto anche molte operazioni di debug.

Buon download!

giovedì 28 novembre 2013

RaspberryPi: slide ITDevCon e giornata FocusOn

Ho pubblicato le slide della mia sessione "RaspberryPi: alta tecnologia a basso costo" per ITDevCon 2013, le trovate a questo link.




Se l'argomento vi interessa, vi ricordo anche la giornata "Focus On: RaspberryPi, alta tecnologia a basso costo" organizzata da Wintech-Italia per il prossimo 10 dicembre!

A presto

mercoledì 27 novembre 2013

TGmailLabel un esempio di custom component FireMonkey

Introduzione

Per la mia sessione "FireMonkey e gli stili: hands on" di ITDevCon 2013, ho preparato alcuni esempi pratici. Uno di questi esempi consiste nel creare un nuovo componente visuale (derivato da TStyledControl) con uno stile grafico che riproduce l'aspetto delle etichette GMail.

L'esempio è pensato per mostrare come:
  • comporre uno stile usando l'IDE di Delphi;
  • creare un nuovo componente derivato da TStyledControl;
  • integrare lo stile del componente nelle risorse dell'eseguibile;
  • installare il componente nell'IDE di Delphi, di modo da poterne disporre a design-time (ed eventualmente customizzarne ulteriormente lo stile, come è possibile fare con tutti i componenti FM).
Nonostante sia d'obbligo un po' di esercizio per prendere confidenza con i nuovi concetti in gioco (rispetto al mondo VCL), è relativamente semplice creare un nuovo componente visuale che possa essere poi utilizzato su tutte le piattaforme attualmente supportate (con Delphi XE5: Windows, Mac OS, iOS e Android).

Le slide che ho preparato per la mia presentazione a ITDevCon offrono una brevissima introduzione a FireMonkey, la nuova application platform presente nelle ultime versioni di Delphi (XE2 - XE5).
Uno dei concetti fondanti è quello di "style": la definizione (e, per estensione, la sua rappresentazione testuale o binaria) della composizione di oggetti (primitivi o a loro volta composti, visuali e non) che implementa l'aspetto visuale di un componente FM derivato da TStyledControl.

In termini più semplici, i componenti visuali FireMonkey possono essere "forme semplici" (linee, rettangoli, forme geometriche elementari, testo...) oppure "complessi" (frutto della composizione di altri oggetti). Chiamiamo stile la composizione di componenti che realizza le caratteristiche visuali di un componente "complesso".

Comporre lo stile in modo visuale

Uno stile è esso stesso un oggetto FM (un'istanza di TFmxObject) e la sua rappresentazione testuale non è affatto diversa da quella usata nei file .FMX dal form designer dell'IDE, usata per caricare a run-time gli oggetti che compongono una form FM (analogamente a quanto avviene con i file .DFM per le form VCL).

Per editare visualmente gli stili, l'IDE ci mette a disposizione uno strumento specifico, analogo al form designer, denominato "Style Designer". Potete vederlo facendo doppio click su un componente TStyleBook e, insieme all'Object Inspector e la Componente Palette, permette anche di manipolare gli elementi di stile e anche di caricare/salvare su file gli stili FireMonkey.  

L'IDE di Delphi XE5 con lo Style Designer aperto

Purtroppo, questo strumento soffre ancora di qualche problema di gioventù e a volte può risultare un po' ostico da usare. Per ovviare a questo, è possibile percorrere un'altra strada e usare il form designer dell'IDE per comporre, su una qualunque form "di appoggio", la parte visuale del componente. 

Il Form Designer dell'IDE di Delphi XE5

Una volta terminato, grazie all'ampia compatibilità fra il formato usato negli stili e quello usato nei file .FMX delle form, basta chiedere all'IDE di mostrarci la form in modalità testuale (Pulsante destro -> "View as text") e copia-incollare la nostra composizione dalla form ad un file testuale (con estensione .style).

La rappresentazione testuale della form "di appoggio"

Una volta effettuate alcune semplici modifiche (come la rimozione di tutti i component name presenti), il file sarà perfettamente compatibile e potrà essere usato come definizione di style per il nostro componente.

Lo style, ottenuto "ripulendo" la rappresentazione testuale del nostro layout

Possiamo ora includere il contenuto del file .style fra le risorse binarie del nostro eseguibile, di modo di poterne usufruire a run-time; per farlo ci basta preparare un altro file testuale con estensione .rc e chiedere l'inclusione del file indicando anche il nome da assegnare alla risorsa relativa. 

Il contenuto del file GMailLabel.rc

Quando l'IDE processerà questo file, verrà generato un file .RES contenente la risorsa "GMailLabelstyle" che verrà (grazie ad apposita direttiva nel file sorgente del componente) compilata e inclusa nell'eseguibile.

Il codice del componente

Una volta preparata la risorsa contenente la definizione dello stile da applicare al componente, non ci resta che creare la classe del componente stesso.
Io ho usato il wizard di creazione dei componenti (nell'IDE di Delphi, Menù File --> New --> Other --> Delphi files --> Component).
NB: Il codice sorgente completo è disponibile al link indicato nella sezione Materiali di questo articolo.

Sostanzialmente ho creato un componente ereditato da TStyledControl e ho fatto override di alcuni metodi fondamentali per la gestione degli stili, fra cui 
  1. procedure ApplyStyle;
  2. function GetStyleObject(const Clone: Boolean): TFmxObject; 
che si occupano rispettivamente di:
  1. eseguire del codice di collegamento fra gli elementi di stile e i dati del componente (es. impostare la proprietà Text del componente TText mostrando il valore della proprietà LabelName);
  2. ritornare, caricandolo dalle risorse dell'eseguibile, l'oggetto di stile da applicare al componente;
L'intero file non è più lungo di 180 righe (incluse quelle vuote) e il codice è particolarmente semplice.


Package: codice e risorse

Una volta creato il componente, possiamo creare un nuovo package che lo contenga (nell'IDE di Delphi, Menù File --> New --> Delphi Projects --> Package) e seguire questi semplici passi:

  • aggiungiamo la unit del nuovo componente;
  • rimuoviamo eventuali dipendenze dalla VCL che il wizard potrebbe aver impostato;
  • aggiungiamo al package tutte le piattaforme per cui vogliamo rendere disponibile il componente;
  • selezioniamo nuovamente la piattaforma Win32, rendendola attiva;
  • click destro sul nome del package dentro il Project Manager, scegliamo Build;
  • click destro sul nome del package dentro il Project Manager, scegliamo Install;
E dovremmo ottenere conferma che il nostro nuovo componente è stato installato nell'IDE.
Se creiamo una nuova applicazione (desktop o mobile) e apriamo una form, dovremmo vedere comparire nella Tool Palette il nostro nuovo componente.
Ricordate che il componente verrà registrato nella pagina della Palette che avete specificato nel wizard di creazione del componente (potete controllare nel file .pas del componente i valori dei parametri della procedura RegisterComponents che il wizard avrà predisposto per voi. Se li variate, ricordare ti reinstallare il vostro package dopo aver fatto una nuova build).



procedure Register;
begin
  RegisterComponents('Andrea', [TGMailLabel]);
end;

La procedura Register del mio componente TGMailLabel

Ricordatevi di includere il file .style e il file .rc, di modo che le risorse vengano compilate ogni volta che fate Build del package e (grazie alla direttiva {$R GMailLabel.res} presente nel file .pas) incluse negli eseguibili delle applicazioni compilate.

Un componente, 4 piattaforme

Abbiamo quindi a disposizione un nuovo componente visuale che possiamo usare sia in applicazioni FireMonkey desktop (su Windows e Mac OS X) che in applicazioni mobile (su Android e iOS). Ecco qualche screenshot sia dell'IDE che delle applicazioni in esecuzione:

La form desktop con alcune istanze di TGMailLabel, a design-time

Come potete notare nella Structure View si vede anche un componente ShadowEffect1 applicato a GMailLabel4: ovviamente è possibile utilizzare il componente TGMailLabel esattamente come tutti i componenti standard FireMonkey e quindi ho aggiunto un effetto ombreggiatura che viene applicato a run-time (NB: nello screenshot seguente non è visibile per colpa del mio Win XP in esecuzione nella virtual machine sul mio MacBookPro, si vede bene nello screenshot Mac OS X).

Una form FireMonkey desktop, in esecuzione su Windows (Win XP)

Form FireMonkey desktop, in esecuzione su Mac OS X (Mavericks)


Una form FireMonkey mobile (piattaforma Android), a design-time

Una form FireMonkey mobile (piattaforma iOS), a design-time

Una form FireMonkey mobile, in esecuzione sul mio Samsung S2

Una form FireMonkey mobile, in esecuzione nel iOS Simulator

Riferimenti

Se siete interessati alla scrittura di componenti FireMonkey, non posso che consigliarvi il materiale di Ray Konopka disponibile in rete, ad esempio le due sessioni dell'ultima CodeRage, "Creating Custom Controls for the FM Application Platform" e "Effectively Using List Controls in Mobile Apps".

Conclusioni

Oltre ad essere uno strumento fondamentale con cui la piattaforma FireMonkey permette di realizzare applicazioni GUI su piattaforme differenti, gli stili possono essere usati per realizzare componenti custom anche complessi con poco sforzo e sfruttando le potenzialità dell'IDE.
Una volta installati nell'IDE, questi componenti possono essere utilizzati in applicazioni desktop e/o mobile sulle quattro piattaforme attualmente supportate da Delphi XE5.

Materiale

Potete scaricare il codice sorgente completo dell'esempio al seguente link:
Source code - Link to DropBox

domenica 24 novembre 2013

Gli strumenti per potenziare l'IDE di Delphi (Parte 1 - MMX)

Se usate le ultime versioni di Delphi (in realtà anche non tanto "ultime", basta usare da Delphi 2007 in avanti) vi sarete abituati al nuovo IDE "Galileo" che, sebbene forse un poco più pesante rispetto ai primi Delphi, è sicuramente più ricco di funzionalità tali da farmi (personalmente) ritenere l'IDE di Delphi 7 - Delphi 1 ormai inadeguato per la scrittura di codice.

Rimandando ad un altro post la descrizione delle funzionalità dell'IDE che ritengo più interessanti, volevo invece passare in rassegna a tool esterni che potenziano l'ambiente di sviluppo e sono compatibili con più versioni di Delphi (in molti casi supportano anche le versioni pre-galileo).

Voglio ancora ricordare che questa non è una presentazione esaustiva dei tool Delphi ma solo la presentazione dei tool che uso personalmente sulle versioni di Delphi che ho installato per i progetti (miei e dei miei clienti), ovvero Delphi 2007 e Delphi XE4/5

La mia lista dei tool Delphi

In questa serie di articoli sui tool di Delphi cercherò di mostrare i vantaggi per lo sviluppo di ognuno di questi tool e in particolare mi focalizzerò sull'aumento di velocità di scrittura del codice e quindi sull'aumento di produttività che questi tool (con le varie funzionalità aggiunte) offrono.
  • ModelMaker Code Explorer (MMX)
  • GExperts
  • CnPack
  • Documentation Insight
  • Andy's IDE Tools
    • DelphiSpeedUp (D2007)
    • IDE Fix Pack (D2007)
    • DDevExtensions (D2007 - DXE5)

ModelMaker Code Explorer (MMX)

Nome:ModelMaker Code Explorer
Sito:http://www.modelmakertools.com/code-explorer/
Licenza:Commerciale
Prezzo:99€ (69€ Upgrade)


Forse alcuni di voi ricorderanno ModelMaker perché veniva installato con Delphi 6 e Delphi 7 (almeno nelle versioni Enterprise) come strumento di refactoring UML poi rimpiazzato da Together.
 è invece uno strumento che velocizza la scrittura, la modifica (refactoring) di codice Delphi che però mostra la sua massima utilità se il codice è scritto ad oggetti.

ModelMaker Code Explorer (da qui in poi MMX) consente di velocizzare la scrittura e la modifica del codice fornendo numerosi strumenti sia sotto forma di wizard sia sotto forma di comandi attivabile con una sequenza di tasti.

Devo subito dire che il maggior risparmio (di tempo e tasti) MMX lo permette quando si scrive o modifica codice ad oggetti e comunque conoscendo le funzionalità principali della programmazione ad oggetti.

La finestra principale (la console dalla quale partire soprattutto le prime volte) è la "Model Maker Code Explorer" e possiamo dire che è una finestra simile alla finestra "Structure" di Delphi, la differenza è che il "navigatore" di MMX è notevolmente più potente: più comandi, più filtri di visualizzazione, più strumenti di refactoring, diverse modalità di navigazione, Live Metrics, ecc...


Con un click è possibile aggiungere visualmente campi, metodi, proprietà, eventi, indexer alla nostra classe, editare (sempre visualmente) qualsiasi entità del vostro codice.

Mentre scrivete il codice nell'editor di Delphi MMX mostra poi due toolbar (personalizzabili) agganciate alla finestra di editing che mostrano i principali comandi di navigazione e modifica.


Se, come me, quando scrivete codice odiate staccare le dita e lo sguardo dalla tastiera e considerate il concetto dei wizard troppo invasivo, MMX allora è davvero lo strumento che fa per voi.
Tutti i comandi che trovate nella finestra principale, nelle toolbar, nel menu MMX di Delphi sono infatti invocabili anche da tastiera con tasti rapidi visualizzabili e personalizzabili tramite le opzioni di MMX.

Veniamo finalmente ad una lista (molto personale) di comandi e funzionalità che ritengo essenziale per una scrittura veloce, e sicura, del proprio codice.

1) Copia e spostamento entità.

Il refactoring di progetti grandi e complicati a volte è davvero un incubo quando dobbiamo spostare metodi, proprietà e anche classi intere da un punto dell'applicativo ad un altro. In Delphi lo spostamento di queste entità è complicato dall'obbligo di tener sincronizzata la dichiarazione e l'implementazione dei metodi. Con MMX posso dimenticarmi di tutto ciò ed utilizzare i comandi di clipboard:





"Cut Entity"   -> "Ctrl-Alt-X"
"Copy Entity"  -> "Ctrl-Alt-C"
"Paste Entity" -> "Ctrl-Alt-V"

Usando questi comandi riuscirete a spostare metodi, proprietà e classi da una unit all'altra senza perdere tempo a cercare in giro pezzi di codice. MMX riesce anche a capire quali sono le unit di cui avete bisogno nei metodi tagliati/copiati e quindi aggiungere tali clausole uses alla unit di destinazione.
Questa è di gran lunga la mia funzionalità preferita di MMX, mi ha fatto risparmiare davvero molto tempo e anche notevoli "mal di pancia" quando ci si ingroviglia con il taglia/incolla tradizionale!

2) Cancellazione entità e sincronizzazione metodi

Un'altra funzionalità forse meno "eclatante" ma ugualmente utile (e parente della prima) è la possibilità di cancellare metodi "Ctrl-Shift-Del" (ma anche campi, proprietà, classi) anche qui senza di preoccuparsi di dove si trovi l'implementazione.


Anche la sincronizzazione dei parametri dei metodi "Ctrl-Alt-Y" è molto semplice ma data l'altissima frequenza di questa operazione risulta davvero un "time-saver".

3) Editing delle entità



L'editing delle entità "Ctrl-E" è davvero molto potente e si risolve nell'invocazione di una finestra (diversa a seconda dell'entità) in cui si possono modificare tutti i parametri in questione. Ad esempio se modifico un metodo posso modificare:
  il nome
  la visibilità (provate, protected, public, ecc...)
  i parametri (aggiungerne, modificarne il tipo, ecc...)
  il tipo del metodo (funzione, procedura, metodo statico, ecc...)
  varie opzioni (virtuale, override, message, final, ecc...)
Anche in questo caso, ovviamente non mi preoccupo minimamente della parte di implementazione, infatti all'OK sulla finestra di modifica il codice sarà perfettamente aggiornato e sincronizzato.

4) Aggiunta Variabili locali



Questa funzionalità (ormai presente anche in Delphi) consente di aggiungere una variabile locale nel metodo corrente. La cosa sembra *troppo* banale per comparire in un wizard, pensate però di essere in un metodo abbastanza lungo e di aver bisogno di una variabile stringa opportunamente inizializzata a stringa nulla ''. Con il comando "Add local var" -> "Ctrl-L" dovete semplicemente scrivere il nome ed il tipo della variabile senza spostarvi dal punto in cui stavate scrivendo e la variabile sarà definita ed inizializzata per voi alla prima riga del metodo. Semplice ma efficace!

5) Rename in Scope



Un'altra operazione molto frequente nella modifica del codice sorgente è il rename dei simboli solitamente fatta dalle classiche operazioni di Search/Replace su testo selezionato oppure sull'intero file. MMX ha invece il comando "Rename in Scope" -> "Ctrl-Alt-L" che consente di rinominare il simbolo su cui è posizionato il cursore e di applicare le regole di rename legate allo scope: localmente, nella classe, nel modulo (unit). Questo limita gli errori dovuti al "Replace Selvaggio" che possono avere conseguenze anche gravi per l'introduzione di bug particolarmente insidiosi.

6) Extract Methods



La funzionalità "principe" per il refactoring è sicuramente il comando "Extract Method" (presente anche in Delphi) è uno strumento molto comodo per riconoscere pezzi di codice da raccogliere a fattore comune o semplicemente effettuare cambiamenti pesanti sui metodi delle mie unit.

7) Aggiunta entità



Le finestre di "Add Entity" (dove Entity può essere un metodo, una classe, un'interfaccia, una property, ecc...) sono le stesse di editing delle stesse con ovviamente i settaggi non inizializzati. Personalmente sull'aggiunta di proprietà, metodi o classi ritengo più veloce l'immissione manuale scrivendo direttamente il codice, tuttavia sono un aiuto importantissimo a chi si avvicina alla programmazione Delphi e soprattutto ai programmatori Delphi, anche di lunga data, che si approcciano seriamente alla programmazione ad oggetti.

Conclusioni

Questa è la lista delle funzionalità che ritengo più utili in ModelMaker CodeExplorer, funzionalità che però non si esauriscono qui anzi sono cosciente di usare lo strumento ad una frazione delle sue potenzialità ma ognuno deve trovare un equilibrio tra le innumerevoli funzionalità che uno strumento offre e quelle che sono davvero in grado di apportare un netto miglioramento nel suo modo di scrivere codice.

Nel prossimo articolo vedremo in azione uno dei più longevi tool di Delphi: i GExperts.

Se avete quesiti o proposte di recensione di altri tool scrivetemi o lasciate un commento su questo blog.



Paolo Rossi.


ITDevCon 2013 - Materiale disponibile

ITDevCon 2013 si è conclusa venerdì scorso e qualche giorno fa il materiale della conferenza (incluse le mie presentazioni e i relativi esempi) è stato reso disponibile a tutti i partecipanti (vi sarà arrivata comunicazione via mail).

In particolare volevo segnalarvi l'esempio CustomComponent della sessione "FireMonkey e gli stili: hands on" in cui mostro come creare un nuovo componente FireMonkey: TGMailLabel che mima il comportamento del pulsante delle etichette di GMail (prossimamente posterò qualche dettaglio in più anche qui).

Per quanto riguarda la conferenza, è stata un'esperienza positiva: molte le sessioni interessanti (ma poche quelle a cui sono riuscito effettivamente a partecipare, per vari motivi fra cui la sistemazione in extremis di slide e demo delle mie sessioni!) e ottimo lo scambio di opinioni e informazioni con gli altri speaker e con i partecipanti.

Sono stato felice di vedere che le mie sessioni (sia quella sugli stili FireMonkey che quella su un argomento "nuovo" come RaspberryPi) hanno avuto buona partecipazione e suscitato interesse fra i partecipanti: più di una persona mi ha scritto, nei giorni seguenti la conferenza, che aveva già ordinato un RaspberryPi per farci qualche esperimento!

Vi ricordo che il 10 dicembre prossimo ci sarà un evento FocusOn dedicato a RaspberryPi in Wintech-Italia a Piacenza.

Vi lascio una fotografia proprio della sessione RaspberryPi e un saluto a tutti i partecipanti / speaker / organizzatori di questo evento.


Qui trovate altre fotografie dagli organizzatori

Qui trovate un reportage a cura di Marco Breveglieri

mercoledì 23 ottobre 2013

ITDevCon 2013

La quinta edizione della conferenza ITDevCon (European Delphi Conference), organizzata da BitTime, si tiene anche quest'anno a Verona, il 14 e 15 Novembre 2013.

La conferenza propone molti speech interessanti, suddivisi in tre sessioni parallele, qui trovate l'agenda, giorno per giorno:
Come sottolineato anche da Primož Gabrijelčič, non sarà facile scegliere fra le sessioni proposte! Al momento questa è la mia selezione (fra parentesi le scelte obbligate):
  • 14 Novembre
    • (Opening Keynote - Fabrizio Bitti e Daniele Teti) 
    • (XE5: The First Delphi... for Android - Marco Cantù)
    • (RaspberryPi: alta tecnologia a basso costo - Andrea Magni)
    • Delphi REST Client Library - Marco Cantù
    • (Firemonkey e gli stili: hands on - Andrea Magni)
    • DelphiMVCFramework: RESTful interfaces and dynamic web pages with Delphi and WebBroker - Daniele Teti
  • 15 Novembre
    • Delphi Toolbox: la cassetta degli attrezzi ideale per le applicazioni moderne - Marco Breveglieri
    • Messaging e notifiche asincrone per mobile - Marco Mottadelli
    • (Going to mobile with Delphi: best practices, problems and solutions - Daniele Spinetti e Daniele Teti)
    • The amazing Leap is not for games only... - Detlef Overbeek
    • From Zero to Hero in 60 minutes - Creating a client/server mobile application in one hour - Primož Gabrijelčič
    • Profiling techniques for Delphi - André Mussche
Ho ancora qualche incertezza ma c'è ancora tempo :-)

Le mie due sessioni (entrambe il 14 Novembre) sono abbastanza differenti fra loro: 
  • la prima è incentrata sul RaspberryPi e su un possibile approccio per sfruttarne le potenzialità riutilizzando le conoscenze di un programmatore Pascal e alcuni progetti opensource molto diffusi (lighttpd, Firebird, ...);
  • la seconda è dedicata a Delphi e soprattutto agli stili FireMonkey, con degli esempi pratici (anche di creazione di un componente visuale custom).
Ci vediamo a Verona!


lunedì 21 ottobre 2013

La "nuova" sfida: un mondo mobile (ma non solo)

Osservando i risultati di un recente sondaggio di Dimensional Research (commissionato da Embarcadero Technologies) si trova un riscontro oggettivo della crescente richiesta di applicazioni mobile che gli sviluppatori si trovano a fronteggiare.



Il sondaggio, svolto nell'agosto 2013, era specificamente rivolto a sviluppatori con esperienza e responsabilità in ambito Windows desktop ed ha raggiunto un campione di circa 1300 soggetti in tutto il mondo (53% EMEA, 25% America, 22% Asia Pacific). Vediamo insieme alcuni punti fondamentali emersi dagli sviluppatori intervistati e, sulla base di questi dati, cerchiamo di fare qualche considerazione.

Il mondo è mobile

Una larghissima fetta degli intervistati (85% circa) ha ricevuto la richiesta di sviluppare applicazioni per il mondo mobile. 
Una percentuale così alta indica che non è più ragionevole, per chi vuole essere competitivo sul mercato, ignorare l'esigenza di annoverare fra le proprie competenze lo sviluppo di applicazioni mobile: i clienti lo chiedono con sempre maggiore insistenza e aumenta il rischio che una mancata risposta comporti che si rivolgano ad altri concorrenti, con i rischi del caso.

Le aspettative dell'utente sono elevate

L'86% degli intervistati dichiara che le aspettative dei propri utenti rispetto alle applicazioni mobile da sviluppare sono elevate. 
Ciò significa che l'utente/committente si aspetta di avere dallo sviluppatore un'applicazione bella, veloce e affidabile... Generalmente, il raffronto viene fatto considerando le applicazioni esistenti più diffuse (GMail, FaceBook, Twitter, ecc.). Se da un certo punto di vista è positivo che i nostri utenti/committenti abbiano aspettative elevate, dal punto di vista di chi deve realizzare queste applicazioni significa confrontarsi con prodotti realizzati da team di sviluppatori dedicati, con elevate competenze specifiche, un budget (economico e di tempo di sviluppo) ben proporzionato e che magari hanno avuto il tempo di maturare (non sono certo alla prima versione!).
Certamente una sfida stimolante che però non sempre è supportata da una comprensione, da parte dell'utente/committente, delle difficoltà tecniche, delle competenze da acquisire e dei costi (sia economici che di tempo) che lo sviluppatore deve assorbire per raggiungere l'obiettivo.

Non si tratta di una transizione "vecchio per nuovo"

Il 99% degli intervistati sostiene che dovranno continuare a supportare le attuali applicazioni desktop (e il 95% sostiene che dovranno anche continuarne lo sviluppo).
Come dicevamo, il fardello di cui lo sviluppatore deve farsi carico, in termini di competenze e difficoltà tecniche, per supportare la nuova classe di dispositivi mobile, non è leggero. Purtroppo, non si può nemmeno dire che le risorse necessarie ad affrontare la nuova sfida possano in qualche modo essere recuperate da altri fronti su cui lo sviluppatore è già impegnato: nonostante il forte cambiamento del mercato a favore dei dispositivi mobile, non è ancora il momento (se mai arriverà) per dismettere lo sviluppo desktop.

In particolare, spesso (65%) viene richiesto di "spostare" alcune funzionalità di un'applicazione esistente in modo che siano fruibili su un dispositivo mobile oppure (43%) di replicare le stesse funzionalità dell'applicazione desktop su un dispositivo mobile (aggiungendo piattaforme senza però dismettere quelle esistenti).
Anche se è presente una forte (58%) richiesta anche di applicazioni mobile "nuove", spesso si tratta di ampliare la compatibilità delle proprie applicazioni rendendole fruibili (in toto o in parte) su nuove piattaforme, trasformando di fatto quella che potrebbe sembrare una transizione netta (butto il desktop, benvenuto tablet o smartphone) in una questione più delicata che introduce il problema di dover poi supportare in ogni fase (analisi, sviluppo, test e deploy) tutte le piattaforme supportate.

Tanti, nuovi e accattivanti ma limitati, a chi importa?

Fra le righe dei dati riportati nei risultati del sondaggio, si intravede quello che è un altro problema di cui non sempre l'utente/committente sembra accorgersi: i nuovi dispositivi, per quanto belli e tecnologicamente sofisticati, hanno alcune limitazioni che possono rivelarsi problematiche per chi deve perseguire l'obiettivo di dare un'esperienza utente di qualità.

Ad esempio, i nuovi dispositivi non sempre hanno capacità di elaborazione comparabili con quelle di un computer "tradizionale" (e lo stesso vale per le capacità di archiviazione disponibili).
Inoltre, il contesto in cui questi dispositivi vengono usati, appunto, mobile, introduce anche limitazioni riguardanti la quantità di informazioni fruibili sul display (considerate anche le caratteristiche di un sistema di input basato su touch e multi-touch) e la connessione dati (in termini di disponibilità, di velocità e di qualità).

La grande varietà di questi dispositivi, che per l'utente/committente spesso viene appiattita in termini di due grandi categorie indistinte (smartphone/tablet), risulta essere un'ulteriore complicazione per lo sviluppatore, cui viene chiesto di garantire non solo un generico funzionamento ma anche un buon livello qualitativo della user-experience fruibile, in ogni caso.

Dato questo scenario è abbastanza comprensibile la preoccupazione degli sviluppatori che, tradotta in cifre nel sondaggio, dichiarano che i propri utenti:
  • si aspettano di avere applicazioni complesse che siano però altrettanto semplici da usare di quelle di base (54%)
  • non considerano le limitazioni dei dispositivi mobile, pensando che possano erogare tutte le funzionalità presenti nel mondo desktop (51%)
  • richiedono che l'applicazione sia disponibile per tutte le piattaforme esistenti (45%)
  • si aspettano delle prestazioni eccessive (28%)

Conclusioni

  1. I dati del sondaggio evidenziano che per moltissimi sviluppatori Windows desktop è giunto il momento di offrire ai propri utenti/committenti delle applicazioni mobile di qualità;
  2. molti di questi sviluppatori sono giustamente intimoriti (o spiazzati) dal numero e dal livello richiesto di competenze tecniche necessarie per fornire un'esperienza all'altezza delle aspettative;
  3. la presenza di diverse piattaforme e di diverse classi di dispositivi mobile da coprire, moltiplica gli sforzi richiesti;
  4. viene comunque richiesto di continuare a supportare attivamente anche le piattaforme desktop, per cui si stanno aggiungendo nuovi fronti, non si tratta di uno scambio;
  5. (parziale conseguenza del punto precedente) molti sviluppatori (95%) riconoscono il valore di avere una soluzione (ambiente di sviluppo) che permetta di avere un approccio unico per lo sviluppo di applicazioni desktop e mobile.
Indubbiamente, un ambiente di sviluppo che permetta di creare applicazioni desktop e mobile che offra agli sviluppatori l'opportunità di avere una singola base di codice da compilare per le principali piattaforme esistenti (Windows, Mac OSX, iOS e Android), potrebbe avere una marcia in più!
Se poi doveste essere già affezionati utilizzatori di uno degli strumenti di sviluppo più produttivi di sempre, non vi resta che procurarvi la versione XE5 e farci un giro :-)

lunedì 14 ottobre 2013

Una form di attesa per operazioni lunghe in una mobile application (iOS/Android)

Introduzione

Un fattore fondamentale del successo delle nostre applicazioni mobile è la loro fluidità: molti utenti "pretendono" che le nostre applicazioni siano sempre reattive e non ci siano momenti di "freeze" che vengono immediatamente classificati come malfunzionamenti.

Nello sviluppo di una applicazione mobile, può capitare di dover effettuare delle operazioni lunghe in termini di tempo che possono pregiudicare la responsività della nostra interfaccia utente. Il problema non è certo nuovo e anche in applicazioni tradizionali (desktop) per risolverlo si può ricorrere alla programmazione multithread, eseguendo l'operazione in background e lasciando il thread principale della nostra applicazione libero di rispondere alle esigenze dell'utente.

Vediamo come, in una applicazione FireMonkey mobile, si può realizzare una form di attesa generica, utile per dare l'impressione al nostro utente che l'applicazione sia effettivamente "viva" e allo stesso tempo fornire alcuni dettagli sull'operazione in corso.

Prenderò come esempio di operazione lunga una GET HTTP, caso abbastanza comune e caratterizzato da alcune caratteristiche peculiari:
  • si tratta di un'operazione bloccante (cioè il flusso di esecuzione è interrotto dall'inizio alla fine dell'operazione);
  • può richiedere un tempo variabile (a seconda della connettività e della risorsa HTTP richiesta);
  • sono disponibili dei dettagli sullo stato di avanzamento dell'operazione (es. bytes scaricati).
Altri esempi di operazioni del tutto analoghe sono:
  • l'esecuzione di operazioni su un database locale;
  • l'apertura di un dataset remoto;
  • la fruizione di servizi remoti (REST, SOAP, XML-RPC ecc);
  • operazioni di calcolo complesse;
  • operazioni di rendering grafico;
  • qualunque operazione che abbia tempi di attesa di più di qualche decimo di secondo.
L'esempio è realizzato con Delphi XE5, su una applicazione FireMonkey mobile, funziona sia per iOS che per Android (in realtà funzionerebbe, usando una form Firemonkey non-mobile, anche sulle altre piattaforme, come Windows e MacOSX) e usa il componente TIdHTTP della suite Indy.
Inoltre, l'implementazione proposta prevede l'uso dei metodi anonimi, una caratteristica del linguaggio, introdotta con Delphi 2009, che permette in modo semplice di delegare l'esecuzione di codice custom (quello specifico dell'operazione che si vuole eseguire, per esempio) all'interno di un contesto più generico (il codice che mostra e gestisce la form di attesa).

1) Eseguire una operazione bloccante

Supponiamo di avere creato una nuova applicazione FireMonkey mobile e avere un componente TIdHTTP da usare per eseguire la GET HTTP e salvare la risposta del server in un TMemoryStream:

var
  LContent: TMemoryStream;
  LDetails: string;

// ...
  LContent := TMemoryStream.Create;
  try
    IdHttp1.Get('http://www.adomain.com/test/large_image.png', LContent);
    LDetails := LContent.Size.ToString + ' bytes';
  finally
    LContent.Free;
  end;

Se eseguiamo questo pezzo di codice in un evento OnClick di un TButton (se l'immagine richiesta è di una certa dimensione e magari la connettività non è particolarmente brillante), quello che succederà quando l'utente clicca sul pulsante è il congelamento dell'interfaccia utente fino al completamento del download. Questo capita perchè l'operazione viene effettuata nello stesso thread di esecuzione che si occupa di gestire l'interfaccia utente dell'intera applicazione.

2) Otteniamo i dettagli di avanzamento dell'operazione

Il componente TIdHTTP ha alcuni eventi disponibili per tracciare l'esecuzione delle operazioni di GET. In particolare si vedano OnWorkBegin, OnWorkEnd e OnWork:
  • il primo permette di sapere che l'operazione è stata avviata e come parametro (AWorkCountMax) offre l'indicazione della dimensione totale del file da scaricare;
  • OnWorkEnd è, come è facile intuire, la notifica di operazione completata (non ci interessa particolarmente);
  • OnWork viene chiamato periodicamente durante l'operazione e ha un parametro AWorkCount che ci permette di sapere a che punto siamo con il download del file.
Potremmo scrivere dei gestori di evento per stampare su un TMemo le informazioni disponibili ma questo non cambierà il fatto che l'interfaccia utente sembrerà comunque congelata durante l'operazione e dovremo cercare un modo esplicito per forzare il ridisegno del componente TMemo una volta aggiunto del testo.


3) Prepariamo una form di attesa da mostrare all'utente

Posto che l'operazione che dobbiamo eseguire richiede un certo tempo, vogliamo mostrare all'utente una form di attesa, magari con una animazione che indichi che l'operazione è in corso, in modo da fugare ogni dubbio riguardante malfunzionamenti o "blocchi", che potrebbero portare il nostro utente a terminare brutalmente l'applicazione.

Non serve molto:
  • una TLabel (TitleLabel) mostrerà un titolo nella parte superiore della form;
  • un TLayout (ProgressLayout) ospiterà delle informazioni sullo stato di avanzamento dell'operazione (usando una TProgressBar e un'altra TLabel), nella parte inferiore della form;
  • un TLayout (ImageLayout) ospiterà una TImage con della grafica (che potete personalizzare a piacimento, io ho usato l'immagine di una rotella da ingranaggio).

4) Incastoniamo l'esecuzione dell'operazione durante la visualizzazione della form

In linea teorica quello che vorremmo ottenere è:
  • lanciare l'operazione lunga;
  • mostrare la form di attesa "viva" (cioè animata e comunque responsiva ad eventuali azioni dell'utente, come una richiesta di annullamento dell'operazione stessa);
  • alla conclusione dell'operazione, chiudere la form di attesa e magari eseguire qualcosa con i dati scaricati.
Cerchiamo di impacchettare il codice dell'operazione in un metodo anonimo per passarlo "come se fosse una variabile" alla form di wait che lo eseguirà al momento opportuno (cioè dopo essersi mostrata all'utente e aver avviato l'animazione):

procedure
var
  LContent: TMemoryStream;
begin
  LContent := TMemoryStream.Create;
  try
    IdHttp1.Get(eHTTPGetURL.Text, LContent);
  finally
    LContent.Free;
  end;
end);

eHTTPGetURL è un TEdit posto sulla form della mia applicazione, dove l'utente potrebbe specificare la URL della risorsa da scaricare.

Prepariamo un metodo nella form di attesa, che riceva questo metodo anonimo che rappresenta il Task da eseuire, insieme ad altri due metodi anonimi che rappresentano il codice da eseguire al completamento dell'esecuzione del task e quello da eseguire in caso avvengano delle eccezioni durante l'esecuzione dello stesso:

procedure TWaitForm.Run(ATask, AOnComplete: TProc(twaitform);
  AOnError: TProc(twaitform, exception));
begin
  Show;
  WaitAnimation.Start;

  TThread.CreateAnonymousThread(
    procedure
    begin
      try
        FHadErrors := False;
        ATask(Self);
      except on E:Exception do
        DoErrorHandling(E, AOnError);
      end;

      DoOnCompleted(AOnComplete);
    end).Start;
end;

Il codice è breve ma presenta alcune caratteristiche degne di nota:
  • i parametri ATask, AOnComplete e AOnError sono dei riferimenti a metodi anonimi con un certo numero e tipo di parametri. La sintassi utilizzata è quella dei generics (altro potente meccanismo introdotto nelle ultime versioni di Delphi e presente il molti altri linguaggi di programmazione), con le parentesi angolari: ATask e AOnComplete sono due procedure che hanno come parametro un'istanza di TWaitForm.
    L'idea è quella di passare al metodo Run le tre cose da fare (esecuzione task, esecuzione codice dopo completamento del task e esecuzione codice in caso di errori), in modo che possa comporle nel modo più appropriato;
  • l'esecuzione di ATask è protetta da un blocco try..except che garantisce l'esecuzione di AOnError in caso di errori (e trappa l'eccezione, in modo che non affiori all'esterno);
  • la form ha un field FHadErrors che tiene traccia del presentarsi di eventuali eccezioni (utile per esempio nel codice di AOnComplete);
  • l'esecuzione del task, della gestione delle eccezioni e delle operazioni post-conclusione del task vengono effettuate in un thread separato, grazie all'utilizzo di TThread.CreateAnonymousThread, che permette di eseguire un metodo anonimo in un nuovo thread, in background;
Come vedete, si tratta di poche righe di codice che permettono di fare però una cosa abbastanza complessa:
  • eseguire un pezzo di codice variabile (ATask) in un thread secondario;
  • avere una chance di agganciare altro codice variabile (AOnError e AOnComplete) a due eventi principali di interesse: i casi di errore e la notifica di completamento di ATask;
  • avere a disposizione in questi pezzi di codice variabili (metodi anonimi) il riferimento alla form di attesa specifica, che può esporre alcuni metodi utili per la modifica del contenuto a video (es. stato di avanzamento), avendo l'opportunità di nascondere al programmatore la necessità di sincronizzare l'esecuzione fra thread diversi.
A proposito di questa ultima necessità, si vedano i metodi (di TWaitForm):
procedure SetupProgress(const AProgress, AMin, AMax: Single; const AMessage: string = '');

procedure UpdateProgress(const AProgress: Single; const AMessage: string = '');

la cui implementazione è thread-safe e che possono quindi essere invocati sia all'interno dei metodi anonimi ATask, AOnError e AOnCompleted che da codice nel thread principale dell'applicazione o in altri thread.
Ad esempio, riporto l'implementazione di UpdateProgress che fa uso di TThread.Synchronize per eseguire il codice che manipola gli elementi della GUI in modo safe rispetto al thread principale di esecuzione:

procedure TWaitForm.UpdateProgress(const AProgress: Single;
  const AMessage: string);
begin
  TThread.Synchronize(
    TThread.CurrentThread,
    procedure
    begin
      ProgressBar.Value := AProgress;
      ProgressLabel.Text := AMessage;
      if AMessage = '%' then
        ProgressLabel.Text := Format('%.1f', [100*(ProgressBar.Value / ProgressBar.Max)]) + '%';

      ProgressBar.Visible := AProgress > 0;
      ProgressLabel.Visible := AMessage <> '';
    end);
end;

Questo metodo, per esempio, può essere chiamato anche nel gestore di OnWork del nostro componente TIdHTTP, per aggiornare la barra di avanzamento presente sulla TWaitForm e visibile all'utente durante il download.

5) Esempi di utilizzo

Se alcuni passaggi esposti finora vi sembrano un po' complicati o poco chiari, non dovete preoccuparvi (al netto magari di voler approfondire qualche argomento :-) ) perchè in realtà il codice necessario per utilizzare la form è molto più semplice, ad esempio potreste avere un button con il seguente handler di OnClick:

var
  LDetails: string;
begin
  FWaitForm := TWaitForm.CreateAndRun(
    'Long operation: HTTP GET',  // titolo

    procedure(AWaitForm: TWaitForm)  // ATask
    var
      LContent: TMemoryStream;
    begin
      LDetails := '';
      LContent := TMemoryStream.Create;
      try
        IdHttp1.Get(eHTTPGetURL.Text, LContent);
        LDetails := LContent.Size.ToString + ' bytes';
      finally
        LContent.Free;
      end;
    end,

    procedure(AWaitForm: TWaitForm) // AOnCompleted
    begin
      if not AWaitForm.HadErrors then
        Memo1.Lines.Add('Completed: ' + LDetails);
    end,

    procedure (AWaitForm: TWaitForm; AException: Exception)  // AOnError
    begin
      Memo1.Lines.Add('Error: ' + AException.Message);
    end
  );
end;

Nei gestori di evento del componente TIdHTTP, grazie al fatto che ci salviamo il riferimento alla TWaitForm in FWaitForm, possiamo scrivere semplicemente:

procedure TForm1.IdHTTP1Work(ASender: TObject; AWorkMode: TWorkMode;
  AWorkCount: Int64);
begin
  FWaitForm.UpdateProgress(AWorkCount, '%');
end;

procedure TForm1.IdHTTP1WorkBegin(ASender: TObject; AWorkMode: TWorkMode;
  AWorkCountMax: Int64);
begin
  FWaitForm.SetupProgress(0, 0, AWorkCountMax, 'Downloading...');
end;

Il risultato è mostrato (senza animazione ovviamente :-) ) di seguito. Il primo screenshot mostra l'applicazione in esecuzione nel simulatore iOS, mentre il secondo è preso direttamente dal mio smartphone Android:


Conclusioni

Abbiamo realizzato una form di attesa che permette di eseguire una qualunque operazione lunga in un thread secondario, lasciando fluire l'esecuzione del thread principale dell'applicazione a garanzia della responsività della nostra interfaccia (e non solo) senza perdere la possibilità di eseguire del codice al completamento dell'operazione richiesta e quella di gestire eventuali eccezioni.

Grazie all'uso dei metodi anonimi e di alcune funzionalità del linguaggio introdotte nelle ultime versioni di Delphi, lo sviluppatore che utilizza questa form non deve avere particolari conoscenze della programmazione multithread e l'utilizzo risulta semplice ed intuitivo nella maggior parte dei casi.

Materiale

Potete scaricare il codice sorgente completo dell'esempio al seguente link:
Source code - Link to DropBox

Se volete provarla direttamente sul vostro dispositivo Android potete scaricare l'APK al seguente link: