Visualizzatore di immagini

{progetto 10}


Questo potremmo considerarlo il primo progetto utile, infatti metteremo insieme le cose che abbiamo visto fino a ora per realizzare un programma che ci permette di visualizzare le immagini in modo semplice. Creiamo un nuovo progetto e chiamiamolo “VisImg”, mettiamo in alto a sinistra un pulsante che chiameremo “Browse” e a seguire, sulla destra un componente TImage. Mettiamo sulla form anche un dialog del tipo “OpenPictureDialog1” e inserite nelle dichiarazioni per le unit quella di sistema “JPEG” per leggere questo tipo di immagini. Questo è quello che dovremo avere alla fine della fase di editing dell’interfaccia:

 

 

Il pulsante dovrà chiamare la funzione Execute del dialog messo sulla form e inviare il file selezionato dall’utente al componente TImage, quindi abbiamo bisogno per prima cosa di una variabile di tipo stringa che memorizza il nome del file selezionato:


procedure TForm1.Button1Click(Sender: TObject);

VAR FileSelez : String;

begin

If OpenPictureDialog1.Execute Then

Begin


End;

end;


L’oggetto OpenPictureDialog1 è simile a “OpenDialog” e come questo possiede la proprietà “FileName” che ritorna il nome del file selezionato. Pertanto:


procedure TForm1.Button1Click(Sender: TObject);

VAR FileSelez : String;

begin

If OpenPictureDialog1.Execute Then

Begin

FileSelez := OpenPictureDialog1.FileName;

Caption := 'VisImg - '+FileSelez;

End;

end;

Anche in questo caso scriviamo nella caption della form il nome del file selezionato. Dopo di questo l’ultima cosa da realizzare per vedere qualcosa è fare in modo che TImage legga il file selezionato. Analizzando nell’object inspector il componente Image, scopriamo che la proprietà che abbiamo usato per leggere una immagine nel primo esempio è “Picture”. Selezionando dall’editor di Delphi questa voce compare una finestra per selezionare una immagine da visualizzare, per fare la stessa cosa a runtime bisogna usare sempre la stessa proprietà oggetto ma richiamare la procedura “LoadFromFile”. Per avere un’idea di tutte le proprietà del componente TImage e dell’oggetto Picture si può sfruttare sempre l’help allegato. Alla fine la nostra procedura per leggere le immagini sarà così:


procedure TForm1.Button1Click(Sender: TObject);

VAR FileSelez : String;

begin

If OpenPictureDialog1.Execute Then

Begin

FileSelez := OpenPictureDialog1.FileName;

Caption := 'VisImg - '+FileSelez;

Image1.Picture.LoadFromFile(FileSelez);

End;

end;


Così facendo funziona tutto ma scopriamo che se l’immagine è grande non riusciamo a visualizzarla del tutto, a questo si ovvia impostando nell’object inspector la proprietà “Autosize” uguale a True.

 

 

Ora va quasi bene però l’immagine è attaccata al pulsante e se è troppo grande comunque risulta non visibile completamente. Potremmo pensare di fare un resize del componente Image così da adattarlo alla grandezza della nostra finestra; quindi come prossimo compito ci prefiggiamo di inserire il nostro visualizzatore in una cornice adattata alla form principale e poi a sua volta adattare TImage alla cornice.

Allora facciamo questo: spostiamo da un lato il riquadro TImage e inseriamo sulla form il componente TPanel

 

 

e useremo la seguente combinazione di effetti grafici:

 

 

Ora bisogna fare in modo che l’immagine venga visualizzata in questo contenitore, così da sembrare essere contenuta in un quadro. Per modificare la proprietà di TImage basta effettuare un “Taglia” e poi “Incolla” sul Panel, oppure a runtime modificare la sua proprietà Parent. Dato che la cosa più semplice è la prima, allora faremo così, avendo come risultato finale:

 

 

Se provate a lanciare il programma vedrete che non ancora abbiamo risolto il nostro problema, se allarghiamo la finestra a runtime una immagine grande viene tagliata e la nostra cornice è sempre piccola:

 

 

