Binding d' une row avec un autre tableau

laurrislaurris Membre
14:43 modifié dans API AppKit #1
Bonjour,

J' essaie de faire ce qui suit depuis plusieurs semaines sans succes.
Dans l' article sur ce site consacré au NSArrayController, on entre les valeurs du tableau grace à  des textField (Titre, Auteur,...).
Ce que j'aimerais faire, c'est d'utiliser un autre tableau à  la place de ces textFields. Seule la deuxième colonne contenant les valeurs serait éditable.
J' ai essayé dans tous les sens avec un deuxième NSArrayController bindé au premier ou avec le "transformer" ... j' obtiens des erreurs à  chaque fois et je crois que l' objectif dépasse mes capacités.
Est-ce que quelqu' un aurait déjà  essayé de faire une telle chose ?
Merci.

Note: C'est pour faire une interface avec une base MySQL

Réponses

  • muqaddarmuqaddar Administrateur
    14:43 modifié #2
    Bonjour et bienvenue laurris.

    J'avoue que j'ai du mal à  cerner ton problème et le but du deuxième tableau.
    Tu peux en dire plus ou faire une capture d'écran stp. :)
  • TiffTiff Membre
    14:43 modifié #3
    Au lieu de n textFields l'un en-dessous de l'autre, un tableau à  n lignes, c'est ça ?
  • muqaddarmuqaddar Administrateur
    14:43 modifié #4
    A ce moment là , autant éditer les champs directement dans le premier tableau non ?
  • laurrislaurris Membre
    septembre 2004 modifié #5
    Merci de votre acceuil,

    Oui il s' agit bien d' un tableau qui correspond aux keys de la ligne sélectionnée dans le premier tableau.
    C' est vrai qu 'on pourrait éditer directement le 1er tableau mais pour des rasisons de design de l'interface je trouve qu'il est plus clair de séparer:
    - Le tableau où on visualise les lignes (des champs d'une base MySQL donc)
    - Dans un drawer à  coté un autre tableau pour éditer ces lignes.

    Comme ça j'aurais la possibilité de n' afficher dans le premier tableau que les keys voulues. Par exemple dans le premier tableau je n'aurais qu'une seule colonne avec la key "Titre" et dans le 2eme tableau la posiblité de tout éditer:

    Exemple: Le 1er tableau ressemble à  ça:

    -TITRE-
    Machin
    Truc

    Quand je sélectionne la ligne "Machin", j'ai la possibilité d' éditer toutes les keys qui ne sont pas visibles dans le 1er tableau:

    TITRE : | Machin |
    Auteur: | John    |
    Prix     : | 12 F    |

    J' ai déjà  essayé avec un NSValueTransformer de transformer une ligne en colonne pour obtenir le 2eme tableau mais les erreurs que j'obtiens sont difficiles à  expliquer parce que peu explicites.
    Voilà , j'espère que c'est plus clair.
    Laurris.
  • muqaddarmuqaddar Administrateur
    septembre 2004 modifié #6
    ok, je crois que j'ai compris.

    Alors, il faut faire 2 NSArrayController effectivement, un par tableau.
    Tu peux les initialiser dans le awakeFromNib 'on the fly", comme nous a appris ClicCool :

    - (void)awakeFromNib {
    [tableau1Controller bind: @contentArray toObject: self withKeyPath:@ta_clé options:nil];
    [tableau2Controller bind: @contentArray toObject: outletTableau1 withKeyPath:@selection.ta_clé2 options:nil];
    }


    Oublie pas de délcarer un outlet sur le premier tableau dans le .h : IBOutlet id tableau1Controller;

    Ensuite, il te reste plus qu'à  binder les colonnes de tes 2 tableaux en "arrangedObject" avec les clés correspondantes, qui pointent sur tes dictionnaires par exemple.

    En revanche, pour transformer les colonnes en lignes (cad mettre les en-tête de colonne sur la droite d'une ligne non ?), je ne sais pas si c'est faisable.
  • TiffTiff Membre
    14:43 modifié #7
    Entre NSTextField et NSTableView, une NSForm ne serait pas un bon compromis ?

    [Fichier joint supprimé par l'administrateur]
  • ClicCoolClicCool Membre
    14:43 modifié #8
    Salut ;)

    Je suis d'accord avec Tiff, le NSForm est une bonne (la meilleure ?) alternative. Tu peux éventuellement gérer à  la volée le binding, le nombre d'élements et les intitulés du NSForm.

    Sinon, pour gérer avec les bindings une autre tableView pour la saisie il te faut construire un nouvel Array contenant des dictionnaires avec 2 entrées, une pour l'étiquette des champs (1ère collone) l'autre avec la valeur à  éditer. Et là  les bindings pour la synchro des 2 tableaux est ardue.

    Autre solution: utiliser (comme les vieux ;D ) les méthodes datasource pour le 2ème tableau.

    Y'a pas moyen, en particulier, d'inverser la présentation Ligne/Colonnes dans les bindings :(
    Je vois pas mieux :(
  • TiffTiff Membre
    14:43 modifié #9
    comme les vieux

    J'allais le dire
  • laurrislaurris Membre
    14:43 modifié #10
    Bon ben j' avance mais en même temps je vous avoue que je ne comprends pas tout.

    Pour ce que je veux faire, je crois qu' un 2eme tableau est préférable à  une NSForm parce que je voudrais avoir des cellules de différents types dans le tableau: textField, popup, check box ...
    Sinon, dans ton exemple osxitan, est-ce que le concept du "binding on the fly" est vraiment nécessaire ou c'est pour pouvoir binder facilement le NSArrayController au Controlleur de l'application ?

    Finalement, pour résumer, je me retrouve avec le tableau2Controller qui contient la selection d' un NSMutableArray, c'est à  dire un NSMutableDictionary.
    Question: si je passe par un NSValueTransformer pour inverser la ligne en colonne est ce que c'est si compliqué ? Il faudrait prendre en entrée un  NSMutableDictionary:

    {Titre= My way;Auteur= Albert}

    Et obtenir un NSMutableArray:
    {
    {KEY= Titre; VALUE = My way; }
    {KEY= Auteur; VALUE = Albert; }
    ...
    }

    ... et retour pour la transformation inverse.

    Ca pourrait le faire ?
    PS: J' ai lu la doc du NStransformer mais j'ai presque jamais touché.

  • muqaddarmuqaddar Administrateur
    14:43 modifié #11
    dans 1095092270:

    Sinon, dans ton exemple osxitan, est-ce que le concept du "binding on the fly" est vraiment nécessaire ou c'est pour pouvoir binder facilement le NSArrayController au Controlleur de l'application ?


    C'est excatement ça, et ça évite d'ajouter un contrôleur objet qui fait le pont entre ton controlleur et le controlleur du premier tableau.

    En reva,che, pour inverse les lignes, ça va être coton... je pense pas comme ClicCool que ce soit faisable.
  • ClicCoolClicCool Membre
    14:43 modifié #12
    pour inverse les lignes...

    C'est même pas que ce soit difficile, c'est absolument impossible sans créer un pseudo-tableau de pseudo-dictionnaires contenant les références aux libellée et contenus. :(

    Il est question d'une plus grande souplesse à  l'avenir mais ce n'est pas documenté (même dans la àŸ de Xcode 2.0)

    P.S. J'ai pas résisté, j'fais un p'tit tour ici par GPRS, tant pis pour la facture, je suis en manque ;)
  • laurrislaurris Membre
    septembre 2004 modifié #13
    Salut,
    Bon j' ai bien saisi que les bindings ne pouvaient pas tout faire. J' ai quand même essayé un truc avec le NSValueTransformer histoire de ne pas abandonner tout de suite. Ce qui est apparemment possible, c'est d'inverser les lignes et de les avoir dans un tableau en lecture seule.
    Voilà  un projet xcode 1.5 qui fait ça en fichier joint. Pour ce qui est d' éditer les lignes et de renvoyer la valeur vers le 1er tableau, je pressens que c'est pas possible mais j' aimerais bien comprendre quand même pourquoi la methode
    - (id)reverseTransformedValue:(id)value
    n'est même pas appelée (pas de Log dans la console). Comme ça après j'aurai la conscience tranquille et je pourrais essayer autre chose. En tout cas merci de vous pencher sur mon problème.
    A+

    [Fichier joint supprimé par l'administrateur]
  • ClicCoolClicCool Membre
    14:43 modifié #14
    Salut Lauris :)

    Je ne peux me me connecter avec mon PB actuellement mais je regarderai volontiers ton code dès que possible.

    En attendant, il me semble qu'il y a peu d'avenir à  détourner les transformers de leur vocation pour ton problème. Ne serait-ce que parcequ'il te sera difficile (impossible peut-être) d'éditer les valeurs.

    La solution est plutôt dans la déclaration d'un pseudo Array (pour les lignes) et un pseudo Dico (pour le colonnes) dont tu implémentera les accesseurs indexés afin que le pseudo array affirme contenir le bon nombre de lignes et renvoie systématiquement un pseudo dico dont tu implémentera également les accesseurs afin qu'il renvoie effectivement le bon objet (pas seulement sa valeur) à  éditer (par ses accesseurs) qui alors sera convenablement mis à  jour dans le premier tableau.
    :crackboom:-
    C'est tordu mais ça marche ;)
    Et ça me semble moins tordu que le coup des transformers réversibles ou pas ;D

    J'espère que ça t'aide :)

    P.S. méfies toi, si la première tableView bindée est triée, les index de lignes ne correspondrons sans doute plus aux index originaux de ton NSMutableArray !!
  • laurrislaurris Membre
    14:43 modifié #15
    Salut ClicCool,


    J'espère que ça t'aide


    Oui ça m' aide ... à  réfléchir au problème et à  essayer d' apprendre un tas de choses que j' ignore encore sur le KVO.
    Tu m' as mis sur la piste mais j' ai du mal à  passer les vitesses (la pente est forte comme dit l'autre). Donc est ce que tu peux me préciser ce qu' est ce pseudo-array et comment je dois déclarer ses accesseurs indexés. Pour l'instant j' ai bindé programmatiquement les 2 Array controllers à  la classe controller de l'application. Le premier a un keyPath qui correspond au NSMutableArray d' origine (pour le 1er tableau) et le second est a un keyPath dont l'accesseur renvoie un Array "retourné" à  partir de la selection du 1er Array Controller.
    Est-ce que les accesseurs indexés correspondent à  countOfArray comme expliqué dans le KVO ?
    Sinon, je ne vois pas comment le pseudo-array peut renvoyer un pseudo-dico et pas un autre array inversé. Comme tu vois, je ne suis pas sûr d'avoir bien saisi le concept que tu me propose mais je n' abandonne pas  :)

    Voilà  ce que j' ai fait pour l'instant:
    <br />-(void)awakeFromNib {<br />  NSString * FilePath = [[NSBundle mainBundle] pathForResource:@&quot;TestTable&quot; ofType:@&quot;plist&quot;];<br />  Tableau = [ NSMutableArray arrayWithContentsOfFile: FilePath] ;  <br />  NSLog(@&quot;tableau %@&quot;, [Tableau description]);<br />  <br />  [tableau1Controller bind: @&quot;contentArray&quot; toObject: self withKeyPath:@&quot;Tableau&quot; options:nil];<br />  [tableau2Controller bind: @&quot;contentArray&quot; toObject: self withKeyPath:@&quot;transformedArray&quot; options:nil ];<br />}<br /><br />-(NSMutableArray*)Tableau<br />{<br />  return Tableau ; <br /> }<br /><br />- (id)transformedArray<br />{<br />  id selection= [tableau1Controller selectedObjects];<br />  NSLog(@&quot;selection %@&quot;, [selection description]);<br />  if ([selection count] == 0 ) return nil;<br />  NSDictionary * selectedRow = [selection objectAtIndex:0];<br />  <br />  NSMutableArray *keys = [NSMutableArray arrayWithObjects:@&quot;KEY&quot;,@&quot;VALUE&quot;,nil]; <br />  NSMutableArray *objects ;<br />  NSMutableArray *inversedArray = [NSMutableArray arrayWithCapacity:10];<br />  <br />  id key;<br />  NSEnumerator *enumerator = [selectedRow keyEnumerator];<br />  while (key = [enumerator nextObject]) {<br />      NSLog(@&quot;key: %@ value: %@&quot;, key, [selectedRow objectForKey:key]);<br />       objects = [NSMutableArray arrayWithObjects: key , [selectedRow objectForKey:key], nil];<br />   <br />  [inversedArray addObject:[NSMutableDictionary dictionaryWithObjects:objects forKeys:keys]];<br />  } <br />  <br />  <br />  return inversedArray;<br />  <br />}<br />
    


    Me gourre-je ? où vais-je dans le bon sens ?
    Merci.
  • ClicCoolClicCool Membre
    14:43 modifié #16
    Salut Lauris :)

    Tu vas bien dans la bonne direction :)

    Mais il me semble que tu te compliques la vie et crées de bien nombreuses variables.

    Un détail me frappe dans ton code:
    Tableau = [ NSMutableArray arrayWithContentsOfFile: FilePath] ;
    

    te renvoie un tableau "autoreleasé" non ?
    ce serait pas mieux, pour une variable d'instance comme ton tableau, d'utiliser:
    Tableau = [[ NSMutableArray alloc] initWithContentsOfFile: FilePath] ;
    
    ?


    Pour ce qui est des termes "pseudo array" et "pseudo dictionnaire" je voulais dire des objets quelquonques qui se comportent comme un array ou un dico, c'est a dire implémentant les accesseurs attendus et définis par le KV Coding.

    En effet tu as en gros 2 solutions extrèmes possible (ainsi que des tas d'intermédiaires entre les 2)

    Soit tu construit à  chaque fois à  la volée le tableau2 contenant une série de dictionnaires contenant les clefs et les valeurs de chaque clef et valeur de l'objet sélectionné dans le tableau1. je sais la phrase est longue et hardue.


    Soit tu fait simplement croire que ces objets existent mais te contentes d'implémenter les accesseurs adéquats.
    Le binding sur le pseudo Array n'ira pas vérifier que l'array existe vraiment pourvu qu'il ait une réponse adaptée quand il s'adresse aux accesseurs.
    Par exemple si tu bindes sur la model key path "tableau2", il suffit d'implémenter: countOfTableau2 et objectInTableau2AtIndex et insertObject: inTableau2AtIndex: et enfin removeObjectFromTableau2AtIndex +/- replaceObjectInTableau2AtIndex: withObject: pour que ça marche et ce même s'il n'existe pas d'array nommé tableau2.
    Tout ça te permet de rester propre et de ne pas multiplier les variables d'instances contenant tes vraies données, celles ci restant sagement dans leur unique tableau. Tes accesseurs de pseudoChoses se chargerons au fur et à  mesure de renvoyer sur les bons objets bindés sur la TableView1.

    Si c'est pas clair  :-\ j'essairais d'être plus concret la prochaine fois :)
  • laurrislaurris Membre
    14:43 modifié #17
    Salut ClicCool,

    Merci pour tes conseils grace auquels j' ai pu écrire le code qui suit. Encore une fois je bute sur le problème de savoir comment le tableau 1 va être mis à  jour avec les valeurs éditées du tableau2. Pour l'instant voilà  ce que j' arrive à  faire en utilisant la 2eme méthode que tu propose et ça marche plus ou moins. Le moins parce que les valeurs ne sont pas mis à  jour instantanément dans le tableau 2 quand on change les valeurs du tableau 1. Il faut sélectionner une ligne du tableau2 pour que les nouvelles valeurs soient pris en compte. Peut - être un mauvais réglage des options dans IB ... ?

    <br />@implementation AppController<br /><br /><br />-(void)awakeFromNib {<br />  NSString * FilePath = [[NSBundle mainBundle] pathForResource:@&quot;TestTable&quot; ofType:@&quot;plist&quot;];<br />  Tableau = [ [NSMutableArray alloc] initWithContentsOfFile: FilePath ] ; <br />  <br />  [tableau1Controller bind: @&quot;contentArray&quot; toObject: self withKeyPath:@&quot;Tableau&quot; options:nil];<br />  [tableau2Controller bind: @&quot;contentArray&quot; toObject: self withKeyPath:@&quot;Tableau2&quot; options:nil ];<br />  <br />}<br /><br /><br />- (unsigned int) countOfTableau2 <br />{<br />  id selection= [tableau1Controller selectedObjects];<br />  if ([selection count] == 0 ) return nil;<br />  NSLog(@&quot;countOfTableau2: %d&quot;, [ [selection objectAtIndex:0] count]);<br /><br />  return  [ [selection objectAtIndex:0] count]  ;<br />  <br />}<br /><br />- (NSMutableDictionary*) objectInTableau2AtIndex:( unsigned int) index <br />{<br /><br />  id selection= [tableau1Controller selectedObjects];<br />  NSDictionary * selectedRow = [selection objectAtIndex:0];<br />  <br />  if ([selection count] == 0 ) return nil;<br />  NSLog(@&quot;&#092;nselection %@&#092;n&quot;, [selection description] );<br />  <br />  NSArray *keys = [NSArray arrayWithObjects:@&quot;KEY&quot;,@&quot;VALUE&quot;,nil];<br />  NSString * key= [[selectedRow allKeys] objectAtIndex:index];<br />  NSArray *objects = [NSArray arrayWithObjects: key , [selectedRow objectForKey:key], nil];<br />  return [NSMutableDictionary dictionaryWithObjects: objects forKeys: keys] ;<br />}<br /><br />- (void) insertObject:(id)object inTableau2AtIndex:(unsigned int)index{<br />  NSLog(@&quot;insert %d&quot;, index);<br />    return;<br />}<br /><br />- (void) removeObjectFromTableau2AtIndex:(unsigned int)index{<br />    NSLog(@&quot;remove %d&quot;, index);<br />    return;<br />}<br /><br />- (id) replaceObjectInTableau2AtIndex: (unsigned int)index withObject: (id)object{<br />  NSLog(@&quot;replace %d&quot;, index);<br />  return;<br />}<br /><br />@end<br />
    




    [Fichier joint supprimé par l'administrateur]
  • laurrislaurris Membre
    14:43 modifié #18
    Salut Laurris,

    Je sais pas si tu vas trouver une solution à  ton problème mais en tout cas c'est bien que tu persiste. C' est sûr que tu n' as pas l' air d' avancer très vite. peut-être un peu paresseux ?
    On sait jamais, ça va peut-être marcher... bon courage.
    Signé: Laurris
  • muqaddarmuqaddar Administrateur
    14:43 modifié #19
    Moi je comprends pas pkoi tu fais binder le tableau2 à  self alors qu'apparemment il dépend du tableau 1 en contenu...
  • ClicCoolClicCool Membre
    14:43 modifié #20
    Oups :(

    T'as bien fait de poster pour réactiver le sujet, je l'avais oublié ! :(

    Dès que j'ai une minute je rejette un oeil promis :)
  • laurrislaurris Membre
    septembre 2004 modifié #21
    Merci ClicCool et oxitan pour votre réactivité :)
    Pour ce qui est de binder le 2eme NSArrayController au 1er, c'est vrai que ça paraitrait plus logique. Je l' ai bindé à  self pour utiliser la méthode de ClicCool de "simulation" d'un Array à  l'aide de ses accesseurs. Maintenant, si je le fais quand même, il faudrait que je sous-classe le 1er NSArrayController, non ?
    Ce que j' ai du mal à  concevoir de toute façon, c'est comment les valeurs modifiées du 2eme NSArrayController vont pouvoir être prises en compte par le 1er.
    C'est là  que ça ( je ) bloque, je pense.
    En tout cas je n' ai pas avancé depuis le projet posté en attachement et qui marche dans un sens seulement (message précédent).
    A+.
  • Eddy58Eddy58 Membre
    14:43 modifié #22
    Ton problème a l'air ardu avec les bindings Laurris :(....pourquoi tu ne repars pas de zero avec les datassources, en mettant les bindings de côté ? Tu as deux arrays, deux tableviews ? Avec ca tu peux sortir sans problème une routine pour faire ce que tu veux.... :crackboom:-
Connectez-vous ou Inscrivez-vous pour répondre.