Category Archives: Tutorials

Starting with Lua programming on the Nspire

Hello and welcome to the tutorials index page!

You have several options to learn TI-Nspire Lua programming.

Inspired-Lua hosts several tutorials, but also links to other websites (like Steve Arnold’s). Here are some good resources:

We highly advise you to start…. by the beginning, and for that, click here for A-to-Z TI-Nspire Lua tutorials.

If you want to dive in the theory-based tutorials about Lua on the TI-Nspire, explaining how things actually work, head over here.

However, if you want to directly start Nspire-Lua scripting on a practice-based way, learning by example, you can go there.

Happy Lua programming on the TI-Nspire !

Leçon 2 : Créer une table dynamique

Dans cette leçon, nous allons introduire deux notions très importantes, et surtout pratiques du Lua : créer une table, et récupérer une variable d’une application TI-Nspire vers un script en Lua. Nous allons aussi rencontrer les boucles « For ». Si vous débutez en programmation, je vous recommande fortement de vous familiariser avec cette boucle dans d’autres langages, par exemple en TI Basic, avant de tenter ce genre de choses en Lua.A nouveau nous utiliserons Oclua pour créer et tester les scripts vus lors de ce cours, seulement pour notre confort. Les scripts en Lua peuvent être écrits dans n’importe quel éditeur de texte (De NotePad à Word, en passant par le bloc-notes). Pour les utilisateurs de windows, la gratuité de NotePad++ en fait un excellent choix pour ce genre de travail car il colore le texte et participe à la structure de votre script. Pour les utilisateurs de Mac, Xcode est sur votre CD d’installation sous les Outils Développeurs, et fera parfaitement l’affaire. Si vous êtes prêts à dépenser quelques euros, TextMate est excellent (et « connaît » même le Lua ce qui peut aider pour la syntaxe et les bugs).

Mais alors, pour quoi ne pas garder Oclua ? Eh bien, le document peut seulement être lu. Lorsque vous créez une page en Lua en collant votre script, tout semble normal. Cependant, fermez et ré ouvrez votre document (même après avoir enregistré) et vous revoilà devant la page vide « Paste your script here ». Ainsi, ce programme est génial pour apprendre et tester les scripts, mais si vous avez besoin d’utiliser un script à n’importe quel moment, vous aurez besoin de TI-Nsipre Scripting Tools, dont nous reparlerons dans la prochaine leçon. En attendant, nous continuerons avec Oclua. Dans la dernière leçon, nous avons découvert les variables locales, et la création de w et h qui représentent la largeur et la hauteur de la fenêtre. Ces variables s’ajustent dynamiquement. Ainsi, si vous passez de la vue en mode ordinateur à la vue calculatrice, ou si vous redimensionnez ou partagez la fenêtre, alors les variables s’adapteront. Utiliser ces variables plutôt que des valeurs statiques signifie que votre page aura le même affichage, quelle que soit la fenêtre.

Dans cet exemple, nous commencerons par définir deux nouvelles variables locales : La table, grâce à des accolades vides (comme sur TI-Nspire), et le nombre de lignes.

Leçon 2.1 : La boucle For en Lua

Comme je l’ai dit précédemment, je suppose que vous avez déjà eu l’occasion de rencontrer la structure “For…End” (Elle s’appelle “For…EndFor” sur TI-Nspire. En Lua, les fonctions de contrôle se terminent toutes par End)

Boucle sur TI-Nspire

For k, 1, 10
   var := k* (k+1)
EndFor

Boucle en Lua

for k = 1, 10 do
   var = k* (k+1)
end

Le résultat de ces deux boucles est identique : à la fin, la variable var vaudra 110. La syntaxe en Lua est similaire à celle de la TI-Nspire. Au lieu d’utiliser For k, de, à, [pas] sur TI-Nspire, le Lua remplace la première virgule par un égal, le mot “do” apparaît à la fin de la ligne de définition, et enfin, veillez à ce que tout soit en minuscules. Le pas est [optionnel] dans les deux cas.

Nous allons maintenant nous appuyer sur un script qui nous servira d’exemple dans la suite de ce cours.

function on.paint(gc)
  local h = platform.window:height()
  local w = platform.window:width()
  local table = {}
  local nbligne = 3
  gc:setFont("sansserif","r",10)
  gc:setColorRGB(158,8,5)
 
  for k = 1, nbligne do
    table[k] = "Ligne #"..k
    strheight = gc:getStringHeight(table[k])
    strwidth = gc:getStringWidth(table[k])
    gc:drawString(table[k], w/2-strwidth/2, h*k/(nbligne + 1) + strheight/2)
  end
end

Dans cet exemple, nous irons de k = 1 à k = 3, en affichant chaque ligne au fur et à mesure. Les possibilités de cette structure sont assez évidentes – ici, on peut facilement afficher 10 lignes, ou n’importe quel nombre …

Vous avez peut-être reconnu les quelques ligne de l’exemple précédent : lignes qui définissent la police, la couleur, la largeur / hauteur de la chaîne de caractères, et enfin qui affiche le string. Ici, on ajoute une ligne qui crée chaque ligne de notre table. Histoire de bien mettre les choses au clair, la syntaxe table[k] fait référence au k-ème élément de la table. Donc table[1] est la première “ligne” ou “rangée” ou quoi que ce soit qui vous permette de visualiser. Cela fonctionne exactement de la même façon que sur TI-Nspire.

Leçon 2.3 : Créons des lignes pour notre Table !

Pensons un instant à l’organisation d’une table sur TI-Nspire. On la définirait ainsi :

 table := { "un", "deux", 7 }

