FreeBASIC – Modello MVC

In questo articolo scopriremo uno dei modi per organizzare il codice in maniera efficace ed efficiente.

Per fare questo sfrutteremo il Modello Architetturale MVC (Model-View-Controller).

Modello Architetturale MVC

In pratica ciò che occorre fare per seguire questo schema di progettazione è raccogliere logicamente i moduli del nostro codice nelle 3 componenti del modello MVC che differiscono per scopo e logica di funzionamento. Vediamole in sintesi:

  • Il componente Modello – che rappresenta il cuore del nostro programma – è quella parte del codice che contiene tutte quelle procedure (subroutines e funzioni) per la gestione delle informazioni secondo le regole e la logica decise dal programmatore. In pratica è questo componente che determina il vero e proprio comportamento del programma.
  • Il componente Vista contiene tutta quella parte del codice concepita per la gestione della grafica e del testo. Questo modulo si occupa sia della visualizzazione dei risultati delle elaborazioni avvenute nel componente Modello (output), sia della visualizzazione di quegli elementi grafici e testuali che permettono all’utilizzatore di interagire con il programma (input).
  • Il componente Controllore si occupa invece della fasi di interlavoro con le altre due componenti: una parte del suo codice permette a questo componente di mettersi in ascolto degli input dell’utilizzatore provenienti dal componente Vista, per poi attivare le relative procedure del componente Modulo, da cui raccoglierà i risultati elaborati per attivare le relative procedure del componente Vista che si occuperà infine di trasformare i dati elaborati in oggetti grafici o testuali.

Forse uno schema può aiutarci a capire ancora meglio ciò che abbiamo provato a descrivere più sopra.

Schema del Modello Architetturale MVC

Di Wdror-wsu-ap and Regis Frey – Opera propria, Pubblico dominio, Collegamento

Per non lasciare tutto questo soltanto ad un livello teorico, scriveremo un programma per disegnare dei quadrati rossi, selezionarli o deselezionarli, e spostarli a video.

Programma di esempio

Volendo seguire lo schema di progettazione MVC, scriveremo un modulo per ciascun componente di questo Modello Architetturale. In aggiunta prepareremo anche un file di intestazione per organizzare meglio le dichiarazioni delle procedure e del tipo di dato creato ad hoc per l’occasione, così come si usa fare in FreeBASIC.

Una volta preparati e salvati i 4 files, si dovrà compilare il file sorgente modulo_controllore.bas e quindi eseguire il file compilato.

File di intestazione

''nome file: intestazione.bas
''sito web: https://ciucoinformatico.home.blog/
''ultimo aggiornamento: 04/08/2019

/' DESCRIZIONE
File di intestazione per la dichiarazione del tipo di dato
creato ad hoc e delle procedure contenute negli altri moduli.
'/

''definizione del tipo di dato figura
type figura
  tipo as string
  coord_x as integer
  coord_y as integer
  dimensione as integer
  colore(1 to 3) as integer
  selezione as boolean
  selezione_x as integer
  selezione_y as integer
end type

''dimensiona un array fisso del tipo figura
dim figure(1 to 10) as figura

/'
Componente VISTA
'/

''procedura per stampare a video il menu
declare sub stampa_menu()

''procedura per visualizzare le informazioni del mouse
declare sub info_mouse(x as integer, y as integer, r as integer)

''procedura per visualizzare il numero delle figure create
declare sub stampa_tot_figure(n as integer)

''procedura per disegnare le figure
declare sub disegna_figura(f() as figura)

/'
Componente MODELLO
'/

''procedura per la creazione di una figure geometrica
declare sub crea_figura(x as integer, y as integer, r as integer, n as integer, f() as figura)

''procedura per la selezione di una o più figure
declare sub seleziona(x as integer, y as integer, f() as figura)

''procedura per lo spostamento delle figure
declare function sposta(x as integer, y as integer, f() as figura) as boolean

Modulo Controllore

''nome file: controllore.bas
''architettura MVC (Model-View-Controller): componente CONTROLLORE
''sito web: https://ciucoinformatico.home.blog/
''ultimo aggiornamento: 04/08/2019

/' DESCRIZIONE
Componente CONTROLLO: modulo per l'ascolto degli input
dell'utilizzatore che tramite il mouse interagisce con lo
schermo, e dispaccio degli eventi generati dall'utilizzatore
alle procedure del componente MODELLO e chiamata alle
procedure di visualizzazione del componente VISTA.
'/

#include once "file_intestazione.bi"
#include once "modulo_vista.bas"
#include once "modulo_modello.bas"

''dimensiona una variabile per ricevere il valore
''restituito dalla funzione getmouse
dim as long verifica_mouse

''dimensiona quattro variabili per ricevere le
''informazioni provenienti dal mouse
dim as integer mouse_x, mouse_y, mouse_ruota, mouse_pulsante

''variabile per il conteggio delle figure
dim num_figura as integer
num_figura = 0

''Visualizza il menu
stampa_menu()

