Premier pas dans Vegas : la claque !

9 views
Skip to first unread message

Fred DUFAU

unread,
Dec 7, 2010, 10:49:01 AM12/7/10
to VEGAS - ECMASCript & ActionScript OpenSource framework
Bonjour,

J'ai enfin pris le temps de mettre le nez dans vegas, et je dois dire
que j'ai pris une grosse claque !
J'y suis depuis 2 jours et je suis loin d'avoir fait le tour tellement
il y a de trucs bien pensé et efficaces.


Aprés avoir joué avec lunas et graphics j'ai tenté de réadapter le
tuto sur mvc fc pour réaliser un petit quizz xml.


Voici comment je m'y suis pris :

J'ai commencé par créer un XMLQuizzModel qui hérite de
OrderedMapModelObject à qui je fournis l'url du xml et qui se charge
de coller les QuestionVO dans la map :

mon xml est une série de noeud question qui contiennent une série de
noeud reponse pour cette question :

<code>
<question id="1">
<text><![CDATA[Question 1]]></text>
<bonne_reponse>0</bonne_reponse>
<reponses>
<reponse>
<text><![CDATA[Oui]]></text>
</reponse>

<reponse>
<text><![CDATA[Oui]]></text>
</reponse>

<reponse>
<text><![CDATA[Peut-être ?]]></text>
</reponse>

</reponses>
</question>
</code>


<code>
package models
{
import events.EventList;
import flash.events.Event;
import flash.net.URLRequest;
import vegas.models.maps.OrderedMapModelObject;
import vegas.net.XMLLoader;
import vo.QuestionVO;
import vo.ReponseVO;
/**
* ...
* @author Fred DUFAU
*/
public class XMLQuizzModel extends OrderedMapModelObject
{
private var _xmlLoader:XMLLoader;

public function XMLQuizzModel()
{
setEventTypeCHANGE( EventList.CHANGE_QUESTION );
setEventTypeADD( EventList.ADD_QUESTION );
setEventTypeCLEAR( EventList.CLEAR_QUESTION );
setEventTypeREMOVE( EventList.REMOVE_QUESTION );

loop = false;


}

public function load( pSrc:String ):void
{
_xmlLoader = new XMLLoader( new URLRequest( pSrc ) );
_xmlLoader.addEventListener( Event.COMPLETE, onLoadXML );
}

private function onLoadXML( pEvt:Event ):void
{
var newQuestionVO:QuestionVO;
var newReponseVO:ReponseVO;
var reponsesVOList:Vector.<ReponseVO>;

for each( var question:XML in _xmlLoader.data.elements() )
{
reponsesVOList = new Vector.<ReponseVO>();

for each( var reponse:XML in question.reponses.elements() )
{
newReponseVO = new ReponseVO( { text:reponse.text } );
reponsesVOList.push( newReponseVO );
}

newQuestionVO = new QuestionVO( { text:question.text.toString(),
bonneReponseIndex: int( question.bonne_reponse ),
id:question.@id.toString(),
reponsesVOList:reponsesVOList } );

addVO( newQuestionVO );
}

run();

dispatchEvent( new Event( Event.COMPLETE ) );
}

public override function supports( value:* ):Boolean
{
return value is QuestionVO ;
}
}

}
</code>


Déja ici j'ai 2 questions :

1) mon quizz est composé de questionVO et dans un questionVo il y a
entre autre un reponsesVOList qui est un vector contenant les
reponseVO associées à chaque question : etait ce le meilleur choix à
cet endroit d'utiliser un vector ou y avait il une map plus
performante ?

2) à la fin du parsage du xml et du remplissage de la map, je
dispatche un simple Event.COMPLETE , y'avait il plus pertinent ?


***********************************************************************************************************************************************

Ensuite pour afficher une question (et ses réponses) j'ai crée une
classe QuestionUI qui hérite de CoreMovieClip que voici :