Maintenant, si on utilisait table[1], on obtiendrait “un”, et pour table[3], on obtiendrait la valeur 7. En Lua, la même chose se produirait. En fait, le Lua est même un peu plus flexible dans la maniabilité des variables. Sur TI-Nspire, il faut bien faire attention à distinguer le string “7” du chiffre 7. Ces deux variables ont une structure de données différente. En Lua, cela n’a pas d’importance, tant qu’elles sont utilisées de manière adéquate. Donc même si vous utilisez une variable num qui contient le string “7”, le Lua peut parfaitement l’utiliser en tant que chiffre, avec par exemple 2*num qui va donner 14. En revanche, si num contenait “sept”, le calcul serait impossible, car on ne peut évidemment pas multiplier un string par un nombre.

Revenons en à notre exemple : on crée en fait chaque ligne de notre table au fur et à mesure que le programme progresse. Quand k = 1, alors table[1] est défini comme la concaténation (Mot barbare, la mise bout à bout de deux chaînes) grâce aux deux points “..” du string “ligne #” et de la valeur de k (1). Donc, table[1] = “ligne#1”. Facile !

Leçon 2.4 : Plaçons ces nouvelles lignes !

Créer toutes ces lignes est plutôt facile. Mais nous devons un peu réfléchir et calculer avant de les afficher correctement. On les centrera horizontalement, comme on l’a déjà fait. Cependant, nous devons les espacer de la même façon verticalement. En d’autres termes, nous devons diviser notre hauteur de fenêtre (h) par un plus le nombre de ligne (1 – nbligne) – car on place en fait les espaces entre les lignes ! Une fois que l’on a compris ceci, le calcul devient plus simple : h*k / (nbligne + 1).
Un petit exemple pour bien comprendre : On veut afficher 3 lignes.
– On aura la première à 1*h / 4
– La deuxième à 2*h / 4 (qui donne h/2)
– La dernière à 3*h / 4
On peut encore faire mieux ! Comment me direz-vous ? En ajoutant la moitié de la hauteur du string ! (h*k / (nbligne + 1) + strheight / 2) Les lignes seront maintenant parfaitement alignées !

________________________

Wow ! On a déjà parcouru un long chemin depuis vos débuts ! Prenez une pause ! Je vous conseille d’essayer de comprendre ce qui se passe plus haut. Modifiez, touchez à tout, comprenez ! On apprend toujours par ses erreurs !

Vous êtes maintenant prêts pour ma dernière partie de ce cours ! On va rendre le tout dynamique !

________________________

Leçon 2.5 : Donnons vie à nos lignes !

Avant de commencer, un petit rappel : un affichage dynamique est par définition le contraire d’un affichage statique. C’est-à-dire qu’un affichage dynamique va s’ajuster grâce à différents paramètres, par exemple un ou plusieurs changements dans une / des variable(s) ; alors qu’un affichage statique ne change pas. (Il porte bien son nom !)

Revenons-en à nos moutons. Nous allons commencer par diviser notre écran (sur le Ti-Nspire Computer Software, en allant dans DOC, et choisir le partage vertical), ensuite dans une des deux parties, nous allons créer un curseur grâce à l’application géométrie.

Après avoir choisi cette application, allez dans Menu > Actions > Insérer un curseur. Ensuite, placez le où vous voulez. Click droit sur le curseur, puis choisissez les paramètres du curseur :
– Variable : lignes
– Valeur : 5 (Cela n’a pas d’importance)
– Minimum : 1
– Maximum : 5
– Incrément : 1
– Style : vertical

Pour une meilleure apparence (optionnel !), click droit sur le curseur, puis choisissez réduire avec le style vertical.

Créer ce curseur est un jeu d’enfant. Mais à quoi ça sert si on ne l’utilise pas dans un script en Lua ? Une solution très simple existe. Elle s’appelle var.recall. Cette commande va nous permettre de remplacer la valeur fixe de nbligne par une valeur dynamique, celle du curseur. On va donc utiliser :

 local nbligne = (var.recall("lignes") or 1)

Ici, var.recall recherche une variable appelée “lignes” et attribue sa valeur à nbligne. (Si la variable lignes existe bien sur) Sinon, elle attribue la valeur 1 – pour ne pas faire planter le programme dans le cas où lignes n’existe pas. Cool.

Et maintenant ?

Bravo ! Ainsi s’achève la deuxième leçon. Prenez un peu de temps pour assimiler toutes ces nouveautés avant de passer à la suite.

Dans la prochaine leçon, on verra comment modifier le contenu de ces lignes, car je suppose qu’obtenir un alignement de “ligne#1”, “ligne#2”, etc ne vous sera que de faible utilité ! 😀

Leçon 1 : Premiers pas avec le Lua sur TI-Nspire

Aujourd’hui, Inspired-Lua vous propose une traduction et adaptation du guide de Steve Arnold que vous pouvez retrouver en cliquant ici. Depuis l’OS 3.0, les Ti-Nspire possèdent une nouvelle fonctionnalité en matière de programmation : le Lua. Ce dernier est un langage de programmation à la fois moderne et rapide. Les scripts écrits en Lua avec un simple éditeur de texte peuvent être convertis en .tns (format des documents TI-Nspire) à l’aide de TI-Nspire Scripting Tool . De tels scripts donnent de nouvelles possibilités étonnantes et puissantes pour la création de documents TI-Nspire afin d’apprendre ou d’enseigner.

Avec un peu d’expérience, les programmeurs de Lua peuvent créer d’incroyables programmes, auxquels l’imagination est la seule limite. A titre d’exemple, voici un tableau périodique des éléments réalisé par l’équipe de TI-Planet. Comme vous pouvez le voir, les possibilités sont énormes. Toutefois, même les débutants peuvent profiter des particularités offertes par le Lua dans des conditions plus limitées mais qui restent quand même puissantes. Ce tutoriel pour débutants offre de bonnes bases grâce auxquelles les élèves comme les enseignants pourront commencer à créer leurs propres documents !

