Site Perso de

Thomas JANNAUD

Des actualités personnelles sous un style impersonnel, et inversement.



Tutoriel AppleScript
Les bases pour créer une belle app en AppleScript + astuces et codes sources 11 Avril 2008
applescript

Le langage AppleScript est un peu au Mac ce que le VBA est à Windows : une syntaxe simple que l'on utilise pour développer rapidement de petites applications. S'il reste loin derrière Python ou Bash, sa grande force réside dans sa capacité à communiquer simplement avec d'autres applications, comme iTunes ou le Finder. Un débutant aura peut être plus envie de commencer avec AppleScript. Il y a aussi possibilité dans nos programmes Cocoa / Objective-C de laisser des endroits qui rendront notre application scriptable.

AppleScript est somme toute assez limité en vitesse et je conseille donc de n'utiliser AppleScript que pour faire des scripts qui communiquent, automatiser des tâches avec Automator, ...
Exemple : je télécharge 100 chansons de Claude François, numérotées 01 - Si j'avais un marteau, 02 - Belles belles belles, 03 - ... alors que moi j'aime bien avoir Claude François - Si j'avais un marteau, Claude François - Belles belles belles, ... Au lieu de renommer à la main 100 fichiers je lance un petit script !

Comment utiliser AppleScript ?

Il vous faut au départ un ordinateur Mac. Vous pouvez ensuite utiliser XCode ou 'Editeur de scripts'.
Je vous conseille d'utiliser le premier pour les applications un petit peu poussées, avec une interface graphique (i.e une ou plusieurs fenêtres, des boutons, des onglets, ...) et le deuxième pour débuter et apprendre à maîtriser le langage.

J'utilise souvent 'Editeur de scripts' pour tester juste une partie du code, rapidement, que je tape dans XCode, pour ne pas avoir à relancer tout un projet à chaque fois, ou pour tester l'utilisation d'une commande quand je la vois sur un site (que je note ensuite dans un doc texte, d'où l'idée de ce site). Il ne faut pas avoir peur de tester dans l'Editeur toutes les choses sur lesquelles on n'est pas sur. C'est le meilleur moyen de progresser.

Tutoriel débutant

Dans codes sources vous accéderez à deux tutoriels Applescript plus que généreux sur les explications (mais aussi plus longs à lire), ainsi qu'à mes codes. Vous trouverez dans le dossier Developer de votre Mac un dossier 'Examples' avec beaucoup d'exemples dans beaucoup de langages, y compris Applescript.

Les éditions O'Reilly sont réputées pour leurs livres de qualité, je ne saurais que trop les recommander aussi.
D'autre part, par expérience, vous verrez qu'un site internet ne remplacera jamais un livre papier.
Cette version est en français.

Rappel : [n] désigne que n est un paramètre optionnel

Structures de controle

global x
variable globale au script, pas aux scripts
property x : 5
obligation d'initialiser la valeur à la déclaration avec "property". Attention, c'est magique : la valeur tient après l'exécution d'un script, c'est à dire que si le script fait x <- x+2 puis affiche x, alors (meme apres redémarrge de l'ordinateur) x n'est plus à 5. Ceci est vrai pour les scripts faits avec éditeur de script, pas avec Xcode (ne me demandez pas pourquoi)
set x to 2
déclaration d'une variable ; on ne déclare pas de type car celui-ci pourra changer
set l to {3, "bonjour", {"une", "sous", "liste"}, 17}
déclaration d'une liste/tableau ; idem il n'y a pas de type imposé
Conditionnement

if ... then
...
else if ... then
...
else
...
end if

Gestion des exceptions

