Outlet qui n'en fait qu'à sa tête...
chaps31
Membre
Situation que je ne comprend pas : dans une classe j'ai une méthode qui rempli des outlets liés à des NSTextField dans IB via un setStringValue, pour tester la méthode est déclarée IBAction et liée à un bouton. Je clique hop mes NSTextField de l'interface se remplissent, impec.
Maintenant j'ai un peu modifié : dans une autre classe une méthode cré un objet de la classe précédente (retypée en void avec un argument), j'envoie une requête sur cette méthode à l'objet avec l'argument (en fait du coup les valeurs rentrées dans les NSTextFields sont piochées dans une base postgresql), tout va bien des variables de la méthode prennent bien les valeurs de ma base sauf que... setStringValue ne marche plus... pas de warning rien, d'ailleurs j'ai passé en argument de setStringValue des chaines @blablabla idem ça ne marche plus les NStextField de l'interface ne s'affiche plus... [nom setStringValue:@nomtest]; où nom est l'outlet...
Je ne sais pas si j'ai été clair... Si l'erreur vous parait évidente, je suis preneur...
Maintenant j'ai un peu modifié : dans une autre classe une méthode cré un objet de la classe précédente (retypée en void avec un argument), j'envoie une requête sur cette méthode à l'objet avec l'argument (en fait du coup les valeurs rentrées dans les NSTextFields sont piochées dans une base postgresql), tout va bien des variables de la méthode prennent bien les valeurs de ma base sauf que... setStringValue ne marche plus... pas de warning rien, d'ailleurs j'ai passé en argument de setStringValue des chaines @blablabla idem ça ne marche plus les NStextField de l'interface ne s'affiche plus... [nom setStringValue:@nomtest]; où nom est l'outlet...
Je ne sais pas si j'ai été clair... Si l'erreur vous parait évidente, je suis preneur...
Connectez-vous ou Inscrivez-vous pour répondre.
Réponses
Les IBOutlets ne sont réellement liés à des objets de ton interface graphique (ton NIB) que quand tu as étali les liens outlet/objet dans IB... ce que tu fais en reliant un outlet d'une instance de ta classe, instance créée au sein même du NIB avec l'objet du NIB (TextField ici) à connecter.
Si tu crées une nouvelle instance de ta classe où tu as tes outlets directement (avec un alloc/init) les outlets ne seront plus reliés à rien.
Pour t"en sortir il faut utiliser :
- soit ne pas créer une nouvelle instance de ta classe toi même (mais laisser le NIB la créer lors de son décodage)
- soit utiliser un contrôleur intermédiaire (revoir ton modèle MVC ?)
- soit si tu dois vraiment créer une nouvelle instance de ta classe (car tu prévois d'avoir aussi plusieurs instances de ton interface, etc) créer ta classe en chargeant le NIB par code
après ça dépend de l'architecture que tu as prévu pour ton appli
Si j'ai bien compris le lien entre les outlet de ma classe et mon interface ne sont vrai que pour l'instance créée sous IB, le beau cube qui apparait lors de l'instanciation sous IB. Après si je cré dans le code de nouveaux objets de cette classe ils ne seront pas liés... Ca me rappel ce que m'a dit un ami codeur (un vrai dont c'est le boulot), "normalement tu crées un objet pour l'interface et un avec lequel tu travailles..."
Bon je ne comprends pas bien comment faire mais avant de reposer la question je vais réfléchir et essayer...
Là je ne vois pas trop ce qu'il a voulu dire.
Peut-être l'association Contrôleur (l'objet avec lequel on travaille) - Vue (l'objet pour l'interface).
Mais sans doute pas 2 contrôleurs. Une interface (ou une partie d'interface) doit être relié à un unique contrôleur.
Bon visiblement il est temps pour moi de me mettre sérieusement au NScontroller, juste une question si dans binding d'un objet (NSTextField) de l'interface le controller key de value est sur selection (en ayant "bind to" mon controller) cet objet va prendre la valeur du model key path de l'objet controller sélectionné (j'ai bon j'espère). Mais je n'arrive pas à comprendre comment je peux ajouter une valeur à mon NScontroller...
Dans Object Class name de l'inspecteur du controller j'ai mis le nom de la classe "modèle" pour mon controller et je veux en fait lors d'un clic sur une ligne d'une tableview de l'interface passer les valeurs du datasource à mon controller (il les refilera alors à des NSTextField). Je me casse les neurone pour cette action qui doit "remplir" mon controller sasn succès....
Une aide ?
NB : il me semblait qu'il fallait que je relie colonne par colonne de ma tableview, avec en réglage pour le bind de chacune arrangedObject... sauf que le menu controller key de mes colonnes contient selection, seletedobject... mais pas arrangedObject...
Classe DisplayTable : gère l'affichage d'une tableview dans l'interface en prenant les valeurs dans un base postgresql, c'est le datasource de ma tableview : marche impec s'affiche sans soucis.
Chaque ligne représente un élément de la base avec plusieurs colonnes de valeur, dont une id non affiché dans la tableview
Classe Elément : qui représente l'organisation d'un élément
Dans l'interface j'ai la tableview, quand on clique sur une ligne cela lance une méthode de la classe Elément afin d'afficher dans des NSTextField de l'interface toutes les données de l'élément sélectionnée .
J'essaie de le faire via un NScontroller mais ça ne fonctionne pas. En gros le controller devrait repérer le clique sur la selectedrow et recevoir l'ID (voir même toute les colonnes) qui est (sont) transmis(es) à la classe Elément qui affiche tout dans les NSTextField..
Donc avec les Outlets ça ne marche comme dis précédemment, mais j'ai beau lire plein de trucs sur les controller je n'arrive pas à les appliquer à mon cas, une bonne âme qui aurait le temps de décortiquer ça à un débutant ? Merci
Suis-je seul ? Une aide serait vraiment la bien venu, car project Omega et autres sources ne me permettent pas de résoudre ce probème de compréhension... je vais attaquer un 3eme jour sans solution, Bigre programmer en PHP-MySQL ne m'a jamais posé autant de problème (merci de ne pas me soumettre l'idée de continuer plutôt le PHP :P )
Bon alors j'ai pas bcp de temps à te consacrer mais comme ça dans le vif je vais essayer de te faire un topo :
Si je comprends bien tu as une base de données postgresql et tu veux remplir ta TableView avec les résultats d'une requête sur cette BdD, en gros.
Le truc c'est donc :
- Dans ton modèle, tu stockes les données. Dans ton cas les résultats de ta requête (tes instances de "Element", une par résultat de la requête ?), stoqués dans un NSMutableArray de Element par exemple (si j'ai bien compris l'utilité de ta classe Element)
- Dans IB tu définis que le dataSource de ta TableView ce sera ton Controller
- Dans les méthodes du protocole NSTableDataSource ([tt]tableView:objectValueForTableColumn:row:[/tt] etc.) implémentées donc dans ton dataSource qui sera ici ton Controlleur, tu récupères la bonne instance de Element en fonction de la row, puis le bon champ de ton instance de Element en fonction de la tableColumn (typiquement en fonction de [tt][tableColumn tag][/tt])
Ainsi au niveau outlets et connexions, il n'y a que la TableView qui est connectée à ton Controlleur (en le définissant comme son dataSource dans IB). Les classes côté modèle (donc la classe Element si c'est bien ça), que tu peux avoir à allouer à la volée (à chaque fois que tu as de nouveaux résultats de requête) n'ont pas à avoir accès aux outlets.
Du coup tu n'as plus de cas où tu as des objets alloués par code (alloc/init) qui auraient besoin d'outlets, et tous tes objets avec des connexions dans IB ne sont alloués que par IB et pas par code via un alloc/init.
En espérant que ça a pu t'éclaircir les idées... (Ou alors j'ai pas compris ton problème ou l'architecture que tu as donnée à ton programme ?)
NSController est une classe à vocation Bindings plutôt.
Dans le modèle simple, un Contrôleur de l'application est en général un NSObject dans les Cocoa-Applications, et une instance de NSDocument dans Document-Based Applications.
Rien n'empêche de faire autrement, mais il faut avoir une raison de le faire ...
Par miracle ?
Ceci dit, il faudrait
1) que l'action du Table View ait pour Target le Contrôleur que tu as mis en place,
2) que ce contrôleur puisse avoir accès aux données, soit par un outlet vers le data-source, soit par lecture dans la table view,
3) qu'il possède un outlet vers le text field,
4) qu'il réagisse au clic selon tes désirs.
PS : ce post est-il bien placé???
Afin d'être efficace je vais préciser mon projet une fois pour toute putôt que simplifier en permanence et du coup parfois être mal compris. Le but arrêter de payer cher un logiciel auquel il manque des fonctionnalité et essayer de le créer par moi-même :
J'ai une 2 classes :
une classe "Personnes" avec en variables d'instances : le nom, prénom, adresse, etc... et pour l'instant une seule méthode, une méthode d'initialisation qui prend un argument. Cet argument est un index, avec cet index la méthode va chercher l'enregistrement correspondant dans une base postgresql et rempli les variables d'instance avec les valeurs trouvées dans l'enregistrement sélectionné. Cela crée un objet "personne", ce qui fonctionne parfaitement bien.
Une classe "DisplayTable" dont le rôle est de récupérer l'ensemble des enregistrements de la base postgresql mais uniquement pour l'id le nom l'adresse et le téléphone puis de les afficher dans une tableview de l'interface. Je récupère tous les enregistrements de la base dans un tableau dont je me sert pour remplir ma tableview de l'interface en suivant le protocole DataSource (implémentation des 2 méthodes nécessaires)ce qui fonctionne parfaitement bien.
Mon interface comporte actuellement ma tableview qui affiche l'ensemble des enregistrements de ma base et en dessous plusieurs textfield pour l'instant vide. A ce stade tout marche impeccable ma tableview se rempli automatiquement (je me sert d'un awakefromNIB pour remplir mon tableau dans DisplayTable).
Et donc LE problème : lorsque je clique sur une ligne de ma tableview : cela lance une méthode de DisplayTable (qui est une IBAction répondant au clic sur la tableview grace à connection-target/action-Actions in DisplayTable maméthode dans l'inspecteur de ma tableview sous IB). Cette méthode récupère l'index de la ligne (selectedrow) puis s'en sert pour récupérer dans le tableau source (au même index) l'id de la personne (présent dans le tableau source mais qui ne s'affiche pas dans la tableview).
Puis toujours dans la même méthode un [[Personne alloc]initperson:idpersonne] appel la méthode précédemment décrite pour initialiser un objet de la Classe Personne et création d'un objet personne avec ses variables remplies,
tout marche impeccable mon objet se crée et ses variables prennent les bonnes valeurs, je contrôle cela avec des NSLog
Jusqu'ici tout va bien... :P
Et là arrive le mur...
Dans mon interface je veux que mes textfields prennent les valeurs des variables d'instances de mon objet "personne" que je viens de créer via le clic sur la tableview et la création de l'objet avec l'IBAction (un textfield pour le nom, un pour le prénom....), mais malgré tous vos conseils ça ne fonctionne pas.
J'ai créé un controller sous IB, je lui spécifie dans ses attributs qu'il est de la classe Personne, là je pense que je n'arrive pas à le lier à la source qui est l'objet personne nouvellement créé, car j'ai créé un bind-selection d'un textfield (via le nom) vers le controller et j'ai un "NO Selection"....
Donc il est clair que je ne sais pas comment faire pour que mon controller "lise", "voit", "se serve"... de mon objet Personne...
Si je réfléchi, je dirais que le controller devrait être informé de la création de l'objet "personne" intégrer ses variables (KVC ?) et mes textfields devraient être à l'écoute (KVO ?) du controller pour prendre ses valeurs de variables... Difficile ce système MVC, du moins pour mon cerveau...
Voilà , le jour où j'aurais compris comment faire je pense que j'aurais franchi un grand pas, merci d'avoir pris le temps de lire ce long descriptif, et si vous avez un peu de temps pour me réaiguiller sur la bonne voie je serais un "codeur" heureux...
Cela crée une instance de la classe "Personnes"
Ok
Dans ton interface doit sans doute se trouver un objet de la classe DisplayTable, que tu associe comme IBOutlet data source pour ta Table View
Ou alors tu fais l'association programmatiquement dans une méthode awakeFromNib ?
Un NSLog(@%d,[myTableView selectedRow]); placée dans cette IBAction te fournit-t-il ce que tu attends à ce stade ?
Cela semble répondre à ma question précédente.
Pour que le binding puisse avoir lieu, il faut que l'instance "personne" soit référencée par une variable newPerson de ton DisplayTable, afin que la trace en soit conservée à la sortie de méthode IBAction. Sinon le binding ne sait pas où chercher.
Cela ne me semble pas nécessaire.
Dans le panel bindings du TextField, il doit suffir d'associer le champ newPerson de l'instance de TableDisplay
Ceci dit sans avoir essayé, ... , et un peu à l'aveugle puisque je n'ai pas sous les yeux ton MainMenu.nib
Sinon j'ai un parse erreur étrange dans le DisplayTable.h : je crée donc une variable d'instance newPersonne :
#import"Personne.h"
...
Personne *newPersonne;
Il me sort "parse error before Personne", mais si je vire cette ligne plus de parse error cela vient bien de la ligne...
oui, essaye cela.
#import <Cocoa/Cocoa.h>
@class Personnes;
@interface DisplayTable : NSObject
{
...
Personnes * newPerson;
}
.....
@end
#import <Cocoa/Cocoa.h>
#import "pgCocoaDB/Connection.h"//Classe pour la connection à la base
#import "Proprio.h"
@interface DisplayTable : NSObject {
NSMutableArray *tableclients;//table source clients
NSMutableArray *tableanimaux;//table source animaux
IBOutlet NSTableView *tableView;//tableviewclient
Connection *thecon;//pour connection a la DB via pgCocoaDB
Proprio *leclient;
}
.../DisplayTable.h:20: error: parse error before 'Proprio'
Et pour :
#import <Cocoa/Cocoa.h>
#import "pgCocoaDB/Connection.h"
@Class Proprio.h;
@interface DisplayTable : NSObject {
NSMutableArray *tableclients;//table source clients
NSMutableArray *tableanimaux;//table source animaux
IBOutlet NSTableView *tableView;//tableviewclient
Connection *thecon;//pour connection a la DB via pgCocoaDB
Proprio *leclient;
}
...DisplayTable.h:11: error: parse error before '@'; tokn
.../DisplayTable.h:20: error: parse error before 'Proprio'
J'ai lu ton code, c'est bien comme ça que je le comprenais (tu codes bien plus proprement que moi ), d'ailleurs je l'ai compilé du velours... Là je ne comprends plus rien à mon avis il y a un truc énorme que je ne vois pas
Connection * theCon;
"before Proprio" cela peut être à la ligne précédente.
As-tu inséré correctement les Classes et Frameworks ?
Essaye aussi un Clean Target, il y a peut-être des transformations non prises e compte ?
C'et sans problème ... :kicking: :brule: :brule: :brule:
C'est juste la ligne Proprio *leclient; qui pose problème, car si je l'enlève, la compilation fonctionne sans erreur....
Cette ligne ne présente aucune erreur. C'est ailleurs.
Ton fichier Proprio.h n'inclut-il pas "DisplayTable.h" créant ainsi une référence circulaire ?
@class Proprio; mais pas @Class Proprio.h;
hum... ok, je l'avais oublié cette référence dasn proprio.h... bon y'a plus de parse error maintenant... Pas encore tout à fait prêt à quitter le forum débutant ::)
Par contre du coup j'ai testé ton astuce, malheureusement ça ne fonctionne pas, j'ai donc créé une variable d'instance (nommée leclient) dans DisplayTable qui prend la valeur d'une instance de la classe Proprio, initialisée grace à ma méthode d'initialisation de variables de la classe Proprio, puis j'ai testé pour un TextField de mon interface avec un binding vers la variable leclient de DisplayTable "leclient.nom" dans le key path, pas de bug mais pas d'affichage non plus dans le textfield du nom... coriace le problème...
J'ai pensé que cela venait du type de la variable nom de la classe proprio (NSString) j'ai donc changé en NSTextField, en Outlet NSTextField (initialisant la valeur avec une setStringValue), rien ne fonctionne, le NSLog d'ailleurs renvoie un (null) pour le nom lorsqu'il est de type NSTextField mais je ne sais pas comment l'afficher dans un NSLog (%s string, %i entier, %d décimal, %@ heu... quand je ne sais pas ::) ).
Encore une idée ? Merci encore
Quelle astuce ?
As-tu lu l'article d'Hoksitan sur les bindings de NSTableView ?
Dans ton cas, un Outlet sur ton text field dans le contrôleur permettrait d'écrire ce que tu veux lors de la création du nouveau client. Ton contrôleur intervient surement lors de cette création puisque tu es en système data source.
De ne pas me servir d'un controller et de créer un bind vers leclient.nom de DisplayTable depuis mon NSTextField où je veux qu'il y ai le nom qui s'affiche.
J'ai lu pas mal de trucs je ne sais pas si j'ai lu celui-là j'ai fais un saut sur les tuto, pas vu mais j'ai vu un PDF que tu as fais sur les binding que je vais lire.
J'allais poster ce qui précède et je lis ton nouveau post, tu me conseilles donc en fin de compte de créer un contrôleur ?
Je confirme que débuter par les bindings ça n'éclaircit pas les idées. Les bindings sont des boà®tes noires, bien pratiques, mais noires. Il vaut mieux dans un premier temps voir clairement ce qui se passe.
Donc un binding dans un cas très simple, c'est simple, mais si ça se complique on y voit plus clair en direct.
Donc soit faire de DisplayTable le contrôleur de ton appli, soit rajouter un autre contrôleur qui a (au moins) un outlet sur la table view, un outlet sur text field, et peut-être sur l'instance de DisplayTable. Â
Heu... comment je fais ? DisplayTable est mon DataSource pour ma table view... :)beta:
Tu ajoutes les -(IBAction) changeMonTruc:(id) sender dans le code de cette classe.
Tu y définis les IBOutlets nécessaires pour que cela puisse se faire.
Ca marche merveilleux, fantastique tu m'as donnné le déclic, je t'en remercie
Ha... enfin j'avance, philippe49 ça y est tu es ma référence en codage, merci beaucoup de m'avoir sorti de là .
Pour la petite histoire je m'appelle aussi philippe, "solidarité prénomiale", je te suis grandement reconnaissant.
C'est un grand jour pour tous les Philippe