L’objectif ici sera de commencer le Lua en affichant du texte. Des graphiques et des images dynamiques suivront plus tard.

Leçon 1.1: Premiers pas avec le Lua sur TI-Nspire

Pour cette première leçon, nous utiliserons le programme Oclua (On Calc lua) développé par Olivier Armand. Commencez par télécharger l’archive Oclua d’après ce lien . Ensuite, décompressez l’archive et transférez le fichier .tns sur votre calculatrice.

Gif Oclua
Oclua fonctionne à la fois sur la calculatrice et sur le logiciel TI-Nspire. Pour commencer à utiliser Oclua, ouvrez tout d’abord le fichier, puis créer une nouvelle application dans le classeur. (CTRL+I) Choisissez l’éditeur mathématique. Vous pourrez écrire le code en Lua ici. Ensuite, pour l’exécuter, vous n’aurez qu’à copier ce code et à le coller dans le premier onglet. (CTRL+A pour tout sélectionner, CTRL+C pour le copier, et enfin CTRL+V pour le coller) Rapide et facile à prendre en main, Oclua est idéal pour commencer à utiliser le Lua. Cette procédure est expliqué à travers l’image en gif ci-contre (Ignorez les deux lignes horizontales, elles sont dues au logiciel utilisé pour enregistrer l’animation – elles n’apparaîtront pas sur votre version du document). Toutefois, nous verrons par la suite que de meilleures solutions existent.

Les choses sérieuses commencent : nous allons créer votre première fonction en Lua ! Mais ne vous inquiétez pas, rien de bien compliqué. Tout d’abord, nous allons apprendre comment afficher du texte en Lua. Insérez une page éditeur mathématique comme nous l’avons vue. Recopiez-y le code ci-dessous

function on.paint(gc)
  gc:drawString("hello world", 0, 20)
end

Notez que l’indentation (ajout d’espace/tabulation dans des scripts) n’est pas du tout obligatoire elle permet juste de mieux se retrouver dans de longues lignes de codes. (Ici elle n’a que peu d’intérêt mais il faut mieux prendre ces habitudes dès le départ)
Le Lua est un langage sensible à la casse c’est-à-dire que vous devez écrire exactement les fonctions comme montrées ici. Dans cet exemple, tout est en minuscules, excepté le « S » de String.
Vous devriez voir le texte « hello world » écrit en haut à gauche de la page. Voyons ce que l’on peut apprendre de cet exemple :

  • La structure pour définir une fonction est la même que dans beaucoup de langages.
function nom(argument)
  quelques instructions
end
  • Dans notre exemple, nous avons utilisé une fonction standard du Lua appelée « on.paint ». Vous la rencontrerez dans la plupart (si ce n’est tous) les scripts en Lua. Lorsque vous utilisez cette fonction, elle affiche le texte sur l’écran.
  • « gc » signifie « graphics context ». (Contexte graphique :P) Il est utilisé quand des éléments graphiques de n’importe quel type sont employés. Vous pouvez voir dans cette fonction, l’unique ligne de code est définie par ce terme gc. Ne vous inquiétez pas, vous vous y habituerez vite !
  • La ligne d’instruction de cette fonction est assez explicative : gc:drawString (“hello world”,0,20). Dans le contexte graphique (gc) on va afficher (draw, dessiner) la chaîne de caractère (String) qui est “hello world” à la position (0,20) sur la fenêtre
  • Il faut savoir que le système de coordonnées à pour origine le coin en haut à gauche de l’écran. Ainsi, les coordonnées ne doivent pas être négative pour afficher correctement du contenu graphique. Dans notre exemple, le point choisi est (0,20). Si l’ordonnée était inférieure, le texte serait coupé.
Vous devriez prendre quelques minutes pour découvrir cette première fonction et appliquer ce que vous venez d’apprendre. Essayez de déplacer le texte afin de comprendre le système de coordonnées. Essayez de centrer votre texte. Vous en apprendrez ainsi plus sur les dimensions de l’écran.
Cependant, vous verrez plus tard qu’un texte centré sur une plateforme (calculatrice, ordinateur) n’est pas forcément adapté à une autre. Mais alors, comment faire ? Nous allons voir cela dans la suite de notre cours ! En attendant, amusez-vous bien ! 🙂

Mais pourquoi ?

Il est peut-être temps de s’arrêter un instant et de se demander – Pourquoi s’ennuyer à faire cela ? Il y a d’autres façons beaucoup simples pour afficher du texte à l’écran.
Deux propriétés très importantes d’une fenêtre en Lua sur TI-Nspire, par opposition à une fenêtre classique dans l’éditeur mathématique :
  • Cliquez sur n’importe quelle fenêtre en Lua. Déplacez-la, essayez de l’éditer. C’est impossible. Le texte que vous affichez ne peut pas être édité par l’utilisateur du document. Dans l’éditeur mathématique, l’utilisateur peut bien sur changer – et en effet détruire – tout ce que l’auteur a pris le temps de faire. En plus, comme on peut le faire dans l’éditeur, le texte affiché peut être à la fois dynamique et statique. Donc on peut facilement créer de puissants outils d’apprentissage tout comme on peut le faire avec l’éditeur, mais dans un environnement plus sécurisé.
  • Dans la seconde partie de cette leçon, vous apprendrez à contrôler la taille de la police, la couleur et le style, et comment facilement centrer le texte sur une ligne, et même au centre de la fenêtre. En Lua, on a un contrôle significatif de la façon dont le texte est affiché – bien plus qu’en utilisant l’éditeur. En terme de couleurs, vous avez accés à des millions de couleurs !