La cosa da fare allora è cercare un modo di permettere al nostro riquadro-cornice di seguire le variazioni della finestra, così da modificare le proprie dimensioni affinché la distanza dai bordi non vari. Tutti i componenti visuali (quelli che si vedono a runtime) hanno una proprietà chiamata “Anchors” che permettere di selezionare il bordo a cui agganciarsi, in modo che la distanza da questo non cambi quando la form principale viene ridimensionata. Impostando tutte le sue scelte su True, facciamo si che la nostra cornice si agganci a tutti i lati seguendone le variazioni, provate.

 

 

Abbiamo sistemato il contenitore ma l’immagine se è grande esce sempre fuori, quindi bisogna scrivere un po’ di codice per ridimensionare in modo corretto il nostro componente, per fare in modo da visualizzare l’intera immagine nel riquadro, ma bisogna ricordarsi di rimuovere la proprietà “Autosize” altrimenti qualsiasi modifica fatta sulle dimensioni del componente verranno vanificate.

Iniziamo con il creare una procedura privata e chiamiamola “ResizeImg” e andrà chiamata subito dopo il caricamento dell’immagine:


procedure TFormVisImg.Button1Click(Sender: TObject);

VAR FileSelez : String;

begin

If OpenPictureDialog1.Execute Then

Begin

FileSelez := OpenPictureDialog1.FileName;

Caption := 'VisImg - '+FileSelez;

Image1.Picture.LoadFromFile(FileSelez);

ResizeImg;

End;

end;


La prima cosa da controllare in questa procedura e la grandezza dell’immagine. Se è più piccola del suo contenitore Panel allora va bene, quindi si esce dalla procedura perché non serve continuare:


PROCEDURE TFormVisImg.ResizeImg;

VAR HImg, WImg : Word;

HPanel, WPanel : Word;

BEGIN

HImg := Image1.Height;

WImg := Image1.Width;

HPanel := Panel1.Height-10;

WPanel := Panel1.Width-10;


If HImg<HPanel Then

If WImg<WPanel Then Exit;

END;


Come si può notare la prima cosa a essere fatta è memorizzare in alcune variabili le dimensioni dell’immagine e del contenitore; si intuisce che le proprietà “Height” e “Width” corrispondono alle dimensioni “altezza” e “larghezza” del nostro componente. Segue il codice che permette di verificare se le dimensioni dell’immagine sono minori del contenitore, nel qual caso si esce dalla procedura (Exit).

Come in matematica anche il Pascal possiede il simbolo di minore (<) e maggiore (>) per effettuare le verifiche sui numeri, la prima riga:


If HImg<HPanel Then


controlla se il primo membro è minore del secondo, questa verifica risulta True se è confermata l’operazione (cioè quando HImg è minore di HPanel). Se risulta vera allora controlla anche la seconda grandezza:

If WImg<WPanel Then Exit;


se viene confermata anche quest’altra misura (WImg più piccolo di WPanel) allora viene chiamata l’uscita dalla procedura (Exit), che ferma la continuazione del blocco codice ed esce.

Passiamo ora a qualcosa di un po’ più complicato, se vogliamo ridimensionare una immagine dobbiamo farlo rispettando il rapporto tra altezza e larghezza, altrimenti l’immagine rappresentata risulterà distorta. Ecco il codice per il calcolo del rapporto:


PROCEDURE TFormVisImg.ResizeImg;

VAR RapportoHW : Single;

HImg, WImg : Word;

HPanel, WPanel : Word;

BEGIN

HImg := Image1.Picture.Height;

WImg := Image1.Picture.Width;

HPanel := Panel1.Height-10;

WPanel := Panel1.Width-10;


If HImg<HPanel Then

If WImg<WPanel Then Exit;


RapportoHW := WImg / HImg;

 

END;


Abbiamo creato una variabile di tipo reale (che possa memorizzare numeri con la virgola) e abbiamo effettuato la divisione per memorizzare il rapporto, il segno “/” viene utilizzato quando si fa una divisione e si desidera avere un valore reale. Una volta che ho questa formula impostando io una altezza o larghezza a mio piacere posso ricavarmi l’altro valore rispettando sempre il rapporto originale. A questo punto se una delle due misure supera il contenitore (Panel) allora fisso questa lunghezza uguale al contenitore e calcolo l’altra. Per spiegarci con un esempio: se ho una foto alta 200 e il mio contenitore è alto solo 100, allora imposto l’altezza della TImage a 100 e con la formula del rapporto mi calcolo lo lunghezza. Ecco il codice per entrambe le misure:


