IMG_2826.JPG

Cocoa & Perl: come ti faccio diventare uno script una applicazione Cocoa

Torniamo a parlare di programmazione Cocoa.

Questa volta parliamo di come integrare una interfaccia Cocoa ad una preesistente applicazione a linea di comando, e perché no, persino un’applicazione interpretata.

Avete preferenze?

Io una si.

Ho scelto di integrare uno script perl che ho sempre utilizzato in Linux e che volevo anche su Mac, ovviamente in una veste più “consona”, passatemi il termine.

Si tratta di cdlabelgen (http://www.aczoom.com/tools/cdinsert/), un utilissimo strumento per generare rapidamente copertine e buste per CDROM e DVD.

Scaricare e provare lo script è stata la prima cosa, anche per verificare quali potevano essere i requisiti per il suo funzionamento su OSX. Tutto tranquillo: funziona senza ulteriori installazioni.
La veste più consona è ovviamente la veste grafica: scartati Automator e AppleScript quali potenziali strumenti, la soluzione è ovviamente una applicazione Cocoa. Come in molti porting di software open source l’applicazione essenzialmente avrà il compito di generare la lista argomenti conseguente alle scelte utente (frontend) e attivare il programma di backend (nel nostro caso cdlabelgen): per non essere dipendente da fattori esterni l’applicazione conterrà lo script nel proprio bundle.

Volendo essere precisi, trattandosi di uno script interpretato, l’applicazione attivata sarà l’interprete perl del sistema a cui verrà passato il nome dello script con i suoi argomenti.
Il risultato mi è sembrato soddisfacente, quindi ve lo propongo. 
Vediamo come si realizza.
Per prima cosa creiamo un nuovo progetto Cocoa Application nel nostro XCode e copiamo lo script cdlabelgen nel gruppo Resources del nostro progetto; parimenti facciamo anche della cartella “postscript” fornita assieme allo script (ci sono i modelli grafici di riferimento).
Poi in un metodo di delega (applicationDidFinishLaunching) dell’applicazione Cocoa leggiamo il percorso alle risorse del bundle: ci serviranno per avviare lo script, in particolare per fornire il percorso delle risorse come program working directory a cdlabelgen in cui cercherà la cartella postScript.

NSString * programWorkingDirectory = [[[NSBundle mainBundle] resourcePath] retain];

NSString * commandPath = [[programWorkingDirectory stringByAppendingPathComponent: @"cdlabelgen"] retain];

(Ovviamente creiamole come variabili dell’istanza di NSObject <NSApplicationDelegate> di cui scegliamo un metodo in cui inserire il precedente codice).
Creiamo quindi una interfaccia per raccogliere le opzioni per la linea di comando ed un pulsante “Vai”, “Disegna”, “Go”, “Salva” o qualsiasi cosa gradiate. Colleghiamo il selettore di questo pulsante al metodo – (IBAction)save:(id)sender che avrete creato nel delegato dell’applicazione.

In questo metodo eseguiremo quanto necessario all’esecuzione di cdlabelgen e alla raccolta eventuale dei suoi frutti.

Un piccolo accenno su come costruire rapidamente una interfaccia per gli argomenti.
Create una istanza di oggetto NSUserDefaultController nello XIB; otterrete un istanza “Shared”; collegate i value dei widget al controller key “value” dell’istanza condivisa di User Defaults: non solo avrete un oggetto per rendere “attive” e visibili al programma le scelte effettuate, ma avrete anche una persistenza delle ultime configurazioni scelte, tutto in uno!

Solo per i tipi derivati da NSPopupMenu c’é una piccola eccezione: occorre in questo caso collegare il “Selected Index”: il value contenuto è per noi solo una label.
Per la costruzione della linea di comando, comprensiva delle opzioni, sarà ora sufficiente decodificare tutte le chiavi negli userDefaults elencandole in un array (è quanto vuole l’API di esecuzione di programmi esterni); ad esempio in questo modo:

NSMutableArray * args = [[NSMutableArray alloc] init];

[args addObject: commandPath];

if ( [[[NSUserDefaults standardUserDefaults] valueForKey:@"noTrayPlaque"] boolValue] == YES )

[args addObject:@"--no-tray-plaque"];

Forse non è elegante, ma questo metodo è il più rapido da realizzare senza dover progettare un complesso modello che tenga conto delle varie necessità o scomodare Core Data. Ricorsarsi di applicare prime il metodo synchronize sui defaults prima di iniziare:

[[NSUserDefaults standardUserDefaults] synchronize];

Per la parte operativa utilizziamo una istanza di NSTask per eseguire il comando esterno.
Per configurare l’esecuzione occorre il percorso del programma interprete.
Su Terminale quindi eseguiamo questo comando:

iMac:~ andrea$ which perl

/usr/bin/perl

Portiamo questo a conoscenza di una istanza NSTask:

NSTask * task = [[NSTask alloc] init];
task.launchPath = @"/usr/bin/perl";

task.currentDirectoryPath = programWorkingDirectory;

oppure (variante senza property):

[task setLaunchPath: @"/usr/bin/perl"];

[task setCurrentDirectoryPath: programWorkingDirectory];

così come per gli argomenti:

NSMutableArray * args = [[NSMutableArray alloc] init];

[args addObject: commandPath];
[args addObject: @"-o"];
[args addObject: filename];

Per ovviare i problemi di bufferizzazione di NSPipe, invece di “leggere” lo standard output di cdlabelgen gli passiamo sempre l’opzione “–output-file” con un nome di file precedentemente fornito dall’utente con un NSSavePanel.
Non rimane che lanciare il nostro programma:

task.arguments = args;
[task launch];

avremo il nostro file generato nel percorso e nome dati.
È opportuno dare un riscontro all’utente con una finestra di dialogo.

Per questo è sufficiente porre un risponditore alla notifica NSTaskDidTerminateNotification che possa presentare un semplice NSAlert. La notifica è lanciata da NSTask al termine del processo, e questo completa l’interfaccia utente.
Potrete aggiungere a piacimento tutto quello che ritenete utile ad una applicazione compiuta, ivi compresa una icona accattivante.

Ovviamente , se decidete di realizzare questo progetto e lo distribuite (anche tra amici), ricordatevi di inserire le credenziale degli sviluppatori di cdlabelgen in qualche parte dell’interfaccia, ossia Avinash Chopde <avinash@aczoom.com> www.aczoom.com e B. W. Fitzpatrick <fitz@red-bean.com>

Al prossimo esperimento.

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *