Un tableau de IBOutlet et de IBAction

HerveHerve Membre
15:05 modifié dans API AppKit #1
Bonjour,

Je découvre votre site qui a l'air très intéressant. Débutant en Cocoa, je ferai de mon mieux pour vous aider néanmoins.

Après des années de Java, je me lance dans ObjectivC/Cocoa. Super!

Je voulais donc adapter un logiciel fait en Java pour bénéficier des méthodes plus complètes de Cocoa. Dans ce logiciel, j'avais fait des tableaux de boutons initialisés grâce à  des tableaux de valeurs et une grille de placement sur la fenêtre.
Le code était du genre :
for (int i = 0; i&lt;66; i++){<br />			BoutonTriangle[i] = new JButton();<br />			BoutonTriangle[i].setPreferredSize(new Dimension(10,10));<br />			GBC.gridx = (XTriangle[i]+2); GBC.gridy = (YTriangle[i]+2);<br />			GBC.gridwidth = 2; GBC.gridheight = 2;<br />			GBC.weightx = 2; GBC.weighty = 2;<br />			BoutonTriangle[i].addActionListener<br />			(new EcouteBoutonTriangle (i, ValCoul, ValTri, ValPal, Tab, palette, action));<br />			BoutonTriangle[i].setBackground(<br />					Color.getHSBColor(action.getHueFond(), ValTri[i].getSaturation(),<br />					ValTri[i].getBright()));<br />			add (BoutonTriangle[i], GBC);<br />			}		<br />