try
...
on error [message erreur, n° d'erreur]
...
end try

Boucles

repeat n times
...
end repeat

ou

repeat while condition
...
end repeat

ou

repeat with i from 1 to 5 [by 2]
...
end repeat

ou

repeat with i in liste -- équivalent du For Each en Visual Basic ; i va itérer tous les éléments de la liste, un à un.
...
end repeat

Déclaration d'une fonction

on mafonction(a, b, c)
...
return a+b+c
end mafonction

Appel d'une fonction
Si vous êtes à l'intérieur d'un tell app "Finder"... end tell, alors il faudra faire my f(x) pour accéder à la fonction f définie au sein de votre script. Vous aurez sinon l'erreur "impossible de poursuivre avec f".
Communiquer avec une application extérieure

tell application "Finder" to close front window
ou bien
tell application "..."
...
end tell

Exemple avec iCal

tell application "iCal"
tell application "iCal"
set a to first item of calendars
set b to events of a
set c to first item of b
set dd to summary of c
end tell

Avec les listes (plus loin) vous saurez lister tous les événements très simplement.

Dans 'Editeur de Scripts' vous pouvez faire Fichier->Ouvrir Dictionnaire. Selectionnez alors une application, et vous voyez alors la liste des attributs et méthodes de celle-ci. AppleScript est donc un peu l'avatar de VB sur Mac : vous pouvez contrôler d'autres applications depuis vos scripts, mais seulement ici vous n'avez pas à chercher sur Internet les méthodes d'une dll : tout est explicite !

Chaines de caractères

set a to "bonjour"

length of a
longueur de la chaîne
word n of a
le n ieme mot de la chaine
set chaine to liste as String
conversion de la liste en chaine
chaine1 & chaine2
concaténation de 2 chaines
item ou character 4 of "bonjour"
le 'j'
set liste to every character of "bonjour"
liste des lettres
set souschaine to text 6 to 9 of "bonjour, comment ça va ?"
renvoie ur,
set findechaine to text 10 to end of "bonjour, comment ça va ?"
renvoie comment ça va ?
set AppleScript’s text item delimiters to " "
ultra-puissant : si on le met à " " ça va détacher les mots d'une phrase. Si on le met à "o" ça va détacher "bonjour" en {"b","nj","r"}. A utiliser avec 'every text item of' ou 'every word of'; le text delimiter est aussi celui utilisé pour recoller les mots d'une liste quand on forme une chaine avec 'as string'. Essayez !!!
opérateurs
texte begins with ou start with, ends with, comes before, comes after (pour ordre alphabétique, même si < et > fonctionnent quand même), is in, contains
ignoring case ou considering case (end ignoring et end considering)
permet d'affiner l'utilisation des opérateurs ci-dessus. idem avec ignoring white space ou punctuation

Les listes et les tableaux

Il n'y a aucune différence en Applescript entre l'un et l'autre, ce qui peut être déroutant au début. Mais alors direz vous, cela ralentit considérablement l'exécution des programmes d'utiliser des tableaux en Applescript, puisque l'accès à un élément est en temps O(n) dans une liste.

Et bien non. La raison (pas officielle, mais celle que je me suis faite) est que les listes sont implémentées en interne (en Applescript en tout cas) par des tableaux : quand on crée une liste, AppleScript crée un tableau beaucoup plus grand, avec un pointeur sur le début et un autre sur la fin de la liste. C'est pour ça qu'il va très vite à calculer la taille de la liste, à ajouter un élément à la fin, ou à accéder au nième élément de la liste. C'est d'ailleurs un bon exercice que d'implémenter les listes de cette manière, dans n'importe quel langage que ce soit.

Au final, c'est très agréable à utiliser puisque l'on a l'accès aux éléments comme dans un tableau, ainsi que l'ajout d'un élément en tête ou queue de liste.

set l to {3, "bonjour", {"une", "sous", "liste"}, 17}
pour créer une nouvelle liste
les indices commencent toujours à 1
item n of maliste
le n ième élément d'une liste. Ci dessus, le 3 ième élément est aussi une liste.
Une liste accepte tous les types d'éléments, ce qui est bien agréable.
Ceci permet de faire des tableaux à n dimensions
copy "toto" to the end of maliste
insertion d'un élément en fin de liste
copy "toto" to the beginning of maliste
insertion d'un élément en tête de liste
item 1 <-> [the] first item <-> 1st item
(marche aussi avec second, third, ...) - la syntaxe d'AppleScript est très, très proche de l'anglais. A noter que 'the' est un élément non pris en compte dans la compilation d'un script, ce qui fait qu'on peut le mettre partout, de manière à avoir un script qui ressemble le plus possible à l'anglais. Une très bonne idée à saluer.
last item of maliste
pour le dernier élément. Equivalent : item -1 ; -2 pour l'avant dernier, ...
rest list
la queue de la liste (liste sans son premier element)
liste1 & liste2
concaténation de 2 listes
set item n of maliste to "toto"
modifie le n ième élément d'une liste
items 2 through (ou thru) 5 of maliste
une sous-liste
reverse
pour inverser une liste
count of ou length of
le nombre d'éléments d'une liste. Plus puissant encore (!) : count integers in liste par exemple
some item of maliste
tirage au sort d'un élément de la liste. A utiliser sans aucune modération

Les enregistrements

Les objets internes d'Applescript utilisent beaucoup cette structure. Ça sert à définir les propriétés d'un objet.

set enregi to {age : 20, taille : 180}
count of enregi = nombre de champs
-- information totalement inutile puisqu'il n'y a pas de commande pour connaitre le noms de champs

if enregi contains {couleur : "verte"} then ...

Les boites de dialogue

Le résultat d'une boite de dialogue est un enregistrement, avec les champs 'button returned', et 'text returned' si c'est une inputbox.
En faisant clic droit dans l'Editeur de scripts, puis Dialogs, on peut créer facilement n'importe quel type de boite.

display dialog texteprincipal [default answer ""] [buttons {"ok", "annuler", "stop", ...}] [default button "ok" ou default button n]
choose from list {"1- Saisir une personne", "2- Voir les personnes"} cancel button name "Quitter"
Autre type de boite
set age to (text returned of the result) as integer
Le retour d'une boite de dialogue est toujours stocké dans la variable result

Les scripts-objets

AppleScript donne la possibilité de créer des objets-scripts (je ne m'en suis encore jamais servi pour être honnête)

script monPremierObjet
property age : 100
property nom : "Thomas"
on dire_mon_nom()
display dialog nom
end dire_mon_nom
end script

set Personne to monPremierObjet
tell Personne to dire_mon_nom()
ou
the dire_mon_nom() of Personne

Le système de fichiers

Il faut souvent faire tell app "Finder" ... end tell pour pouvoir utiliser les choses ci-dessous. Attention, pour appeler une fonction de votre script dans un tell app..., il faut faire my mafonction(x).

ouvrir un texte :

tell app "TextEdit"
set doc to open "Ordinateur:Users:Thomas:..."
set letexte to text of doc
close doc
end tell

ou

set thepath to "Ordinateur:Users:Thomas:Desktop:toto.txt" as alias
open for access thepath with write permission
write "au revoir" to thepath
ou bien
set contenu to read thepath
close access thepath

set thepath to a reference to "Ordinateur:Users:Thomas:..."
ou encore set thepath to alias "Ordinateur:Users:Thomas:..."
à ne pas trop trop utiliser
choose folder ["choisissez un .."]
boite de dialogue 'ouvrir dossier'
open folder, file
path to desktop as text = ordinateur:users:thomas:desktop
Création d'un nouveau dossier

tell application "Finder"
set thepath to "Ordinateur:Users:Thomas:NouveauDossier"
set a to exists thepath
if not a then make new folder at folder "Ordinateur:Users:Thomas" with properties {name:"NouveauDossier"}
end if

Parcourir un dossier

set maison to a reference to home
set dossierCalendriers to folder "Calendars" of folder "Library" of maison
repeat with dossier in dossierCalendriers -- boucle sur les dossiers
repeat with fichier in folder "Events" of dossier -- boucle sur les fichiers
...
end repeat
end repeat

avec "Finder"
  • empty the trash
  • close the front window
  • the name of the front window
  • open the startup disk
  • the second file of folder(2) of startup disk

Automator

AppleScript permet d'appeler des scripts automators simplement.

Si en Applescript on est sensé pouvoir faire des choses comme : (je cherche à imprimer une page web)

keystroke "p" using {command down}
delay 2
click menu button "PDF" of sheet 1
click menu item "Save as PDF…" of menu 1 of menu button "PDF" of sheet 1

ça ne marche pas toujours. Utiliser Automator peut être une solution.

Il faut créer un processus ("workflow") Automator et l'enregistrer comme application, et non comme processus. Pour l'appeler : tell application "MacBook:Users:Thomas:Desktop:nomautomator.app:Contents:MacOS:nomautomator" to launch

Quelques précisions :

Interface graphique, Gestion des événements

La gestion d'événements est intimement liée à l'existence d'une interface graphique. Pour ce faire, ouvrez XCode, créez une application Applescript, (la première proposée dans la liste des nouveaux projets), et ouvrez dans les "NIB Files" le fichier .nib. L'interface Builder se lance tout seul. Il est assez bien fait, même si je trouve que Visual Studio surpasse Xcode (de loin). Prenez alors autant d'éléments que vous voulez dans la "library" (dans le menu Tools) et faites les glisser sur la fenetre. Ouvrez ensuite la fenetre des propriétés (Tools, Inspector) pour régler... les propriétés des objets que vous venez de mettre.

Pour gérer les événements associés à ces objets, dans le dernier onglet des propriétés d'un objet, sélectionnez Applescript, et votre script. Donnez alors un nom à l'objet. (Ne donnez pas 2 noms pareils à 2 objets !). Vous pouvez alors cliquer sur les événements qui vous intéressent (comme clicked, ...).Attention, vous devez également cocher Applescript et donner un nom à tous les éléments qui contiennent l'objet en question En clair, si vous avez un bouton dans un onglet, nommez la fenetre, les onglets, l'onglet en question, et le bouton. Vous allez comprendre pourquoi dans un instant.

Contrairement à Visual Basic où les événements sont biens distincts les uns des autres, ici tout est regroupé : tous les événements "clic" sont regroupés dans la procédure on clicked theObject(). Heureusement, pour savoir quel bouton a été cliqué, on peut tester le paramètre 'theObject'.

Ex :
on clicked theObject
if the name of theObject is "boutonbonjour" then
display dialog "bonjour"
else if the name of theObject is "boutonaurevoir" then
direaurevoir()
end if

Ceci est bien sur valable pour n'importe quel événement...

Pour agir sur l'environnement graphique (sur les objets), par exemple en affichant des informations dans une zone texte, il faut faire :
set content of text field "txtnom" of window "fenetreprincipale" to "vous vous appelez Thomas"
Je pense que c'est le principal défaut d'AppleScript : c'est que c'est ultra chiant (pardonnez moi l'expression) pour accéder aux objets, vous pouvez vous en rendre compte par vous même. (idem on peut faire set x to content of text field ... pour récupérer la valeur d'un champ, d'une case à cocher, ...)

Une autre difficulté est que... il faut connaitre quel est le nom des propriétés des objets. De temps en temps, c'est title of, ou content of (pour savoir ce qui est écrit dans un contrôle), mais cela peut varier. Je vous conseille de vous référer à ce site d'apple pour de plus amples informations ou à Guide des Objets AppleScript si vous préférez le français.

Evenements particuliers

on idle
"réveille" le programme régulièrement. Essayez on idle display dialog "bonjour" end idle. Pour obtenir idle,sélectionner File Owner (dans l'espèce de fenetre Finder qui n'en est pas une, où l'on voit le menu, ...), cocher Applescript, ... et sélectionner on idle.
on awake from nib
Au démarrage du programme. Utile pour faire les premiers réglages.

Objets

Voici cependant quelques informations utiles : vous pouvez vous en inspirer pour deviner comment régler la propriété voulue d'un de vos objets.

onglets

label of tab view item "..."
Pour savoir quel onglet à été cliqué par exemple
dans on will select ou on should select
On peut mettre à la fin "return true ou return false" pour décider si oui ou non il faut switcher sur d'autres onglets. Essayez par vous meme.
set current tab view item to tab view item "tab2"
Pour régler quel onglet est visible
Un onglet est un tab et il contient un tabview : c'est sur les tabview qu'on insère les controles (boutons, texte, ...)
ex : set mot to content of text field "txtbox" of tab view item "onglettoto" of tab view "tabs" of window "fenetreprincipale"

Combos

make new combo box item at end of combo box items of combo box "macombo" of window "main" with data "du texte"
Pour insérer un élément dans la Combo

Labels, zones texte

Propriétés utiles

Table View

Une espèce de tableur, assez pratique pour afficher certains types de données.

set l to {....}
set content of table view "tabviewtrain" of scroll view "scrstattrain" of window "main" to l

Objets en général

Propriétés qu'ont souvent les objets

Commandes en vrac

-- un commentaire sur une ligne

(* un commentaire
sur plusieurs lignes *)

beep [n] (n bips sinon un seul)

say "lecture d'un texte à vive voix" [using "Zarvox" ou "Trinoids" ou "Cellos" ou "Fred" ou "Victoria" ou ...]

class of valeur
renvoie string, integer, real, ...
load script "toto.applescript"
current date
Pour obtenir la date du jour, avec aussi heures, minutes, secondes. Utiliser weekday of, day of , ...
set a to (current date + 5 * days)
Pour se "balader" dans l'espace des dates. On peut aussi utiliser les constantes weeks, hours, minutes.
Applescript gère aussi les unités
set aire to 5 as square kilometers ; aire as square feet (pour la conversion)
random number
fonction rand (nombre aléatoire entre 0 et 1)
pour simuler l'appui sur une touche du clavier. Ici pomme + Q, sur Safari

tell application "System Events" to tell process "Safari"
keystroke "q" using command down
end tell

Contrôle de iTunes
tell app iTu-nes to pause (Rappel : ouvrir le dictionnaire des applications pour connaître toutes les commandes possibles)
delay 2
attendre 2 secondes
Ouvrir une page web
shell script "open -a Firefox http://www.google.com"
activate
si un script lance une application, c'est le script qui reste "first responder", i.e qui réagira aux raccourcis clavier, aux clics, ... activate permet de donner la main à l'application lancée par le script (même si le script continuera de tourner en fond). tell app xxx ... activate... end tell

Vous pouvez toujours aller voir la rubrique codes sources pour télécharger quelques scripts tout faits, notamment imprimer automatiquement des pages web (AppleScript + Automator) ou lire des événements de iCal par 2 manières différentes (lecture des fichiers, ou appel de iCal)

À lire aussi :
Laissez un commentaire !

Pas besoin de vous connecter, commencez à taper votre nom et une case "invité" apparaîtra.