Leçon 1.2: Un peu plus intéressant ?

Bon, retour au travail. Prêt pour la suite ?
Si vous n’êtes pas sur la page 1.2 d’Oclua, retournez-y. Nous allons ajouter quelques lignes à notre script on.paint.
function on.paint(gc)
  gc:setFont("sansserif", "b", 12)
  gc:setColorRGB(158, 5, 8)
  gc:drawString("Ceci est mon texte", 20, 20)
end
A nouveau, étudiez ces quelques lignes – essayez de modifier les différentes valeurs. Vous allez vite vous rendre compte que setFont possède trois entrées :
  • la famille : « serif » ou « sansserif »
  • le style : « b » pour bold (gras) ; « r » pour regular (normal) ; « i » pour italic (italique) ; « bi » pour bold italic (gras et italique)
  • la taille

setColorRGBpermet d’accéder simplement à une multitude de couleurs : RGB signifie Red (rouge ; 255,0,0) Green (vert ; 0,255,0) Blue (bleu ; 0,0,255). Il y a de nombreuses couleurs entre ces valeurs, ou vous pouvez accéder à la liste en ligne ou juste télécharger le PDF que j’utilise. Mes couleurs fétiches sont burgundy (158, 5, 8 )> et navy (20, 20, 138), et qui vont très bien ensemble ! C’est vraiment mieux qu’une simple palette de 9 à 15 couleurs actuellement disponible sur TI-Npire !Si vous n’êtes pas perdu en lisant ce cours, c’est que vous êtes sur la bonne voie ! Nous allons finir cette leçon par une notion importante en Lua : centrer le texte. Nous en profiterons pour introduire les variables locales et globales.

Le Lua dispose de toutes sortes de commandes très utiles, comme platform.window:heigth() et platform.window:width().

Comme vous pouvez le deviner, ces commandes permettent de récupérer les valeurs actuelles de la fenêtre. Ainsi, platform.window:heigth() permet de connaître la hauteur de l’écran tandis que platform.window:width(). permet d’en connaître la largeur. Une nouvelle fois, faites bien attention aux majuscules et aux espaces ! Ici, tout est en minuscules.
Si on stock ces valeurs dans des variables, que nous appellerons w et h (pour width et height respectivement), nous pourrons les réutiliser hors de la fonction et dans un script plus grand. Mais nous aurons l’occasion d’y revenir un peu plus tard.

Voici deux nouvelles fonctions tout aussi importantes : getStringHeight et getStringWidth. Comme leurs noms l’indiquent, elle permettent de déterminer la largeur et la hauteur d’une chaîne de caractères.

function on.paint(gc)
  local h=platform.window:height()
  local w=platform.window:width()
  gc:setFont("sansserif", "b", 12)
  gc:setColorRGB(158, 5, 8)
  local sw = gc:getStringWidth("Ceci est mon texte")
  local sh = gc:getStringHeight("Ceci est mon texte")
  gc:drawString("Ceci est mon texte", w/2 - sw/2, h/2 + sh/2)
end
On commence par définir les dimensions de la fenêtre avec w et h. On peut aussi obtenir les dimensions de la chaîne de caractères
Maintenant, pour centrer la chaîne :
  • Horizontalement : on prend le centre de la fenêtre (w/2) puis on recule de la moitié de la largeur de la chaîne de caractères (w/2 – sw/2).
  • Verticalement : on suit la même procédure, à une différence près : verticalement, on compte à l’envers en Lua : plus on va bas, plus le nombre est grand ; plus on va haut, plus il est faible ! (h/2 + sh/2)

Et maintenant ?

C’est tout pour cette leçon – à vous de jouer !
Pourquoi ne pas essayer d’aligner votre texte à droite ? Jetez un œil au centrage que nous venons de réaliser et inspirez-vous-en pour aligner le texte à droite et à gauche.
Et pour le placer en haut ou en bas ? Comment feriez vous ? Essayez différentes couleurs !Dans la prochaine leçon, nous apprendrons à donner vie à notre texte et à utiliser une table pour organiser plusieurs lignes de texte de façon structurée sur votre page !

How to have a nice little “input” function in Lua…

(improved version of Nick Steen’s website’s example)

You could be quite surprised by the fact that there isn’t any native way to have a keyboard text input function on the Nspire Lua API. Indeed, it’s a little weird since this is often used for many types of programs (whether in games for typing the username, or other apps to type user data etc.).

Anyway, don’t worry, you can program it yourself with a little piece of code to add to your script ! It actually catches the keypresses with the on.charIn function, and stores what the user types in a string (appending it). You then just have to display this string on the screen and that’s all.

If you want the user to be able to delete some letters, just add some code to the on.backspaceKey function, and that’s all !

In the example below, we set a character limit to 25, but that’s totally up to you.

Well, here’s the complete code ready to be used :

input = ""   
 
function on.paint(gc)
    gc:drawString(input,5,5,"top")  -- display string
end
 
function on.charIn(char)
    if string.len(input) <= 25 then   -- limit of 25 chars
        input = input..char   -- concatenate
        platform.window:invalidate()   --screen refreh
    end
end
 
function on.backspaceKey()
    input = string.usub(input,0,-2)  -- deleting last char
    platform.window:invalidate()  
end

Bye !

Save a high score (or anything) without cheating possibility !

In this tutorial we’re going to take a look at a pretty interesting feature of the TI-Nspire framework in Lua : save and restore any kind of data directly in Lua, that lets us playing around with a high score for a game or any other configuration data for a more complex program.

Example : You just released a wonderful game. You’d like to share it with your friends, that way they can challenge you thanks to your high-scores system. But how is that working?

