SubMenu dynamique

muqaddarmuqaddar Administrateur
04:24 modifié dans API AppKit #1
Salut,

Je voudrais créer un nsmenu dynamique mais j'ai des erreurs :

2005-03-01 12:01:22.094 alphaProject[3459] *** Assertion failure in -[NSMenuItem initWithTitle:action:keyEquivalent:], Menus.subproj/NSMenuItem.m:116
2005-03-01 12:01:22.095 alphaProject[3459] Exception raised during posting of notification.  Ignored.  exception: Invalid parameter not satisfying: charCode != nil

Le code :
[subMenu addItemWithTitle:[dico objectForKey:@"myKey"] action:nil keyEquivalent:nil];


J'ai mis "nil" à  action pour l'instant mais je ne crois pas que ce soit ça qui foire.
J'ajoute que [dico objectForKey:@myKey] me renvoie bien une string quand je fais un NSLog.
merci
«1

Réponses

  • ClicCoolClicCool Membre
    04:24 modifié #2
    Salut :)

    initWithTitle:action:keyEquivalent:

    - (id)initWithTitle:(NSString *)itemName action:(SEL)anAction keyEquivalent:(NSString *)charCode
    Returns an initialized instance of an NSMenuItem. The arguments itemName and charCode must not be nil
  • muqaddarmuqaddar Administrateur
    04:24 modifié #3
    Merci Hervé, je l'avais pas vu.

    j'ai fait ça :

    [subMenu addItemWithTitle:[dico objectForKey:@"myKey"] action:@selector(iconeQuitNotification:) keyEquivalent:@"<no key>"];
    


    ça marche. :)
  • ClicCoolClicCool Membre
    04:24 modifié #4
    Impec,
    une simple chaine vide @";" aurait fait l'affaire aussi. ;)
  • muqaddarmuqaddar Administrateur
    04:24 modifié #5
    OK.

    J'ai un soucis pour mon action. Je dois y envoyer un argument en plus... par le sélecteur or c'est pas possible. Comment faire ?

    Une idée ?

  • cbrandtcbrandt Membre
    mars 2005 modifié #6
    le paramètre qui sera passé lors de l'appel de l'action est le menuitem qui l'aura déclenchée.
    pour y ajouter d'autres paramètres, tu pourrais lier un objet (un array d'objets si tu en a plusieurs) au menuitem avec setRepresentedObject et le récupérer avec representedObject dans ton action...

    edit:
    HA HA HA j'ai réussi à  griller Renaud !!!!  >:D  :P :P
  • mars 2005 modifié #7
    Si l'argument en question est un objet, tu peux faire lors de la création du menuitem:
    [tt][mi setRepresentedObject:tonArg];[/tt]

    que tu repèches comme suit
    [tt]-(void)monAction:(id)sender {
         obj = [sender representedObject];
    }[/tt]

    Attention ceci dit, le representedObject n'est pas retenu par le menu item, donc veille à  ce qu'il soit retenu ailleurs (par exemple dans un tableau variable d'instance).

    [EDIT] Grilled, la prochaine fois je me relirai pas na!
  • muqaddarmuqaddar Administrateur
    04:24 modifié #8
    Merci à  vous deux.
    J'ai compris. :-)
  • mpergandmpergand Membre
    mars 2005 modifié #9
    dans 1109683973:

    Attention ceci dit, le representedObject n'est pas retenu par le menu item, donc veille à  ce qu'il soit retenu ailleurs (par exemple dans un tableau variable d'instance).


    T'es sûr de ton coup là Â  ;)

    Si l'objet n'était pas retenu j'aurais de sérieux problèmes en Java (weak reference) et ce n'est pas le cas.

    <br />@implementation Controller<br /><br />-(void) awakeFromNib<br />{<br />	NSMenuItem* menu=[menuPopUp itemAtIndex:0];<br />	NSNumber* num=[NSNumber numberWithInt:100];<br />	printf(&quot;%d &#092;n&quot;,[num retainCount]);<br />	[menu setRepresentedObject:num];<br />	printf(&quot;%d &#092;n&quot;,[num retainCount]);<br />	<br />}<br /><br /><br />@end<br />
    


    ça me donne:
    1
    2
  • muqaddarmuqaddar Administrateur
    04:24 modifié #10
    Ici aucun pb avec la méthode de Renaud.
    Pas de plantage.
  • Eddy58Eddy58 Membre
    04:24 modifié #11
    Oui, mais apparemment le test de mpergand montre que setRepresentedObject effectue un retain, donc tu effectues toi-même un retain qui n'est pas utile et produit une fuite mémoire...:)
  • muqaddarmuqaddar Administrateur
    04:24 modifié #12
    Oui, j'avais pas pris le soin de faire de retain ici ;-).

    ++
    merci
  • 04:24 modifié #13
    Pour compléter le dirais de penser au "tag" [menuItem tag], c'est peut être plus "rapide" qu'un representedObject ou tout aussi complémentaire.
    Voir aussi le setTarget: si tu souhaites avoir le "sélecteur" dans une autre classe...
  • wiskywisky Membre
    04:24 modifié #14
    Le relance le sujet car j'ai un problème qui est similaire.
    J'ai lié ma barre de menu dans IB et elle est accessible avec ce pointeur: [tt]menuFile[/tt]
    Mon problème est que ça ne marche pas:
    NSMenuItem *openMenu;<br />	openMenu=[[NSMenuItem alloc] init];<br />	openMenu=[menuFile itemWithTag:18];<br /><br />		NSMenu *menus = [[NSMenu alloc] init];<br />		//menus = [openMenu submenu];<br />		NSMenuItem *tmpMenus;<br />		//[menus setTitle:[openMenu title]];<br />		tmpMenus = [[NSMenuItem alloc] init];<br />		[tmpMenus setTitle:@&quot;Test SubMenu1&quot;];<br />		[tmpMenus setEnabled:YES];<br />		[tmpMenus setAction:@selector(showInPlayer:)];<br />		[menus addItem:[tmpMenus copy]];<br />		<br />		tmpMenus = [[NSMenuItem alloc] init];<br />		[tmpMenus setTitle:@&quot;Test SubMenu2&quot;];<br />		[tmpMenus setEnabled:YES];<br />		[tmpMenus setAction:@selector(showInPlayer:)];<br />		[menus addItem:[tmpMenus copy]];<br />		<br />		//NSLog(@&quot;Creation menu &#39;Open With...&#39; : %@&quot;,menus);<br />		<br />		<br />		[openMenu setSubmenu:menus];
    


    J'ai même essayé :
    [[[menuFile itemWithTag:18] submenu] addItemWithTitle:@&quot;Test&quot; action:@selector(showInPlayer:) keyEquivalent:@&quot;&quot;];
    

    mais ça ne marche toujours pas...

    Où j'ai faux???? :why?: :why?:
  • Eddy58Eddy58 Membre
    janvier 2006 modifié #15
    Déjà , est-ce que le submenu existe ? Si tu fais le log suivant ça donne quoi ? :o
    [tt]
    NSLog(@submenu:%@",[[menuFile itemWithTag:18] submenu]);
    [/tt]
  • Eddy58Eddy58 Membre
    04:24 modifié #16
    Voilà  un projet exemple avec le code ci-dessous. :)
    Je passe par la méthode initWithTitle plutôt que par addItemWithTitle, car avec cette dernière les actions ne sont pas exécutées, sûrement de par l'absence de la target je pense.

    [tt]
    -(void)awakeFromNib
    {
    NSMenuItem *nouveauItem;

    // Initialisation du submenu
    NSMenuItem *menuItem18=[monMenu itemWithTag:18];
    NSMenu *monSubmenu=[[NSMenu alloc] initWithTitle:@";"];

    // Ajout à  monSubmenu de l'item1
    nouveauItem=[[NSMenuItem alloc] initWithTitle:@Item 1 action:@selector(action1) keyEquivalent:@";"];
    [nouveauItem setTarget:self];
    [monSubmenu addItem:nouveauItem];
    [nouveauItem release];

    // Ajout à  monSubmenu de l'item2
    nouveauItem=[[NSMenuItem alloc] initWithTitle:@Item 2 action:@selector(action2) keyEquivalent:@";"];
    [nouveauItem setTarget:self];
    [monSubmenu addItem:nouveauItem];
    [nouveauItem release];

    // Ajout à  monSubmenu de l'item3
    nouveauItem=[[NSMenuItem alloc] initWithTitle:@Item 3 action:@selector(action3) keyEquivalent:@";"];
    [nouveauItem setTarget:self];
    [monSubmenu addItem:nouveauItem];
    [nouveauItem release];

    // Attachement de monSubmenu au menuItem18
    [menuItem18 setSubmenu:monSubmenu];
    [monSubmenu release];
    }

    -(void)action1
    {
    NSLog(@Action 1);
    }

    -(void)action2
    {
    NSLog(@Action 2);
    }

    -(void)action3
    {
    NSLog(@Action 3);
    }
    [/tt]

    [Fichier joint supprimé par l'administrateur]
  • wiskywisky Membre
    04:24 modifié #17
    [tt]NSLog(@submenu:%@",[[menuFile itemWithTag:18] submenu]);[/tt] donne : [tt]submenu:(null)[/tt]

    ta solution ne marche pas!  :-\\ :'( :'( :'( :'(
  • Eddy58Eddy58 Membre
    janvier 2006 modifié #18

    NSLog(@submenu:%@",[[menuFile itemWithTag:18] submenu]); donne : submenu:(null)

    ta solution ne marche pas!

    Ce n'est pas une solution, mais un simple log...Et puis en plus qui n'a plus d'intérêt avec ce que je t'ai mis par la suite.
    Tu n'as donc pas regardé le projet d'exemple que je t'ai mis en attachement ici ? ::)
  • wiskywisky Membre
    04:24 modifié #19
    La solution dont je parle c'est celle là :
    [tt]-(void)awakeFromNib
    {
      NSMenuItem *nouveauItem;

      // Initialisation du submenu
      NSMenuItem *menuItem18=[monMenu itemWithTag:18];
      NSMenu *monSubmenu=[[NSMenu alloc] initWithTitle:@";"];

      // Ajout à  monSubmenu de l'item1
      nouveauItem=[[NSMenuItem alloc] initWithTitle:@Item 1 action:@selector(action1) keyEquivalent:@";"];
      [nouveauItem setTarget:self];
      [monSubmenu addItem:nouveauItem];
      [nouveauItem release];
     
      // Ajout à  monSubmenu de l'item2
      nouveauItem=[[NSMenuItem alloc] initWithTitle:@Item 2 action:@selector(action2) keyEquivalent:@";"];
      [nouveauItem setTarget:self];
      [monSubmenu addItem:nouveauItem];
      [nouveauItem release];

      // Ajout à  monSubmenu de l'item3
      nouveauItem=[[NSMenuItem alloc] initWithTitle:@Item 3 action:@selector(action3) keyEquivalent:@";"];
      [nouveauItem setTarget:self];
      [monSubmenu addItem:nouveauItem];
      [nouveauItem release];

      // Attachement de monSubmenu au menuItem18
      [menuItem18 setSubmenu:monSubmenu];
      [monSubmenu release];
    }

    -(void)action1
    {
      NSLog(@Action 1);
    }

    -(void)action2
    {
      NSLog(@Action 2);
    }

    -(void)action3
    {
      NSLog(@Action 3);
    }[/tt]
    Je regarde l'exemple ;)
  • Eddy58Eddy58 Membre
    04:24 modifié #20
    Mon exemple marche parfaitement, donc je ne vois pas pourquoi ça ne marcherait pas dans ton cas ? :o
  • wiskywisky Membre
    04:24 modifié #21
    L'exemple marche bien mais je n'y arrive pas. Le submenu est : (null)
    et impossible de l'ajouter. Dans IB j'ai mis un élément de menu normal (sans sous menu) avec le tag 18.
    Dans le code j'ai fait un bête copier coller de l'exemple, et ça ne marche pas.

    Il n'y a pas un moyen d'acceder au menu de l'application sans un Outlet?
  • BruBru Membre
    04:24 modifié #22
    Ton probème est tout con.

    Eddy a mis le doigt dessus plusieurs posts précédents, mais je ne pense pas que tu as tout compris.

    menuFile est ton outlet qui pointe sur le submenu "File" (ou "Fichier").
    Dans ce submenu "File", tu as un second submenu dont le tag est 18. Apparemment, ce submenu est du style "Open with..." ("Ouvrir avec...").

    Je ne sais pas trop ce que tu veux faire... Mais 2 cas peuvent se présenter :
    1. ton submenu est initialisé 1 seul fois (en début d'application par exemple).
    2. ton submenu est dynamique, et son contenu évolue toujours (en fonction de ce que tu fais dans ton appli).

    Le premier cas est le plus simple : sois tu créés des menus-item dans IB et tu les inclues dans ton submenu... soit tu fais tout ça par programmation dans le awakeFromNib de ton contrôleur par exemple :
    <br />- (void)awakeFromNib<br />{<br />&nbsp; &nbsp; NSMenuItem *openWithSubMenuItem=[[menuFile submenu] itemWithTag:18];<br /><br />&nbsp; &nbsp; NSMenu *openWithMenu=[[NSMenu alloc] init];<br />&nbsp; &nbsp; [openWithMenu addItemWithTitle:@&quot;app 1&quot; action:nil keyEquivalent:@&quot;&quot;];<br />&nbsp; &nbsp; [openWithMenu addItemWithTitle:@&quot;app 2&quot; action:nil keyEquivalent:@&quot;&quot;];<br /><br />&nbsp; &nbsp; [openWithSubMenuItem setSubmenu:openWithMenu];<br />&nbsp; &nbsp; [openWithMenu release];<br />}<br />
    



    Pour le second cas, tu dois mettre un delegate à  ton submenu, et dans la classe delegate, tu dois implémenter la méthode :
    [tt]- (void)menuNeedsUpdate:(NSMenu *)menu[/tt]

    Le delegate va se charger de recréer/reremplir dynamiquement le menu attaché au submenu en fonction de critères à  l'instant T, au moment où le système va afficher le contenu du submenu :
    <br />- (void)menuNeedsUpdate:(NSMenu *)menu<br />{<br />&nbsp; &nbsp; int ix;<br /><br />&nbsp; &nbsp; // suppression des anciens menuItems<br />&nbsp; &nbsp; for (ix=0; ix&lt;[menu numberOfItems]; ix++) [menu removeItemAtIndex:0];<br /><br />&nbsp; &nbsp; // ajout des nouveaux menuItems<br />&nbsp; &nbsp; [menu addItemWithTitle:[[NSDate date] description] action:nil keyEquivalent:@&quot;&quot;];<br />&nbsp; &nbsp; [menu addItemWithTitle:[[NSDate date] description] action:nil keyEquivalent:@&quot;&quot;];<br />}<br />
    



    Bref, que du classique...

    .
  • wiskywisky Membre
    04:24 modifié #23
    En effet que du classique...
    Merci quand même. Ca marche nickel <3
  • wiskywisky Membre
    04:24 modifié #24
    Je revient à  la charge.
    Cette boucle ne va pas bien:
    [tt] for (ix=0; ix<[menu numberOfItems]; ix++) [menu removeItemAtIndex:0];[/tt]
    La première fois elle supprime tout, la seconde il en reste 1, et après il en reste 2. Comment faire pour être sûr qu'elle supprime tout??
  • AliGatorAliGator Membre, Modérateur
    04:24 modifié #25
    Ohhh noooonnn, mon Dieu, pas ce coup classique de la suppresssion des éléments d'un (pseudo-)tableau en incrémentant l'index !

    Un coup des plus classiques !

    soit un tableau de 5 éléments tab[0] à  tab[4] que tu veux vider. Tu commences avec idx = 0, tu supprimes tab[idx] donc tab[0]... mais là  que se passe-t-il ? les éléments se décalent dans le tableau ! tab[0] est supprimé, du coup tab[1] devient tab[0], la case 2 passe en position 1, la case 3 en position 2, etc.
    Du coup au coup d'après avec idx++ donc idx=1, tu supprimes tab[1]... et le tab[0] (qui était l'ancien tab[1] qui a été décalé) ne sera pas supprimé...

    La solution ? Allez, un petit effort, tu peux la trouer tout seul !
  • BruBru Membre
    04:24 modifié #26
    Essaie ça :

    - (void)menuNeedsUpdate:(NSMenu *)menu<br />{<br />&nbsp; &nbsp; int nbitem, ix;<br /><br />&nbsp; &nbsp; // suppression des anciens menuItems<br />&nbsp; &nbsp; nbitem=[menu numberOfItems];<br />&nbsp; &nbsp; for (ix=0; ix&lt;nbitem; ix++) [menu removeItemAtIndex:0];<br /><br />&nbsp; &nbsp; // ajout des nouveaux menuItems<br />&nbsp; &nbsp; [menu addItemWithTitle:[[NSDate date] description] action:nil keyEquivalent:@&quot;&quot;];<br />&nbsp; &nbsp; [menu addItemWithTitle:[[NSDate date] description] action:nil keyEquivalent:@&quot;&quot;];<br />}<br />
    


    Il semblerait que [tt][menu numberOfItems][/tt] soit évalué plus d'une fois...

    .
  • BruBru Membre
    février 2006 modifié #27
    dans 1138834567:

    Ohhh noooonnn, mon Dieu, pas ce coup classique de la suppresssion des éléments d'un (pseudo-)tableau en incrémentant l'index !
    Un coup des plus classiques !


    Sauf que là , cher ami, on ne joue pas avec l'index !



    dans 1138834567:

    La solution ? Allez, un petit effort, tu peux la trouer tout seul !


    Allez, un petit effort, et tu comprendras !

    .
  • AliGatorAliGator Membre, Modérateur
    04:24 modifié #28
    dans 1138834679:

    dans 1138834567:

    Ohhh noooonnn, mon Dieu, pas ce coup classique de la suppresssion des éléments d'un (pseudo-)tableau en incrémentant l'index !
    Un coup des plus classiques !


    Sauf que là , cher ami, on ne joue pas avec l'index !
    Oui et non car c'est le même principe : la condition est toujours réévaluée à  chaque itération de la boucle (encore heureux puisqu'il a à  faire le test pour sortir de la boucle), donc forcément qu'elle change de valeur au cours des itérations !

    Ce n'est pas le même problème certes, mais il est très similaire, et la boucle classique :
    for(ix = [menu numberOfItems] ; ix &gt;= 0 ; ix--) [menu removeItemAtIndex:ix]
    
    telle qu'on l'écrit d'habitude quand on fait une suppression d'élément d'un (pseudo-)tableau aurait résolu le problème ;)

    Maintenant tant qu'à  faire un truc bien, autant faire un simple :
    while([menu numberOfItems]) [menu removeItemAtIndex:0];
    
    c'est aussi bien ;)

    dans 1138834679:

    dans 1138834567:

    La solution ? Allez, un petit effort, tu peux la trouer tout seul !

    Allez, un petit effort, et tu comprendras !
    Oh oh joli celle là  :D Ca me "trouve" le c*l ;D  :)

    PS à  Renaud : ouais t'as raison, et c'est de ta faute en fait :D
    [EDIT] Renaud : tricheur !
  • wiskywisky Membre
    février 2006 modifié #29
    Désolé mais là  je suis crevé et donc les truc tout simple deviennent dur.
    Mais j'ai bien essayé différente boucle et ça donne le même résultat. Je test avec le "while".

    Merci beaucoup  :o :o :o

    [edit] ça va mieux avec le while ;)
  • gibet_bgibet_b Membre
    04:24 modifié #30
    Alors j'ai une question très proche des notions abordées ici.

    Lorsque l'on supprimer un sous-menu, est-ce que cela efface de la mémoire les items qui étaient dans ce sous-menu ? Comment faire pour supprimer ces items sinon ?
  • Eddy58Eddy58 Membre
    04:24 modifié #31
    Bien sûr, une fois les items ajoutés dans le menu, tu n'as plus à  te soucier de leur destruction, car le menu fait implicitement un retain dessus lorsque tu les ajoutes, et donc il releasera ses items lui-même le moment venu. :)
Connectez-vous ou Inscrivez-vous pour répondre.