Overblog Suivre ce blog
Administration Créer mon blog
27 septembre 2010 1 27 /09 /septembre /2010 13:00

Bonjour à tous,

 

Voici un petit article d'introduction à la programmation d'applications sur iPhone/iPod Touch/iPad. Si vous avez des notions de programmation sur les plateformes Mac ou NextStep, vous verrez que cela n'est pas très différent. Question équipement, vous aurez besoin d'un Mac sous OS X 10.5 minimum.Vous aurez besoin d'avoir installé les outils de développement présents sur le DVD d'installation de Mac OS et le SDK iPhone/iPad disponible sur http://developer.apple.com

 

Niveau connaissances, des notions de programmation orientée objet sont indispensables. Il faut connaître le motif de conception MVC (Modèle Vue Contrôleur), qui est très utilsé par Cocoa. Connaître Objective C, ou au moins Java est très utile.

Création d'un nouveau projet

Ouvrez XCode, choisissez de créer un nouveau projet. Dans l'assistant de nouveau projet, choisissez une cible "iPhone OS Application", et "View-based Application" dans la liste de gauche :

 

helloworld1

 

Choisissez un nom pour le projet (ici, j'ai mis HelloWord). L'assistant vous crée toute une liste de fichiers nécessaires à votre application :

 

helloworld2

 

Quelques remarques sur les fichiers importants :

  • La classe HelloWorldAppDelegate est le délégué à la classe NSApplication. Elle contient les méthodes de spécialisation de la logique de votre application.Contrairement à Java, on dérive très peu avec Objective-C et Cocoa, on délègue.
  • La classe HelloWorldViewController est le contrôleur lié à la fenêtre principale de votre application. C'est dans celle-ci que vous gérerez les évènements lié à l'interface.
  • Le fichier HelloWorldViewController.xib est un fichier Interface Builder, l'application qui permet de gérer l'interface de votre application de manière graphique.

Gestion de l'interface graphique

Notre interface sera constituée d'une vue unique avec un bouton. Lors du clic sur celui-ci, le message "Hello, World" s'affichera sur l'écran. Ouvrez le fichier HelloWorldViewController.h, il contient l'interface du contrôleur de notre vue. Nous avons besoin de la personnaliser afin qu'elle référence un champ texte (label) et ajouter la méthode d'évènement du clic sur le bouton. Complétez la classe de cette manière :

 

helloworld3

 

IBOutlet et IBAction sont utilisés par Interface Builder pour reconnaître des types qu'il doit prendre en compte. IBOutlet désigne un élément de l'interface (bouton, champ texte, liste, etc.) et IBAction un gestionnaire d'évenement. Une IBAction prend toujours un paramètre sender de type id, qui est un pointeur vers l'objet à l'origine de l'évenement. En pratique IBOutlet est une macro préprocesseur vide et IBAction un typedef vers void.

 

Dans le fichier HelloWorldViewController.m, nous allons rajouter l'implémentation de la méthode onButtonClick :

 

helloworld4

 

Rien de bien compliqué, on envoie le message setText à l'objet pointé par lblHello, afin de lui faire afficher la phrase "Hello, world". N'oubliez pas l'arobase devant les guillemets, celà indique que la chaîne est un objet NSString codé en UTF-16, plutôt qu'un tableau de caractères terminé par un 0, comme en C.

 

Personnalisation de l'interface graphique

Double-cliquez maintenant sur le fichier HelloWorldViewController.xib. Cela va lancer Interface Builder, l'application dédiée à la création d'interfaces. Le fichier contient trois objets : Une référence vers le contrôleur que nous venons de modifier sous l'appellation "File's Owner", une autre référence (inutile dans notre cas) et une vue (la fenêtre de notre application) :

 

helloworld5

 

Ouvrez la vue, et glissez-déposez un label et un bouton sur votre vue. J'ai mis "Say hello" comme titre de mon bouton :

 

helloworld6

 

Selectionnez maintenant l'objet "File's owner". Maintenez le bouton Ctrl : le curseur de votre souris vous permet de tracer une ligne. Cela permet d'indiquer les objets vers lequels les pointeurs à l'intérieur de votre contrôleur vont pointer. Reliez le "File's owner" au label : une bulle s'ouvre, listant tous les pointeurs vers des UILabel de votre contrôleur. Il n'y en a qu'un seul : le champ lblHello. Selectionnez-le. À l'exécution, le champ lblHello pointera alors vers le label à l'intérieur de la vue.

 

helloworld7

 

Maintenant recommencez l'opération, en partant du bouton et en le reliant à votre File's owner. Cette fois, vous avez le choix de la méthodé à déclencher lors du clic sur le bouton. Sélectionnez la méthode onButtonClick.Cette méthode sera maintenant déclenchée lors du clic sur le bouton.

 

helloworld8

 

Selectionnez à nouveau le UILabel et effacez son texte, afin qu'il ne soit pas visible au démarrage de l'application. Sauvegardez le fichier .xib via le menu File/Save. Vous pouvez maintenant quitter Interface Builder et revenir à la fenêtre XCode. Cliquez sur le bouton "Build and Run" en haut de la fenêtre de votre projet. Vous verrez alors votre application se lancer dans le simulateur iPhone :

 

helloworld9

 

Conclusion

Nous avons vu aujourd'hui les bases de la programmation d'applications sur iPhone/iPod Touch/iPad. Vous avez appris à créer un projet, vous avez vu les composants de base d'une application : la vue et le contrôleur. Vous avez appris à écrire des gestionnaires d'évènements et à lier des éléments d'interface à votre code afin de les manipuler depuis celui-ci. Vous pouvez maintenant vous entraîner à rajouter des contrôles et autres éléments d'interface et à réagir à leur évènements.

 

Merci de votre attention.

Repost 0
Published by Olivier - dans Programmation
commenter cet article
26 septembre 2010 7 26 /09 /septembre /2010 16:45

 

Bonjour à tous,

Nous avons vu l'autre jour que l'algorithme LZW était très simple à implémenter en Python : une quinzaine de lignes suffit pour coder la fonction de compression et la décompression n'est pas plus longue. Pour autant, nous nous sommes arrêtés à produire un tableau d'entiers de 32 bits en sortie de notre fonction de compression. Il s'agit maintenant de stocker ces entiers de manière à ce qu'ils tiennent le moins de place possible en mémoire.

Nous allons utiliser un codage très simple. L'algorithme LZW utilise un compteur qui s'incrémente pour identifier les combinaisons d'octets rencontrées. C'est la variable n des fonctions compress et uncompress. Cette valeur ne fait qu'augmenter au fur et à mesure. Les premiers nombres, de 256 à 511, ne nécessitent que 9 bits pour être représentés. Une fois qu'on aura dépassé cette limite, les nombres de 512 à 1023 nécessiteront 10bits, et ainsi de suite. On commence à 8 bits et chaque fois que l'on franchit une nouvelle limite, on double la quantité de nombres représentables en ajoutant un bit.

 

Codage

Nous allons coder n'importe quel nombre N par la valeur N+1. Ainsi, on s'assure qu'on ne pourra jamais rencontrer la valeur 0 dans la séquence codée. Le zéro sera utilisé pour signifier que l'on ajoute un bit supplémentaire pour représenter les nombres suivants. C'est ce que fait la fonction « pack » :

 def pack(seq):
nbits = 8 # nombre de bits par nombre
bits = array.array('B') # tableau de bits
output = array.array('B') # sortie codée

# convertit les entiers en liste de 0 ou 1
for n in seq:
if n+1 > 2**nbits-1: # si on dépasse la limite
b = _int_to_bits(0, nbits) # ajoute 0
bits.extend(b)
nbits += 1 # on utilise un bit de plus
b = _int_to_bits(n+1, nbits) # ajoute n+1
bits.extend(b)

# allonge la séquence sur un multiple de 8
while len(bits) % 8:
bits.append(0)

# code les bits 8 par 8 dans des octets.
index = 0
size = len(bits)
while index < size:
byte = _int_from_bits(bits[index:index+8])
output.append(byte)
index += 8
return output



Entiers et séquences de bits

La fonction pack fait appel à deux fonctions utilitaires pour convertir des nombres en suites de bits et vice et versa. Voici leur codes. Tout d'abord la fonction « _int_to_bits » :

 def _int_to_bits(num, nbits):
output = array.array('B')
for bit_index in range(nbits-1, -1, -1):
mask = 1 << bit_index
if num & mask:
output.append(1)
else:
output.append(0)
return output

 

Le paramètre « num » indique le nombre à convertir, le paramètre « nbits » est le nombre de bits à utiliser en sortie. La fonction inverse, ne prend qu'un seul paramètre, une séquence de bits dont on connait nécessairement la taille :

 def _int_from_bits(seq):
output = 0
nbits = len(seq)
for bit_index in range(nbits):
if seq[(nbits-1) - bit_index] == 1:
output |= 1 << bit_index
return output

 

Décodage

La fonction de décodage n'est pas plus compliquée que la fonction de codage :

 def unpack(seq):
bits = array.array('B')
output = [] # sortie décodée

# convertir les octets en suite de bits
for n in seq:
b = _int_to_bits(n, 8)
bits.extend(b)

index = 0
nbits = 8 # nombre de bits par nombre
size = len(bits)

# convertir les bits en entiers
while index < size:
limit = min(index+nbits, size)
i = _int_from_bits(bits[index:limit]) # bits -> nombre
if i == 0:
nbits += 1 # code les nombres sur un bit de plus
else:
output.append(i-1) # N -> N-1
index += limit-index
return output

Utilisation 

Pour tester le résultat de notre petite séance de codage, je vais utiliser le fameux faux texte latin Lorem ipsum  dans sa version populaire. Le tout se passe dans un shell Python interactif :

 >>> import lzw
>>> s = "Lorem ipsum [...] amet." # Je vous épargne les détails
>>> len(s)
2054 # la taille de la chaine non compressée
>>> sc = lzw.pack(lzw.compress(s)) # compresse et code le texte dans sc
>>> len(sc)
1197 # La version compressée par LZW tient presque deux fois moins de place :)
>>> s2 = lzw.uncompress(lzw.unpack(sc)) # décompression
>>> s2 == s
True # La preuve que tout marche bien ! Ouf !

Voilà nous avons fait le tour de l'algorithme, il est assez simple et fonctionne remarquablement bien sur du texte. J'ai testé celui-ci avec Python 2.6.5 sur Linux, mais il devrait marcher sans trop de modifications avec Python 3.

Merci de votre attention !

Repost 0
Published by Olivier - dans Programmation
commenter cet article
24 septembre 2010 5 24 /09 /septembre /2010 13:26

Bonjour à tous,

Dans le cadre de ce blog, j'inaugure une section programmation. Je vais parler aujourd'hui de l'algorithme de compression de données LZW. C'est un algorithme très simple, ce qui me permettra de toucher un large public. La compression de données, tout le monde a une idée de ce à quoi ça sert : réduire la place occupée par des données. LZW est un algorithme de compression non destructeur, c'est-à-dire que les données compressées puis décompressées sont strictement identiques à l'original. Compresser avec LZW « n'abime pas » les données (contrairement au JPEG pour les images, ou au MP3 pour l'audio, par exemple). Cet algorithme est très célèbre, car il est au coeur du format d'image GIF, qui a secoué le web au début des années 2000. La société qui possédait le brevet sur cet algorithme s'est mise à menacer tous les éditeurs de navigateurs et de logiciels d'édition d'images pour non-paiement de royalties. C'est d'ailleurs ce qui a poussé à l'élaboration du format d'image PNG, qui est plus riche en fonctionnalités que le GIF et qui n'utilise pas LZW. Depuis 2004, ce brevet est tombé dans le domaine public, on peut donc utiliser LZW sans souci partout dans le monde.

 