The first way you could think of is to use the var API. Therefore, we save the high score in the document as a global variable (math variable).

Your high score system is done ! Hum … wait a second, one of your friend managed to score 10 000 000 points to your game ! What the heck ? He simply modified the global variable you’re using as an high score, since it’s freely accessible via a Calculator application. Here, it is not hard to cheat !

A second way is to protect the first one by using math.eval() which lets us launch any TI-Basic instruction, as the Lock one for example. Then, if we do like so :

math.eval("Unlock highscore")
var.store("highscore", highscore)
math.eval("Lock highscore")

our high score variable will be write protected. But, since Lock is a TI-Basic command, it can be launch outside the Lua program. Things aren’t going to be better.

What about looking for a specific Lua command ? Actually, there is var.monitor() that probes a specified variable and checks if this variable will be changed. If so, on.varChange() event is called. This lets us to control any variable changes.

Here is an example :

function on.create()
  if not var.recall("highscore") then
    highscore = 0
    var.store("highscore", highscore)
  end
  var.monitor("highscore")
end
 
function on.varChange(list)
  for k, v in pairs(list) do
    if k == "highscore" then
      if var.recall(k) ~= highscore then
        return 1 -- it is an external change, block modifications
      else
        return 0 -- it is an internal change, allow modifications
      end
    end
  end
  return 0 -- allow modifications of other monitored variables if any
end

This code will disallow any external modification of the highscore variable, with the following error message :

Cannot accept change : Invalid input

Obviously, if you have to do this for multiple variable, for example configuration data, don’t do this since it is a heavy and repetitive method …
It is at this moment that we take a look to the documentation. We can see two trivial events : on.save() & on.restore().
In fact, this event couple does exactly what we expected to do from the beginning !

Actually, when the widget get closed (when we close the document, or copy/cut the widget) the on.save() event is called. You have to define on.save() to make it return any kind of value (boolean, number, string, table etc …). The next time the widget will open (when we open the document, or paste the widget), the on.restore() event will be called, with the data returned by on.save() as the parameter !

Here we are, easy-going :

function on.save()
  return highscore
end
 
function on.restore(data)
  if type(data) == "number" then
    highscore = data
  end
end
 
function on.create()
  if not highscore then
    highscore = 0 -- first time we initialize highscore
  end
end

Object Classes

Back to part 4

In this part, we will talk about a quite interesting and useful topic in Nspire Lua programming, which relies on working with “objects” instead of (or in addition to…) classic variables.

If you already have some Lua knowledge, you should know that it is using a lot of key-indexed or key/value tables. This means that each element of a table (or list) is linked to a key that lets us to refer with. For example the table below is key-indexed with strings :

tab = {"cle1"="valeur1", "cle2"="valeur2"}
print(tab["cle1"]) -- va afficher "valeur1"

In Lua, keys are polymorphic, that way we can easily store strings, tables or functions as key or as value :

tab = {
  [1]="exemple",
  "2"="de",
  [function() return 3 end]="table",
  [false]="polymorphe"
}
 
for k, v in pairs(tab) do
  print(k, v)
end

Will display :

1                 exemple
2                 de
function: 10f6840 table
false             polymorphe

Even the Lua global environment (_G) is a table.

It’s in this way we’re going to introduce Lua object-oriented-programming (“OOP”) : Each object is a table and each object has multiple elements named methods or properties. In the future if we would like to create a new object we simply duplicate the table from its meta-table. If we would like to create inherited objects, we will also duplicate its meta-table and change it a little bit to make it different from the main object.

myObject = {
    nom="toto",
    age=21,
    ChangerNom=function(self, nom)
        if #nom > 1 then
            self.nom = nom
        end
    end,
    ChangerAge=function(self, age)
        if age > 1 then
            self.age = age
        end
    end
}

We can easily access/write stuff on any element. For example :

print(myObject.age) -- affiche 21
myObject:ChangerAge(18) -- identique à myObject.ChangerAge(myObject, 18)
print(myObject.age) -- affiche 18

You have to understand that there is no such thing as private/public things in Lua as you could find in C++/C#/Java…, since this is just a representation, and not a real implementation. So it can be easier in Lua, especially for beginners.

You can now understand why you call  platform.window:width()  this way, and not with a global function such as  “GetWindowWidth()”.  In fact, width() is a method of the window object, which itself is a property of the platform object. Same thing for platform.gc() which we can find shortened as ‘gc‘. Actually, gc is an object and platform.gc() is its “constructor”.
In one hand, it’s pretty much the same thing (they both have the same methods), but in the other hand, they have some differences (some of their properties aren’t the same).
(NB : In Nspire Lua code, try to avoid using gc by calling platform.gc(), because of some unexpected behaviors you can get if you don’t know exactly what you’re doing. You should always use the gc that the on.paint(gc) event provides as a parameter)

Let’s tell you what classes look like in code and how to create them.

In the Nspire Lua API, we can find a “class()” method, that usualy doesn’t come with Lua. In fact, this method was created to help programmers create their class by having a much easier and shorter way to do it. For the curious people among our readers, here’s what we would have to do to create and use a class without the class() method (here, for the “Account” class).
(it can be scary if you don’t know Lua well – you can skip this paragraph if you want)

Account = {}
Account.__index = Account
 
function Account.create(balance)
    local acnt = {}             -- our new object
    setmetatable(acnt,Account)  -- make Account handle lookup
    acnt.balance = balance      -- initialize our object
    return acnt
end
 
function Account:withdraw(amount)
    self.balance = self.balance - amount
end
 
-- create and use an Account
acc = Account.create(1000)
acc:withdraw(100)
More info here : http://lua-users.org/wiki/SimpleLuaClasses

Now, with the Nspire Lua API, with  class() :