<code>
package display
{
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import lunas.components.buttons.LabelButton;
import lunas.components.buttons.LabelButtonStyle;
import lunas.components.labels.Label;
import system.data.iterators.VectorIterator;
import vegas.display.CoreMovieClip;
import graphics.layouts.BoxLayout;
import graphics.Direction;
import graphics.*;
import vegas.events.ValueEvent;
import vo.QuestionVO;
import vo.ReponseVO;
/**
* ...
* @author Fred DUFAU
*/


public class QuestionUI extends CoreMovieClip
{



protected var _questionLabel:Label;
protected var _reponsesLayout:BoxLayout;
protected var _reponsesContainer:Sprite;



public function QuestionUI():void
{

}


override protected function addedToStage( pEvt:Event = null ):void
{
_questionLabel = new Label();
_questionLabel.autoSize = true;
_questionLabel.label = "yo";
addChild( _questionLabel );

_reponsesContainer = new Sprite();
_reponsesContainer.x = 0;
_reponsesContainer.y = 30;
addChild( _reponsesContainer );


_reponsesLayout = new BoxLayout( _reponsesContainer );
_reponsesLayout.verticalGap = 10;
_reponsesLayout.direction = Direction.VERTICAL;



}



public function update( pVO:QuestionVO ):void
{
clear();

_questionLabel.label = pVO.text;

var vi:VectorIterator = new VectorIterator( pVO.reponsesVOList );

var newReponseButton:LabelButton;

var labelStyle:LabelButtonStyle = new LabelButtonStyle();
labelStyle.color = 0x000000;
labelStyle.textRollOverColor = 0xFF0042;



while ( vi.hasNext() )
{
newReponseButton = new LabelButton();
newReponseButton.addEventListener( MouseEvent.CLICK,
clickHandler );
newReponseButton.label = ReponseVO( vi.next() ).text;
newReponseButton.style = labelStyle;
newReponseButton.index = vi.key() - 1;
_reponsesContainer.addChild( newReponseButton );
}

_reponsesLayout.run();
}

private function clickHandler(pEvt:MouseEvent):void
{
if ( QuestionVO( quizzModel.getCurrentVO() ).bonneReponseIndex ==
LabelButton( pEvt.currentTarget ).index )
{
dispatchEvent( new ValueEvent( Event.SELECT, 1);
}
else
{
dispatchEvent( new ValueEvent( Event.SELECT, 0);
}
}

public function clear():void
{
_questionLabel.label = "";

while ( _reponsesContainer.numChildren > 0 )
_reponsesContainer.removeChildAt( 0 );
}

public function remove():void
{
removeChild( _questionLabel );
removeChild( _reponsesContainer );
}
}
}


</code>


Dans cette classe je pense avoir fauté au niveau de la vérification de
la bonne réponse, plus précisément dans le clickHandler :

<code>
private function clickHandler(pEvt:MouseEvent):void
{
if ( QuestionVO( quizzModel.getCurrentVO() ).bonneReponseIndex ==
LabelButton( pEvt.currentTarget ).index )
{
dispatchEvent( new ValueEvent( Event.SELECT, 1);
}
else
{
dispatchEvent( new ValueEvent( Event.SELECT, 0);
}
}
</code>


Puisque je suis dans un view et que je tape directement sur le model,
est ce formellement gênant et si oui quelle autre stratégie aurais-je
pu utiliser ?

**************************************************************************************************************************
Enfin une dernière question existentielle concerne la class
Application elle même et la gestion du score

<code>
package {

import controllers.models.quizz.AddQuestion;
import controllers.models.quizz.ChangeQuestion;
import controllers.models.quizz.ClearQuestion;
import controllers.models.quizz.RemoveQuestion;
import display.QuestionUI;
import display.questionView;
import display.scoreScreen;
import events.EventList;
import flash.events.Event;
import flash.events.KeyboardEvent;
import flash.net.URLRequest;
import flash.ui.Keyboard;
import graphics.layouts.*;
import vegas.display.Root;
import vegas.events.ValueEvent;
import vegas.net.XMLLoader;
import vo.QuestionVO;
import vo.ReponseVO;


[SWF(width="800", height="600", frameRate="31",
backgroundColor="#eeeeeee")]


public class Application extends Root
{
private var _score:int;

public function Application()
{
super();
}


override protected function addedToStage( e:Event = null ):void
{
questionView.addEventListener( Event.SELECT, selectHandler );
addChild( questionView ) ;


controller.add( EventList.ADD_QUESTION , new AddQuestion() ) ;
controller.add( EventList.CHANGE_QUESTION , new
ChangeQuestion() ) ;
controller.add( EventList.CLEAR_QUESTION , new ClearQuestion() ) ;
controller.add( EventList.REMOVE_QUESTION , new
RemoveQuestion() ) ;


quizzModel.load( "quizz.xml" ); // le run est auto à la fin du
chargement XML
}



private function selectHandler(pEvt:ValueEvent):void
{
_score += pEvt.value;

if ( quizzModel.hasNext() )
{
quizzModel.next();
}
else
{
showScoreScreen();
}

}

private function showScoreScreen():void
{
questionView.remove();
scoreScreen.label = "Score = " + _score.toString();
addChild( scoreScreen );
}



}
}

</code>

Je gére ici le score avec un simple integer, à nouveau d'un point de
vue formel, aurait il mieux valu crée un model supplémentaire pour
gérer ce score ?


***************************************************************************************
Enfin une derniere question, j'ai découvert par l'intermédiaire du
tuto la mise en place de singleton d'une manière nouvelle pour moi
(j'étais plus habitué à crée une methode static getInstance() avec un
SingletonEnforcer pour l'instanciation et j'avoue que j'ai été dérouté
par cette méthode qui permet de disposer effectivement d'un singleton
mais qui ne protége pas pour autant la classe de base ? En même temps
c'est peut être le but recherché ?

****************************************************************************************

Voila en tout cas trés bonne impression globale sur ce framework qui
me parait dense, fournit et quasi complet, et en même temps discret et
laissant de la place au developpeur.

Je dirais que pour l'instant le seul petit faible est le manque de
composant UI pret à l'emploi, même si il y a tout ce qu'il faut pour
créer des composants, mais bon c'est vraiment histoire de
pinailler ;-)

Je pense que je vais avoir beaucoup de questions mais je ferais
d'autres post pour ça.

En tout cas merci pour le partage que tu fais de ton framework.
Fred

eKameleon

unread,
Dec 7, 2010, 2:38:08 PM12/7/10
to VEGAS - ECMASCript & ActionScript OpenSource framework
Hello :)

Oui beaucoup de chose mais faut s'y prendre doucement avec tous les
exemples dans trunk/examples et ensuite avec les tutoriaux et pour
finir les exemples dans le framework documentaire AST'r :

http://code.google.com/p/astr/

1 - franchement tu devrais essayer d'utiliser "eden" avec ma classe
vegas.net.EdenLoader et tu verras que tu vas gagner beaucoup de temps
en créant directement tes VO dans le fichier texte externe de
configuration :)

Tu commences par créer un fichier texte avec du contenu au format eden
simple avec dedans ("datas.eden") :

questions =
[
new vo.QuestionVO
(
{
id : 1
text : "Question1" ,
score : 0 ,
responses :
[
new vo.ResponseVO( { text : "oui" } ) ,
new vo.ResponseVO( { text : "oui" } ) ,
new vo.ResponseVO( { text : "peut être ?" } )
]
}
)
];

Ensuite dans ton code AS3 :

public var loader:EdenLoader ;

et dans ton code principal

loader = new EdenLoader() ; // au lieu de XMLLoader

loader.addEventListener( Event.COMPLETE, complete ); // remarque le
nom simple de la méthode

Et dans ta fonction d'écoute "complete" tu as par exemple :

protected function complete( e:Event ):void
{
var questions:Array = loader.data.questions as Array ;
if (questions && question.length > 0)
{
var len:int = questions.length ;
for( var i:int ; i<len ; i++ )
{
model.addVO( questions[i] as QuestionVO ) ;
}
}
}

Vaut mieux séparer la logique de chargement des données du modèle (MVC
ne doit pas connaitre les vues, ne doit pas connaitre la logique de
chargement des données, etc). C'est plus simple ensuite de gérer
justement le loader selon des stratégies différentes et d'éviter la
dépendance inutile avec le modèle.

Tu peux donc créer un singleton de ton modèle avec un singleton simple
(constante de package)

2 - Pour ta première question, franchement pas besoin d'utiliser
l'artillerie lourde (Vector ou map complexe) pour ce que tu cherches à
faire :) Les ValueObjects doivent toujours être très simple :) Si tu
veux les complexifié pense à utiliser des getter/setter mais attention
car par exemple si un jour ton VO tu dois l'utiliser avec AMFPHP et du
remoting tu risques d'avoir des soucis avec la classe Vector (existe
pas dans AMFPHP)

