Utilisation d'une sheet qui se rafraichit
Jijo
Membre
Voilà je sais créer une sheet qui s'ouvre et se ferme pendant un traitement cependant je ne sais pas comment actualiser les NSTextfield, NSimage et NSProgressIndicator au cours du temps.
Le sheet n'actualise pas les informations.
Merci
Voici une photo de ce que j'ai
Le sheet n'actualise pas les informations.
Merci
Voici une photo de ce que j'ai
Connectez-vous ou Inscrivez-vous pour répondre.
Réponses
Mais, ce n'est pas la seule solution:
http://www.objective-cocoa.org/forum/index.php?topic=3223.msg31816;topicseen#new
Je voulais savoir si quelqu'un a des exemples simples , basics qui correspond à mon cas.
Merci
Si je comprends bien, tu charges un nombre connu d'images, donc tu peux faire:
[Edit: projet ci-joint]
(c'est bon là Aligator ?)
[EDIT]Loool ouais c'est bon là :P[/EDIT]
Sinon en thread cela est-il compliqué?
L'argument passé à "withObject" sera passé en argument au sélecteur qu'on a choisi (ici j'ai mis nil mais tu pourrais mettre autre chose, qui sera alors passé en argument à ton loadImageThread et que tu pourras alors utiliser dans ce thread). Evidemment le "target" correspond à l'objet contenant ta méthode "loadImageThread", ici j'ai mis self mais si tu l'as mis dans une classe à part il faut passer une instance de cette classe, ça va de soi.
Après là où il faut faire attention c'est juste si tu utilises des variables dans ton thread que tu peux utiliser aussi de l'extérieur du thread. C'est à dire si tu as des variables qui pourront être accédées ou modifiées à la fois depuis ton thread principal et ton thread loadImageThread. Dans ce cas il faut penser à protéger ces variables avec des "@synchronized" (ou ne pas mettre "nonatomic" pour les @property correspondantes situ utilises les propriétés d'Objective-C 2.0) pour éviter les accès concurrentiels à ces variables qui sinon te feraient planter ton programme.
Ici c'est simple, y a pas de variable partagée, mais tu en auras peut-être, et comme le dit Aligator, bien faire attention à ça !
Dans l'exemple, il peut y avoir un doute sur imageCount, mais son accès est "atomic" (variable simple) et waitUntilDone:YES devrait éviter tout problème, normalement...
Il me reste à voir avec instrument ou le moniteur d'activité quand on met annuler si le thread est détruis. En tout cas en utilisant [NSThread exit] cela fait planter mon appli. Mais bon sans le détruire cela fonctionne même si cela n'est pas propre d'avoir un thread en activité qui sert a rien.
En tout cas merci.
Ce n'est pas comme ça que l'on fait pour terminer un thread !
Mon conseil, comme tu sembles ne pas vraiment maà®triser le sujet, est d'éviter absolument les threads !!!
De plus dans ton cas, ça ne sert à rien, sauf à avoir les pires problèmes, plantages aléatoires et j'en passe ...
Reprends le 1er exemple, sans thread, que j'ai fait dans le projet "PanneauDeChargement".
Après quand tu auras un programme fonctionnel, je te ferais une version avec thread, mais il ne faut pas griller les étapes.
Et du coup quand tu cliques sur ton bouton "Cancel" dans ta sheet window, il "suffit" de mettre cancelled à YES pour que le thread s'annule, du moins le plus tôt qu'il pourra (il va finir le traitement en cours, c'est à dire le chargement de l'image en cours, quand même, avant. Ce qui n'est pas plus mal pour être sûr de l'intégrité des données et que ton thread ne s'arrête pas en plein milieu après avoir alloué de la mémoire mais pas avoir eu le temps de la relâcher...)
Par contre si tu utilises cette méthode classique pour annuler ton thread, il faut penser à protéger l'accès à cette variable partagée, pour que quand tu la lis dans ton thread et quand tu l'écris à l'intérieur de ton thread il n'y ait pas de risque d'accès concurrentiel. Bon pour une variable booléenne c'est pas si dramatique car à priori c'est atomique, mais quand même c'est bien de prendre les bonnes habitudes.
Après pour faire ça il y a 2 possibilités :
- soit tu mets ton BOOL cancelled directement dans ta classe qui gère ta sheetWindow et contient aussi ta méthode utilisée pour le thread de chargement des images... du coup les deux ont accès à la variable "cancelled" directement normalement (faut juste utiliser les setter/getter de la variable, dans lesquelles tu auras mis un @synchronized(self), pour accéder à la variable de façon thread-safe) ;
- soit tu te crées une classe genre ThreadParam contenant ta variable cancelled (et éventuellement d'autres que tu voudrais partager entre ton code principal et ton thread), et tu passes une instance de cette classe en argument de ton thread (à la place du "nil" de "withObject" quand tu crées ton thread). Comme ça à la fois le code de ton sheet qui a connaissance de ton ThreadParam et le thread lui-même pourront accéder à ces variables, en plus ça marche du coup aussi même si tu mets le code du thread dans une autre classe (target autre que "self") que celle qui gère ta sheetWindow et crée ton thread...
Voilà pour les pistes... En tout cas c'est toujours mieux d'utiliser un test booléen dans ton thread à chaque boucle d'itération pour savoir s'il faut l'annuler que d'appeler [tt][NSThread exit][/tt]... qui en plus interrompt le thread courant, donc si tu fais appel à cette méthode de classe depuis ton code qui gère la sheetWindow et donc depuis le main thread (et non pas depuis le thread que tu as créé pour charger tes images), tu vas interrompre le thread principal... d'où plantage
Ainsi, pour quitter le thread en cours de route, il suffit de mettre cancelled à YES ce qui sort de la boucle, stop le thread et nettoie la mémoire (il me semble).
C'est pour ça que je mentionnais qu'il fallait plutôt utiliser un booléen que l'on teste à chaque itération, comme tu le fais si bien dans ton code, aussi pour être sûr que l'itération en cours se termine ce qui est mieux pour l'intégrité de la mémoire que de l'interrompre un peu n'importe où...
Par contre j'avais totalement zappé l'AutoreleasePool, et tu as parfaitement raison il faut absolument l'ajouter !!
En effet le thread principal a automatiquement son NSAutoreleasePool donc on ne s'en soucie en général pas, mais dans un thread par contre il faut en créer au moins une nous même ! Pour que tout ce qui est "autorelease" fonctionne (que ce soit un autorelease envoyé explicitement à un objet dans le code du thread, ou sous le capot dans du code Apple dont tu ne vois pas le code) !
Ou utiliser detachDrawingThread:toTarget:withObject: de NSApplication
Pas besoin de bool, suffit de rajouter la méthode:
[edit] et du coup y a une ambiguà¯té sur l'instruction imageCount--, faux mettre des @synchronize !
Bon, on est en train de discuter de l'utilité d'un thread qui sert, en fait, à mettre à jour l'interface, cad à revenir dans le main thread
Par contre, ce qui serait plus efficace, c'est de créer un thread par image à charger !
Je viens de jetter un oe“il sur les threads en 10.5, les choses on bien changer, en fait maintenant c'est comme en java, on dérive NSThread et tout ce passe dans main.
Si je suis de bonne humeur, j'essayerais peut-être de faire un exemple, si c'est pas trop casse tête :P
- Les threads avec NSThread. Pour les utiliser, on peut par exemple utiliser [tt]+detachNewThreadSelector:toTarget:withObject:[/tt], où tu peux préciser alors la méthode (@selector) à appeler. Ou sinon on peut aussi sous-classer NSThread et surcharger la méthode "main". Les deux sont possibles, au choix.
- Les threads posix, mais bon y'a pas grand intérêt pour toi
- Les NSOperations et NSOperationQueue qui permettent d'empiler des tâches unitaires à réaliser et de les laisser s'exécuter "quand le CPU aura le temps"...
- Sinon y'a la solution de la RunLoop proposée plus haut en effet, avec laquelle je ne suis pas familier pour ma part
- Y'en a p'tet d'autres que je connais pas ?
Bref, y'a que l'embarras du choix :P
Un peu de lecture chez Apple.
As-tu une expérience dans ce domaine ? Est-ce approprié pour le chargement des images ?
Ceci dit, si c'est par exemple pour charger une liste d'images dont on veut afficher la progression de chargement ou traitement, je pense que le NSThread avec une boucle sur les images est plus approprié. les NSOperations je vois plutôt ça pour du post-traitement par exemple, ou pour par exemple télécharger des images plus haute qualité, genre on affiche l'image basse qualité qu'on a en cache et on charge l'image haute qualité quand on a le temps, et quand elle arrivera il sera toujours temps de l'afficher à la place de la vignette... Enfin c'est my two cents
Après l'avantage que je vois des NSOperations c'est qu'on peut définir des dépendances entre les NSOperations, pour qu'une opération ne se lance que si une ou plusieurs autres ont été terminées précédemment... On peut aussi demander à une opération de s'annuler (ce qui n'aura aucun effet si la tâche est déjà lancée, mais la déprogrammera si elle n'est pas encore lancée)... 8--)
Après, pour le cas qui nous intéresse ici, à savoir juste charger une liste d'images, c'est pas utile d'utiliser des NSOperations, à mon avis un simple [NSThread detachNewThreadSelector:... toTarget:... withObject:...] suffit amplement (ou le truc avec les NSRunLoops que j'ai jamais testé peut-être)
for(i=0; cancelUpload==NO && i<ImageCount; i++)
{
[self performSelectorOnMainThread:@selector(updateInterface withObject:progressInfo waitUntilDone:YES];
if(cancelUpload==NO)//1
{
// traitements long
[obj upload:session Eimage:image Efilename:filename];
if(cancelUpload==NO)//2
{
[obj processing:session Eupload:upload Efilename:filename];
}// fin 2
}// fin 1
}// fin for
// fermeture de la fenêtre sheet
NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1;
[NSApp endSheet:progressPanel];
[progressPanel close];
En tout cas merci à vous car j'ai vraiment avancé.
1) on ne peut pas tuer un thread !
2) toute opération sur l'interface doit se faire dans le main thread, d'où l'utilité de la méthode performSelectorOnMainThread: !
Ma proposition: y a rien n'a faire, sauf à afficher un message: cancel operation, please wait...
J'ai tenté de mettre la méthode performSelectorOnMainThread: dans l'action du bouton annuler mais sans résultat non plus.
J'ai essayé aussi de fermer la sheet dés le clique du bouton annuler et d'en afficher une autre mais la seconde apparaà®t vide sans les labels.
Voilà si quelqu'un a une solution une piste.
Merci
Donc, dans la méthode cancel, tu affiches un textfield de la sheet avec "Cancel operation, please wait" pour que l'utilisateur voit que la sa demande est prise en compte.
Maintenant, il te faut savoir quand le thread de chargement n'est plus actif, pour cela tu va t'enregistrer pour recevoir une notification, dans awakeFromNib, tu ajoutes:
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(threadDidExit name:NSThreadWillExitNotification object: nil];
puis tu ajoutes:
A partir de là , tu peux tout faire, mettre à jour la fenêtre, par exemple.
-(void) cancel:(id)sender
{
[label setStingValue:@Cancel operation, please wait ...];
}
La ligne ci-dessous ne faisait rien
[self performSelectorOnMainThread:@selector(updateWindow) withObject:nil waitUntilDone:NO];
en tout cas merci
OK, merci.
Le prog charge les images du dossier Images de l'utilisateur (on peut spécifier un sous dossier)
Un master thread est créé au démarrage du chargement et ensuite un thread pour chaque image, pour un maximum de 8 threads simultanés.
Le tout sans l'utilisation d'un seul @synchronized (evil inside )
Les images s'affichent dans un NSCollectionView.
Projet Xcode 3.1, Leo 10.5.4 mini, dans les versions antérieures, NSImage lockFocus n'est pas thread safe (bouh ! Apple la teuon !)