''ciclo per l'ascolto degli input (informazioni dal mouse)
do
  verifica_mouse = getmouse(mouse_x, mouse_y, mouse_ruota, mouse_pulsante)
  if verifica_mouse = 0 then
    ''visualizza a video le informazioni del mouse
    info_mouse(mouse_x, mouse_y, mouse_ruota)
  else
    ''non fare nulla
  end if
  if mouse_pulsante = 1 then
    ''tasto sinistro del mouse
    seleziona(mouse_x, mouse_y, figure())
    disegna_figura(figure())
    stampa_menu()
    stampa_tot_figure(num_figura)
  elseif mouse_pulsante = 4 then
    ''tasto centrale del mouse
    if sposta(mouse_x, mouse_y, figure()) then
      disegna_figura(figure())
      stampa_menu()
      stampa_tot_figure(num_figura)
      sleep 200
    else
    end if
  elseif mouse_pulsante = 2 then
    ''tasto destro del mouse
    if num_figura < 10 then
      num_figura = num_figura + 1
      crea_figura(mouse_x, mouse_y, mouse_ruota, num_figura, figure())
      disegna_figura(figure())
      stampa_menu()
      stampa_tot_figure(num_figura)
      sleep 200
    else
      beep
    end if
  else
    ''non fare nulla
  end if
loop until verifica_mouse = 1

Modulo Vista

''nome file: vista.bas
''architettura MVC (Model-View-Controller): componente VISTA
''sito web: https://ciucoinformatico.home.blog/
''ultimo aggiornamento: 03/08/2019

/' DESCRIZIONE
Componente VISTA: modulo per la rappresentazione a video
del testo e delle figure.
'/

''Imposta una pagina video con la modalità grafica 19 per
''una risoluzione dello schermo di 800x600 pixels, una
''dimenzione testuale di 100 colonne x 37 righe e
''una profondità di colore a 32bpp.
screen 19, 32, 1

''posiziona il mouse al centro
setmouse 400, 300

''procedura per stampare a video il menu
sub stampa_menu()
  locate 1,1: print "Mouse:"
  locate 2, 1: print "tasto sinistro = seleziona le figure"
  locate 3, 1: print "tasto centrale = sposta le figure selezionate"
  locate 4, 1: print "tasto destro = crea una figura"
  locate 5, 1: print "Nota:"
  locate 6, 1: print "Per ingrandire o rimpicciolire la figura, prima"
  locate 7, 1: print "di crearla, usa la ruota."
  locate 36, 1: print "Per uscire dal programma porta il puntatore del";
  print " mouse fuori dalla finestra grafica."
end sub

''procedura per visualizzare il numero delle figure create
sub stampa_tot_figure(n as integer)
  locate 8, 1: print "Totale figure create:"; n; _
                     " (al massimo ne puoi creare 10)"
end sub

''procedura per visualizzare le informazioni del mouse
sub info_mouse(x as integer, y as integer, r as integer)
  locate 9, 1
  print "x:"; x; " y:"; y; " ruota:"; r
end sub

''procedura per disegnare le figure
sub disegna_figura(f() as figura)
  cls 1
  ''itera gli elementi del contenitore delle figure
  for i as integer = lbound(f) to ubound(f)
    if f(i).tipo = "quadrato" then
      line(f(i).coord_x, f(i).coord_y)-(f(i).coord_x + f(i).dimensione, f(i).coord_y), rgb(f(i).colore(1),f(i).colore(2),f(1).colore(3))
      line(f(i).coord_x + f(i).dimensione, f(i).coord_y)-(f(i).coord_x + f(i).dimensione, f(i).coord_y + f(i).dimensione), rgb(f(i).colore(1),f(i).colore(2),f(i).colore(3))
      line(f(i).coord_x + f(i).dimensione, f(i).coord_y + f(i).dimensione)-(f(i).coord_x, f(i).coord_y + f(i).dimensione), rgb(f(i).colore(1),f(i).colore(2),f(i).colore(3))
      line(f(i).coord_x, f(i).coord_y + f(i).dimensione)-(f(i).coord_x, f(i).coord_y), rgb(f(i).colore(1),f(i).colore(2),f(1).colore(3))
    end if
  next i
end sub

Modulo Modello

''nome file: modello.bas
''architettura MVC (Model-View-Controller): componente MODELLO
''sito web: https://ciucoinformatico.home.blog/
''ultimo aggiornamento: 03/08/2019

/' DESCRIZIONE
Modulo contenente le procedure per la gestione dei dati
e la logica di funzionamento del programma.
'/

''procedura per la creazione di una figure geometrica
sub crea_figura(x as integer, y as integer, r as integer, n as integer, f() as figura)
  f(n).tipo = "quadrato"
  f(n).coord_x = x
  f(n).coord_y = y
  f(n).dimensione = 80 + r
  f(n).colore(1) = 255
  f(n).colore(2) = 0
  f(n).colore(3) = 0
  f(n).selezione = false
end sub

''procedura per la selezione di una o più figure
sub seleziona(x as integer, y as integer, f() as figura)
  ''itera gli elementi del contenitore delle figure
  for i as integer = lbound(f) to ubound(f)
    if x >= f(i).coord_x and x <= (f(i).coord_x + f(i).dimensione) and y >= f(i).coord_y and y <= (f(i).coord_y + f(i).dimensione) then
       f(i).colore(2) = 255
       f(i).selezione = true
       f(i).selezione_x = x - f(i).coord_x
       f(i).selezione_y = y - f(i).coord_y
    else
      f(i).colore(2) = 0
      f(i).selezione = false
      f(i).selezione_x = 0
      f(i).selezione_y = 0       
    end if
  next i
end sub