Account = class()
 
function Account:init(balance)
    self.balance = balance      -- initialize our object
end
 
function Account:withdraw(amount)
    self.balance = self.balance - amount
end
 
-- create and use an Account
acc = Account(1000)
acc:withdraw(100)

That’s much better, right ?

Here, “init()” is the constructor. You *have* to have an “init(…)” method for your classes since it’s written that way in the class() method. The init(…) function will be the one that will be called when your objects are going to be created.

We are going to show you a real example of OOP programming in Lua, by creating a script that will create a class, called “Shape”, which will have several methods including a constructor allowing us to creates shapes.

Forme = class()
 
-- Constructeur
function Forme:init(x, y, image)
    self.xpos = x
    self.ypos = y
    self.pic = image
end
 
-- Dessiner la forme
function Forme:paint(gc)
    gc:drawImage(self.pic, self.xpos, self.ypos)
end
 
-- Repositionner l'objet en indiquant les nouvelles coordonnees
function Forme:setxy(newx, newy)
    if newx > xmin and newx < xmax and newy > ymin and newy < ymax then
        self.xpos = newx
        self.ypos = newy
   end
end
 
-- Repositionner l'objet en indiquant le deplacement a effectuer
function Forme:move(dx, dy)
    local newx,newy
    self:setxy(self.xpos + dx,self.ypos + dy)
end

You may have noticed the use of global variables like xmin, xmax. These are defined earlier in the script, without . For example :

xmin = 20
ymin = 30
xmax = 250
ymax = 200

We can now play around with our “Shape” class. We’re now going to create two images and convert them into TI.Images using the official developement tool :

carre_rouge = image.new("\010\000\000\000\010\000\000\000\000\000\000\000\020\000\000\000\016\000\001\000\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252\227\252")
smiley = image.new("\016\000\000\000\018\000\000\000\000\000\000\000 \000\000\000\016\000\001\000\156\243\156\243\156\243\189\247\213\222\235\193\136\181g\181\135\181g\181\137\185O\202\189\247\156\243\156\243\156\243\156\243\156\243{\239O\206\168\222F\243D\243d\247d\247#\243\004\235E\214\170\1858\231\156\243\156\243\156\243\213\222m\210\231\230d\247\132\251\195\255\131\255\131\255\131\255C\251\003\247\132\226\233\193r\210z\239\156\243\204\189\198\226\132\247\166\218\007\198\198\226\195\255\131\255#\243\231\197\007\198\002\251\131\230\135\185\246\222\212\218g\210D\243\166\218\166\218\197\226D\239\131\255\131\255C\247\165\218d\222\006\206\194\242\196\205O\206\170\185\005\239\131\255\197\226D\239\131\255\131\255\131\255\131\255C\251\034\255\002\255E\218\226\250b\234\167\193G\173C\247\131\255\131\255\130\218\001\198\162\222\131\255c\251\002\243\161\197\161\197\226\250\226\250\162\246\133\193f\181#\243C\251\131\255\194\193\194\156\002\202C\251\034\255\162\234\194\156\194\156\193\250\193\254\193\254\133\193g\181\003\247c\251\034\255\196\230e\218\196\234\034\255\034\255\226\242%\218%\218\226\250\226\250\161\254\133\193&\169\226\242\034\255\034\255\034\255\034\255\034\255\002\255\002\255\193\254\193\254\226\250\161\254\161\254\161\254d\189G\173\163\234\002\255\226\242\194\242\163\234\162\242\162\242\162\242\130\238\130\242B\242\130\242\162\246B\242\133\193\014\198E\214\194\242f\181\198\156\231\156\231\156\231\156\231\156\231\156\231\156\198\156\231\156B\242\227\213\235\197Y\235\136\185\130\238\193\254\132\189s\206\222\251\222\251\222\251\222\251{\239\198\156\130\242\129\254d\189\179\214\156\243\237\193\196\205\162\242\162\242\198\201)\165)\165)\165)\165H\173B\242\129\254\227\213\169\193\023\227\156\243z\239\137\185\034\226\162\246\130\242B\234\034\230\034\230B\234\034\234\129\254\130\242D\181\213\222\156\243\156\243\156\243\023\227\201\197\002\222\162\246\161\254\129\254\161\254\129\254\129\250\034\234d\189\147\214\156\243\156\243\156\243\156\243\156\243\246\222\011\206\196\205\002\222B\238\034\238\034\230\196\209\165\197\147\214{\239\156\243\156\243\156\243\189\247\156\243\156\243\023\227p\210\011\206\198\205\198\205\232\205O\210\147\214\156\243\156\243\156\243\156\243")
forme1 = Forme (xmin + 20, ymin + 20, smiley)
forme2 = Forme (xmax - 30, ymax - 30, carre_rouge)

You can see how we’re not explicitely calling the init() constructor of the Shape class.
In fact, we just have to call Shape(…) and the init will be called with the arguments.

Now we just have to create the main structure of the lua script, with the basic events you should know by now. Here’s what it looks like :

-- Affichage
function on.paint(gc)
    gc:setColorRGB(231, 231, 231)
    gc:fillRect(xmin, ymin, xmax-xmin+15, ymax-ymin+15)
    forme1:paint(gc)
    forme2:paint(gc)
    gc:setColorRGB(0, 0, 255)
    gc:setFont("sansserif" , "b", 11)
    x = gc:drawString("(" .. forme1.xpos..", ".. forme1.ypos .. ")", 50, 0, "top")
end
-- Permet de deplacer forme1 avec les fleches directionnelles
function on.arrowLeft()
    forme1:move(-5, 0)
end
 
function on.arrowRight()
    forme1:move(5, 0)
end
 
function on.arrowUp()
    forme1:move(0, -5)
