Affichage en cours de calcul

GGGG Membre
21:00 modifié dans API AppKit #1
Bonjour à  tous,
je cherche à  faire une vue qui permet d'afficher des vignettes d'image en cours de lecture (en gros comme l'application phoenix http://blyt.net/phxslides/).
Savez vous comment je peux procéder ?
Car tous les essais que j'ai fais, stockent tout en mémoire et affiche une fois fini.

Jerome
«1

Réponses

  • Philippe49Philippe49 Membre
    21:00 modifié #2
    rafraà®chir l'interface graphique dès qu'une vignette est créée

    ou plus compliqué un thread par création de vignette comprenant l'affichage
  • UniXUniX Membre
    21:00 modifié #3
    Oui, entre chaque affichage, force le rafraà®chissement de ta vue avec - (void)display de NSView.
  • Philippe49Philippe49 Membre
    21:00 modifié #4
    ou si possible -(void)setNeedsDisplay:(BOOL)
  • UniXUniX Membre
    21:00 modifié #5
    Oui bien sûr, mais je pense que c'est ce que GG utilise, et comme il doit mettre à  jour plusieurs fois sa vue au sein d'un même event, si il utilise setNeedsDisplay, elle ne se met à  jour qu'une seule fois à  la fin de l'éxécution de l'event.
  • Philippe49Philippe49 Membre
    21:00 modifié #6
    Sight
  • MalaMala Membre, Modérateur
    21:00 modifié #7
    Comme suggéré par Philippe, le mieux c'est de créer un thread de chargement pour tes vignettes (un seul thread doit suffir) afin que le chargement se fasse en parallèle de la gestion des événements de l'interface graphique.

    C'est assez simple à  mettre en oeuvre:

    <br />- (void)ChargerVignettes:(NSString*)repertoireACharger<br />{<br />&nbsp; &nbsp; // On lance le thread qui exécuter une méthode de notre classe...<br />&nbsp; &nbsp; [NSThread detachNewThreadSelector:@selector(threadChargerVignettes:) <br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;  toTarget:self <br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;  withObject:repertoireACharger];<br />}<br />//-----------------------------------------------------------------------------<br /><br />- (void)threadChargerVignettes:(NSString*)repertoireACharger<br />{<br />&nbsp; &nbsp; // Un thread doit toujours avoir un pool d&#39;autorelease<br />&nbsp; &nbsp; NSAutoreleasePool *subPool = [NSAutoreleasePool new];<br />&nbsp; &nbsp; <br />&nbsp; &nbsp; // Boucle de chargement des vignettes<br />&nbsp; &nbsp; // A chaque nouvelle vignette, on fait un setNeedsDisplay mais<br />&nbsp; &nbsp; // jamais de display directement dans le thread car l&#39;affichage <br />&nbsp; &nbsp; // graphique est obligatoirement géré par le thread principal <br />&nbsp; &nbsp; // de l&#39;application.<br />&nbsp; &nbsp; // ....<br /><br />&nbsp; &nbsp; <br />&nbsp; &nbsp; // Un fois les vignettes chargées, on supprime les objets autorelease<br />&nbsp; &nbsp; // en supprimant le pool<br />&nbsp; &nbsp; [subPool release];<br />}<br />//-----------------------------------------------------------------------------<br />
    


    En mettant, un test sur un BOOL dans la boucle de traitement on peut très facilement stopper le chargement des vignettes si on change par exemple de répertoire en cours de route. On a donc moyen de faire une appli beaucoup plus réactive pour l'utilisateur.  :p D'où l'intérêt des threads pour toutes les tâches de chargement en arrière plan.

  • schlumschlum Membre
    21:00 modifié #8
    dans 1189347835:

    Oui, entre chaque affichage, force le rafraà®chissement de ta vue avec - (void)display de NSView.


    Rhâââ, bouh !! Surtout pas !

    On ne fait pas exécuter de longs calculs par le thread principal et surtout en faisant rafraà®chir de cette manière !

    On crée un thread dédié pour le calcul, et dans ce thread on fait rafraà®chir par le thread principal avec ça :

    - (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait
    
  • GGGG Membre
    21:00 modifié #9
    heu désolé les gars, mais vous m'avez paumé la :).

    Dois je lancer un thread pour chaque vignette donc le calcul est le suivant :
    - chargement de l'image (NSimage)
    - modification pour la mettre en vignette par copie
    - release de la première image.
    - ajout de ce NSImage à  une NSImageCell
    - ajout de cet NSImageCell à  une NSMatrix (d'ailleurs comment dois je faire pour la rattacher à  ma vue ? ).

    Le rafraichissement s'effectue comment de cet manière ?

  • schlumschlum Membre
    21:00 modifié #10
    dans 1189423224:

    heu désolé les gars, mais vous m'avez paumé la :).

    Dois je lancer un thread pour chaque vignette donc le calcul est le suivant :
    - chargement de l'image (NSimage)
    - modification pour la mettre en vignette par copie
    - release de la première image.
    - ajout de ce NSImage à  une NSImageCell
    - ajout de cet NSImageCell à  une NSMatrix (d'ailleurs comment dois je faire pour la rattacher à  ma vue ? ).

    Le rafraichissement s'effectue comment de cet manière ?




    Ca dépend... Si tu veux que les vignettes soient traitées en parallèle oui, il faut un thread pour chacune ; si les traiter l'une après l'autre suffit, un seul thread suffit.

    Le rafraà®chissement s'effectue en appelant une méthode de rafraà®chissement via "performSelectorOnMainThread"
  • 21:00 modifié #11
    Il me semblait que les theads ne sont pas des trucs à  utiliser à  la légère, du moins il est difficile d'intéragir avec l'interface, car ça créée parfois des bugs.. J'ai faux où ça a changé ?  :crackboom:-

  • UniXUniX Membre
    21:00 modifié #12
    OK Schlum.
    Tu utilises quoi comme méthode de rafraichissement avec performSelectorOnMainThread ?
  • schlumschlum Membre
    21:00 modifié #13
    dans 1189458023:

    Il me semblait que les theads ne sont pas des trucs à  utiliser à  la légère, du moins il est difficile d'intéragir avec l'interface, car ça créée parfois des bugs.. J'ai faux où ça a changé ?  :crackboom:-


    Non, il faut juste interagir avec l'interface via "performSelectorOnMainThread"
  • schlumschlum Membre
    21:00 modifié #14
    dans 1189492167:

    OK Schlum.
    Tu utilises quoi comme méthode de rafraichissement avec performSelectorOnMainThread ?


    Ben juste des appels à  des méthodes qui font ce qu'il faut...
  • UniXUniX Membre
    21:00 modifié #15
    Oui, mais dans le cas du rafraà®chissement d'une vue, tu utilises setNeedsDisplay: ou display ?
  • schlumschlum Membre
    21:00 modifié #16
    dans 1189504491:

    Oui, mais dans le cas du rafraà®chissement d'une vue, tu utilises setNeedsDisplay: ou display ?


    Toujours "setNeedsDisplay" Et même souvent "setNeedsDisplayInRect"...
    Comme a c'est l'application qui choisit quand il est adéquat de la dessiner.
  • MalaMala Membre, Modérateur
    21:00 modifié #17
    A noter que "setNeedsDisplay" ne fait que mettre un flag pour le rafraà®chissement. On peut considérer cette appel comme "thread safe" et donc l'appeler directement dans son thread.
  • BruBru Membre
    21:00 modifié #18
    dans 1189683506:

    A noter que "setNeedsDisplay" ne fait que mettre un flag pour le rafraà®chissement. On peut considérer cette appel comme "thread safe" et donc l'appeler directement dans son thread.


    setNeedsDisplay: n'est pas totalement thread-safe selon la doc Apple (lire ici).
    Il y est clairement mentionné qu'il faut faire exécuter cette méthode dans le thread principal, comme expliqué par Schlum.

    .
  • ChachaChacha Membre
    21:00 modifié #19
    dans 1189684747:

    Il y est clairement mentionné qu'il faut faire exécuter cette méthode dans le thread principal, comme expliqué par Schlum.


    Il existe une methode peu connue (enfin, moi je l'ai découverte récemment) :
    performSelectorOnMainThread

    C'est toujours bon à  rappeler

    +
    Chacha
  • schlumschlum Membre
    21:00 modifié #20
    Ben oui, c'est de cette méthode qu'on parle depuis le début du sujet  :P

    Et il vaut mieux la connaà®tre si on veut gérer une interface graphique via des threads !
  • ChachaChacha Membre
    21:00 modifié #21
    dans 1189756042:

    Ben oui, c'est de cette méthode qu'on parle depuis le début du sujet  :P

    Ah ben oui... désolé j'avais pas lu ce qui précède, j'ai juste réagi au dernier post.
    :o
  • GGGG Membre
    21:00 modifié #22
    Hello,
    je suis toujours bloqué sur ce problème.
    Rien à  faire, je n'arrive pas à  afficher les images en cours de chargement.
    En gros l'algo est :
    - je récupère les paths des images dans mon controller.
    - je passe à  ma classe nsmatrix cet array contenant les paths.
    - ma nsmatrix récupère la nsimagecell courante
    - chargement de l'image dans nsimagecell, puis à  la fin

    <br />[self performSelectorOnMainThread:@selector(setNeedsDisplayThreaded:)&nbsp; 		withObject:[NSValue valueWithRect:[self cellFrameAtRow:line column:column]] waitUntilDone:NO];<br />// ou line et column pointe sur la bonne nsimagecell <br /><br />
    


    Oû setNeedsDisplayThreaded est
    <br />- (void)setNeedsDisplayThreaded:(NSValue*)v&nbsp; {<br />[self setNeedsDisplayInRect:[v rectValue]];<br />}<br />
    


    Puis on continue sur l'image suivante.

    Ou est le problème ?
  • schlumschlum Membre
    21:00 modifié #23
    Comment ça "puis on continue sur l'image suivante" ?
    Le but c'était pas de lancer le chargement des images dans des threads à  part ?

    Tu lances bien un thread quand même ?
  • GGGG Membre
    septembre 2007 modifié #24
    En fait lorsque je fais :
    <br />[NSThread detachNewThreadSelector:@selector(loadImageIntoOYAMatrixThreaded:) <br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;  toTarget:self <br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;  withObject:theArray];<br /><br />
    


    dans cette fonction loadImageIntoOYAMatrixThreaded, je gère le nombre de vignettes en fonction du NSRect de la matrice (NSMatrix)
    <br />NSRect matrixRect = [self frame];<br />
    


    et matrixRect.size.width me retourne 0  alors que juste avant elle vaut 700.
    Comment est ce possible que dans le thread, les informations de la matrice soit perdues ?
    En gros j'ai perdu le pointeur de la classe principale NSMatrix.
  • GGGG Membre
    septembre 2007 modifié #25
    Pour que cela soit plus clair voici le code quasiment complet :) :
    <br /><br />- (IBAction)DisplayOYAImage:(id)sender imageArray:(NSArray *)theArray {<br />	<br />	[NSThread detachNewThreadSelector:@selector(loadImageIntoOYAMatrixThreaded:) <br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;  toTarget:self <br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;  withObject:theArray];<br />	<br />&nbsp; &nbsp; [self sizeToCells];				// matrix se redimmentonne pour les ascenseurs<br />&nbsp; &nbsp; [self setNeedsDisplay:YES];			// On rafraichi l&#39;affichage<br />	[self selectCellAtRow:CurrentSelectedRow column:CurrentSelectedColumn];<br />	<br />}<br /><br />+(void)loadImageIntoOYAMatrixThreaded:(NSArray*)filesPath {<br />	NSAutoreleasePool *subPool = [NSAutoreleasePool new];<br />	// Quelques initialisation de variables<br />&nbsp; &nbsp; int line=0, column=0, nbOfPictures,currentImage=0,i;<br />	<br />&nbsp; &nbsp; // Notre objet dérivé de NSCellImage (cf PNImageCell.h/.m)<br />&nbsp; &nbsp; OYAImageCell* cell;<br /><br />&nbsp; &nbsp; // Une NSImage (pour afficher l&#39;image)<br />&nbsp; &nbsp; NSImage* thePicture;<br /><br />&nbsp; &nbsp; // Un NSRect pour connaà®tre le rectangle dans lequel s&#39;inscrit<br />&nbsp; &nbsp; // PhotonavMatrix<br />&nbsp; &nbsp; NSRect matrixRect = [self frame];<br />	NSLog(@&quot;size:%f,%f,%f,%f&quot;,matrixRect.origin.x,matrixRect.origin.y, matrixRect.size.height,matrixRect.size.width);<br />&nbsp; &nbsp; // Calculer le nombre d&#39;image par ligne (normalement 5)<br />&nbsp; &nbsp; float numberOfPicturesInARow = matrixRect.size.width /96;<br />&nbsp; &nbsp; NSLog(@&quot;NB Pictures in a row: %f&quot;,&nbsp; numberOfPicturesInARow);<br />	<br />&nbsp; &nbsp; // Calculer le nombre de ligne<br />&nbsp; &nbsp; nbOfPictures = [filesPath count];		// On obtient le nombre d&#39;élément du tableau reçus en paramètre<br />	//NumberOfPictures = nbOfPictures;<br />	NSLog(@&quot;NB pictures : %d&quot;,nbOfPictures);<br />	if ( NumberOfRows != 0 ) // force l&#39;effacement des NSImageCell <br />		for ( i=NumberOfRows; i&gt;= 0; --i)<br />			[self removeRow:i];<br />&nbsp; &nbsp; int nbOfLines = (int)(nbOfPictures/numberOfPicturesInARow) +1;<br />&nbsp; &nbsp; if (nbOfPictures % (int)numberOfPicturesInARow == 0)<br />&nbsp; &nbsp; &nbsp; &nbsp; nbOfLines--;<br />	<br />&nbsp; &nbsp; // On redimmentionne matrix avec le nombre de ligne et colonnes calculé <br />//	[self removeAllToolTips];<br />	<br />&nbsp; &nbsp; [self renewRows:nbOfLines<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;  columns:numberOfPicturesInARow];<br /><br />	NumberOfRows = nbOfLines;<br />	NumberOfColumns = numberOfPicturesInARow;//(unsigned int) (NumberOfPictures / NumberOfRows) + (NumberOfPictures % NumberOfRows);<br />	CurrentSelectedRow = 0;<br />	CurrentSelectedColumn = 0;<br />	NSLog(@&quot;NumberOfRows:%d,NumberOfColumns:%d&quot;,NumberOfRows,NumberOfColumns);<br />	<br />&nbsp; &nbsp; // La boucle d&#39;affichage des images<br />&nbsp; &nbsp; while(currentImage &lt; nbOfPictures)<br />&nbsp; &nbsp; {<br />&nbsp; &nbsp; &nbsp; &nbsp; if(column &gt; numberOfPicturesInARow -1 ) // Si on arrive en bout de ligne<br />&nbsp; &nbsp; &nbsp; &nbsp; {<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 			// sur la ligne en dessous<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; [self addRow];			// qu&#39;il faut ajouter à &nbsp; matrix<br />			NSLog(@&quot;addRow:%d&quot;,line);<br />			column = 0;				// on repart de la première colonne<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ++line;	<br />&nbsp; &nbsp; &nbsp; &nbsp; }<br /><br />&nbsp; &nbsp; &nbsp; &nbsp; // On est sur une cellule, il faut positionner certaines valeur dans les variables<br />&nbsp; &nbsp; &nbsp; &nbsp; // de notre objet (de type OYAImageCell)<br />&nbsp; &nbsp; &nbsp; &nbsp; cell = [self cellAtRow:line column:column];<br />&nbsp; &nbsp; &nbsp; &nbsp; thePicture = [[NSImage alloc] initWithContentsOfFile:[filesPath objectAtIndex:currentImage]];<br />		<br />&nbsp; &nbsp; &nbsp; &nbsp; // Si le fichier dont on a le chemin est une image<br />&nbsp; &nbsp; &nbsp; &nbsp; if([thePicture isValid])<br />&nbsp; &nbsp; &nbsp; &nbsp; {<br />			thePicture = [self ReduceImageMemory:thePicture];<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; [cell setBordered:YES];				// On dessine une bordure<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; [cell setEnabled:YES];				// On peremt la sélection<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; [cell setImageAlignment:NSImageAlignCenter];	// On aligne en centrnt l&#39;image dans la cellule <br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; [cell setImageScaling:NSScaleProportionally];	// On a une image proportionné<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; [cell setImageFrameStyle: NSImageFrameGrayBezel];	// On choisi le type de bordure<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; [cell setObjectValue:thePicture];			// On met dans la cellule l&#39;image lue<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; [cell setPath:[filesPath objectAtIndex:currentImage]];<br />			[cell setTag:currentImage];				// On ajoute le tag<br /><br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; [self setNeedsDisplay:YES];		// il faut afficher l&#39;image<br />			//[self performSelectorOnMainThread:@selector(setNeedsDisplayThreaded:) <br />			//		withObject:[NSValue valueWithRect:[self cellFrameAtRow:line column:column]] waitUntilDone:0.1];<br />			[self performSelectorOnMainThread:@selector(setNeedsDisplayThreaded:) <br />					withObject:nil waitUntilDone:NO];<br /><br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; column++;				// Colonne suivante<br /><br /><br />&nbsp; &nbsp; &nbsp; &nbsp; }<br />&nbsp; &nbsp; &nbsp; &nbsp; currentImage++;				// Image suivante<br />&nbsp; &nbsp; &nbsp; &nbsp; [thePicture release];			// On relache l&#39;objet NSImage dont on a plus besoin<br />&nbsp; &nbsp; }<br />	LastColIndice=column-1;<br />	NumberOfRows = line;<br />	NumberOfPictures = currentImage;<br />	[subPool release]; <br />}<br /><br />- (void)setNeedsDisplayThreaded:(NSValue*)v&nbsp; {<br />//	[self setNeedsDisplay];//:[v rectValue]];<br />	[self setNeedsDisplayInRect:[v rectValue]];<br />//	[self setNeedsDisplay:YES];<br />//	[self needsToDrawRect:v];<br />}<br /><br />-(unsigned int)NumberOfColumns{<br />	return NumberOfColumns;<br />}<br />-(unsigned int)NumberOfRows{<br />	return NumberOfRows;<br />}<br />-(unsigned int)NumberOfPictures{<br />	return NumberOfPictures;<br />}<br />-(unsigned int)CurrentSelectedRow{<br />	return CurrentSelectedRow;<br />}<br />-(unsigned int)CurrentSelectedColumn{<br />	return CurrentSelectedColumn;<br />}<br />-(void)setCurrentSelectedColumn:(unsigned int)n {<br />	CurrentSelectedColumn = n;<br />}<br />-(void)setCurrentSelectedRow:(unsigned int)n {<br />	CurrentSelectedRow = n;<br />}<br /><br /><br /><br />
    
  • schlumschlum Membre
    21:00 modifié #26
    Pourquoi "+(void)loadImageIntoOYAMatrixThreaded" est une méthode de classe plutôt qu'une méthode d'instance ??
  • GGGG Membre
    21:00 modifié #27
    c'était juste pour tester car, lorsque je laisse ne méthode d'instance, self (la matrice), possède une frame avec des valeurs nulles ou négatives.

    En gros, le self dans detachNewThreadSelector possède la même adresse que dans la fonction display... mais sa frame par exemple contient des valeurs nulles ou négatives (height et width).

    Comment régler ce problème.


  • schlumschlum Membre
    21:00 modifié #28
    Une méthode de classe ça n'ira pas, surtout que tu utilises "self" dedans, là  ça ne veut plus rien dire !
  • GGGG Membre
    21:00 modifié #29
    justement dans le cas d'une méthode d'instance, je perds les attributs de ma NSMatrix.
    Comment peut on régler ce problème ?
  • schlumschlum Membre
    septembre 2007 modifié #30
    C'est pas normal, normalement ça devrait être exactement comme si tu l'appelais directement... Juste que c'est dans un autre thread.

    PS : attention à  ne pas oublier [NSThread exit]; à  la fin de ta fonction !

    La j'ai pas trop le temps de regarder en détail, et ça va pas s'arranger ; je pourrai regarder plus précisément Lundi
  • GGGG Membre
    21:00 modifié #31
    Il n'y a aucun problème ;) .
    Bon weekend.
Connectez-vous ou Inscrivez-vous pour répondre.