''procedura per lo spostamento delle figure
function sposta(x as integer, y as integer, f() as figura) as boolean
  dim controllo as boolean
  controllo = false
  ''itera gli elementi del contenitore delle figure
  for i as integer = lbound(f) to ubound(f)
    if f(i).selezione = true then
      f(i).coord_x = x - f(i).selezione_x
      f(i).coord_y = y - f(i).selezione_y
      ''deselezione
      f(i).colore(2) = 0
      f(i).selezione = false
      f(i).selezione_x = 0
      f(i).selezione_y = 0       
      controllo = true     
    else
      ''non fare nulla
    end if
  next i
  return controllo 
end function

Bene. Per oggi ci fermiamo qui.

A rileggerci tra circa una settimana. 🙂

FreeBASIC – mouse

Nei precedenti articoli abbiamo appreso come lavorare con le finestre grafiche e come disegnare una semplice figura geometrica.

Proviamo ora a fare un piccolo passo in avanti per cercare di interagire prima con la finestra grafica e poi con le figure geometriche che disegneremo su questa.

Il primo obiettivo sarà raggiunto con questo articolo, il secondo con il prossimo.

Per interagire con la finestra grafica dovremo trovare il modo di fornire degli input che ci serviranno per far accadere delle cose. Benvenuti nella programmazione a eventi!

La libreria grafica del compilatore FreeBASIC ci mette a disposizione una serie di funzioni per gestire gli input dell’utente. Tra queste ci sono quelle per lavorare con due strumenti esterni (external devices):

  • il mouse
  • la tastiera (keyboard)

In questo articolo ci concentreremo sul mouse.

Gestire il mouse

Il compilatore FreeBASIC ci permette sia di recuperare le informazioni dal mouse, sia di impostare, per esempio, la sua posizione.

Come recuperare le informazioni dal mouse

Per intercettare lo spostamento del mouse e la pressione di uno dei tre pulsanti che vi si trovano normalmente, dobbiamo utilizzare la funzione getmouse.

La funzione getmouse ritorna un valore di tipo long che può assumere due soli valori:

  • 0 (zero) se dal mouse arrivano correttamente le informazioni
  • 1 se invece non arriva nulla (per esempio perché il puntatore è uscito fuori dai bordi della finestra grafica)

Questa funzione però non si limita soltanto a questo, ma permette di recuperare la posizione del puntatore del mouse in coordinate espresse in pixels e lo stato dei suoi pulsanti. Per farlo occorre passarle delle variabili i cui valori saranno restituiti per riferimento.

Se per qualche motivo alcune o tutte queste informazioni non vengono recuperate, le variabili coinvolte assumeranno valore -1.

Come impostare la posizione del mouse

Può essere molto utile fare in modo che all’avvio del nostro programma il puntatore del mouse si trovi in un punto specifico all’interno della finestra grafica.

Per farlo si utilizza la funzione setmouse.

Programma di esempio

Vediamo un piccolo esempio per provare queste due funzioni:

'dimensiona una variabile per ricevere il valore
'restituito dalla funzione getmouse
dim as long verifica_mouse

'dimensiona quattro variabili per ricevere le
'informazioni provenienti dal mouse
dim as integer x, y, ruota, pulsante

'imposta la finestra grafica con risoluzione dello
'schermo 800x600 pixels e dimensioni testuali 100x37
screen 19

'posiziona il puntatore del mouse al centro della
'finestra grafica
setmouse 400, 300

'ciclo per gestire le informazioni dal mouse
do
  verifica_mouse = getmouse(x, y, ruota, pulsante)
  if verifica_mouse = 0 then
    locate 1, 1
    print "Riesco a ricevere le informazioni dal mouse."
    locate 3, 1
    print "x:"; x; " y:"; y
    locate 4, 1
    print "ruota:"; ruota; " pulsante:"; pulsante
    locate 6, 1
    print "Per uscire dal ciclo porta il puntatore"
    print "del mouse fuori dai bordi della finestra grafica."
  else
    cls
    locate 1, 1
    print "Il puntatore del mouse e' fuori dai"
    print "bordi della finestra grafica."
    locate 36, 1
    print "Premi il tasto INVIO per uscire dal programma."
    sleep
  end if
loop until verifica_mouse = 1

Istruzione locate

Come avrete notato per posizionare il testo nei punti voluti è stata utilizzata l’istruzione locate che definisce la posizione del cursore attraverso il numero di riga e di colonna del primo carattere della stringa che si vuole stampare con l’istruzione print.

Bene. Per oggi possiamo dirci soddisfatti.

Nel prossimo articolo vedremo come gestire le informazioni della tastiera. 🙂

FreeBASIC – Grafica [3]

Dopo aver parlato della risoluzione dello schermo (modalità grafica) e della risoluzione grafica (profondità di colore), in questo articolo continueremo l’esplorazione della istruzione screen giocando con il suo terzo attributo: quello che specifica il numero di pagine video con cui si vuole lavorare.

Pagine video

In FreeBASIC è possibile lavorare con più pagine video.

Quindi, oltre alla normale procedura che prevede di disegnare su una pagina mentre questa è già visibile a schermo, avendo a disposizione più pagine video, si può per esempio disegnare su una pagina mentre se ne sta rendendo visibile un’altra su cui si era disegnato in precedenza.

Qual è il vantaggio?

