Ascenceur NSTextField / Récupérer certains caractères / NSThread

respawnrespawn Membre
août 2008 modifié dans API AppKit #1
Bonjour à  tous,

je débute en Obj-C Cocoa et je suis en train d'adapter un programme que j'ai fait en C. J'ai fait une petite interface graphique très simple, j'ai relié mes 2 boutons à  mes 2 fonctions et j'arrive à  afficher mon texte dans ma zone NSTextField multiline.

Première question : comment faire pour que quand le texte descend en dehors de la fenêtre, un ascenseur apparaisse pour permettre de lire le texte jusqu'au bout ?
Faut-il utiliser les TextView ? Si oui, comment afficher du texte dedans ?

2ème question : je dispose d'une case d'un tableau contenant un texte du type "button2", comment récupérer seulement le 2, cad supprimer les 6 premiers caractères ?
(en C, j'utilise strcpy puis sprintf)

Merci beaucoup

Réponses

  • schlumschlum Membre
    14:10 modifié #2
    Oui, NSTextView avec un scroller vertical qui se cache par défaut (tout ça se règle dans Interface Builder).
    Pour le remplir : setString de NSText dont il descend.
  • schlumschlum Membre
    14:10 modifié #3
    Pour récupérer une partie d'une NSString :

    - (NSString *)substringWithRange:(NSRange)aRange
    

  • respawnrespawn Membre
    14:10 modifié #4
    Merci pour ces réponses rapides, je vais essayer pour le TextView, mais je viens d'essayer pour récupérer une partie d'une String et ... ben je débute.

    j'ai essayé avec cette ligne :
    test = [[user objectAtIndex:1] substringWithRange:NSMakeRange(5,2)];
    ca marche et pour chaque user, ca me sort bien "n" suivi du numero, le souci c'est que les numeros commencent à  1 et vont jusqu'à  12 ... il me faudrait donc cette ligne
    test = [[user objectAtIndex:1] substringWithRange:NSMakeRange(6,2)];
    mais ca ne marche pas car de 1 à  9, il n'y a pas de 2e caracteres après le n ... le 0 du 10, le 1 du onze et de 2 du douze ...

    comment faire ? y a-t-il une variable "fin", ou "end" pour dire "du caratère 6 à  la fin" ?

    j'essaie le TextViex en attendant :-)
  • respawnrespawn Membre
    14:10 modifié #5
    Parfait pour le TextView, exactement ce qu'il fallait, je viens juste de découvrir qu'il y avait 2 zones : le scroll et le text en cliquant dedans  :)

    merci beaucoup, je continue
  • schlumschlum Membre
    14:10 modifié #6
    Du caractère 6 à  la fin, ça donne :
    [theStr substringWithRange:NSMakeRange(6,[theStr length]-6)];
    
  • respawnrespawn Membre
    14:10 modifié #7
    Absolument parfait ! Merci sclum

    Problème suivant : je viens de faire des recherches concernant NSThread ... et je suis tombé sur des posts à  toi sur macbidouille, je pense donc que tu connais pas mal le sujet ... je voulais passer au thread en C mais je n'avais pas eu le temps, je pense que ce serait une bonne idée d'apprendre ça en même temps que Obj-C et cocoa. J'ai quelques connaissances théoriques mais ça s'arrête là  ;-) ...

    Voici ce que je veux faire : j'ai en fait un serveur qui vérifie en permanence une base de données MySQL (j'arrive à  communiquer avec, ça c'est fait), et qui, dès qu'une valeur change, enverra une requête TCP/IP (je m'attaquerai à  cette partie plus tard). Mon interface graphique est très simple, un bouton start, un bouton stop, et dans la fenêtre, le statu du serveur + affichage des qu'une valeur de la base de données a changée.

    Comme tu peux le deviner, je souhaite que le serveur se lance quand j'appuie sur le bouton start (et boucle "infiniment") et s'arrête quand j'appuie sur stop. Jusqu'à  présent en C, le serveur tournait dans une boucle while(1) et je quittais "salement" avec un ctrl-C.

    Donc voilà , je pense que le thread peut m'aider à  faire quelque chose de propre

    Merci de ton aide !
  • schlumschlum Membre
    14:10 modifié #8
    Pourquoi ne pas utiliser NSThread dans ton cas ? C'est basique, mais pour le coup, largement suffisant à  ton besoin  ;)
  • NoNo Membre
    14:10 modifié #9
    hormis dans un but d'apprentissage, est ce que NSThread n'est pas trop "surdimensionné" pour cet usage ?
    Un NSTimer pourrait peut-être suffire (car l'interrogation de la base mysql et l'envoi de la requête ne devrait pas figer l'interface trop longtemps).
  • respawnrespawn Membre
    14:10 modifié #10
    C'est tout à  fait ce que je veux faire  :o et je pensais que tu pourrais me donner un (des) petit(s) exemple(s) de code pour voir où mettre quoi et surtout comment relier mon bouton start et mon bouton stop ...

    @No : peut-être :) ... mais timer, je sais pas, j'ai l'impression que ca va attendre une fois puis stopper (mais je me trompe surement), il faut qu'une fois le bouton start appuyé, le programme tourne tout le temps, et soit rapide (consultation de la BDD toutes les secondes à  peu près, la BDD est en locale)

    8--)

  • NoNo Membre
    14:10 modifié #11
    dans 1218549480:

    @No : peut-être :) ... mais timer, je sais pas, j'ai l'impression que ca va attendre une fois puis stopper (mais je me trompe surement), il faut qu'une fois le bouton start appuyé, le programme tourne tout le temps, et soit rapide (consultation de la BDD toutes les secondes à  peu près, la BDD est en locale)


    NSTimer n'attend pas... En fait il s'agit de déclencher l'exécution d'une méthode à  intervalle régulier.
    Entre chaque appel, l'interface graphique reste réactive, donc le bouton STOP reste utilisable (et ferait en gros un [NSTimer invalidate] pour couper le NSTimer).

    De l'ordre de la seconde, c'est "très lent", même pour un NSTimer.
  • schlumschlum Membre
    14:10 modifié #12
    Oui, mais NSTimer ça utilise les événements, et c'est dans le même thread si je ne m'abuse...
    Donc ça va bloquer l'interface si la requête au serveur bloque trop longtemps.
  • schlumschlum Membre
    14:10 modifié #13
    NSThread est très simple, il suffit d'appeler :

    [NSThread detachNewThreadSelector:@selector(monSelecteurThread:) toTarget:self withObject:nil];
    


    ensuite, le thread donne ça :
    - (void)monSelecteurThread:(id)obj<br />{<br />    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];<br />    // ... Thread ...<br />    [pool release];<br />    [NSThread exit];<br />}
    


    La partie // ... Thread ... peut être une boucle while avec une condition qui teste une variable partagée pour sortir.

    PS : Pour l'accès ou la modification de la variable partagée, ne pas oublier d'entourer par des @synchronized { } les parties de code dites " critiques " !
  • respawnrespawn Membre
    août 2008 modifié #14
    Si j'ai à  peu près compris :

    je met la ligne
    [NSThread detachNewThreadSelector:@selector(monSelecteurThread:) toTarget:self withObject:nil];
    

    au début de la fonction liée à  mon bouton start.
    en dehors de la fonction mais dans mon fichier monapplication.m, il faut que je declare un selecteur c'est ça ? ça peut être n'importe quel type ? un int ?

    ensuite un peu plus loin dans mon fichier monapplication.m, je met ce morceau de code
    - (void)monSelecteurThread:(id)obj<br />{<br />    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];<br />    // ... Thread ...<br />    [pool release];<br />    [NSThread exit];<br />}
    


    en mettant tout ce qu'il y avait avant dans ma méthode liée au bouton start, à  la place du // ... Thread ... C'est çà  ? et donc dedans, en condition de ma boucle while, je met mon "selecteur" ?

    en simplifiant, ca pourrait donner qqch comme ça ?
    <br />@implementation monapplication<br /><br />int monselecteur = 1;<br /><br />- (IBAction)a_connect:(id)sender {<br />	<br />	[NSThread detachNewThreadSelector:@selector(monSelecteurThread:) toTarget:self withObject:nil];<br />	<br />while (monselecteur){<br />monselecteurThread();<br />blablabla<br />}<br /><br />}<br /><br />- (IBAction)a_disconnect:(id)sender {<br />monselecteur = 0;<br />	...<br />}<br /><br />@end<br />
    


    Je vois pas trop pour les @synchronize ... et quel objet envoyer à  monselecteurThread ...

    il doit y avoir de l'idée mais ca doit pas être ça lol ;D car j'ai l'impression que disconnect n'aura jamais la main pour mettre le selecteur à  0 non ?
  • respawnrespawn Membre
    14:10 modifié #15
    ça marche ... presque parfaitement

    je fais des tests pour savoir ce qui en va pas et je vous tiens au courant

    Merci beaucoup vous cette aide super rapide et très précise. Première fois que je viens sur ce forum et sûrement pas la dernière

    Bonne soirée et à  demain

    Toma
  • schlumschlum Membre
    14:10 modifié #16
    Il n'y a pas besoin d'appeler explicitement "monSelecteurThread" ; il est appelé par "detachNewThread...".
    Ensuite, une fois appelé, tout se passe dedans, sauf la mise à  jour de la variable monselecteur qu'il faut effectivement faire dans "disconnect" (mais normalement, il faut entourer par @synchronized { }, sinon tu risques à  la fois d'accéder à  la variable et de la modifier ; ça s'appelle une section critique... Dans ton cas, je pense que c'est pas gravissime car c'est assez atomique).

    La boucle "while" c'est dans monSelecteurThread qu'il faut la mettre...
  • respawnrespawn Membre
    14:10 modifié #17
    Oui, tout à  fait. Au final, voici mon code qui marche, ca servira surement à  d'autres :)
    @implementation TheApp<br /><br />NSString *display;<br />int continu;<br /><br />- (void)run_server:(id)sender<br />{<br />	<br />&nbsp; &nbsp; NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];<br />	<br />	while (continu) {<br /><br />		... mon code qui s&#39;exécute en continu ...<br /><br />		sleep(1);<br />		<br />	}<br />	<br />&nbsp; &nbsp; [pool release];<br />&nbsp; &nbsp; [NSThread exit];<br />	<br />}<br /><br />- (IBAction)a_connect:(id)sender {<br />		<br />	continu=1;<br />	[NSThread detachNewThreadSelector:@selector(run_server:) toTarget:self withObject:nil];<br />	<br />}<br /><br />- (IBAction)a_disconnect:(id)sender {<br />	<br />	continu=0;<br />	<br />	...<br />}<br /><br />@end
    


    Je n'ai en effet pas mis de @synchronized ... tout à  l'air de marcher comme ca :)

    Aujourd'hui je m'attaque aux sockets/requêtes TCP ... je créerai un autre post

    Merci encore !
  • AliGatorAliGator Membre, Modérateur
    août 2008 modifié #18
    Hello,

    Alors c'est vrai que ta variable "continu" (tu aurais pu mettre un BOOL avec les valeurs YES et NO quand même  :)) étant atomique, les risques d'accès concurrents sont limités... et au pire tu vas mal lire la variable "continu" au moment où tu vas l'affecter en parallèle dans l'autre thread, ça te fera faire une itération de plus de ta boucle while... c'est pas un drame.
    Mais bon si tu voulais faire les choses bien, tu pourrais mettre un setter et un getter pour ta variable "continu", et dans le code du setter comme du getter, encadrer leur code de @synchronized(self){...}.

    Même mieux, si tu codes en Objective-C 2.0 y'a une solution simple :
    • Déclare ta variable "continu" en tant que @property et utilise @synthesize pour qu'il te génère automatiquement les getter et setter sans avoir à  rien écrire de ton côté : les setter/getter des @property sont thread-safe par défaut (sauf si tu indiques l'attribut "nonatomic") donc...
    • Dans ce cas (Objective-C 2.0), mettre [pool drain] au lieu de [pool release] dans ton code de ton thread, au passage (c'est plus moderne :P et permet de marcher y compris si tu actives le Garbage Collector un jour)

  • respawnrespawn Membre
    14:10 modifié #19
    Merci beaucoup, je vais regarder ça un peu plus tard

    La suite de mes aventures concernant une connexion TCP ici
    http://www.objective-cocoa.org/forum/index.php/topic,2827.0.html
Connectez-vous ou Inscrivez-vous pour répondre.