Tuto AMFPHP : https://sites.google.com/site/ekameleon/amf-and-remoting-services

Par contre pour le modèle... faut un id surtout sur les MapModelObject
et je ne vois pas de propriété "id" dans tes VO... c'est un soucis ;)

A noter idem que si tu utilises les ResponseVO uniquement pour un
champ de texte cela ne vaut pas le coup, autant utiliser un simple
champ de texte et pas forcément se prendre la tête à typer la réponse
(plus rapide d'envoyer des strings qu'un objet typé) par contre si tu
commences à avoir des réponses avec d'autres propriétés là cela
devient indispensable.

3 - Comme je te l'ai dit pas besoin pour ta question 2 de dispatcher
un événement, surtout en sortant la logique de chargement dans ton
application principal sans dépendance avec le modèle. A la fin tu peux
directement lancer du coup la fonction qu'il faut ou je te conseille
vivement d'essayer d'utiliser les Signals :)

TUTO Signals :
https://sites.google.com/site/ekameleon/Home/opensource/vegas-opensource-project/vegas-tutorials/signalers-and-receivers

##### Note : la prochaine fois fais plusieurs questions dans plusieurs
posts ;) C'est plus simple... là je passe une plombe à lire toutes tes
questions et cela va être pareil pour toi ;) Pour que je puisse
t'aider efficacement pense à poser des questions précises et si il
faut pense à numéroter le titre des tes questions avec une base de
titre identique pour qu'on s'en sorte mieux ;) ########