If HImg>HPanel Then

Begin

HImg := HPanel;

WImg := Round(RapportoHW * HImg);

End;


If WImg>WPanel Then

Begin

WImg := WPAnel;

HImg := Round(WImg / RapportoHW);

End;


Round è una funzione che opera un arrotondamento sul valore che gli viene passato. Nella nostra operazione, dividendo o moltiplicando si ottengono dei valori con decimali non compatibili con le nostre variabili (HImg e WImg sono variabili intere), questa funzione ci permette di arrotondare un valore e trasformarlo da Reale a Intero.

Nel primo blocco controlliamo l’altezza, e se la troviamo più grande


If HImg>HPanel Then


si procede con fissarla uguale a quella del contenitore, poi mi calcolo la lunghezza


WImg := Round(RapportoHW * HImg);


Nel secondo blocco verifico se con le nuove misure anche la lunghezza mi rientra nel riquadro, se risulta più grande


If WImg>WPanel Then


allora effettuo di nuovo il calcolo ma questa volta impostando la lunghezza uguale al contenitore


WImg := WPAnel;


e ricavandomi l’altezza


HImg := Round(WImg / RapportoHW);


In questo modo la nostra immagine è adattata al suo contenitore così da occupare tutta l’area disponibile eccetto per un piccolo spazio (il -10 che si nota nell’assegnazione della variabile).

Con tutto questo, se proviamo a lanciare il programma ci accorgiamo che la sezione dell’immagine è adattata ma questa resta sempre non visibile per intera. Questo perché l’immagine rappresentata non è stata adattata alle nuove dimensioni, per fare questo bisogna impostare la proprietà “Stretch” di TImage su True. In questo modo costringiamo il componente a disegnare la figura entro lo spazio assegnato.

 

 

Ora finalmente la nostra foto si vedrà come si deve ma c’è sempre qualcosa che non ci piace, infatti l’immagine non risulta centrata nella cornice, e questo è brutto da vedersi. Per questo creiamo una nuova procedura che serve a centrare la nostra foto:


PROCEDURE TFormVisImg.CentraImg;

BEGIN

Image1.Left := (Panel1.Width-Image1.Width) Div 2;

Image1.Top := (Panel1.Height-Image1.Height) Div 2;

END;


In pratica si fa la differenza tra la grandezza del contenitore e quella dell’immagine e poi divido per due, ottenendo l’esatta distanza per avere lo stesso spazio su entrambi i lati. Provate a runtime ad allargare la finestra a tutto schermo, le immagini grandi saranno adattate, ma se carichiamo una immagine piccola dopo una grande? Cosa succede? Succede che essendo più piccola del contenitore la procedura viene saltata e l’adattamento si perde, per avere un controllo anche sulle immagini piccole bisogna abilitare la funzione “Autosize”, ma solo per le figure piccole. Ecco la procedura intera:


PROCEDURE TFormVisImg.ResizeImg;

VAR RapportoHW : Single;

HImg, WImg : Word;

HPanel, WPanel : Word;

BEGIN

HImg := Image1.Picture.Height;

WImg := Image1.Picture.Width;

HPanel := Panel1.Height-10;

WPanel := Panel1.Width-10;


If HImg<HPanel Then

If WImg<WPanel Then

Begin

Image1.Stretch := False;//per le immagini piccole

Image1.AutoSize := True;

Exit;

End;


RapportoHW := WImg / HImg;

Image1.Stretch := True;//ripristino dei valori per immagini grandi

Image1.AutoSize := False;


If HImg>HPanel Then

Begin

HImg := HPanel;

WImg := Round(RapportoHW * HImg);

End;


If WImg>WPanel Then

Begin

WImg := WPAnel;

HImg := Round(WImg / RapportoHW);

End;


Image1.Width := WImg;

Image1.Height := HImg;

END;

In questo modo dopo la verifica se l’immagine è piccola, viene selezionato l’autosize del componente e non tocca fare altro. Se invece l’immagine è da adattare allora questa opzione viene disabilitata, si abilita però lo stretch che permette di adattare l’immagine alla forma del componente Image.