end
 
function on.arrowDown()
    forme1:move(0, 5)
end
-- Permet de choisir l'emplacement de forme2 avec la souris
function on.mouseDown(wx, wy)
    forme2:setxy(wx, wy)
end

Here’s what you get !

Have fun !

NB : you can find examples of classes and OOP programming in real games programmed in the Nspire Lua API. For instance : the BreakOut game by Adriweb, which uses different classes to have objects such as the ball(s), the blocks, the paddle, the bonuses etc.

Using a predefined variable in a TI-Nspire document

Back to part 3

This part will show you how well the Lua language is integrated in the TI-Nspire framework.
For this, we’re going to study the var API and expected events.

Retour à la partie 3

Cette partie vous montrera que le Lua est bel et bien en parfaite harmonie avec la TI-Nspire et son framework. Pour cela nous vous proposons d’étudier l’API var et les évènements qui en découlent.

Tout d’abord , créez un nouveau classeur TI-Nspire et définissez y une variable n, par exemple un curseur permettant d’en faire varier la valeur entre 0 et 10 ou directement dans la page de l’application calculs.

C’est cette variable que nous allons récupérer dans le script Lua !

Pour cela, créez un nouveau fichier Lua avec votre éditeur de texte préféré et nous allons commencer à le remplir.

L’API var permet de faire pleins de choses intéressantes et nous allons les étudier.

Tout d’abord, notre script doit savoir si la variable n change. Nous trouvons donc cette ligne :

 

-- On indique ici qu'il faut surveiller le contenu de la variable n
var.monitor("n")

Au passage, nous pouvons écrire une fonction récursive qui renverra la factorielle d’un nombre, rien que pour l’exemple :

function factorielle(n)
	if math.floor(n) < n or n < 0 then
		return ("Erreur")
	elseif n == 0 then
		return(1)
	else
		return(n * factorielle(n - 1))
	end
end

Maintenant, nous allons nous intéresser au couplage des évènements. Il nous faut deux évènements. D’abord celui pour dessiner à l’écran, c’est à dire on.paint(), mais également un évènement déclenché lorsque notre variable change. Cet évènement est on.varChange() et est lancé chaque fois qu’une variable appartenant à la liste des variables surveillées (avec var.monitor()) est modifiée.

function on.varChange(varlist)
	-- On provoque une reactualisation de la fenetre
	platform.window:invalidate()
end

Concrètement, dès qu’une variable surveillée change, on demande à ce que l’écran soit actualisé. Donc maintenant, il faut faire cette partie d’affichage en créant la fonction on.paint(). En effet, appeler platform.window:invalidate() marque l’écran comme “invalide”, donc bon à être actualisé. Au prochain tour de boucle de processus, comme l’écran doit être actualisé, le framework de la TI-Nspire lancera la fonction on.paint()

function on.paint(gc)
	local wh, ww, n, x
	-- nombre de pixels (hauteur et largeur)
	wh = platform.window:height()
	ww = platform.window:width()
 
	-- affichage d'un trait de separation
	gc:setPen("medium", "smooth")
	gc:drawLine(0, 60, ww, 60)
 
	-- affichage de la taille de la fenetre en bas de l'ecran
	-- il s'agit juste d'un exemple !
	gc:setColorRGB(200, 200, 200)
	x = gc:drawString(wh, 10, wh - 10, "bottom")
	gc:drawString(ww, x + 10, wh - 10, "bottom")
 
	-- affichage de n!
	n = var.recall("n")
	if math.floor(n) < n or n < 0 then
		gc:setColorRGB(255, 0, 0)
		gc:setFont("sansserif" , "b", 10)
		gc:drawString("n n'est pas un ",10,10,"top")
		gc:drawString("entier naturel ",10,25,"top")
	else
		gc:setColorRGB(0, 0, 255)
		gc:setFont("sansserif" , "b", 20)
		x = gc:drawString(n.."! = ", 10, 10, "top")
		x = gc:drawString(factorielle(n), x + 5, 10, "top")
	end
end

Voilà ! C’est tout !

Lancez ensuite TI-Nspire Scripting tools et cliquez sur le premier bouton (ou choisissez tools, Lua Script to Clipboard) puis sélectionnez le fichier contenant le script que nous venons de créer. Allez dans le logiciel TI-Nspire, et utilisez Ctrl V pour copier le code Lua.

Faites ensuite varier la valeur de n, et la miracle !

La valeur dans la fenêtre Lua est actualisée !

Tip : Si vous avez modifié le script, il vous suffit dans TI-Nspire Scripting Tools de

  • cliquer sur le second bouton (ou choisissez tools, Reload Script).
  • Allez ensuite dans le logiciel TI-Nspire, cliquer sur la fenêtre correspondant au code Lua.
  • Ctrl K pour sélectionner cette fenêtre
  • Ctrl V pour copier le nouveau script Lua

Idées d’améliorations :

Vous pouvez tester une première “amélioration” de ce script en ajoutant les lignes suivantes, permettant de “piloter” n avec les touches “vers le haut” ou “vers le bas” :

function on.arrowUp()
	var.store("n", var.recall("n") + 1)
end
 
function on.arrowDown()
	var.store("n", var.recall("n") - 1)
end

>> Partie 5

Linking Events

Back to part 2

This part will explain how each event is linked to the core.

First of all, we have to understand how this works. Basicly the script is launched before the executive engine (master). This lets us initialize our variables. Sadly, this also means that we can neither evaluate expressions nor use some of the TI-Nspire API (like platform, var) until the engine is launched. Here is a call summary :

  • Launch string library
  • Launch math library
  • Launch TI-Nspire FrameWork (toolpalette, image, …)
  • Open and launch Lua user’s script
  • Launch var API (store, recall, …)
  • Launch platform API (window, gc, …)
  • Link events (pseudo code)