Reprenons maintenant l'exemple sur QuestionUI :)

4 - Pour ta classe de base de ton affichage si tu as pas d'animation
cela sert à rien d'utiliser un CoreMovieClip .. autant utiliser un
CoreSprite (ou un Sprite tout simplement si tu cherches pas à utiliser
les logs et les fonctions lock/unlock de la classe CoreSprite). Faut
penser à utiliser les classes qu'il faut selon tes besoins.

Pour des exemples plus complet je te conseille de regarder ma galerie
vidéo dans AST'r (récupère les sources du répertoire AS3) :

http://code.google.com/p/astr/source/browse/#svn/trunk/AS3/trunk/examples/evideo

Avec une liste de thumb remplies par un modèle :)

Avec le controller quand je modèle de VideoVO se rempli :

http://code.google.com/p/astr/source/browse/trunk/AS3/trunk/examples/evideo/trunk/src/evideo/controllers/models/videos/AddVideo.as

et la classe ListValueObject :

http://code.google.com/p/astr/source/browse/trunk/AS3/trunk/examples/evideo/trunk/src/evideo/display/ListValueObject.as

Utilisée pour créer la classe VideoList :

http://code.google.com/p/astr/source/browse/trunk/AS3/trunk/examples/evideo/trunk/src/evideo/display/VideoList.as

Regarde le SWF de l'exemple avant de te lancer dans la lecture du code
(qui utilise de l'ioc pour se mettre à jour... mais bon avec du code
simple cela marche pareil)

#######

5 - Oui les singletons c'est avant tout une instance unique d'une
classe et surtout pas une classe qui est "fermée" :) Si tu veux fermer
ta classe pourquoi pas mais :

* Si tu fermes une classes tu peux plus la tester
* Hériter de cette classe cela devient difficile
* Mettre du getInstance statique dans toutes les classes cela sert à
rien ;) Faut en avoir besoin.. et sérieux on se rend compte à force
que c'est un mauvais design de code (anti-pattern si on maitrise la
notion d'inversion de contrôle avec injection de dépendance via une
fabrique IoC)


Regarde :

http://code.google.com/p/maashaack/wiki/Singleton

Tu peux donc lire ici les bonnes pratiques ECMASCript et AS3 pour
faire du singleton (faut pas penser JAVA mais ActionScript ;))


Remarque : Pour les composants UI tu as tout ce qu'il faut pour faire
les composants de bases d'une application (bouton, progress/scroll
bar, textinput, label) ensuite vu que dans mon taf j'ai toujours des
composants spécifiques à réaliser en fonction de l'ergonomie et la
créativité du graphiste.. j'aime pas me limiter ;)

Pas de soucis pour d'autres questions mais pense à diviser pour régner
et pense à faire plusieurs post pour différentes questions ;)

EKA+ :)
>                                                                                                         id:questi...@id.toString(),

Fred DUFAU

unread,
Dec 8, 2010, 3:49:29 AM12/8/10
to VEGAS - ECMASCript & ActionScript OpenSource framework
merci pour ta réponse détaillée :)


1) XML vs Eden : ok pour eden ca a l'air vraiment sympa ce format,
c'est de toi ?

ok donc pour séparer le chargement du model, c'est logique


2) Vector vs basic Array : effectivement si eden permet de coller un
array directement, plus besoin de se poser la question du format de
données ?

sinon pourquoi un id est obligatoire dans le mapModelObject ?