Finalmente ora tutto funziona a dovere ma se dopo aver caricato una foto si modifica la grandezza della finestra l’immagine non si adatta. Per evitare questo brutto effetto bisogna capire quando la form viene ridimensionata, e quando avviene dobbiamo richiamare la procedura per ricalcolare le dimensioni.

Se sfogliamo la pagina Events della form scopriamo che possiede l’evento “onResize”, ed è proprio quello che serve al nostro scopo. Quindi facciamo doppio clic in corrispondenza di questa voce:

 

 

e scriviamo la chiamata alla nostra procedura:


procedure TFormVisImg.FormResize(Sender: TObject);

begin

ResizeImg;

CentraImg;

end;


Ora che tutto va bene possiamo pensare di abbellire il nostro programma dandogli una apparenza di professionalità, la mia intenzione è di inserire in basso una barra informazioni, quindi spostare il nome del file dalla caption alla barra e scrivere altre info riguardo all’immagine visualizzata.

Nella pagina Win32 della Tool Palette selezionate “TStatusBar” e ponetela sulla form, automaticamente si aggancia in basso. A questo punto allarghiamo un poco la nostra form e accorciamo un pelo il contenitore delle immagini TPanel, così da dare un po’ di spazio tra il bordo inferiore e la status bar. Nelle proprietà di questo componente selezioniamo:

 

 

così lo utilizzeremo come una semplice barra con un solo campo. In altri esempi utilizzeremo più campi dove scrivere ma per ora a noi basta questo.

Nell’evento onClick del pulsante browse, al posto della riga di comando scritto in precedenza


Caption := 'VisImg - '+FileSelez;


scrivere il codice per fare in modo che il nome del file compaia nella barra in basso, ma questa volta bisogna inserire questa riga dopo che l’immagine viene letta da TImage. In questo modo abbiamo la possibilità di conoscere anche la grandezza della nostra immagine e scriverla di seguito al nome:


procedure TFormVisImg.Button1Click(Sender: TObject);

VAR FileSelez : String;

GrdzStr : String;

begin

If OpenPictureDialog1.Execute Then

Begin

FileSelez := OpenPictureDialog1.FileName;

Image1.Picture.LoadFromFile(FileSelez);

GrdzStr := IntToStr(Image1.Picture.Width)+'x'+IntToStr(Image1.Picture.Height);

StatusBar1.SimpleText := FileSelez+' - '+GrdzStr;

ResizeImg;

CentraImg;

End;

end;


La variabile “GrdzStr” contiene il valore della grandezza dell’immagine in formato stringa, mentre alla proprietà “SimpleText” viene assegnato il testo da visualizzare nella status bar. Tenete presente che “SimpleText” viene visualizzato solo se “SimplePanel” è su True. Togliendo dalla caption il nome del file bisogna che la barra in alto mostri perlomeno il nome del programma o una descrizione di quello che fa, quindi nella proprietà Caption della form scriviamo il titolo della nostra finestra:

 

 

Un’altra cosa che potremmo fare è far diventare di bianco lo sfondo del contenitore per l’immagine così da far risaltare di più i colori della figura rappresentata; selezionate il componente “TShape” in “Additional” e posizionatelo all’interno di Panel1. Impostate la sua proprietà “Align” uguale a “alClient”, questa impostazione permette al componente di adattarsi all’oggetto che lo contiene (in questo caso Panel1). Se TImage non risulta più visibile basta modificare il controllo facendolo disegnare al di sotto:

 

 

se non ricordate come si fa basta cliccare con tastino destro del mouse sull’oggetto Shape. Se preferite eliminare la riga nera che circonda la figura bianca basta agire sulla proprietà “Style” di questo componente:

 

 

Come ultima cosa vorrei fare un passo avanti con questo nostro programmino e inserire un componente che mostra i file-immagine contenuti nella cartella, così da poterli sfogliare in modo semplice e veloce. Purtroppo in questa versione freeware di Delphi esiste solo un vecchio componente che fa al caso nostro, realizzato per le piattaforme a 16 bit ma comunque funzionante anche se l’interfaccia non è delle più moderne.

In “Win 3.1” della tool palette selezioniamo il componente “TFileListBox” e posizioniamolo sul lato sinistro, poi prelevate da questa stessa sezione il componente “TFilterComboBox” e posizionatelo sotto il pulsante Browse.