while(Exit)
	------- Some OS routines here
 
 	---- Begin of Event link
	buffer:captureDataFromKeyPad() 	--  (N) some underground routines to catch events
	if buffer.charInput ~= "" then 	 	--  (N)
		on.charIn(buffer.charInput)
		buffer.charInput= "" 	 	 	--  (N)
	end
	if buffer.arrowKey ~= "" then 	 	--  (N)
		on.arrowKey(buffer.arrowKey)
		buffer.arrowKey = "" 	 	 	--  (N)
	end
	----- etc ... 	
	if platform.window.isInvalidate then 
	 	local gc = platform.gc()	 	  	--  Create a new graphical context
	 	gc:begin()	 		 	   		-- initialize the graphical context
		on.paint(gc) 	  	 	  		-- save all the things we have to draw
	 	gc:finish()  	  	  	    		--  (N) draw all those things	
		platform.window.isInvalidate = false 	-- say that the window has been drawn
	end
	----- End of Event link
end

Note : the (N) commented line only indicates the meaning of the routine. Those functions don’t really exist.

Now we can understand how everything is linked, just by a main loop. This helps you to understand that you don’t have to code a loop yourself, because the screen wouldn’t be refreshed. This also helps to see when the screen gets refreshed. In other words, we cannot use niether gc nor platform.gc() in order to draw somthing on the screen if we are outside of on.paint() (except if your outside function is called within on.paint() ).

Here is an example of a simple Lua program that displays a message only when a key is pressed (and let the screen blank otherwise).

function on.paint(gc)
	if message then
	 	gc:setFont("sansserif", "r", 10)	-- initialize font drawing
	 	gc:drawString(message, 0, 0, "top")	-- display the message at (0, 0) coordinates
	 	message = nil 				-- erase the message
	 	timer.start(1) 	 	 	-- start a timer to exit on.paint() but keep in mind that we have to redraw the screen
	end
end
 
function on.timer()
	timer.stop()
	platform.window:invalidate()
end
 
function on.charIn(ch)
	message = "Hello World !"		-- store a message
	platform.window:invalidate()		-- force display
end

When you open the document, the script is read once. It initializes and overwrites all the functions and globals with the ones you defined. Thus, message is nil. Once the on.paint() event is called, message is nil, thus, nothing is done. When you press a key that calls on.charIn()message is now “Hello World” and we tell the platform that the screen has to be refreshed. Then, on.paint() is called again, message is not nil then we display it, erase message and launch a timer. Why is that ? Because if we call platform.window:invalidate() right there, we won’t refresh the screen. Why again ? Just look at the pseudo-code above. We set the window as drawn after each call of on.paint(). Thus a timer is necessary to manually recallon.paint() and exit the on.paint() function to draw the screen. When the timer is ended, on.timer() is called and we refresh the screen. The screen is then redrawn but there is nothing to draw because message is nil. Thus, the graphic context lets the screen blank.

>> Partie 4

Concepts and Basics

This part will explain you how the Lua actually works inside the OS and help you to figure out what you’re doing when you write a script for the TI-Nspire. It is recommended to have some basics on Lua programming or some knowledge of event-driven languages, but keep in mind that it is not required.

The Lua is an interpreted script language which means that it isn’t as fast as ASM/C programs, but is still better than the TI-BASIC. One good thing is that this language is in a complete harmony with the OS with basic events and a powerful graphic context.

Lua is normally a sequential script language. For example, when we use the print() command to display a value, we can easily guess when the command will be run in the script. Here’s an example :

a = 1
print(a)
a = a + 1
print(a)

Output :

1
2

Nothing special. However, on the TI-Nspire, Lua has a completely different approach. We meet this approach with high-level programming or with object-oriented languages (like C++, C#, …). In those languages, we don’t have the ability to control the flow/execution of any function. Yes, it can be quite strange to hear that, but it’s the way it is. Are we going to learn a language that doesn’t do what we tell it to do ? Well, in a way, yes.

But don’t worry ! We’re here to learn how to cross this quite unstable bridge ! A wonderful world is on the other side.

First of all, you have to “change team”. As in before, you were the boss, this means you used to tell the machine to compute 1 + 1 and it would proudly output “2”. Now, you are a worker. A task authorization is given to you, thus, you explain to the machine how to do this work. Actually, those “authorizations” are called events. When the event is called, you can do what ever you want. Here is a pseudo-code explaining what to do when the “Cook” event is called :

function Cook()
	organize_the_bench()
	cut_ingredients()
	assemble()
	heat_up()
	serve()
end

You can easily understand that you won’t cook when you get the job. You’ll wait for your boss’ order ! Well, it is exactly the same thing between the TI-Nspire and you. But this time, the TI-Nspire framework is the boss. Everything is event-based.

In a nutshell, our functions have to be called by the TI-Nspire. But how to be sure that they will be executed ? It is the moment to look at the Events list. When an event is fired, the TI-Nspire gives zero, one or multiple parameters (“arguments”) that we can use in our function. This lets us know, for example, which key has been pressed, because when the TI-Nspire executes the charIn(ch) event, it gives also a string as an argument corresponding to the key. However, when the enterKey() event is fired, no argument is given.

Before, in a non-event-based language (like BASIC, C, PHP …) we used to program like this :

-- Init Constants here
...
k = 0
while k == 0 do
	k = getKey()
end
if k == 72 then
	-- do something
end

Now, it looks like that :

-- Init Constants here
...
function on.charIn(ch)
	if ch == "7" then
		-- do something
	end
end

We hope you understood that very important part…

 

Let’s go to Part 2 now !