Programmation d'une technique d'interaction : le Bubble Cursor

Note préliminaire : il est préférable de lire toutes les "étapes" en entier (en particulier les notes ou remarques) avant de commencer ce TP. Le fichier CSV nécessaire à ce TP peut être téléchargé à cette adresse

Exercice : programmer un Bubble cursor basé sur la distance

Cet exercice consiste à programmer un environnement target agnostic, c'est à dire dans lequel le curseur connait les propriétés des cibles qui sont affichées. Dans cet environnement, vous devrez développer un Bubble cursor basé sur la distance, c'est à dire que le curseur highlightera la cible dont le bord est la plus proche, comme illustré par l'image ci-dessous.

1e Etape: Démarrage

Une fois le fichier csv nécessaire pour ce TP téléchargé, créer un fichier MainBubble.py avec votre éditeur de texte préféré. Dans ce fichier, écrire une méthode main dans laquelle instancier une QApplication, une QMainWindow dont la dimension sera (1024,800). Affichez la QMainWindow et executez l'application Qt.

2e Etape: classe Target

Créer une classe Target qui aura trois variables de classe de type QColor defaultCol, highlightCol et toSelectCol (vous choisirez des couleurs différentes de manière arbitraire), et 5 variables d'instances de type entier x, y, size et deux de type booleen toSelect et highlighted.

Écrire un constructeur pour la classe Target qui prends en parametre 3 entiers qui seront utilisés pour initialiser les variables x, y et size, et qui initialise selected à False.

Déclarer une méthode paint prenant en paramètre un objet de type QPainter et faire en sorte que cette méthode dessine dans l'objet QPainter un disque de diametre size et centré autour des coordonnées x et y de la Target. Ce disque devra être de couleur différente fonction de si la cible est toSelect (selectCol),highlighted (highlightCol) ou ni l'un ni l'autre (defaultCol).

3e Etape: classe BubbleWidget

Créer un fichier BubbleWidget.py et créer dedans une classe BubbleWidget qui étendra de QWidget. Cette classe doit avoir une variable d'instance targets de type liste. Dans le constructeur de la classe BubbleWidget, chargez le fichier targets.csv et instancier un objet de type Target par ligne de ce fichier en utilisant respectivement la 1e, 2e et 3e valeur de chaque ligne du fichier comme valeurs pour le x, y et la width de la target. Ajoutez chaque cible à la liste targets.

Redefinissez la méthode paintEvent de la classe BubbleWidget, héritée de la classe QWidget, pour qu'elle dessine chacune des Target stockée dans la liste targets dans son contexte graphique (QPainter).

4e Etape: classe BubbleCursor

Créer un fichier BubbleCursor.py et créer dedans une classe BubbleCursor qui aura une variable de classe de type QColor defaultCol (choisissez une couleur arbitraire différente des couleurs choisies dans la classe Target) et 5 variables d'instance, 3 de type entier x, y et size, une de type list targets et une variable closest initialisée à None.

Ecrire un constructeur de la classe BubbleCursor qui prendra une liste en paramètre pour initaliser targets, initialisera x et y à 0, size à une valeur arbitrairement grande, et closest à None.

Déclarer une méthode paint prenant en paramètre un objet de type QPainter et faire en sorte que cette méthode dessine dans l'objet QPainter un disque de couleur defaultCol, de diametre size x 2 et centré autour des coordonnées x et y du BubbleCursor.

Déclarer une méthode move qui prends deux valeurs entieres en paramètre, mets à jour les valeurs x et y du BubbleCursor, va stocker une reference vers la cible dont le bord est le plus proche dans la variable closest et mettre la variable selected de cette cible à True.

5e Etape: programmer l'interaction

Modifier la classe BubbleWidget en y ajoutant une variable d'instance cursor de type Cursor initialisée en passant la liste de cible comme paramètre du constructeur.

Redefinir la méthode mouseMoveEvent héritée de QWidget pour qu'elle appelle la méthode move de la variable cursor et demande le raffichage du BubbleWidget.

Appeler la méthode paint de la variable cursor dans la méthode paintEvent.

À ce moment vous devez avoir un bubble cursor fonctionnel.

6e Etape: Mesure du temps de selection de cible

Modifier la classe BubbleWidget pour qu'un des élements de targets voit sa variable toSelect passer à True et que celle-ci le reste tant que la cible n'a pas été sélectionnée par l'utilisateur (c'est à dire que l'utilisateur appuie sur le bouton de la souris alors que la cible est highlightée par le BubbleCursor). Faire en sorte qu'au moment de la selection, la variable toSelect de la cible qui vient d'être sélectionnée passe à False, que le temps de selection en millisecondes soit affiché dans la console, et qu'une autre cible soit tirée au hasard et voit sa variable toSelect passer à True.

7e Etape: Selection "classique"

Créer des fichiers/classes NormalWidget et NormalCursor qui reproduiront un comportement classique de selection de cible, c'est à dire que pour sélectionner une cible il faut simplement positionner le curseur au dessus.

8e Etape: RopeCursor

Créer un fichier RopeCursor.py et déclarer dedans une classe RopeCursor qui étendra de BubbleCursor. Tout comme le Bubblecursor, le RopeCursor permet de sélectionner la cible dont le bord est le plus proche, mais diffère en terme de retour visuel. Au lieu d'afficher une bulle centrée sous le curseur souris et s'etendant jusqu'à la cible, le RopeCursor affiche un segment reliant la cible et le curseur comme illustré ci-dessous.