Adattate il contenitore delle immagini per far spazio alla lista dei file, magari allargando un poco la form, dovrebbe essere più o meno così:

 

 

FilterComboBox mostra i file contenuti in una cartella che noi indichiamo e ci permette anche di indicare un filtro per la visualizzazione del contenuto. Questi due componenti hanno una funzione che li lega tra di loro, in modo che se modifico la lista dei filtri il risultato lo vedo subito nella lista dei file, senza scrivere niente. Per fare questo, nelle proprietà di FilterComboBox, aprire la lista a discesa in corrispondenza della proprietà “FileList” e selezionare “FileListBox1”

 

 

così, agendo sulla lista dei filtri si applicano le modifiche anche alla lista file. Però il filtro indicato in questa lista deve essere identico al filtro del dialog che ci permette di aprire le immagini. Questa operazione la facciamo a runtime, nell’evento onCreate della form:


procedure TFormVisImg.FormCreate(Sender: TObject);

begin

FilterComboBox1.Filter := OpenPictureDialog1.Filter;

end;


Se lanciate il programma vedrete che la lista a discesa dei filtri si è popolata, infatti sono le stesse voci che trovate in OpenPictureDialog.

Ora bisogna che quando apriamo una immagine con il pulsante Browse la posizione dove leggo la devo passare anche alla lista che mi mostra i file, così mi fa vedere tutti i file immagine contenuti in quella cartella:



procedure TFormVisImg.Button1Click(Sender: TObject);

VAR FileSelez : String;

GrdzStr : String;

begin

If OpenPictureDialog1.Execute Then

Begin

FileSelez := OpenPictureDialog1.FileName;

Image1.Picture.LoadFromFile(FileSelez);

GrdzStr := IntToStr(Image1.Picture.Width)+'x'+IntToStr(Image1.Picture.Height);

StatusBar1.SimpleText := FileSelez+' - '+GrdzStr;

FileListBox1.Directory := OpenPictureDialog1.FileName;

ResizeImg;

CentraImg;

End;

end;



Ultima cosa per vedere i file presenti nella lista e fare in modo che quando l’utente seleziona un file questo venga subito letto. Nella lista degli eventi di FileListBox fate doppio clic si “onClick” anche in questo caso questo evento viene chiamato quando si fa un clic nella lista dei file. Nella procedura che compare dobbiamo scrivere la lista di codice per leggere l’immagine selezionata.

Come si capisce le operazioni da fare per visualizzarla sono le stesse fatte nella procedura del pulsante Browse, allora per non riscrivere le cose due volte creiamo una procedura ad oc che processa il file selezionato:

 

 

La procedura CaricaImg come parametro vuole il nome del file da leggere e il codice che contiene è quello che stava nella procedura onClick del pulsante. Ho fatto un taglia-incolla e ho solo modificato la variabile che contiene il file selezionato (NomeFile al posto di FileSelez). La procedure del pulsante Browse si trasforma in questa:



procedure TFormVisImg.Button1Click(Sender: TObject);

VAR FileSelez : String;

begin

If OpenPictureDialog1.Execute Then

Begin

FileSelez := OpenPictureDialog1.FileName;

CaricaImg(FileSelez);

FileListBox1.Directory := OpenPictureDialog1.FileName;

FileListBox1.SetFocus;

ResizeImg;

CentraImg;

End;

end;


Si può notare che al posto delle tre righe di codice ho inserito la chiamata alla procedura, passandogli attraverso la variabile FileSelez il nome del file da leggere. “FileListBox1.SetFocus;” è stato messo così dopo aver caricato una immagine col pulsante il fuoco va alla lista, che è sfogliabile con le frecce della tastiera.

La procedura nell’evento onClick della lista file non deve far altro che sapere quale file è selezionato nella lista e inviarlo alla procedura appena creata:


procedure TFormVisImg.FileListBox1Click(Sender: TObject);

VAR FileTrn : String;

begin

FileTrn := FileListBox1.FileName;

CaricaImg(FileTrn);

ResizeImg;

CentraImg;

end;


Con la lista dei file ora è molto più semplice sfogliare le immagini salvate, ma se allarghiamo la form la lista non si allunga insieme a tutto il resto, ovviamo a questo problema agendo sulla solita proprietà “Anchors”, attivando la posizione “akBottom”.

Indietro