Lo “spettatore” si gode lo spettacolo sul palcoscenico (pagina visibile), mentre nel retropalco (pagina di lavoro) si prepara la scenografia successiva. 😉

Come definire il numero di pagine video

Per definire il numero delle pagine video che necessitano al nostro programma basta scriverlo come terzo attributo dell’istruzione screen:

'Imposta 3 pagine video con la modalità grafica 19 per
'una risoluzione dello schermo di 800x600 pixels e
'la profondità di colore a 32bpp.
screen 19, 32, 3

Se questo attributo viene omesso, il nostro programma avrà comunque una pagina video di default.

Come specificare la pagina di lavoro e quella visibile

Se il nostro programma ha una sola pagina video, le istruzioni per il disegno agiranno e saranno visibili su questa pagina, ma se possiamo disporre di più pagine video, allora dovremo gestire sia la pagina di lavoro che la pagina visibile.

Per farlo si usa la subroutine screenset che ha due attributi: il primo per definire la pagina di lavoro, il secondo quella visibile. Vediamo alcuni esempi:

screen 19, 32, 2 'per definire due pagine video
screenset 0, 0 'disegna e visualizza la I p.v.
screenset 0, 1 'disegna sulla I p.v. mentre visualizza la II
screenset 1, 0 'disegna sulla II p.v. mentre visualizza la I
screenset 1, 1 'disegna e visualizza la II p.v.

Come è facile notare da questa porzione di codice, l’identificativo delle schede video parte dal valore 0 (zero).

Nel programma dei 3 cerchi colorati riportato più sotto ne vedremo un possibile utilizzo.

Un po’ di grafica 2D

Iniziamo ad avvicinarci alla grafica 2D utilizzando una delle funzioni per il disegno 2D: la funzione circle.

Funzione circle

La funzione circle permette di disegnare cerchi ed ellissi.

Sintassi

Un uso semplificato è questo:

circle (coordinata x, coordinata y), raggio, [colore]

I valori relativi alle coordinate x e y del centro del cerchio e quello del raggio sono obbligatori, mentre l’attributo del colore è opzionale: se omesso sarà quello del primo piano corrente impostabile con la funzione color().

Nell’esempio che vedremo più sotto ci baseremo sul modello di colori RGB utilizzando la funzione rgb() passandole tre attributi che rappresentano rispettivamente la componente rossa, quella verde e quella blu. Il range dei valori ammessi per questi attributi va da 0 a 255.

Programma dei 3 cerchi colorati

'Imposta 3 pagine video con la modalità grafica 19 per
'una risoluzione dello schermo di 800x600 pixels e
'la profondità di colore a 32bpp.
screen 19, 32, 3

'Imposta come pagina di lavoro la prima (valore 0).
screenset 0
'Disegna 255 cerchi al centro dello schermo, di
'raggio e tono di colore rosso crescenti.
for i as integer = 1 to 255
  circle (400,300), i, RGB(i,0,0)
next i

'Imposta come pagina di lavoro la seconda (valore 1).
screenset 1
'Disegna 255 cerchi al centro dello schermo, di
'raggio e tono di colore verde crescenti.
for i as integer = 1 to 255
  circle (400,300), i, RGB(0,i,0)
next i

'Imposta come pagina di lavoro la terza (valore 2).
screenset 2
'Disegna 255 cerchi al centro dello schermo, di
'raggio e tono di colore blu crescenti.
for i as integer = 1 to 255
  circle (400,300), i, rgb(0,0,i)
next i

'Doppio ciclo per rendere alternativamente visibili,
'una volta al secondo, le tre pagine. Il ciclo si
'ripete per 3 volte.
for i as integer = 1 to 3
  for j as integer = 0 to 2
    screenset ,j
    sleep 1000 'pausa di un secondo
  next j
next i

Un po’ ipnotico. No? 🙂

Nel prossimo articolo vedremo come impostare alcune proprietà della finestra grafica.

FreeBASIC – Grafica [2]

Nel precedente articolo abbiamo parlato della risoluzione dello schermo: ovvero di come impostare le dimensioni digitali delle nostre finestre in termini di quanti pixels si possono disegnare in orizzontale (larghezza) e di quanti in verticale (altezza).

Il rapporto tra la larghezza e l’altezza di una finestra grafica rappresenta il cosiddetto rapporto d’aspetto.

In questo nuovo articolo parleremo invece della risoluzione grafica: ovvero della qualità delle immagini che si possono disegnare a video.

Profondità di colore

In FreeBASIC per definire la qualità delle immagini occorre impostare nella istruzione screen il parametro della profondità di colore (color depth): ovvero il numero di bits utilizzati per descrivere il colore di un singolo pixel. L’unità di misura di questo parametro è quindi espressa in bpp (bits per pixel).

Questo parametro ha effetto soltanto per le modalità grafiche dalla 14 alla 21 e può assumere i valori: 8, 16 o 32 bpp.

Più bits vengono utilizzati per un singolo pixel, più la qualità dell’immagine sarà migliore.

Scheda video

Arrivati a questo punto occorre fare una precisazione:

Le caratteristiche tecniche della scheda video del nostro computer limitano le possibilità di scelta della modalità grafica in relazione alla profondità di colore da noi scelta.

Come facciamo quindi a sapere quale modalità grafica (parametro mode) possiamo scegliere con uno specifico valore di profondità di colore (parametro depth)?