Ce code permettait de créer en quelques lignes 66 boutons, puis 44 pour une autre série, et enfin 24 pour la dernière, avec placement, couleur, et écoute (l'équivalent Java du IBAction).

En Cocoa, j'ai vu les "matrix", mais j'ai dû dériver NSButton pour les mettre en couleur, et les CustomView utilisées dans InterfaceBulder ne semblent pas être "matriciables" (pardon pour le barbarisme).

Verriez-vous une possibilité autre que de rentrer 134 IBOutlet  et autant de IBActions dans le code de ObjectivC? (cent trente quatre, et plus encore!! mais les autres sont des boutons Aqua normaux, et il n'y en a pas trop)

Merci d'avance si vous avez une idée.

Réponses

  • tabliertablier Membre
    15:05 modifié #2
    Je ne l'ai jamais fait, mais il me semble que la création de boutons par programme est possibles. Il y a un adhérent qui a déjà  posé des questions la-dessus (à  rechercher sur ce site). Si je me souviens bien, il voulait faire une interface utilisateur entièrement par programmation.
    D'autre part, le passage d'un langage a un autre n'est pas toujours facile. Chaque langage à  ses particularités et essayer de copier une méthodologie d'un langage à  un autre n'est pas forcément la chose à  faire.
  • CéroceCéroce Membre, Modérateur
    janvier 2011 modifié #3
    Bienvenue à  toi, Hervé.

    dans 1295553330:

    En Cocoa, j'ai vu les "matrix", mais j'ai dû dériver NSButton pour les mettre en couleur, et les CustomView utilisées dans InterfaceBulder ne semblent pas être "matriciables" (pardon pour le barbarisme).


    Oui, NSMatrix ne fonctionne qu'avec des NSCell (je n'ai jamais eu besoin d'utiliser les matrices, alors je ne connais pas les détails).
    Pour info, NSMatrix est une implémentation du design pattern Prototype. Ce système a donc été conçu pour améliorer les performances (vitesse/occupation mémoire) quand on a un grand nombre de contrôles identiques. Note que ce ne sont pas des NSButton dans la matrice, mais des NSButtonCells.

    NSButtonCell ne peut pas être colorié, il me semble. Sous-classer est la seule solution.

    dans 1295553330:

    Verriez-vous une possibilité autre que de rentrer 134 IBOutlet  et autant de IBActions dans le code de ObjectivC? (cent trente quatre, et plus encore!! mais les autres sont des boutons Aqua normaux, et il n'y en a pas trop)


    On peut tout à  fait créer les boutons par le code. Cependant, dans ce cas, tu n'auras pas d'outlets, puisque les outlets sont des pointeurs sur objets initialisés lors du désarchivage du NIB. Tu auras de "simples" pointeurs.

    ça donnerait quelque chose comme ça:
    <br />@interface VueControlleur : NSViewController<br />{<br />	NSButton *buttons[ROWS][COLUMNS];	<br />	<br />}<br /><br /><br />- (void) createButtons<br />{	<br />	NSUInteger row, column;<br />	<br />	for(row = 0; row &lt; ROWS; row++)<br />	{<br />		for(column = 0; column &lt; COLUMNS; column++)<br />		{<br />			// Créer le bouton<br />			NSRect frame = NSMakeRect(...)	// Rect qui définit le cadre du bouton<br />			NSButton *button = [[NSButton alloc] initWithFrame:frame];<br />			buttons[row][column] = button;<br />			<br />			// Fixer sa cible et l&#39;action<br />			NSActionCell *cell = [button cell];<br />			[cell setTarget:self];<br />			[cell setAction:@selector(buttonClicked:)];<br />			<br />			// Insérer dans la vue parente<br />			[[self view] addSubview:button];<br />		}<br />	}<br />}<br /><br />- (void) buttonClicked:(NSButton *)sender<br />{<br />	// Trouver le bouton dans &#39;buttons&#91;]&#91;]&#39;<br />	// et lancer l&#39;action appropriée	<br />}<br />
    


    Note bien que pour ce que tu veux faire, la bonne méthode est d'utiliser une matrice, encore une fois, pour des questions de performance.

    Pour finir, je rejoins l'avis de tablier: ne cherche pas trop à  retranscrire ton expérience Java dans Cocoa. Swing n'est certainement pas un modèle à  suivre; mon expérience est que créer des IHM par des ressources est bien plus rapide et précis que le faire par le code.
  • HerveHerve Membre
    15:05 modifié #4
    Merci à  tous les deux pour vos réponses très inspirantes. Je suis d'accord avec tablier : il faut m'adapter au nouveau langage. Ceci dit, si je l'ai choisi, c'est bien parce que j'y cherche des choses nouvelles non offertes par les autres.

    Céroce, j'ai lu que l'on pouvait écrire en Cocoa avec "l'orthographe" (ou le style) Java. Mais j'ai appris le style C aussi. C'est marrant d'ailleurs que ce langage accepte deux "orthographes" : Apple nous surprendra décidément toujours...

    J'allais dans mes réflexions du jour vers ce type de solution: un NSView avec un IBOutlet et un IBAction, liés à  une classe qui gère les choses en interne. Il faut que j'étudie les "NSCell" et dérivations : je les ai un peu snobé - ou plutôt survolé - lors de la lecture du livre de Hillegards parce que il traitait de tableaux type Excell, mais il semble que ce soit plus riche que cela...

    Je travaille ce WE, je vous tiens au courant. Si je trouve la solution, je la copie ici. ;)
  • HerveHerve Membre
    15:05 modifié #5
    Je dis n'importe quoi en fait!!!

    Après une journée passée à  étudier les NSCell sans en voir l'intérêt pour mon problème, je me rends compte que Céroce a fait un travail magnifique. Merci beaucoup Céroce.

    En fait, je ne savais pas que l'on pouvait instancier un tableau avec "NSButton *buttons[ROWS]
    ;"  D'où ma confusion idiote.

    Merci beaucoup Céroce.
  • HerveHerve Membre
    15:05 modifié #6
    La méthode semble marcher du point de vue de l'initialisation des boutons (vérification en console avec NSLog), mais ils ne s'affichent pas. Je pense que cela vient du fait que le tableau n'est peut-être pas initialisé.

    Je voulais savoir :
    J'ai écrit :
    NSButton *mesBoutons[66];  (dans "AppView.h")

    Est-ce que j'aurais dû mettre [66][1]???
    En écrivant cela, est-ce que je crée un "CFArray", un "NSArray" ou autre chose encore?? Cette écriture me rappelle les tableaux 2D de Java.

    J'ai essayé pour l'initialiser (dans la méthode init de l'AppView) :
    mesBoutons = [[NSArray alloc]init];    ou  mesBoutons = [[CFArray alloc]init];
    le compilateur rejette les deux.

    Je pense que cela ne s'affiche pas à  cause de l'initialisation de ce tableau. Qu'en pensez-vous?
  • AliGatorAliGator Membre, Modérateur
    15:05 modifié #7
    Si tu utilises les [] dans la déclaration de ta variable, tu déclares un tableau C.
    Rien à  voir avec la couche Objective-C du langage finalement. C'est un tableau statique de type ceux qu'on voit en langage C, rien à  voir donc avec les NSArrays.

    Si tu veux utiliser les NSArray, il faut bah... utiliser les NSArray :P

    Donc, pour solution en utilisant des tableaux C statiques (ne pouvant être redimentionnés, et sur lesquels tu ne pourras pas appeler de méthodes pour demander leur nombre d'éléments, etc) tu fais pareil que si tu avais un seul NSButton sauf que tu le mets dans la i-ème case du tableau mesButtons :
    // Dans le .h<br />NSButton* mesBoutons[66]; // déclare un tableau C de 66 éléments de type &quot;NSButton*&quot;<br /><br />// Dans le .m<br />mesBoutons[0] = [[NSButton alloc] initWithFrame:...]; // on crée un nouveau bouton et on le met dans la case 0 du tableau<br />// autre solution, faire en 2 temps<br />NSButton* tmp = [[NSButton alloc] initWithFrame:...];<br />mesBoutons[1] = tmp;
    
    Et la solution avec NSArray :
    // Dans le .h<br />NSArray* tableauDeBoutons; // En Objective-C un tableau (NSArray) peut contenir n&#39;importe quel NSObject, on n&#39;a pas à  préciser le type du contenu<br /><br />// Dans le .m<br />NSButton* btn1 = [[NSButton alloc] initWithFrame:...];<br />NSButton* btn2 = <br /><br />tableauDeBoutons = [[NSArray alloc] initWithObjects:btn1, btn2, ..., nil];
    
    Y'a encore d'autres solutions avec les NSArray, entre autres utiliser plutôt un NSMutableArray qui peut donc modifier son contenu, et faire "addObject" à  chaque bouton... bref t'as le choix.
    Après si ton nombre de boutons est statique par essence, le tableau C est plus simple et plus rapide. L'avantage de NSArray étant que c'est lui-même un NSObject (qui peut être manipulé comme tel, être mis dans un conteneur, être archivé/sérialisé, suit les règles de gestion mémoire et retiens son contenu...) donc à  toi de voir.



    Dans tous les cas, quelle que soit la solution que tu choisis... c'est bien beau de créer un NSButton ou une NSView ou quoi que ce soit par code... mais ne pas oublier dans ce cas de l'ajouter en tant que subview à  une vue qui est à  l'écran, sinon tu ne la verra jamais à  l'écran ;) Par exemple l'ajouter à  ta keyWindow.
  • HerveHerve Membre
    janvier 2011 modifié #8
    Merci AliGator pour ta réponse très complète. J'oubliais le C évidemment.

    J'écris ici que les boutons sont initialisés mais ne s'affichent pas : une unième modif et ils s'affichent!!

    Aussi je réédite mon message. Les boutons s'affichent enfin, mais ne répondent pas encore.

    Merci à  tous pour votre aide.
  • HerveHerve Membre
    15:05 modifié #9
    Ce devrait être la dernière question :

    Pour que mes boutons communiquent avec l'AppController, j'ai mis dans la classe MonBouton :
    - (void) mouseDown:(NSEvent *)theEvent{<br />	NSLog(@&quot;mouseDown bouton triangle&quot;);<br />	[self action];<br />}<br />
    


    et dans l'AppController :
    - (void) monBoutonClick: (MonBouton *) sender{<br />	NSLog(@&quot;appel boutonTriangleClick&quot;);<br />
    


    Le mouseDown du bouton est bien appelé, mais n'est pas reçu par l'AppController. La méthode "- (void) monBoutonClick: (MonBouton *) sender" n'est pas appelée.  J'ai essayé aussi [self target], [self state] etc.

    Sans la méthode  mouseDown dans MonBouton, j'ai un NullPointerException lorsque je clique sur le bouton.

    Vous n'auriez pas une idée s'il vous plaà®t??

    Mes débuts en Java avaient été difficiles aussi. Après, ça allait mieux...
  • AliGatorAliGator Membre, Modérateur
    janvier 2011 modifié #10
    Oulà  !
    Révise le Programming Guide sur le mécanisme "Target-Action", et la documentation sur NSControl !!!
    Tu n'as pas à  surcharger mouseDown (et c'est une mauvaise idée sur les NSButtons vu qu'ils implémentent déjà  cette méthode avec derrière le mécanisme de target/action justement) mais plutôt à  indiquer un target et une action à  ton bouton

    Control and Cell Programming Guide
    NSControl class reference : implementing the Target/Action Mechanism
  • HerveHerve Membre
    15:05 modifié #11
    Tu as raison AliGator. Le message d'erreur en console est :
    2011-01-23 16:20:27.900 LesCouleurs[431:a0f] -[AppView buttonClicked:]: unrecognized selector sent to instance 0x1001755a0<br />
    


    L'AppController reçoit l'info mais ne sait pas l'interpréter.

    Bon, je cherche encore...

  • HerveHerve Membre
    janvier 2011 modifié #12
    Ouf, j'y suis arrivé.

    En fait, j'avais changé la méthode
    - (void) buttonClicked:(NSButton *)sender
    


    par
    - (void) buttonClicked:(MonBouton *)sender
    


    Aussi...

    Dans cette méthode, j'ai fait :
    - (void) buttonClicked:(NSButton *)sender<br />{	for (int i = 0; i&lt;66; i++){<br />		if (sender == (MonBouton *) boutonsCouleur[i]){<br />			[self setValSat1:[boutonsTriangle[i] valSat]]; //mes méthodes de classe<br />			[self setValBright1:[boutonsTriangle[i] valBright]];<br />		}<br />	}<br />
    


    ça marche. Y a t-il mieux??

    Merci encore à  tous pour votre aide. J'essaierai d'aider ici aussi, en fonction de mes progrès...

    Problème résolu : l'indique t-on dans ce forum? Je ne vois pas l'onglet.
  • AliGatorAliGator Membre, Modérateur
    15:05 modifié #13
    Rebonjour Hervé,

    Une question : pourquoi as-tu sous-classé NSButton ? Est-ce vraiment toujours utile maintenant que tu as compris comment tout cela marche ? A priori il n'y a aucun besoin de le faire (sauf si tu veux effectivement personnaliser plus encore le look du bouton ou quoi, mais ce n'est pas nécessaire pour juste créer une grille de boutons et leur affecter une action)

    Sinon pour "Résolu" il n'y a aucun bouton sur les forums pour le faire (cela a fait l'objet d'un petit débat pour/contre, enfin bon) mais si tu souhaites le mentionner, tu peux éditer ton tout premier message de ce sujet pour modifier le titre et rajouter "[Résolu]" au début du titre du sujet.
  • HerveHerve Membre
    15:05 modifié #14
    Ben en fait, non seulement les boutons sont en couleur, mais ils doivent pour une partie d'entre eux pouvoir changer de couleur durant l'utilisation du logiciel. Et puis je suis content d'avoir appris à  faire une interface sans Interface Builder. Les trois-quart du temps, je ferais avec IB, mais parfois j'ai envie de faire autre chose.

    Pouvoir personnaliser l'interface utilisateur et créer toutes sortes de contrôles est, avec la richesse des classes multimédia et son une des raisons qui m'ont fait passer à  Cocoa, malgré le fait de devoir apprendre un nouveau langage.

    Merci encore AliGator pour ton aide, ainsi qu'à  Céroce.
  • laudemalaudema Membre
    15:05 modifié #15
    dans 1295812854:

    ....Et puis je suis content d'avoir appris à  faire une interface sans Interface Builder. Les trois-quart du temps, je ferais avec IB, mais parfois j'ai envie de faire autre chose.

    avec ou sans utiliser IB, mais probablement plus facile avec, tu as aussi la classe NSCollectionView qui permet d'afficher la même vue (qui peut être un bouton donc puisqu'un NSButton est un NSControl qui est une NSView..) dans autant de lignes et colonnes que tu le veux en les liant à  un tableau via un NSArrayController. Je me demande si ça aurait pu aider car il me semble que c'est la manière la plus "Cocoa" avec bindings et MVC. http://developer.apple.com/library/mac/#documentation/cocoa/Reference/NSCollectionView_Class/Introduction/Introduction.html
  • CéroceCéroce Membre, Modérateur
    janvier 2011 modifié #16
    NSCollectionView ne sert pas à  ça. Cette classe utilise une vue prototype qui peut contenir d'autres vues, mais ne fonctionne pas comme une matrice: les vues sont réparties comme du texte (de gauche à  droite et de haut en bas). De plus leur ordre peut être modifié, auquel cas Core Animation est utilisé.

    Bref, c'est difficile à  expliquer sans la voir en fonctionnement, mais le rôle de NSCollectionView est différent, et je ne crois pas que ce soit ce qu'Hervé cherchait à  faire.
  • HerveHerve Membre
    15:05 modifié #17
    Je ne connaissais pas cette classe en effet. Je suis très heureux de me mettre à  Cocoa. Je pense que cela va être très intéressant de développer avec ce langage.
Connectez-vous ou Inscrivez-vous pour répondre.