gérer un script comme une coroutine

L'auteur de ce sujet a trouvé une solution à son problème.
Auteur du sujet

Bonjour,

dans le cadre d'une application de test, j'aimerais avoir un script lua qui « gère » une application Qt. Le problème se pose lorsque je veux que le script lua attende la fin d'une fonction.

Pour le moment mon script ressemble à :

1
2
3
4
5
6
function main()
    local bgID = load_image("image.png")
    show(bgID)
    fade_out(bgID, 600)
    fade_in(bgID, 600)
end

L'application est une simple QMainWindow et les images affichées le sont via des QLabel.

Dans ce script d'exemple, j'aimerais que fade_out se termine entièrement avant que fade_in ne soit exécutée.Les fonctions fade_xx appellent cette méthode :

1
2
3
4
5
6
7
8
9
void GameWidget::setPropertyAnimation(QObject * target, QByteArray property, double start, double end, int duration)
{
    QPropertyAnimation * anim = new QPropertyAnimation(target, property, this);
    anim->setStartValue(start);
    anim->setEndValue(end);
    anim->setDuration(duration);
    anim->start(QAbstractAnimation::DeleteWhenStopped);
    connect(anim, SIGNAL(finished()), this, SLOT(animationFinished()));
}

Donc après l'appel à fade_out, il faudrait que le script lua soit mis en pause et dans le slot animationFinished j'appelle une fonction de l'API lua pour reprendre son exécution. (et si possible, sans avoir à gonfler mon script avec des coroutine.yield() de partout.)

Merci d'avance.

ps: sinon, s'il y a un langage qui permet ça (autre que de coder son propre interprète bytecode).

+0 -0
Staff

Cette réponse a aidé l'auteur du sujet

Ha oui pardon (ça fait un moment que je n'ai pas utilisé Qt). Si tu peux surcharger animationFinished tu peux récupérer la fin de l’animation. Cependant le plus simple reste d'utiliser le fais que la durée de l'animation est fixé en paramètre. les fade_xxx doivent spécifier les duration de setPropertyAnimation (ou utiliser une valeur par defaut). Tu dois donc pouvoir déterminer la durée total. Et donc un sleep de la même durée devrait correspondre.

+0 -0
Auteur du sujet

J'y ai pensé, ou bloquer dans setPropertyAnimation via :

1
2
3
QEventLoop loop;
connect(anim, SIGNAL(finished()), &loop, SLOT(quit()));
loop.exec();

mais j'ai déjà eu des problèmes avec cette technique (par exemple, quitter alors que le script n'est pas terminé, this devenant invalide). Je vais testé avec sleep, je verrais bien ce que ça donne (même si ne sera pas autant précis).

merci.

edit: lua est dans le thread principal, donc appeler sleep pause tout (l'animation ne se lance qu'une fois sleep terminée) :(

Édité par minirop

+0 -0
Auteur du sujet

j'utilise juste l'API C de lua en lui ajoutant quelques fonctions pour faire des actions (créer une image, afficher l'image, bouger l'image, etc.). Pour exécuter le script, je fais juste lua_dofile("script.lua"); lua_pcall("main");, raison du freeze du thread principal lors de l'appel à sleep.

je viens de trouver une solution parfaitement immonde en utilisant un thread win32 (dans setPropertyAnimation j'appelle SuspendThread et dans animationFinished j'appelle ResumeThread). ça marche pas mal pour main, mais j'ai pas encore trouvé de façon d'appeler un callback (je stockes des ref vers des fonctions lua pour réagir à certains events perso, comme "la souris survole l'image") dans le thread en question.

+0 -0

Tu as déjà essayé d'utiliser les fonctions C lua_yield et lua_resume ?

Sinon, c'est peut-être crado mais il y a moyen d'appeler coroutine.yield depuis l'API C, de façon classique avec un truc du genre:

1
2
3
lua_getglobal(L,"coroutine");
lua_getfield(L, -1, "yield");
lua_call(L, -1, 0, 1);

Quoi qu'il en soit c'est sûrement moins crado que du CreateThread/__beginthread en Win32 directement.

IL me vient une idée: peut-être que le mieux, en fait, ce serait de changer complètement de fonctionnement: ne pas exécuter lua dans le thread principal, et utiliser un système de passation de messages. Comme ça tu peux t'en sortir avec un simple sleep sans pour autant tout bloquer.

Par exemple de mon côté, programmant en Win32 directement, je fais ceci (C++11 uniquement) :

1
2
3
4
5
6
std::function<void(void)> f = []{
// Ceci s'exécute toujours dans le thread principal, quelque soit le thread où je me trouvais avant.
// Je peux en profiter pour mettre à jour la GUI comme je veux sans risquer des comportements étranges et insolubles
};
SendMessage(hwnd, WM_RUNPROC, 0, &f);
// Ici je peux sleep si je veux, on s'en fout on n'est pas dans le thread principal

ET puis dans la boucle de messages de windows :

1
2
3
4
5
case WM_RUNPROC: {
//typedef std::function<void(void)>* LPUSERPROC;
LPUSERPROC f = (LPUSERPROC)(lParam);
(*f)(); // exécution!
}break;

SendMessage est garanti synchrone (voir MSDN), donc aucun risque de détruire des objets avant qu'ils ne soient utilisés.

Je ne peux pas te renseigner sur les équivalents QT, je n'utilise pas ce framework; quoi qu'il en soit tu dois très probablement pouvoir créer tes propres types d'évènements et les traiter, si QT ne propose pas déjà un truc similaire à mon example.

Sinon concernant des langages de script alternatifs, tu peux peut-être regarder [AngelScript] (http://www.angelcode.com/). En gros résumé, c'est 3 fois plus lent et plus lourd que lua, mais ça a l'avantage de supporter le multithreading (plusieurs context, équivalent des state, peuvent s'exécuter simultanément), et c'est vraiment orienté C++ (l'API principale est en C++ et pas en C).

Édité par QuentinC

Ma plateforme avec 23 jeux de société classiques en 6 langues et 13000 joueurs: http://qcsalon.net/ | Apprenez à faire des sites web accessibles http://www.openweb.eu.org/

+0 -0
Vous devez être connecté pour pouvoir poster un message.
Connexion

Pas encore inscrit ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte