Appeler une méthode d'une instance à  partir d'un objet sous classé

skimpyskimpy Membre
mars 2006 modifié dans API AppKit #1
Bonsoir,

J'ai un objet Toto, instancié au niveau de IB, qui a une méthode createServer. Mon interface graphique possède un bouton qui affiche un menu lorsque l'utilisateur clique dessus. Pour faire ceci, j'ai sous-classé NSButton et j'ai surchargé la méthode mouseDown. Le problème est que la méthode createServer se trouve dans mon objet Toto et je ne sais pas comment faire pour l'appeler. Quel est le moyen pour y remédier ?

<br />- (void)mouseDown:(NSEvent *)theEvent<br />{<br />	<br />	NSLog(@&quot;mouseDown de NLButton&quot;);<br /><br />	NSMenu* menu = [[[NSMenu alloc] init] autorelease]; <br />   [menu addItemWithTitle:@&quot;Ajouter un serveur&quot; action:@selector(test) keyEquivalent:@&quot;&quot;];<br />      <br />   [NSMenu popUpContextMenu:menu withEvent:theEvent forView:self];<br />   <br />} <br />


Merci.

Le action:@selector(test) est simplement un exemple. Ca appelle bien la méthode test de la sous classe de NSButton, mais si je remplace en mettant createServer, il me met bien évidemment qu'il ne reconnaà®t pas la méthode createServer.

Réponses

  • BruBru Membre
    07:42 modifié #2
    Quand tu ajoutes ton menu-item dans ton menu (méthode addItemWithTitle:action: keyEquivalent:) il faut aussi que tu fasses un setTarget avec comme paramètre l'instance toto.

    .
  • BruBru Membre
    mars 2006 modifié #3
    <br />- (void)mouseDown:(NSEvent *)theEvent<br />{<br />	<br />&nbsp; &nbsp; NSLog(@&quot;mouseDown de NLButton&quot;);<br /><br />&nbsp; &nbsp; NSMenu *menu;<br />&nbsp; &nbsp; NSMenuItem *menuItem;<br /><br />&nbsp; &nbsp; menu=[[[NSMenu alloc] init] autorelease];<br />&nbsp; &nbsp; menuItem=[[NSMenuItem alloc] initWithTitle:@&quot;Ajouter un serveur&quot; action:@selector(test) keyEquivalent:@&quot;&quot;];<br /><br />&nbsp; &nbsp; [menuItem setTarget:toto];<br /><br />&nbsp; &nbsp; [menu addItem:menuItem];<br />&nbsp; &nbsp; [menuItem release];<br /><br />&nbsp; &nbsp; [NSMenu popUpContextMenu:menu withEvent:theEvent forView:self];<br />} <br />
    


    .
  • skimpyskimpy Membre
    mars 2006 modifié #4
    Bonjour,

    J'ai modifié un peu le code mais ça ne fonctionne pas :

    <br />- (void)mouseDown:(NSEvent *)theEvent<br />{<br />	<br />    NSLog(@&quot;mouseDown de NLButton&quot;);<br /><br />    NSMenu *menu;<br />    NSMenuItem *menuItem;<br /><br />    menu=[[[NSMenu alloc] init] autorelease];<br />    menuItem=[menu initWithTitle:@&quot;Ajouter un serveur&quot;];<br />	[menuItem setTarget:toto];<br />	[menuItem setAction:@selector(test)];<br /><br />    [menu addItem:menuItem];<br />    [menuItem release];<br /><br />    [NSMenu popUpContextMenu:menu withEvent:theEvent forView:self];<br />} <br />
    


    J'avais en warning m'indiquant NSMenu ne répondrait pas à  initWithTitle:action:keyEquivalent donc j'ai modifié comme ci-dessus et après il me sort une erreur en me disant que 'toto' n'est pas déclaré. Bru, tu me demandes bien de mettre le nom de l'objet instancié dans IB (celui représenté par un carré bleu) ?
  • BruBru Membre
    07:42 modifié #5
    Mauvais copier/coller...

    Dans le code, c'est
    menuItem=[[NSMenuItem alloc] initWithTitle:@Ajouter un serveur action:@selector(test) keyEquivalent:@";"];
    qu'il faut lire bien sûr !

    .
  • skimpyskimpy Membre
    07:42 modifié #6
    Oui je n'ai plus le warning concernant le NSMenuItem mais il reste le point du [menuItem setTarget:toto]; où il me dit que toto n'est pas déclaré.
  • BruBru Membre
    07:42 modifié #7
    dans 1141633315:

    Oui je n'ai plus le warning concernant le NSMenuItem mais il reste le point du [menuItem setTarget:toto]; où il me dit que toto n'est pas déclaré.


    J'ai mis toto car c'est ce que tu évoquais dans ton premier post. C'est juste un exemple, que tu dois modifier et adapter à  ton cas.

    Dans ta problématique (que je devine difficilement puisque tu ne nous donnes aucune information hormis 3 bouts de lignes de code), j'utiliserais le target du bouton que tu as sous-classé pour en faire aussi le target des menu-items.

    Dans ce cas, il te suffit de faire  :
    [menuItem setTarget:[self target]];

    Ensuite, dans IB, il ne te reste plus qu'à  tirer une ligne entre ton "Toto" bleu et ton bouton, et de connecter le "target".

    .
  • skimpyskimpy Membre
    07:42 modifié #8
    Ok, je vais essayer de développer le point que je n'arrive pas à  saisir :

    Actuellement, mon interface graphique est composée de 2 boutons : un bouton pour "ajouter un serveur" et un autre pour "ajouter un favoris". Je me suis dit qu'il serait mieux d'avoir un seul bouton Ajouter qui laisserait apparaà®tre un menu "Ajouter un serveur" et "Ajouter un favoris".

    Au niveau de IB, je possède un objet toto (qui est une instance de la classe Toto). Cette classe Toto est composée de 2 méthodes :
    1. - (IBAction)addServer:id(sender)
    2. - (IBAction)addFavoris:(id)sender

    Aujourd'hui, dans IB, le target du bouton "ajouter un serveur" est relié à  la méthode addServer de l'objet toto ; et le target du bouton "ajouter un favoris" est relié à  la méthode addFavoris de l'objet toto.

    -> Mon but, en tant que débutant, est de faire apparaà®tre un menu lorsque je clique sur le bouton Ajouter. Ce menu doit laisser apparaà®tre 2 possibilités :
    - ajouter un serveur, qui doit appeler la méthode addServer de l'objet toto
    - ajouter un favoris, qui doit appeler la méthode addFavoris de l'objet toto

    Pour réaliser le bouton, j'ai sous classé NSButton (j'ai créé une classe NLButton) qui contient l'implémentation donnée lors du 1er post.

    Ce que je n'arrive pas à  résoudre :
    NLButton ne connaà®t pas l'objet toto et ne peut donc pas appeler ses méthodes addServer et addFavoris.
    Le action:@selector(addServer) situé dans ma méthode mouseDown va chercher une méthode addServer située dans la classe NLButton (ce qui est logique). Donc ce que je souhaiterais, c'est qu'il appelle la méthode addServer de la classe Toto.

    Voilà , j'espère que mes explications sont plus compréhensibles.
  • 07:42 modifié #9
    Petite remarque: l'action d'un menu/bouton doit toujours prendre un argument (l'objet qui envoie le message d'action), dont il faut tenir compte dans l'écriture du selector en ajoutant un ":", ce qui devient
    [tt]@selector(addServer:)[/tt]
  • BruBru Membre
    07:42 modifié #10
    Tous les contrôles dérivant de NSControl ont un mécanisme d'appel d'une méthode qui se déclenche sur action sur ledit contrôle.

    Ce mécanisme se nomme target/action. Il s'agit des 2 propriétés (target et action) de NSControl dont le but est de stocker :
    - pour action : le sélecteur de la méthode à  appeler après action sur le contrôle.
    - pour target : l'id de l'instance de l'objet qui contient la méthode ci-dessus.

    Dans ton cas, tu possèdes bien 2 méthodes (addServer: et addFavoris:), mais tu dois aussi préciser le target (l'instance toto présente dans IB).

    Voilà  la solution que je te propose.

    Implémente une méthode bidon dans ta classe Toto du style :
    <br />- (IBAction)addServerOrFavoris:(id)sender<br />{<br />&nbsp; &nbsp; return;<br />}<br />
    

    Cette méthode ne fera rien. Mais tu devras la relier à  l'action de ton NLButton. Normalement, elle devrait être appelée sur appui du bouton, mais cela n'arrivera jamais, du fait que tu as customisé le mouseDown du bouton.

    Ensuite, dans le code du mouseDown, tu vas initialiser le target de chaque menu-item que tu vas créer avec le target du NLButton par :
    <br />[menuItem setTarget:[self target]];<br />
    


    Ainsi, les méthodes addServer: et addFavoris: seront cherchées dans le target du NLButton, c'est à  dire celui de l'instance toto (qui aura été correctement initialisé par le fait d'avoir relier ton instance toto au bouton précédemment).

    .
  • skimpyskimpy Membre
    07:42 modifié #11
    Ca marche ! J'ai ajouté les : derrière le addServer et la méthode est bien appelée.

    Donc pour résumer, il manquait les 2 entrées suivantes :

    1. [menuItem setTarget:[self target]];
    2. @selector(addServer:)

    Merci pour vos explications.
  • AliGatorAliGator Membre, Modérateur
    07:42 modifié #12
    Moui, moi pour faire plus propre j'aurais plutôt fait un outlet "_target" ou "_menuTarget" à  la classe NLButton, et fait un [menuItem setTarget:_menuTarget] lorsque tu crées ton menu.

    Ainsi dans IB au lieu de relier l'action du NLBouton alors que ce serait un "fake" (une action qui ne fait rien, et si tu passes ta classe à  une autre personne parce qu'elle veut faire autre chose je trouve que c'est difficile de comprendre le principe que tu relies à  une action qui en fait ne sert à  rien), tu passerais par un peu l'équivalent d'un delegate. (D'ailleurs limite c'est exactement comme un delegate, mais bon)

    Donc un [tt]Outlet id _menuTarget[/tt], tu fais des [tt][menuItem setTarget:_menuTarget];[/tt] quand tu crées tes menuItems.
    Et dans IB, au lieu de relier l'action du bouton à  ton instance de Toto, tu relies l'outlet _menuTarget à  cette instance de Toto (comme tu le ferais pour un delegate)

    Enfin moi je vois ça comme ça je trouve ça plus propre.
  • BruBru Membre
    07:42 modifié #13
    dans 1141649986:

    Enfin moi je vois ça comme ça je trouve ça plus propre.


    Ah, Ali la lavandière...

    Ici la "méthode-qui-ne-fait-rien" ne fait rien car elle n'a rien à  faire (ouf, c'est dit).
    Mais rien n'empêche de réutiliser l'action du NLButton pour, par exemple, effectuer une action sur le clic du bouton, mais sans choix d'un item dans le popup-menu...

    La seule astuce est ici de partager le target du NLButton avec ceux des NSMenuItem attachés, ce qui, dans un design MVC, arrive dans 90% des cas (l'objet contrôleur du bouton étant généralement le même que celui des "sous-contrôles" associés). Bref, c'est une solution comme une autre, qui de plus, est adaptée à  son cas.

    Rien ne permet de dire qu'une méthode est plus propre que l'autre !

    .
  • AliGatorAliGator Membre, Modérateur
    07:42 modifié #14
    Bah si : tu viens de me passer un savon, donc ma méthode est forcément plus propre  :) :o <3 :(renaud):
Connectez-vous ou Inscrivez-vous pour répondre.