Multi-tableview

2

Réponses

  • muqaddarmuqaddar Administrateur
    novembre 2004 modifié #32
    C'est à  dire que Renaud me force à  ne pas utiliser les bindings pour bien comprendre la POO... et il a pas tort. :)

    Dommage donc que les deux exemples utilisent les bindings...

    J'aimerais bien voir un filtrage sur des arrays sans rearrangedObjects qui est réservé aux bindings apparemment. :)
  • 17:34 modifié #33
    C'est plutôt dans l'autre sens ;) tant que t'as pas bien compris la POO, les bindings risquent de plus embrouiller qu'aider, car ils demandent un niveau d'abstraction supplémentaire.

    Ceci dit, pour les prefs, tu peux les utiliser ;)
  • muqaddarmuqaddar Administrateur
    17:34 modifié #34
    Mouais, en tout cas, pour ma première petite appli, j'avais pas eu de mal à  faire découler le contenu d'un tableau par rapport à  un autre avec les bindings, chaque tableau utilisant une classe propre en plus.

    Là , j'ai tout ds mon controlleur et ça commence à  être la panique à  bord.
  • Eddy58Eddy58 Membre
    17:34 modifié #35
    dans 1101126450:

    Là , j'ai tout ds mon controlleur et ça commence à  être la panique à  bord.

    N'hésites pas à  scinder ton appli en plusieurs controleurs si tu peux. Par exemple, pour gérer un NSTabView qui comporte pas mal d'éléments, je met un contrôleur par TabItem... Tu verras, ça va tout de suite mieux avec une bonne structure...:)
  • muqaddarmuqaddar Administrateur
    17:34 modifié #36
    Bein justement Eddy, si tu peux m'en dire plus sur la façon "d'éclater" son application...
    Quand tu fais plusieurs controlleurs, comment gères-tu l'import des uns vers les autres, tu n'as jamais de pb d'outlet ou autre ? Il retrouve tjs ses petits ?
  • Eddy58Eddy58 Membre
    17:34 modifié #37
    Il n'y a rien que tu ne saches faire Oxitan dans le faites de faire plusieurs controleurs... :)
    Il faut dès le début bien déterminer quels sont les objets que tu vas gérer avec tel contrôleur, quelles données, quelles classes, et quels seront les liens entre les différents controleurs.
    Je crée un contrôleur pour chaque ensemble principal de mon interface graphique. Ensuite si tu sous-classes par exemple une NSView, et bien tu gères cette NSView avec le contrôleur qui sera responsable de la partie de l'interface graphique où réside la NSView.
    Une fois que tu as déterminé ceci, tu n'as plus qu'a ramener tes différents outlets et actions sur les controleurs prévus, puis gérer ton programme normalement. Le point le plus délicat à  gérer est l'échange de données entre les contrôleurs. Tu peux transmettre des pointeurs sur les données soit par méthode accesseur, soit par notification. Personnellement j'ai un peu trop tendance à  abuser des notifications :P, car c'est très rapide à  mettre en oeuvre, mais je ne me suis pas encore interessé à  une comparaison vitesse/efficacité avec les méthodes accesseurs....
    Voilà , si tu as d'autres questions n'hésites pas... ;)
  • muqaddarmuqaddar Administrateur
    novembre 2004 modifié #38
    OK.
    Les variables globales de mon controlleur principal que j'initialise à  partir de plists dans le -(id)init sous forme d'array, comment je peux les récupérer dans le controlleur qui s'occupe ensuite de gérer uniquement cet array, avec tri, filtrage...etc ? Par des méthodes accesseurs dans ce controlleur secondaire (je préfère plutôt que les notifications ? J'ai du mal à  visualiser le truc. ???

    Remarque, je peux l'initialiser dans mon controller dédié...
  • mpergandmpergand Membre
    novembre 2004 modifié #39
    Pas si fastoche la programmation par objets, hein ?

    Essayons de voir les structures possibles de ces contrôleurs dans ton cas.
    Un des principes de base de la prog objet est l'encapsulation des données, chaque objet ne devant avoir accès qu'aux données qui lui sont strictement nécessaires

    Première possiblité la structure en étoile:

    C1
    /
    C -- C2
    \
    C3

    Ici le contrôleur principal (C), renseignera les contrôleurs auxilaires sur les données les concernant.

    Une autre possiblité est une hiérarchie du type parent/enfant, ce qui est ton cas en fait:

    C --> C1 --> C2 --> C3

    Dans ce cas, chaque contrôleur renseignant leur contrôleur enfant.

    Après tu peux écrire des accesseurs si tu le désires (pour faire comme les pros) mais le plus important est de partir d'une structure d'objets saine.

    Quant aux notifications évoquées par Eddy, c'est effectivement très pratique pour permettre la communication entre deux objets n'ayant aucun lien selon la structure du programme, mais ceci doit rester l'exception, l'abus de cette technique devrait à  coup sur révéler un problème de conception de base du prog.

    Dans ton cas, communiquer entre contrôleurs par notification est plutôt une idée bizarre ;)
  • muqaddarmuqaddar Administrateur
    17:34 modifié #40
    Merci pour ce petit cours mpergand.

    En fait, ce matin, j'ai tout explosé. Je me retrouve avec 5 controlleurs dont un principal. Chaque controlleur a son dico et son array propres en variable d'instance et aussi ses 4 accesseurs. Le controlleur principal initialise surtout les vues et les barres d'outils. Bref, ça a l'air pas mal et pour l'instant, ça tourne comme avant. Maintenant que ma structure a l'air nettement plus propre, je vais pouvoir enfin essayer ce fameux filtrage.
    J'ai aussi 2 catégories pour les 2 barres d'outils et une autre pour la gestion des erreurs vers laquelle j'envoie un niveau d'erreur et un numero d'erreur.
    Enfin, le plus gros gros du travail est à  venir avec la classe principale de mon application, une classe "DataObjects" qui va communiquer avec les 5 controlleurs. On verra... :)
  • mpergandmpergand Membre
    17:34 modifié #41
    Maintenant que ma structure a l'air nettement plus propre, je vais pouvoir enfin essayer ce fameux filtrage.


    C'est bien le plus dur à  comprendre dans ton prog vu de loin ;)
    Est-ce le controler final, responsable de l'affichage, qui effectue ce filtrage ou est-ce le controler supérieur, si j'ai bien compris, chaque controler ne gère qu'un sous ensemble des données gérées par le controler supérieur. Dans le cas d'une structure parent/enfant, evidemment.

    On pourrait aussi faire se filtrage dans l'objet Model (DataObjets ?) à  la manière d'une base de données: je veux toutes les données ayant tel et tel critère.
  • muqaddarmuqaddar Administrateur
    17:34 modifié #42
    Bueno,

    Voilà  comment je voulais procéder.

    Dans la classe controlleur de la tableView sélectionnée :

    - (void)tableViewSelectionDidChange:(NSNotification *)aNotification <br />{<br />	//on prend l&#39;id du pays de la ligne selectionnee<br />	NSDictionary* tempPaysDico = [NSMutableDictionary dictionary];<br />	NSString* tempPaysId&nbsp; = [NSMutableString string];<br />	tempPaysDico = [paysArray objectAtIndex: [[aNotification object] selectedRow]];<br />	tempPaysId = [tempPaysDico objectForKey:@&quot;paysId&quot;];	<br />	NSLog(@&quot;tempPaysId : %@&quot;, tempPaysId);<br />	<br />	[self sendLigne: tempPaysId];<br />}
    


    Ensuite, je voulais utiliser la même méthode que pour filtrer un tableau par recherche, avec  arrangeObjects, mais en envoyant le paysId et pas une chaà®ne de recherche.

    Seulement, arrangeObjects est une méthode de NSArrayController, et mon controlleur est une sous-classe de NSObject et pas de NSArrayController.

    Il faudrait que je crée une classe rienq ue pour le filtrage qui est une sous-classe de ArrayController. Seulement, comme lui envoyer le numéro de ligne [self sendLigne: tempPaysId]; depuis ma classe controller ?
  • muqaddarmuqaddar Administrateur
    novembre 2004 modifié #43
    En fait, si je fais une sous classe de NSArrayController de mon controlleur, ça bronche pas. :)

    Reste que rearrangeObjects n'a pas l'air d'appeler arrangeObjects.

    - (NSArray *)arrangeObjects:(NSArray *)toutesLesLignes {<br />	NSLog(@&quot;ligne2 :%@&quot;, ligne);<br />	// si le champ est vide, alors afficher tout, en demandant a super de trier<br />	if ([ligne length] == 0) return [super arrangeObjects: toutesLesLignes]; <br />		<br />	// creation d&#39;une nouvelle liste provisoire, dans laquelle on va mettre toutes les lignes correspondant à  la recherche<br />	NSMutableArray* filtreArray = [NSMutableArray arrayWithCapacity: [toutesLesLignes count]];<br />	<br />	// l&#39;enumerateur permet d&#39;examiner de facon simple tous les objets d&#39;une liste<br />	NSEnumerator *enumerateur = [toutesLesLignes objectEnumerator];<br />	id item;<br />	NSRange range;<br />	while (item = [enumerateur nextObject]) {<br />		range = [[item valueForKeyPath:@&quot;regionsDico.regionId&quot;] rangeOfString: ligne options: nil];<br />		if (range.location != NSNotFound) [filtreArray addObject:item];<br />	}<br />			<br />	// travail terminee, super va maintenant trier la liste des lignes trouvees<br />	return [super arrangeObjects: filtreArray]; <br />} <br /><br />- (void)sendLigne:(NSString *)s {<br /><br />	[self setLigne: s];<br />	[self rearrangeObjects];<br />}<br /><br />- (void)setLigne:(NSString *)s<br />{<br />&nbsp; &nbsp; [s retain];<br />&nbsp; &nbsp; [ligne release];<br />&nbsp; &nbsp; ligne = s;<br />	NSLog(@&quot;ligne :%@&quot;, ligne);<br />}
    
  • muqaddarmuqaddar Administrateur
    17:34 modifié #44
    Bon, j'ai tout changé, et finalement j'ai procédé comme cela :

    Alors dans le controlleur pays :

    - (void)tableViewSelectionDidChange:(NSNotification *)aNotification <br />{<br />	//on prend l&#39;id du pays de la ligne selectionnee<br />	NSDictionary* tempPaysDico = [NSMutableDictionary dictionary];<br />	NSString* tempPaysId  = [NSMutableString string];<br />	tempPaysDico = [paysArray objectAtIndex: [[aNotification object] selectedRow]];<br />	tempPaysId = [tempPaysDico objectForKey:@&quot;paysId&quot;];<br />	<br />	[regionsController sendPaysLigneToRegions: tempPaysId];<br />}
    


    On envoie le paysId au controlleur de régions.

    Et dans le controlleur de régions, on reconstruit le tableau filtré :

    //====================//<br />//FILTRAGE DES REGIONS//<br />//====================//<br /><br />- (void)sendPaysLigneToRegions: (NSString *)stringSend<br />{<br />	regionsFiltreArray = [[NSMutableArray alloc] init];<br />	[regionsFiltreArray removeAllObjects];<br />	<br />	if ([stringSend isEqualToString: @&quot;&quot;]) regionsFiltreArray = regionsArray;<br />	else {<br />		NSEnumerator* enumerator = [regionsArray objectEnumerator];<br />		NSDictionary* dict;<br />		while (dict = [enumerator nextObject]) {<br />			if ([[dict objectForKey:@&quot;paysId&quot;] isEqualToString: stringSend]) {<br />				NSMutableDictionary *tempDict = [NSMutableDictionary dictionary];<br />				[tempDict setObject: [dict objectForKey:@&quot;regionId&quot;] forKey:@&quot;regionId&quot;];<br />				[tempDict setObject: [dict objectForKey:@&quot;regionName&quot;] forKey:@&quot;regionName&quot;];<br />				[regionsFiltreArray addObject: tempDict];<br />			}<br />		}<br />	}	<br />	[regionsTable reloadData];<br />}
    


    Et voilà , ça marche nikel mon filtrage. :spot:
    Pas besoin de s'embêter avec NSArrayController...
  • muqaddarmuqaddar Administrateur
    17:34 modifié #45
    Une dernière petite chose.
    Pour ma notification, enfin mon delegate selectionDidChange, quand je clique dans le tableau mais pas sur une ligne, ça me renvoie ça :

    Exception raised during posting of notification.  Ignored.  exception: *** -[NSCFArray objectAtIndex:]: index (-1) beyond bounds (2)

    J'ai essayé de rajouter cela sans succès :
    if (![[aNotification object] selectedRow]) { code }

    ou encore cela :
    if (![[aNotification object] selectedRow] != -1) { code }

    mais j'ai tjs le message, je veux simplement lui dire de rien faire si aucune ligne n'est sélectionnée...
  • muqaddarmuqaddar Administrateur
    17:34 modifié #46
    if (![[aNotification object] selectedRow] != -1) { code }

    marchait bien... je sais pas ce que j'avais bidouillé.
    :)
  • muqaddarmuqaddar Administrateur
    17:34 modifié #47
    J'ai pas trouvé dans la doc comment on demande à  une tableView de ne sélectionner aucune ligne ?
    Une idée ?
  • ClicCoolClicCool Membre
    17:34 modifié #48
    As tu essayé d'utiliser deselectRow: indexDeLaLigneSelectionnee ?
  • muqaddarmuqaddar Administrateur
    novembre 2004 modifié #49
    mouais, ça veut dire qu'il faut encore demander quelle ligne est sélectionnée... je pensais qu'il y avait un truc du genre : [maTable déselectionne toutes leslignes];


    EDIT :

    [regionsTable deselectRow: [regionsTable selectedRow]];

    ça marche. 
  • ClicCoolClicCool Membre
    17:34 modifié #50
    et un selectRow .... avec -1 comme argument ? (j'ai jamais essayé)
  • BruBru Membre
    17:34 modifié #51
    Ben, mon p'tit Alex, que vois je dans la doc Apple sur NSTableView ?

    deselectAll:

    - (void)deselectAll:(id)sender

    Deselects all selected rows or columns if empty selection is allowed; otherwise does nothing. Posts NSTableViewSelectionDidChangeNotification to the default notification center if the selection does in fact change.
    As a target-action method, deselectAll: checks with the delegate before changing the selection, using selectionShouldChangeInTableView:.

    See Also: – allowsEmptySelection, – selectAll:, – selectColumn:byExtendingSelection:


    Le lien, c'est ici.

    .
  • muqaddarmuqaddar Administrateur
    17:34 modifié #52
    non, j'ai essayé aussi avec -1 , ça foire.

    Tient me voilà  "artisan chocolatier".
  • BruBru Membre
    17:34 modifié #53
    Ah, j'oubliais : il faut que la sélection vide ("empty selection") soit permise dans ta table.

    Soit tu fais ce réglage dans IB (dans la palette), soit tu utilises la méthode [tt]- (void)setAllowsEmptySelection:(BOOL)flag[/tt] de NSTableView.

    .
  • muqaddarmuqaddar Administrateur
    17:34 modifié #54
    Ah bein merci mon p'tit bru !

    [regionsTable deselectAll: self];

    nikel chrome !

    Oui, oui, allowsEmptySelection est activé.
  • muqaddarmuqaddar Administrateur
    17:34 modifié #55
    Coucou, c'est moi.

    Bon, mon filtrage de tableView et bien... il va falloir faire la même chose ailleurs sur des popup. Un popup alimente un autre popup suivant sa sélection. Gloups.
    Je compte me servir de addItemsWithTitles avec un array pour l'alimenter. La chose que je me demande est la suivante : dans un popupButton, on met une liste de strings mais pas des dico avec 2 clés par exemple, or moi je veux que mon nouvel enregistrement enregitre l'id correspondant à  la selection dans le popup et pas la string qui s'affiche.
    Je vois pas trop comment m'y prendre à  part en comparant la string du popup à  celle de mon array de dico qui contient aussi cette string en plus de la key Id pour ensuite insérer cette key Id... Suis-je clair ?
  • ChachaChacha Membre
    17:34 modifié #56
    Ce message est plus rassurant qu'utile : apparemment il n'y a pas d'astuce, on ne peut effectivement que stocker des NSString dans un popup. Du coup, oui, il faut à  la main faire le lien avec un objet particulier.

    Dans la doc sur les popup, il y a l'exemple suivant, où l'on voit bien que c'est la galère pour identifier un objet en fonction de la sélection:

    - (void)setLanguage:(id)sender{
        NSString *title = [languagePopUp titleOfSelectedItem];
        if ([title isEqualToString:@English])
            language = English;
        else if ([title isEqualToString:@French])
            language = French;
        else if ([title isEqualToString:@German])
            language = German;
        else if ([title isEqualToString:@Spanish])
            language = Spanish;
        else if ([title isEqualToString:@Swedish])
            language = Swedish;
    }
  • mpergandmpergand Membre
    17:34 modifié #57
    Il est possible d'associer à  chaque menuItem un autre objet grace à  setRepresentedObject.
  • ChachaChacha Membre
    17:34 modifié #58
    mpergand a raison !
    j'ai essayé de faire un petit exemple : une fenêtre qui contient un bouton popup, et une classe dans mon NIB qui connaà®t le popup sous le nom "popup"
    Le code suivant est tout à  fait convenable

    //quand on change la selection du popup
    -(IBAction) changeSelection:(id) sender
    {
      id selected = [sender selectedItem]; //renvoie un NSMenuItem
      id object = [selected representedObject]; //le fameux representedObject
      int value = [object intValue]; //bon là  je vais afficher sa valeur
      NSLog(@selected = %d, value); //affiche bien l'int voulu !
    }

    -(void) awakeFromNib
    {
      NSLog(@awake from nib);
      //je créee trois objets
      NSNumber* one = [[NSNumber alloc] initWithInt:1];
      NSNumber* two = [[NSNumber alloc] initWithInt:2];
      NSNumber* three = [[NSNumber alloc] initWithInt:3];
      NSLog(@one = %x, one);
      NSLog(@two = %x, two);
      NSLog(@three = %x, three);

      //je remplis mon popup
      [popup removeAllItems];
      [popup addItemWithTitle:@one];
      [[popup itemAtIndex:0] setRepresentedObject:one];
      [popup addItemWithTitle:@two];
      [[popup itemAtIndex:1] setRepresentedObject:two];
      [popup addItemWithTitle:@three];
      [[popup itemAtIndex:2] setRepresentedObject:three];
    }

    Et ça marche!
  • ClicCoolClicCool Membre
    17:34 modifié #59
    Oui en effet
    c'est un peu sur ce schéma que sont également conçus les bindings avec la possibilité de binder le content ou le content value, le selected object ou le selected value ;)
  • muqaddarmuqaddar Administrateur
    17:34 modifié #60
    Bien, ça a l'air cool comme solution setRepresentedObject.
    J'espère juste que ça demandera pas trop de code.

    Merci en tout cas mpergand et chacha pour cette info. :)
  • muqaddarmuqaddar Administrateur
    17:34 modifié #61
    J'ai un petit soucis avec ceci :

    [addDico setObject: [newOriginePopup indexOfSelectedItem] forKey:@origine];

    me renvoie un warning : make pointer without a cast

    [addDico setObject: [[newOriginePopup indexOfSelectedItem] intValue] forKey:@origine];

    encore pire...
    en fait, je voudrais mettre le chiffre de l'index dans <string>1</string dans mon dico....
Connectez-vous ou Inscrivez-vous pour répondre.