L’istruzione screenlist

La risposta alla domanda che ci siamo posti più sopra ce la fornisce l’istruzione screenlist.

Vediamo nel dettaglio i passi da seguire per utilizzare al meglio questa istruzione:

  1. Nella prima chiamata alla funzione screenlist è necessario specificare il valore della profondità di colore desiderata. La funzione restituirà la più bassa risoluzione dello schermo possibile in relazione alla profondità di colore specificata.
  2. Le successive chiamate alla funzione non necessiteranno più di alcun argomento: ciascuna chiamata restituirà le possibili risoluzioni dello schermo in maniera crescente.
  3. La restituzione del valore 0 (zero) starà a significare che non ci sono risoluzioni dello schermo più grandi dell’ultima restituita.

Scriviamo ora un semplice programma per scoprire quali risoluzioni dello schermo sono possibili con la scheda video del nostro computer in base alle tre diverse profondità di colore gestite dal FreeBASIC.

Per realizzare il nostro programma utilizzeremo due strutture di controllo:

  1. un ciclo for-next per selezionare di volta in volta le diverse profondità di colore;
  2. un ciclo do-loop per eseguire le diverse chiamate alla funzione screenlist.
'dimensiona e poi crea un array con i tre valori della
'profondità di colore disponibili in FreeBASIC
dim prof_col (1 to 3) as long
prof_col(1) = 8
prof_col(2) = 16
prof_col(3) = 32

'dimensiona una variabile per ricevere il
'risultato della chiamata alla funzione screenlist
dim risultato as long

'dimensiona una variabile stringa per la
'risoluzione dello schermo
dim risoluzione as string

'doppio ciclo per ottenere tutte le risoluzioni dello schermo
'gestite dalla scheda video del computer in base alle
'diverse profondità di colore
for i as integer = lbound(prof_col) to ubound(prof_col)
  print "Lista risoluzioni dello schermo disponibili"
  print "con la profondita' di colore "; prof_col(i)
  risultato = screenlist(prof_col(i)) 'prima chiamata
    do while risultato <> 0
      print risultato
      risultato = screenlist()
    loop
  print "---"
next i

Ecco il risultato a video:

Che dire? Ciò che abbiamo ottenuto è veramente poco soddisfacente: una serie di numeri che sembrano non dirci nulla. Perché?

La risposta ci permetterà di conoscere una tecnica che potrà tornarci utile in molte occasioni.

Lavorare con le codifiche binarie

Il risultato della funzione screenlist è codificato in un valore a 32 bit: dove i 16 bits più alti (ovvero quelli a sinistra) rappresentano la larghezza della finestra, mentre i 16 bits più bassi (ovvero quelli a destra) rappresentano l’altezza della finestra.

Il compilatore FreeBASIC ci mette a disposizione due utili funzioni per estrarre queste informazioni: l’istruzione hiword e l’istruzione loword.

Modifichiamo quindi il ciclo do-loop del nostro programma in questo modo:

do while risultato <> 0
      risoluzione = hiword(risultato) & "x" & loword(risultato)
      print risoluzione
      risultato = screenlist()
loop

Vediamo ora il nuovo risultato a video:

Molto meglio. No? 🙂

Ora non ci resta che fare un confronto con le modalità grafiche disponibili in FreeBASIC per verificare quali sono disponibili.

Modifichiamo quindi il ciclo do-loop inserendo una istruzione select case:

do while risultato <> 0
  risoluzione = hiword(risultato) & "x" & loword(risultato)
  select case risoluzione
    case "320x240"
      print hiword(risultato); "x"; loword(risultato); " -> "; "14"
    case "400x300"
      print hiword(risultato); "x"; loword(risultato); " -> "; "15"
    case "512x384"
      print hiword(risultato); "x"; loword(risultato); " -> "; "16"
    case "640x400"
      print hiword(risultato); "x"; loword(risultato); " -> "; "17"
    case "640x480"
      print hiword(risultato); "x"; loword(risultato); " -> "; "18"
    case "800x600"
      print hiword(risultato); "x"; loword(risultato); " -> "; "19"
    case "1024x768"
      print hiword(risultato); "x"; loword(risultato); " -> "; "20"
    case "1280x1024"
      print hiword(risultato); "x"; loword(risultato); " -> "; "21"
    case else
      print hiword(risultato); "x"; loword(risultato)
  end select
  risultato = screenlist()
loop

Ecco il risultato:

Ora possiamo dirci soddisfatti! 🙂

Abbiamo finalmente la lista delle risoluzioni dello schermo gestibili dalla scheda video del nostro computer con evidenziate quelle che hanno una corrispondenza con le modalità grafiche gestite dal compilatore FreeBASIC.

Nel prossimo articolo continueremo ad approfondire l’istruzione screen testando un altro dei suoi attributi: quello che gestisce le pagine video.

FreeBASIC – Grafica [1]

E così siamo finalmente arrivati a parlare di grafica.

Disegnare delle semplici figure 2D sullo schermo e interagire con queste sarà l’obiettivo principale di questa nuova serie di articoli.

Come è ormai nostra abitudine, partiamo subito con qualcosa di concreto.

Come creare una finestra grafica

La prima cosa che ci serve è creare una finestra grafica su cui poi andremo a disegnare.

Per farlo si utilizza l’istruzione screen.