3) ok pour les signals, j'ai testé la lib de penner et effectivement
si le chargement n'est plus dans le model pas besoin de dispatcher un
event.


4) QuestionUI : c'est effectivement un coreSprite dont j'ai besoin

5) ok sur les singletons, tout ça est logique, faut remettre en cause
mes habitudes ;)


6) Reste une question mais je comprends que mon post n'etait pas
forcement simple à décortiquer (je note pour la prochaine fois de
faire plusieurs questions) :

celle concernant la vérification des resultats aprés un clic sur un
labelbutton.

dans cette premiere version je fais ça dans questionUI :

<code>
private function clickHandler(pEvt:MouseEvent):void
{
if ( QuestionVO( quizzModel.getCurrentVO() ).bonneReponseIndex
== LabelButton( pEvt.currentTarget ).index )
{
dispatchEvent( new ValueEvent( Event.SELECT, 1);
}
else
{
dispatchEvent( new ValueEvent( Event.SELECT, 0);
}
}
</code>

C'est ce qui me parassait le plus simple mais ca me parait bizarre
d'appeler le modele dans la vue et de dispatcher ensuite un event vers
Application pour incrémenter le score ?

De plus je subodore que j'aurais eventuellement pu crée un model
supplémentaire pour gérer le score ?

Qu'en penses tu ?



Merci pour tes réponses !

a+
Fred

eKameleon

unread,
Dec 8, 2010, 4:01:52 AM12/8/10
to VEGAS - ECMASCript & ActionScript OpenSource framework
Hello :)

1 - eden c'est à la base un format d'échange de donnée mis en place
par Zwetan (avec qui je bosse sur le noyau de VEGAS avec le projet
Maashaack sur google code avec les packages core+system+graphics de
VEGAS) et maintenant je suis également auteur sur la library dans
sytem.eden :) On peut un peu le comparer au format JSON mais avec
beaucoup plus de fonctionnalités et surtout pensé ECMAScript
(standard) et pas simplement JAVAScript comme JSON ;)

2 - Pour le MapModelObject quand tu enregistres les value objects
dedans avec addVO il stocker les vo avec une clé et une valeur dans
une ArrayMap. Du coup il a besoin d'un identifiant unique pour chaque
VO :)

exemple :

model.addVO( new VideoVO( { id : 1 , url : "toto.flv" } ) ) ;

trace( (model.getVO( 1 ) as VideoVO).url ) ;

Dans une ArrayMap ou HashMap tu peux mettre une clé de tout type, donc
l'id de ton VO peut être un String, un Number, etc...

Si tu ne mets pas de id sur les ValueObjects tu auras null pour chacun
d'eux et cela ne va pas aller du tout surtout si par la suite tu
cliques sur un bouton ou que tu as besoin des informations d'un VO en
particulier en fonction de son identifiant :)

3 - Tu peux en dispatcher un mais en général pas besoin de modèle
événementiel pour un truc très simple :) un callback sur une fonction
peut suffire.

4 - Ok :)

5 - Oui toujours ;) Justement VEGAS change tout doucement depuis ces
dernières versions et je vire toute sorte d'implémentation oldschool
que je mettais en place en AS1/AS2 et qui depuis n'ont plus lieu
d'être.

6 - Regarde mes exemples avec eVideo et le lecteur de vidéo avec une
liste de thumb pour voir comment je cable tout cela de mon côté :)
Peut t'aider pour piger la bonne logique. Tu verras que je cable dans
une HashMap pour une vue donnée (bouton, label, etc.) le ValueObject
qui lui correspond et du coup quand je clique sur un des boutons du
pseudo menu je peux retrouver en fonction du event.target la référence
du VO qui lui correspond sans rendre dépendant le display avec le
ValueObject ;)

Sinon que la vue appelle le modèle c'est pas un soucis, c'est surtout
le modèle qui ne doit pas connaitre la vue en ayant une dépendances
aux vues dans sa propre classe.

Oui pour le score c'est pas mal d'avoir un modèle à part :) Un modèle
qui sert à tout ... faut s'en méfier ;) Diviser pour régner !!!

EKA+ :)

Fred DUFAU

unread,
Dec 8, 2010, 4:06:44 AM12/8/10
to VEGAS - ECMASCript & ActionScript OpenSource framework
ok, je potasse tout ça et je me permettrai peut etre de reposter une
nouvelle version de ce petit quizz.

Merci à toi.
a+ Fred
Reply all
Reply to author
Forward
0 new messages