un Giff animé dans une appli Cocoa

ClicCoolClicCool Membre
14:34 modifié dans API AppKit #1
Bonjours à  tous,

Je suis pas du tout, mais alors pas du tout calé sur le coté Graphique de cocoa.

Mais voilà , j'aimerais bien placer un gif animé dans la fenêtre de document.
Juste en élément "décoration", pas en lien direct avec le modelView quoi.

ça peut se faire aussi simplement que l'ajout de celui-ci:  :spot: dans ce post ?


Merci pour vos lumières :)

Réponses

  • BruBru Membre
    14:34 modifié #2
    Pour l'animation du gif, t'es obligé de passer par une routine qui va égrainer puis afficher chaque trame qui compose l'animation...

    Donc à  mon avis, il te faut une NSImageView côté IB, et une méthode pour l'affichage de la trame côté Xcode dans le controleur (sans parler d'une modif mineure du awakeFromNib...).

    .
  • ClicCoolClicCool Membre
    14:34 modifié #3
    Merci Bru,

    Il me faut donc un NSTimer ou plutot utiliser NSThread ?

    Quelle voie me conseilles tu pour une simple patite animation décorative ?
  • Eddy58Eddy58 Membre
    14:34 modifié #4
    Un NSTimer doit suffire, il se charge d'appeler la méthode d'affichage et hoopp :)
  • MalaMala Membre, Modérateur
    14:34 modifié #5
    Sinon, sous 10.3 uniquement : Glisser son GIF dans un NSImageView et activer l'option "Animates" dans les attributs.
  • BruBru Membre
    14:34 modifié #6
    dans 1097333248:

    Merci Bru,
    Il me faut donc un NSTimer ou plutot utiliser NSThread ?
    Quelle voie me conseilles tu pour une simple patite animation décorative ?


    Bon, vraiment parce que c'est toi !

    dans le .h du controleur :
    #import &lt;Cocoa/Cocoa.h&gt;<br /><br />@interface controleur : NSObject<br />{<br />      IBOutlet id img;<br />      NSBitmapImageRep *rep_gif;<br />      NSTimer *tim;<br />}<br /><br />- (void)awakeFromNib;<br />- (void)afficheTrame:(NSTimer *)timer;<br /><br />@end<br />
    


    et dans le .m :
    @implementation controleur<br /><br />- (void)awakeFromNib<br />{<br />      double secondes;<br /><br />      // récupération de la NSImageRep du gif.<br />      rep_gif=[NSBitmapImageRep imageRepWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@&quot;spotting&quot; ofType:@&quot;gif&quot;]];<br />      rep_gif=[rep_gif retain];<br /><br />      // récupération du délai en seconde entre chaque trame du gif.<br />      secondes=[[rep_gif valueForProperty:@&quot;NSImageCurrentFrameDuration&quot;] doubleValue];<br /><br />      // création d&#39;un timer pour affihcage de chaque trame du gif.<br />      tim=[[NSTimer scheduledTimerWithTimeInterval:secondes<br />                              target:self<br />                              selector:@selector(afficheTrame:)<br />                              userInfo:nil<br />                              repeats:YES] retain];<br />}<br /><br />- (void)afficheTrame:(NSTimer *)timer<br />{<br />      NSImage *image;<br />      int trame;<br /><br />      // récupération de la trame en cours d&#39;affichage et incrémentation.<br />      trame=[[rep_gif valueForProperty:@&quot;NSImageCurrentFrame&quot;] intValue] + 1;<br />      if (trame&gt;=[[rep_gif valueForProperty:@&quot;NSImageFrameCount&quot;] intValue]) trame=0;<br /><br />      // récupération d&#39;une trame parmis toutes celles du gif.<br />      [rep_gif setProperty:@&quot;NSImageCurrentFrame&quot; withValue:[NSNumber numberWithFloat:trame]];<br /><br />      // création d&#39;une NSImage à  partir de la trame du gif.<br />      image=[[NSImage alloc] initWithSize:[rep_gif size]];<br />      [image lockFocus];<br />      [rep_gif draw];<br />      [image unlockFocus];<br />      [img setImage:image];<br />      [image release];<br />}<br /><br />@end<br />
    


    .
  • muqaddarmuqaddar Administrateur
    14:34 modifié #7
    Y'a des privilégiés ici.  8)

    En plus, les mecs doués trouvent encore le moyen de travailler le samedi pour progresser.
    Désespérant. :crackboom:-
  • ClicCoolClicCool Membre
    octobre 2004 modifié #8
    dans 1097335427:

    Bon, vraiment parce que c'est toi !


    merci Bru  :-* ::)

    Sans toi j'aurais mis des heures à  trouver la bonne façon de gérer ce type d'image et tout et tout ... ;)

    Mais j'abuse peut-être mais j'ai des erreurs qui me font penser que peut-être mes gif ne sont pas au bon format ?

    à  chaque "valueForProperty" je reçois un:
    -[NSCFArray valueForProperty:]: selector not recognized
    


    On dirait que je n'ai qu'un array d'image et pas un dictionaire ou autre formormat plus élaboré :(

    peux tu me dire si mon gif animé qui me sert de test est convenable pour ce code ?
    Je le t'atache à  mon code en bas (mais c'est un des émoticones d'O.C. mais je l'ai peut-être pas récupéré dans le bon format, faut que je le convertisse ?)

    [Fichier joint supprimé par l'administrateur]
  • muqaddarmuqaddar Administrateur
    14:34 modifié #9
    J'ai ouvert ce gif sans pb dans ImageReady.
    Je pense que le problème ne vient pas de l'image.
  • ClicCoolClicCool Membre
    14:34 modifié #10
    dans 1097348205:

    J'ai ouvert ce gif sans pb dans ImageReady.
    Je pense que le problème ne vient pas de l'image.

    Merci Oxitan, (à  en juger par ton avatard t'as pris un sérieux coup de vieux ! ;D )

    En effet toutes les images sont bien présentes semble-t-il.

    Mais peut être que le code attend un NSBitmapImageRep plus complet.

    C'est vrai aussi qu'à  la compil j'ai déjà  un:
    GiffAnimCtrl.m:9: assignment from incompatible pointer type<br />
    


    pour la ligne:
    rep_gif=[NSBitmapImageRep imageRepsWithContentsOfFile:gifPath];<br />
    


    ???
  • BruBru Membre
    14:34 modifié #11
    Je comprends pas trop car chez moi ça marche...

    Je suspecte un problème de fuite de mémoire...

    Je te joins le projet XCode que j'ai fait pour tester mon code avant de le poster.

    .

    [Fichier joint supprimé par l'administrateur]
  • ClicCoolClicCool Membre
    14:34 modifié #12
    ah ben oui,
    ton code marche impec :D

    C'est de mon coté que ça foire :(

    Merci Bru :)

    Je poursuis mes investigations pour comprendre mon pb avec tous les éléments qu'il faut maintenant  :D
  • ClicCoolClicCool Membre
    14:34 modifié #13
    Eh bien j'ai passé du temps à  essayer d'éplucher mon code en comparaison avec le tiens sans rien trouver :(

    après n essais, de guerre lasse, j'ai sauvagement fait un copier coller entre ton .m et le mien et ... ça marche impec. !  :o

    Le pb c'est que du coup je suis incapable de dire ce qui ne collait pas dans mon code, ce qui est bien domage pour ce qui est de mon apprentissage :(

    Mais bon MERCI BRU,  :trinque:
    c'est drolement chouette ces gifs animés  :spot:
    Et en plus j'ai tout de même appris pas mal de trucs sur cet aspect de la programmation Cocoa que je n'avais jamais touché encore.

    Promis, demain je réécrit tout depuis zéro et décortique encore une fois tout ça, et si je trouve mon erreur initiale j'en fait profitter le forum. ;)
  • BruBru Membre
    14:34 modifié #14
    dans 1097357513:

    Le pb c'est que du coup je suis incapable de dire ce qui ne collait pas dans mon code, ce qui est bien domage pour ce qui est de mon apprentissage :(


    Peux tu me filer tes 2 fichiers (le m et le h) pour que j'y jette un oeil ?

    .
  • ClicCoolClicCool Membre
    14:34 modifié #15
    dans 1097359042:

    dans 1097357513:

    Le pb c'est que du coup je suis incapable de dire ce qui ne collait pas dans mon code, ce qui est bien domage pour ce qui est de mon apprentissage :(


    Peux tu me filer tes 2 fichiers (le m et le h) pour que j'y jette un oeil ?

    .


    Oh en voilà  un avatard sympa !
    Salut Bru :)

    Le pb c'est que c'est dans le .m que se situait le pb et qu'après mon copier-coller + build and Run il ne reste rien de mon fichier d'origine.

    Je vais essayer de reprendre à  zéro et peut-être de provoquer la même erreur, si j'y parvien je t'envoies les fichiers car j'aimerai bien comprendre ???

    Pour l'heure ça marche impec  :brule:

    Merci encore Bru :D
  • ClicCoolClicCool Membre
    14:34 modifié #16
    Salut :)

    Une précision au sujet de ce sympathique code.

    Le timer ne sera jamais désaloué et, lors de la déallocation de la vue connectée, le timer risque de planter l'Appli.

    Le mieux semble donc d'appeler [timer invalidate] lors de la déallocation de la vue ou de son controller. ::)
  • Eddy58Eddy58 Membre
    14:34 modifié #17
    Oui tout à  fait ClicCool, de plus un retain sur le timer est inutile, car quand il est inséré dans la runloop, il se retain automatiquement, ensuite le invalidate: le vire de la runloop et du coup le release.
    <br /> tim=[NSTimer scheduledTimerWithTimeInterval:secondes<br />                                      target:self<br />                                       selector:@selector(afficheTrame:)<br />                                 userInfo:nil<br />                                      repeats:YES];<br />
    


    Autre chose, l'instance rep_gif compte un retain mais pas de release, donc fuite mémoire. Méthode dealloc: a ajouter
    <br />-(void)dealloc<br />{<br />    [tim invalidate];<br /> [rep_gif release];<br />}<br />
    


    Là  le code est plus propre :)
  • ClicCoolClicCool Membre
    14:34 modifié #18
    dans 1097593437:

    Méthode dealloc: a ajouter
    <br />-(void)dealloc<br />{<br />   [tim invalidate];<br /> [rep_gif release];<br />}<br />
    


    Là  le code est plus propre :)


    Pas encore tout à  fait Eddy ;)

    Ta méthode dealloc risque fort de ne jamais être appelée  ::)

    En effet il me semble que dans la mesure ou la "target" du timer est ici self (le controller qui l'a crée) la méthode dealloc ne sera jamais appelée car un Timer retient sa cible !

    Dans un shéma où on a un Contrôleur (qu'on appèlera par exemple DayZunPau) qui instancie un contrôleur de gif animé (qu'on appèlera Catherine ou plus simplement GifAnimCtrl) (contenant le code de Bru)

    Le mieux est sans doute d'implémenter au gifAnimCtrl une méthode invalidateTimer en plus de dealloc

    <br />-(void) invalidateTimer {<br />       [tim invalidate];<br />}<br />-(void)dealloc {<br />       [rep_gif release];<br />}<br />
    


    Le contrôleur général DayZunPau devra d'abord appeler [monGifAnimCtrl invalidateTimer]
    Le timer enverra alors un release à  sa target (ici l'instance de gifAnimCtrl) qui alors seulement verra sa méthode dealloc appelée, libérant enfin le rep_gif.

    Me gourres-je ?  ::)
  • Eddy58Eddy58 Membre
    14:34 modifié #19
    Oui c'est vrai ClicCool ! :)
    Ce détail fort important ne m'avait même pas effleuré l'esprit.... ::)
    Je pense que tu as raison, dans ce cas ta théorie est pas mal, dans ton contrôleur DayZunPau (quel drôle de nom ;)), tu envoies à  quel moment le message au sous-contrôleur pour dévalider le timer ? Tu fais un appel direct dans la méthode dealloc: ou bien il faut détecter la fermeture de l'appli ?
  • Eddy58Eddy58 Membre
    14:34 modifié #20
    En faites ce type d'instance NSTimer est autoreleasée, tout comme des constructeurs de commodités en faites, d'après la documentation. Donc à  la fermeture de l'appli, le timer se désalloue, tout simplement. :)

    In Objective-C, there are three ways to create a timer. The following two class methods automatically register the new timer with the current NSRunLoop object in the default mode (NSDefaultRunLoopMode).

    scheduledTimerWithTimeInterval:invocation:repeats:


    scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:



    The following two class methods create timers that you may register at a later time by sending the message addTimer:forMode: to an NSRunLoop object.

    timerWithTimeInterval:invocation:repeats:


    timerWithTimeInterval:target:selector:userInfo:repeats:



    Finally, you can allocate an NSTimer object yourself and send it an initWithFireDate:interval:target:selector:userInfo:repeats: message. This method allows you to specify an initial fire date independently of the repeat interval. Unlike timers created with the previous methods, this timer instance is not autoreleased, so you are responsible for releasing it when you are done with it. Note that run loops retain their timers, so you can release the timer after you have added it to a run loop.
  • ClicCoolClicCool Membre
    octobre 2004 modifié #21
    ben oui mais il y a une boucle quand même.

    Le timer est peut-être autoreleasé mais si son controller n'est jammais releasé parce que l'objet qu'il a créé le retient, on va au devant de pb.
    Et puis autoreleasé oui et non car, si rien de le retient, après une boucle du runLoop adieu Berthe (une copine elle aussi ;) ) ???
    En fait je crois que le timer est retenu par le runLoop lui même et pas par l'objet qui l'a créé.
    D'autant qu'il ne me parrait pas certain que le Timer soit jamais désalloué une fois qu'il est dans le runLoop, il semble simplement désactivé et relache sa cible ... mais bon les runLoop ne sont pas simple à  analyser et je ne suis pas certain de ce point. :-\

    Par ailleurs mon petit usage du code de Bru (saluons le encore au passage ;) ) est affecté à  des documents (dans la toolBarr). Donc ce n'est pas quand l'appli quitte mais à  chaque fermeture d'un document qu'il faut veiller à  la bonne déallocation du timer qui, dans mon projet se passe bien depuis que j'ai ajouté le bout de code appelé par mon contrôleur général DayZunPau. Un bon gras finalement ce contrôleur DayZunPau ;) ;D
  • Eddy58Eddy58 Membre
    14:34 modifié #22
    Oui.....par sécurité il vaut mieux faire comme tu dis, avec un contrôleur DayZunPau...on n'est jamais assez prudent ;)
    Mais les timers sont des objets assez particuliers de par leur intégration dans la runloop, et quelque chose me dis qu'ils ont été conçus dans la même optique de fonctionnement que les constructeurs de commodités, c'est à  dire ne pas se préoccuper de leur sort :)
    Je vais partir à  la recherche d'exemples avec NSTimer pour qu'on en sache un peu plus, car ça me paraà®t louche cette histoire de contrôleur DayZunPau.  :D ;D
  • Eddy58Eddy58 Membre
    14:34 modifié #23
    Bon me revoici avec du nouveau : :P
    Voici ce que j'ai glané sur CocoaDev.com :


    Here are a couple of rules that might help you with NSTimer:

    A timer retains the target and userInfo objects.
    A timer is automatically retained by the run loop when scheduling it.
    If a timer is not set to repeate, it will automatically invalidate itself upon firing.
    A timer is released from the run loop when calling invalidate.
    A timer releases the target and userInfo objects when calling invalidate.

    In other words, if you release a repeating timer without invalidating it, it will continue to repeate because the run loop is retaining it. However, if you don't want to stop the timer before the application quits, or if the timer is non-repeating, you can release it after scheduling it without calling invalidate.

    La dernière phrase parle de notre cas : "Cependant, si vous ne voulez pas stopper le timer avant que l'application ne quitte, ou si le timer ne se répète pas, vous pouvez le libérer après l'avoir "schéduler" sans appeler invalidate."
    -> Notre timer est schédulé si je puis dire ;), et l'autorelease s'occupe de sa libération.

    Il y a aussi le tutoriel Animation graphique avec Cocoa, Partie 1, sur O'Reilly. En regardant le projet complet, le timer est mis dans la runloop sans le stocker dans une variable :

    <br />   [NSTimer scheduledTimerWithTimeInterval:0.04<br />               target:self <br />              selector:@selector(stepAnimation:) <br />               userInfo:nil <br />             repeats:YES];<br />
    


    Et l'animation ne prend fin que quand on quitte l'appli, comme dans notre cas. Donc avec ces deux éléments, je pense vraiment de plus en plus fort qu'il est inutile de gérer l'invalidation du NSTimer à  la sortie de l'appli.  8) :D ;D
  • ClicCoolClicCool Membre
    14:34 modifié #24
    Tu ne m'as pas lu assez attentivement on dirait ;)

    Mon timer devient invalide avec une référence sur une vue qui n'existe plus à  chaque fois que je ferme un document, pas seulement en quitant l'application :(

    le gif animé est dans LES fenêtres de document ;)

    Je suis obligé d'invalider le timer du document AVANT de désallouer sa fenêtre ... sino plantage sur appel d'une image qui a été désallouée :(
  • Eddy58Eddy58 Membre
    14:34 modifié #25
    Désolé ClicCool, ma réponse était dans le cas d'une simple application :)
    Dans ton cas, en document-based, c'est en effet plus délicat, et ta méthode est bien trouvée en effet. 
  • ClicCoolClicCool Membre
    14:34 modifié #26
    :D :trinque:
  • cbrandtcbrandt Membre
    14:34 modifié #27
    et le [super dealloc] dans
    <br />-(void)dealloc {<br />       [rep_gif release];<br />}<br />
    

    ???

    pas glop ! ;D
  • ClicCoolClicCool Membre
    14:34 modifié #28
    dans 1097651986:

    et le [super dealloc] dans
    <br />-(void)dealloc {<br />       [rep_gif release];<br />}<br />
    

    ???

    pas glop ! ;D


    T'as raison c'est pas glop pas glop ça.
    J'aurais du ajouter un .../... dans le post pour figurer le coté "extrait" de code plutot que de fermer l'accolade.  :-\

    Merci de ta précision cbrandt   :D
Connectez-vous ou Inscrivez-vous pour répondre.