screen 16
print "Ecco la mia prima finestra grafica in"
print "modalita' 16: ovvero con risoluzione 512x384 pixels."
sleep 'istruzione per sospendere l'esecuzione del programma

Dare un titolo alla finestra grafica

Per abbellire la nostra finestra grafica diamole un titolo sfruttando la subroutine windowtitle:

windowtitle("La mia prima finestra")
screen 19
sleep

Modalità grafica

Il numero subito dopo l’istruzione screen definisce la modalità grafica (mode) sulla base delle seguenti due tabelle:

Le modalità dalla 1 alla 13 sono compatibili anche con i vecchi programmi scritti con il glorioso QuickBASIC, mentre le nuove modalità dalla 14 alla 21 sono specifiche del FreeBASIC. Sarà proprio su queste ultime che ci concentreremo.

Le due tabelle ci mostrano con chiarezza che più la modalità scelta sarà alta, più grandi saranno le dimensioni digitali della nostra finestra: ovvero la risoluzione dello schermo (resolution).

La dimensione digitale di uno schermo (nel nostro caso della finestra) è espressa come numero di pixels orizzontali e verticali (p.e. 512×384).

Scriviamo ora un semplice programma per visualizzare una finestra in tutte e sedici le diverse modalità. Per farlo sfrutteremo un array e un ciclo for.

'dimensiona un array con tutte le modalità grafiche
 dim mode_array (1 to 16) as integer

 'assegna i valori
 mode_array(1) = 1
 mode_array(2) = 2
 mode_array(3) = 7
 mode_array(4) = 8
 mode_array(5) = 9
 mode_array(6) = 11
 mode_array(7) = 12
 mode_array(8) = 13
 mode_array(9) = 14
 mode_array(10) = 15
 mode_array(11) = 16
 mode_array(12) = 17
 mode_array(13) = 18
 mode_array(14) = 19
 mode_array(15) = 20
 mode_array(16) = 21

 'visualizza con un ciclo le varie finestre
 for i as integer = lbound(mode_array) to ubound(mode_array)
   screen mode_array(i)
   print "Finestra grafica in modalita': "; mode_array(i)
   print "---"
   print "premi un taso per continuare…"
   sleep 'sospende l'esecuzione del programma
 next i

Funzione screenres

Le modalità grafiche che abbiamo visto hanno delle dimensioni digitali preimpostate, ma se volessimo creare una finestra grafica di dimensioni diverse?

Niente panico! FreeBASIC viene in nostro aiuto con la funzione screenres che ci permette proprio di definire le dimensioni digitali che vogliamo.

Questa funzione restituisce il valore 0 (zero) se le dimensioni digitali specificate sono realizzabili dal nostro computer, altrimenti viene restituito un numero che specifica il tipo di errore.

Per approfondire l’argomento vai all’articolo dedicato alla gestione degli errori.

Dopo aver provato il codice più sotto, provate a specificare per esempio un numero negativo come larghezza o come altezza:

''crea una finestra grafica larga 800px e alta 50px 
select case screenres(800,50)
  case 0 ''tutto OK
    print "Ecco una strana finestra grafica!"
  case else ''errore
    beep
    print "Non posso creare la finestra grafica che desideri."
end select
sleep ''rimani in pausa

Bene. Per oggi ci fermiamo qui.

Nel prossimo articolo parleremo della risoluzione grafica: ovvero della qualità delle immagini. 🙂

FreeBASIC – lavorare con i files [4]

Nel precedente articolo abbiamo visto la modalità di accesso binary per lavorare con i files binari.

Oggi vedremo come creare un database: ovvero un file binario contenente dati organizzati in records.

Possiamo immaginare un record come una singola riga di una tabella contenente una o più di queste righe. Un record è composto a sua volta da uno o più campi che contengono ciascuno uno specifico tipo di dato (stringa, intero, ecc.). I records devono avere una lunghezza in bytes predefinita.

Per accedere ai campi dei singoli records si utilizza la modalità di accesso random.

Prima però di aprire un file binario in modalità random, è necessario definire il tipo di dato che rappresenterà il nostro record . Abbiamo già visto come farlo nell’articolo che trattava appunto il tipo di dato definito dall’utente.

Vediamo un esempio:

'definizione del tipo di dato (record)
type persona
nome as string * 10
cognome as string * 20
anno_di_nascita as integer
patente as boolean
end type

'dimensionamento e creazione della variabile associata al record
dim p1 as persona
dim p2 as persona
dim px as persona

'valorizzazione dei dati
p1.nome = "Mario"
p1.cognome = "Rossi"
p1.anno_di_nascita = 1977
p1.patente = false
p2.nome = "Maria"
p2.cognome = "Bianchi"
p2.anno_di_nascita = 1985
p2.patente = true

'crea il file
open "mio_db.dat" for random as #1 len=sizeof(persona)
put #1, 1, p1
put #1, 2, p2
close #1

'apri il file e leggi un dato
open "mio_db.dat" for random as #1 len=sizeof(persona)
get #1, 2, px
close #1

'stampa i dati del record 2
print "Nome: "; px.nome
print "Cognome: "; px.cognome
print "Anno di nascita: "; px.anno_di_nascita
print "Patente: "; px.patente

Come avrete notato è stato necessario, sia in fase di creazione che di lettura del file binario, specificare la lunghezza in byte del record. Per farlo si è utilizzata la funzione sizeof().

Bene. Con i files per adesso possiamo fermarci qui.

Nei prossimi articoli inizieremo a creare le nostre prime finestre grafiche. 🙂

FreeBASIC – lavorare con i files [3]

Dopo aver trattato nei precedenti due articoli i files di testo vedendo sia la modalità di scrittura/lettura che la modalità append, in questo nuovo articolo discuteremo di un tipo di file molto particolare: il file binario.

Un file binario è un file che contiene esclusivamente codice binario che può rappresentare qualsiasi tipo di dato.

In FreeBASIC per lavorare con i files binari si utilizza la modalità di apertura binary.

Con questa modalità di apertura si informa il compilatore FreeBASIC che si intende scrivere o leggere dei dati binari.

Puntatore di file

Nel momento in cui si crea un nuovo file binario e si scrive il primo dato, il puntatore del file viene valorizzato a 1.

Scrivere un dato binario

Per scrivere su un file un dato binario, proveniente da un buffer di dati, si utilizza l’istruzione put #.

Leggere un dato binario

Per leggere da un file un dato binario si utilizza l’istruzione get # che riversa i dati su un buffer di dati.

Con un po’ di fantasia, possiamo assimilare il buffer di un computer alla memoria a breve termine di noi umani. 😉

Esempio

dim pi_greco as single
pi_greco = 3.1415

print "La variabile pi_greco e' stata inizializzata al valore: "; pi_greco
 
'Crea il file dati e salva in binario il valore della variabile.
open "prova.dat" for binary as #1
  put #1, , pi_greco
close #1

pi_greco = 0
print "La variabile pi_greco dopo essere stata azzerata vale: "; pi_greco

open "prova.dat" for binary as #1
  get #1, , pi_greco
close #1

print "La variabile pi_greco letta dal file ora vale: "; pi_greco

Abbastanza semplice, no? 🙂

Nel prossimo articolo vedremo invece la modalità di apertura random.

FreeBASIC – lavorare con i files [2]

Nel precedente articolo abbiamo visto come creare un file di testo e come leggerlo riga per riga.

Oggi scopriremo come aggiungere del contenuto testuale.

Per farlo dobbiamo aprire un file di testo esistente in modalità append. Se il file non dovesse esistere ne verrebbe comunque creato uno nuovo:

'Crea un file di testo e scrive la prima riga.
open "prova.txt" for output as #1
 print #1, "riga 1"
close #1

'Apre il file di testo esistente e aggiunge una nuova riga di testo.
open "prova.txt" for append as #1
  print #1, "riga 2"
close #1

Scrivere e leggere valori in formato tabellare

Oltre a poter leggere una riga di testo per volta con l’istruzione line input # e a scriverne una per volta con l’istruzione print #, è possibile memorizzare in un file di testo delle informazioni in formato tabellare (dati separati da virgole) attraverso l’istruzione write # e quindi leggerli con l’istruzione input #:

dim as string a, c
dim as integer b

'Crea un file di testo con tre informazioni separate da virgola.
open "prova.txt" for output as #1
  write #1, "la gatta ha partorito", 4, "gattini"
close #1

'Apre in lettura il file di testo ed assegna a tre variabili le rispettive informazioni.
open "prova.txt" for input as #1
  input #1, a, b, c
close #1

'Stampa il valore delle variabili.
print "la prima informazione e': "; a
print "la seconda informazione e': "; b
print "la terza informazione e': "; c

Bene. Per oggi ci fermiamo qui.

Nel prossimo articolo affronteremo la modalità di apertura binary.

FreeBASIC – lavorare con i files [1]

Dopo un po’ di tempo che si è iniziato a programmare, è inevitabile che sorga l’esigenza di lavorare con delle informazioni salvate su files.

È bene quindi sapere come farlo anche con FreeBASIC.

Istruzione open

L’istruzione che si utilizza in FreeBASIC per lavorare con i files è l’istruzione open.

Di per sé l’istruzione open non specifica cosa in realtà vogliamo fare: se, per esempio, leggere un file esistente, oppure scriverci sopra, o magari crearne uno nuovo.

Per dire al nostro computer cosa vogliamo fare occorre specificare una delle seguenti cinque modalità di apertura:

  1. Per scrivere del testo a partire da un file vuoto (parola-chiave output)
  2. Per leggere il contenuto testuale (parola-chiave input)
  3. Per aggiungere del testo ad un file esistente (parola-chiave append)
  4. Modalità binaria (parola-chiave binary)
  5. Modalità casuale (parola-chiave random)

In questo articolo vedremo le prime due.

Creare un file di testo (output)

Proviamo a creare il nostro primo file di testo:

dim nome_file as string
dim numero_file as long

nome_file = "prova.txt"
numero_file = freefile 'restituisce un numero di file libero

open nome_file for output encoding "utf8" lock write as #numero_file
print #numero_file, "FreeBASIC è figo!"
close #numero_file

Vediamo di spiegare nel dettaglio cosa abbiamo fatto.

Numero del file

In FreeBASIC è necessario identificare il file su cui stiamo lavorando con un numero.

Nonostante siamo liberi di specificare direttamente noi un valore che sia compreso tra 1 e 255, è preferibile utilizzare la funzione freefile che evita il rischio di utilizzare un valore già in uso.

Nome del file