Je ne vais pas détailler ici l'algorithme complet, la page Wikipédia qui y est consacrée serait de toute manière bien plus pédagogique que ma modeste prose. Je vais me contenter de fournir une implémentation de l'algorithme en Python en respectant les noms et les conventions qui sont utilisés sur Wikipédia. Ça permettra au lecteur de faire la correspondance entre le code et la description de l'algorithme plus facilement. Le choix d'un langage de haut niveau comme Python facilite la traduction de certaines opérations comme « ajouter au dictionnaire », puisque le langage possède un type dictionnaire intégré.

 

Compression

Voyons tout d'abord la compression des données. C'est le propos de la fonction « compress » :

 def compress(data):

dic = dict([[chr(i), i] for i in range(256)]) #Crée le dictionnaire initial

output = array.array('I') #liste de nombres en sortie

n = 256 #code de la prochaine combinaison

w = ""

for c in data:

wc = w + c

if wc in dic:

w = wc

else:

dic[wc] = n

n += 1

output.append(dic[w])

w = c

output.append(dic[w])

return output

 

C'est tout ? Oui. Le terrible algorithme qui a fait trembler le web au début des années 2000 peut être implémenté en 15 lignes de Python... Je vous laisse juges de savoir s'il était utile de faire tout un pataquès pour si peu. En tout cas, il est pratique, car il est suffisamment simple et concis pour être abordé dans le cadre d'un blog. :) Magie du typage dynamique, le paramètre data peut être une chaîne de caractères 8 bits, un objet array ou une liste d'octets. Pour des raisons d'efficacité mémoire, je préfère renvoyer un array plutôt qu'une liste. Voyons maintenant la décompression...

Décompression

Sans surprise, la décompression est tout aussi simple :

 def uncompress(data):

dic = dict([i, chr(i)] for i in range(256))

output = [dic[data[0]]]

n = 256

w = data[0]

for c in data[1:]:

try:

entry = dic[c]

except KeyError:

entry = dic[w]

entry += entry[0]

output.append(entry)

dic[n] = dic[w] + entry[0]

n += 1

w = c

return "".join(output)

 

Conclusion

L'implémentation présentée ici n'est pas complète. En effet je me suis intéressé ici à l'implémentation théorique de l'algorithme, qui renvoie comme résultat une liste d'entiers. Chacun de ces entiers va prendre une place fixe non optimale en mémoire (généralement 32 bits). Or un nombre comme 260 (par exemple) n'a besoin que de 9 bits pour être exprimé. La prochaine étape consistera donc à réduire leur volume en utilisant un codage à nombre de bits variable. Ce sera le sujet d'un prochain billet. Je proposerai alors le code source complet (et directement utilisable) de mon module LZW.

Merci de votre attention.

 

Repost 0
Published by Olivier Pisano - dans Programmation
commenter cet article

Présentation

Recherche

Liens