Se nella stringa del nome del file non viene specificato alcun percorso, il file verrà aperto nella cartella corrente (vedi anche la funzione curdir).

Codifica dei caratteri

Per quanto riguarda la scelta della codifica dei caratteri, in FreeBASIC possiamo sceglierne una tra le seguenti quattro attraverso l’istruzione encoding:

  1. ASCII: digitando “ascii” (codifica di default se non specificata)
  2. UTF-8: digitando “utf8” (codifica consigliata dal W3C)
  3. UTF-16: digitando “utf16”
  4. UTF-32: digitando “utf32”

Accessibilità al file

Con l’istruzione lock si può specificare se l’accesso al file è esclusivo o se ne è permesso l’accesso in lettura o scrittura anche ad altri programmi presenti sul nostro computer:

  • read: impedita la lettura da parte di altri programmi
  • write: impedita la sola scrittura
  • read write: accesso totalmente negato ad altri programmi (valore di default)

Come leggere un file di testo

Proviamo ora a leggere il contenuto del file appena creato utilizzando la modalità input e aggiungendo al nostro programma le seguenti linee di codice:

dim riga_di_testo as string

numero_file = freefile

open nome_file for input as #numero_file
line input #numero_file, riga_di_testo
close #numero_file

print riga_di_testo

Compilando e mandando in esecuzione il programma ci accorgiamo subito che la lettura del carattere accentato “è” non è andata a buon fine. Questo perché non avendo specificato la codifica dei caratteri, di default FreeBASIC ha interpretato il testo come codificato in ASCII. Basterà quindi modificare il codice aggiungendo la codifica in UTF-8 e la lettura del testo sarà corretta.

Modalità di lettura

Per leggere il contenuto di un file di testo abbiamo due diverse istruzioni:

  • line input #: che permette la lettura di una intera riga di testo sino al carattere di fine riga che viene scartato;
  • input #: che permette la lettura di valori separati da virgola (ne parleremo nel prossimo articolo).

Intercettare la fine di un file

Una volta scelta la modalità di lettura, dovremo inserirla in un ciclo in modo tale da leggere tutto il contenuto del file.

Qui si pone un problema: quando uscire dal ciclo di lettura?

Niente panico! FreeBASIC ci mette a disposizione la funzione eof che restituisce il valore -1 (true) se si è raggiunta la fine del file, altrimenti viene restituito il valore 0 (false).

Bene! Per oggi ci fermiamo qui.

Nel prossimo articolo continueremo comunque ad approfondire l’argomento e vedremo come lavora la modalità di apertura append. 🙂

FreeBASIC – ritorno dei valori

Come abbiamo visto nel primo articolo dedicato alle procedure, a differenza delle subroutines le funzioni hanno la particolarità di ritornare uno o più valori una volta che sono state chiamate.

Ciò significa, per esempio, che se scrivo una ipotetica funzione somma in grado di ricevere due argomenti, una volta eseguito il codice all’interno della funzione, questa restituirà al codice chiamante un valore pari alla somma dei due parametri (che possiamo quindi interpretare come addendi).

Ritorno dei valori per valore

Diciamo subito che in FreeBASIC la modalità del ritorno per valore è quella di default.

Non vi è quindi la necessità di specificarla attraverso la parola-chiave byval nella dichiarazione e nella definizione della funzione.

Tipologie di restituzione

Esistono tre differenti tipologie di restituzione dei valori:

'1. usando lo stesso nome della funzione
function semaforo() as string
  [...]
  semaforo = "rosso"
  [...]
end function

'2. usando la parola-chiave function
function semaforo() as string
  [...]
  function = "rosso"
  [...]
end function

'3. usando la parola-chiave return
function semaforo() as string
  [...]
  return "rosso"
end function

La differenza tra queste tre modalità sta nel fatto che nelle prime due, una volta restituito il valore, viene comunque eseguito il resto del codice presente nella funzione, mentre, nel terzo caso, l’esecuzione della funzione termina con la restituzione del valore.

Ritorno dei valori per riferimento

Alle volte potrebbe essere utile avere il riferimento al risultato di una funzione piuttosto che il suo valore.

Avere il riferimento al risultato permette infatti al codice chiamante di modificare successivamente il valore della stessa variabile utilizzata dalla funzione per fornire il risultato conoscendone appunto il suo indirizzo di memoria.

Per scegliere questa modalità di restituzione dei valori è necessario specificarla, sia in fase di dichiarazione che in quella di definizione della funzione, attraverso la parola-chiave byref.

Vediamo un esempio direttamente tratto dalla Guida del programmatore disponibile sul sito ufficiale di FreeBASIC:

'dichiarazione della funzione
declare function minimo(byref i as integer, byref j as integer) byref as integer

'dimensionamento e valorizzazione delle variabili
dim as integer a = 13, b = 7 

'stampa del valore originale delle variabili
print a, b

'stampa del valore minimo
print minimo(a, b)

'modifica del valore della variabile con valore minimo
minimo(a, b) = 0

'stampa del valore delle variabili aggiornato
print a, b

'definizione della funzione
function minimo(byref i as integer, byref j as integer) byref as integer
  if i < j then
    return i
  else
    return j
  end if
end function

Bene. Anche oggi abbiamo fatto un piccolo passo in avanti nella conoscenza del FreeBASIC.

A rileggerci al prossimo articolo. 🙂