Réponse à mpergand : pour retracer mon historique (voir ma présentation), j'ai appris Qt en essayant de faire une application me permettant de lire une succession d'images d'un geste sportif. A chaque image, je clique sur un point particulier, la main par exemple, afin de pouvoir définir sa vitesse entre chaque image.
Donc pour l'instant, j'essaye de m'approprier le MVC sous cocoa. Au fil des réponses, je crois comprendre qu'il faut que je crée une classe spécifique Model dans la quelle je "stocke" toutes mes variables, sachant que par la suite, elles seront importées d'une bd SQlite.
Donc mon exercice aujourd'hui est de récupérer la position d'un click, je suppose ensuite "stocker" ces valeurs x et y dans mon Model et tant qu'à faire, afficher ces valeurs dans ma fenêtre.
Mais actuellement, tu as raison, je ne suis pas encore du niveau pour comprendre tout les réponses, même si cela pas d'un bon sentiment et je trouve ça génial. Mais maintenant, à force d'essayer, j'ai l'impression de m'enliser.
Donc mpergand, si tu comprends mon objectif et que tu as quelque chose de simple pour moi, je suis preneur.
Réponse à mpergand : pour retracer mon historique (voir ma présentation), j'ai appris Qt en essayant de faire une application me permettant de lire une succession d'images d'un geste sportif. A chaque image, je clique sur un point particulier, la main par exemple, afin de pouvoir définir sa vitesse entre chaque image.
Merci d'avance
Ok, donc c'est bien à ta vue (sous classe de NSImageView ?) de mémoriser les points lors des clics.
Après pour le traitement, sauvegarde, etc, c'est dans cette vue qu'il faut définir les méthodes adéquates comme:
setPoints(NSArray* points);
-(NSArray*) points;
Et comme ça dans ton NSDocument tu récupères ces points et tu en fais ce que tu veux: sauvegarde dans un fichier, bdd, etc.
pas de KVO, bindings, NSCoding ou je ne sais quoi d'autre ...
Par ailleurs, ça me rappelle un question sur un autre forum où le but était de dessiner un rectangle par dessus une image, voir ICI
Il me semble que c'est cependant ce que j'ai fais : voici mes codes. En fait, j'arrive bien à stocker de Vue à ModelData mais si je veux récupérer les valeur sous My document, elles sont à nouveau égale à 0. Idem de MyDocument à ModelData et récupérer ces valeur sous Vue. J'ai le sentiment que lorsque je quitte soit Vue soit MyDocument, ModelData est remis à 0 /huh.gif' class='bbc_emoticon' alt='???' />
Si tu pouvais me dire ce qui ne fonctionne pas, même si , je sais, ce code doit certainement comprendre des trucs bizarre !!
J'ai le sentiment que lorsque je quitte soit Vue soit MyDocument, ModelData est remis à 0
Cette phrase n'a aucun sens .. Qu'appelles-tu "quitter Vue" ?? On ne quitte pas une vue ! Une vue est un objet, c'est à dire un certain nombre d'octet en mémoire duquel tu as un pointeur, noté *lObjet. Celui-ci te permet d'accéder aux valeurs de ses variables d'instance (ou les pointeurs vers d'autres objets), et d'envoyer des messages à cet objet. Point. On ne "rentre pas", ni "quitte pas" une vue : On peut créer la vue, la détruire, modifier ses variables d'instances ...
Voici le guide qui te permettra d'avoir les bases.
Pour ton code, il y a des choses étranges. Par exemple, tu crées une instance de ton ModelData dont tu conserves un pointeur comme variable d'instance de MyDocument (tu remarqueras que je me force à utiliser le vocabulaire, et que mes phrases sont longues, de manière à SAVOIR DE QUOI ON PARLE)
Tu crées aussi une AUTRE instance de ModelData, A CHAQUE CLICK, et tu gardes le pointeur comme variable d'instance de le Vue ... sauf qu'au prochain click, ce sera une nouvelle instance de ModelData qui sera créée et le pointeur mesDatas pointera sur cette nouvelle instance, l'ancienne étant désallouée depuis longtemps (car autorelease au moment de l'allocation donc pas retenue) => Est-ce bien cela que tu voulais faire ?
Le pointeur monModel NE POINTE PAS sur la même instance de ModelData que la vue !!
Pour la classe ModelData, pas grand chose à dire, mis à part qu'il faudrait implémenter les fameuses méthodes encodeWithCoder et decodeWithCoder si tu veux pouvoir archiver les données pour enregistrement du fichier.
Essaye de corriger le tir en ne créant qu'une seule instance de ModelData, et de maintenir les pointeurs nécessaires comme variables d'instance des objets qui ont besoin d'y accéder. N'hésite pas à poser les questions nécessaires.
<br />
- (void)windowControllerDidLoadNib:(NSWindowController *) aController<br />
{<br />
[super windowControllerDidLoadNib:aController];<br />
// Add any code here that needs to be executed once the windowController has loaded the document's window.<br />
[vue setdelegate:self];<br />
}<br />
<br />
-(void) clicDansMaVueAuPoint:(NSString) PointStr<br />
{<br />
// appelé lors d'un clic dans la vue<br />
NSLog(@"%@",pointStr);<br />
}<br />
Je me suis plongé ds vos conseils. Pas simple /crybaby.gif' class='bbc_emoticon' alt=' ' /> J'ai donc essayé de faire quelque chose de peut être plus précis : créer une classe réunissant toutes mes variables que j'importerai d'une base de données. Puis j'ai chercher à modifier des variables au sein de cette classe via myDocument et bien sur afficher la modif et enfin, via une 3e classe traitant du click souris, j'ai cherché à modifier la valeur de mes variable (seulement x) ds ma classe spécifique et afficher le changement. J'ai cru comprendre que puisque ma classe des data était instancier par ds MyDocument, il fallait "passer par là ". Donc mon cheminement est le suivant, récupérer la valeur du clique, la transférer ds MyDocument qui affectera la nouvelle valeur à data.
Bon le résultat fonctionne et donc je voulais avoir votre avis sur ce que j'ai fait, même si les termes ne sont pas exactes et sachant que j'essaye de réfléchir comme le curseur du déboggeur ! c'est à dire, "par où ça passe" ligne par ligne ...
[font=arial, helvetica, sans-serif]Tu as simplifié le problème en replaçant id delegate par MyDocument* doc ![/font]
[font=arial, helvetica, sans-serif]Fonctionnellement, c'est pareil sauf que ta vue dépend strictement de MyDocument, si tu décides par la suite d'utiliser un autre controller, il te faudra modifier cette variable dans ta vue.[/font]
D'ailleurs, parlons de cette vue, que fait-elle ?
Pour moi c'est une "meta vue", c'est à dire qu'elle est composée de plusieurs autres vues et c'est à cette "meta vue" de les gérer.
Pour l'instant, je vois que tu as une vue ZoneClick et deux textFields val et nouveauVal,
alors, au lieu de passer par ton document, pourquoi ne pas le faire directement dans la vue ?
En effet, j'ai cherché à simplifier au plus le problème car pour moi, il y a des choses encore obscures, donc en cherchant à ne pas (ou peu) utiliser le builder, je comprends mieux ce que je fais.
Pour cette vue, il s'agissait plus d'un exercice visant à utiliser et "jongler" avec plusieurs classes, c'est donc la raison de cette démarche.
Du reste, si tu as un peu de temps à me consacrer, j'aimerais vérifier si ce que j'ai compris est juste. En effet, en lisant les doc et le bouquin de Hillegas, tout est limpide ! sauf que en face de mon écran, ce n'est pas du tout pareil !
Donc si j'ai bien compris, une classe fonctionne en "autonomie" (indépendante des autres classes) et comporte des objets que l'on déclare dans le .h. Prenons un exemple d'objets compris dans cette classe : NSTextField *a et NSTexteField *b. Toujour dans le .h, on "se donne les moyens" de pouvoir les lire et les modifier avec @property (readwrite, copy) NSTextField *a et idem pour b. @synthesize a (et b ) sont à placer dans le .m après @implementation (ce qui serait "l'équivalent" des getter et setter).
Déjà , est ce que j'écris est juste ??
Bon ensuite, j'ai compris que chaque classe est autonome. Pour pouvoir accéder à une classe depuis une autre classe, il faut (si j'ai bien compris le terme) l'instancier. Un exemple : ma classe Classe0 comprend les a et b précédent. Une 2e classe appelée Classe1 comprend les 2 mêmes types d'objet mais nommés c et d. Et une 3e classe appelée Classe2 comprend aussi les 2 mêmes types d'objets appelés e et f.
Si je veux accéder à Classe0 "depuis" Classe1, il faudrait que dans Classe1, (et dans init) je fasse quelque chose comme :
maClasse0 = [[Classe0 alloc]init]; (sachant que ds Classe1.h j'ai inscrit Classe0 *maClasse0;. maClasse0 devra aussi être supprimé ensuite via un -(void) dealloc { [maClasse0 dealloc]... .
A partir de cela, je pourrai accéder à a et b ([[maClasse0 a] intValue]), et les modifier ([[maClasse0 a] setIntValue : 3] par exemple);
Est ce que je me trompe ??
Encore une fois, j'essaye de comprendre à fond le système d'où cette simplification sans doute à l'extrême.
Si je veux accéder à nouveau à Classe0 mais cette fois-ci de Classe2 (sous un autre menu ou via un bouton spécifique par exemple); je devrai faire la même démarche.
Sauf que (sauf si j'ai fais une erreur et c'est fort probable), si je modifie a ou b via Classe1 (comme je viens de le faire) et que je veuilles consulter l'entier de a via cette fois-ci Classe2, je ne retrouve pas la valeur 3 (pour reprendre l'exemple précédent) mais 0, comme si Classe0 à été "initialisée" et remise à 0.
Pourtant, le pointeur de Classe0 crée ds Classe1devrait "m'écrire la valeur 3" dans Classe0 et donc, en faisant un [ [maClasse0 a] intValue] dans Classe2, je devrais retrouver 3 ?
Je ne sais pas si j'ai été clair dans mon explication mais si tu pouvais prendre un peu de temps pour m'éclairer, sachant que je suis encore débutant, que je cherche à comprendre la logique (donc sans l' IB) avec des termes que je puisse comprendre !!
En effet, si cela n'est pas limpide pour moi, je ne vois pas comment je pourrais avancer dans mon apprentissage.
Toujour dans le .h, on "se donne les moyens" de pouvoir les lire et les modifier avec @property (readwrite, copy) NSTextField *a et idem pour b.
Je ne sais pas comment tu comprends la chose, mais c'est bien la référence qui est modifiable. C'est à dire que readwrite indique qu'on peut remplacer la référence vers un NSTextField par une référence vers un autre.
@synthesize a (et b ) sont à placer dans le .m après @implementation (ce qui serait "l'équivalent" des getter et setter).
En fait, il synthétise automatiquement les accesseurs. C'est à dire que tu as tout à fait le droit d'écrire ta propre implémentation de - (void) setTextFieldA:(NSTextField *)textField ou - (NSTextField *)textFieldA.
Pour pouvoir accéder à une classe depuis une autre classe, il faut (si j'ai bien compris le terme) l'instancier.
Non, ça c'est faux. La classe est le moule pour un objet.
Instancier une classe signifie créer un objet (une instance) à partir de cette classe.
Ce qui nous ramène à cette histoire de référence: pour appeler les méthodes d'instance (celles qui commencent par un moins), il faut une référence vers un objet. Et ce n'est pas forcément ton objet qui a instancié cet objet, ça peut aussi une autre objet qui a passé la référence, ou ton objet qui a demandé une référence vers un objet.
Au niveau de la gestion mémoire, ceci amène deux questions:
1) Quand dois-je libérer la mémoire occupée par un objet ?
2) Combien de temps une référence reste-elle valide ?
C'est questions étant relativement complexes, je te conseille de lire attentivement le chapitre dans le livre d'Hillegass.
Ceci étant Cérose, peux tu être plus clair dans l'explication suivante, quitte à l'appuyer d'un exemple très très simple:
" pour appeler les méthodes d'instance (celles qui commencent par un moins), il faut une référence vers un objet. Et ce n'est pas forcément ton objet qui a instancié cet objet, ça peut aussi une autre objet qui a passé la référence, ou ton objet qui a demandé une référence vers un objet."
Est que cela se traduirait concrètement au fait que si je veux lire un -(void) d'une autre classe, je ne suis pas obliger de créer l'objet via alloc init ? Donc comment est il possible de faire ?
Je pense que tu confonds un "objet", c'est à dire un certain nombre d'octets, avec un pointeur, qui contient lui l'adresse mémoire où se situe l'objet.
L'objet unDRH fait ceci : Secretaire *maSecretaire = [ [Secretaire alloc] initWithCouleurDeCheveu:blonde] => Ceci a pour effet de créer et initialiser un objet, donc de réserver un coin de mémoire où seront stockés toutes les choses nécessaires, et en plus d'assigner l'adresse de ce coin de mémoire à la variable maSecretaire qui est donc un pointeur. (comme indique *)
Imaginons que l'objet unDRH ait aussi un pointeur monPDG, qui pointe vers un objet de type PDG .
Les objets de la classe PDG peuvent avoir une variable d'instance Secretaire *maSecretaire. Si c'est la même secrétaire que le DRH, il faudra que unDRH envoie le message monPDG.maSecretaire=maSecretaire pour assigner l'adresse (N° du bureau...) de cette secrétaire du DRH à la variable d'instance maSecretaire du PDG.
Ainsi, unDRH et unPDG auraient tous les deux accès ici à la même INSTANCE de Secretaire, à savoir une blonde.
Si unPDG appelait la méthode de classe [Secretaire alloc] pour l'assigner à maSecretaire, cela créerait une nouvelle instance stockée à une autre adresse mémoire... pourquoi pas l'initialiser avec des cheveux bruns ?... Le PDG et le DRH n'auraient donc pas la même secrétaire ici.
Est que cela se traduirait concrètement au fait que si je veux lire un -(void) d'une autre classe, je ne suis pas obliger de créer l'objet via alloc init ? Donc comment est il possible de faire ? Merci d'avance
Voici un exemple concret. Une objet Voiture possède une référence vers un Volant:
Maintenant, un objet Garage, qui dispose de références vers des objets Voiture et Reparateur veut lancer des réparations. Il devra appeler la méthode -reparerVoiture: et passer un pointeur sur la voiture pour cela:
Tu as raison : tes excellentes explications confirment que j'ai tendance à confondre objet et pointeur.
Du coup, ton exemple m'a énormément éclairé notamment sur le rôle réel de alloc init, ce qui fait que le 1er et dernier paragraphe m'ont réellement aidés. Cela me permet de comprendre mon problème : j'ai créé 2 objets alors que j'aurais dû en créer 1 et utiliser que le pointeur ds mes 2 classes.
Cependant, le paragraphe du milieu est encore un peu flou. Je comprend qu'il n'est pas utile pour monPDG de créer l'objet Secretaire puisqu'il est déjà créé dans unDRH et qu'il suffit de faire appel à son pointeur *maSecretaire. Ca, maintenant c'est fluide (enfin je pense!)
Bon, maintenant, c'est le comment qui me bloque. Tu dis : "imaginons que unDRH a aussi un pointeur monPBG". Sous quelle forme ? : tu le créés comme maSecretaire ( PDG *monPDG [[PDG alloc]init]) dans unDRH ?
Et comment envoyer le message à monPDG pour qu'il récupère le pointeur *maSecretaire?
Désolé Ceroce mais ça ne m'aide pas beaucoup. Je vais attendre la réponse de Mick, sauf si tu peux également reprendre son exemple et répondre à la question que je viens de lui poser.
Et bien, dans la classe PDG, tu déclares une variable d'instance Secretaire *maSecretaire : un PDG aura donc la possibilité de maintenir un lien (en tout bien tout honneur) avec une secrétaire.
Le tout est d'assigner l'adresse de la bonne secrétaire à cette variable d'instance, c'est ton soucis je crois. C'est pour cela que dans mon exemple, j'ai supposé que le DRH avait un pointeur vers le PDG, ainsi, le DRH a pu assigner l'adresse de sa secrétaire à la variable d'instance maSecretaire du PDG avec le message monPDG.maSecretaire=maSecretaire : (Equivalent à :
[monPDG setMaSecretaire:maSecretaire];
).
De ce fait, le DRH et le PDG ont la même secrétaire. PDG n'a pas créé une nouvelle instance de secrétaire.
Bon, en même temps, c'est pas souvent que le PDG et de DRH ont la même secrétaire...
Attention par contre à la gestion mémoire... Si par exemple le setter setMaSecretaire du PDG ne fait qu'une simple assignation, alors si le DRH disparait, au moment de sa disparition il enverra le message release à la secrétaire qui sera donc virée sur le champ. le PDG n'aura alors plus de secrétaire, et si il ne le sait pas, il lui enverra des messages qui n'arriveront jamais (CRASH). Il vaut mieux alors que dans le setter de la secrétaire du PDG on retienne la secrétaire en lui envoyant un message retain. Du coup, même si le DRH est viré, la secrétaire ne l'est pas vu que le PDG a pris la responsabilité...
Enorme merci Mick mais c'est bien ce que j'avais compris et fait, mais ça ne marche pas (je vais craquer !). Dans mon essai, la secrétaire correspond à Data, le DRH à MyDocument et lePDG à ZoneClick.
Voici le fichier de mes codes. Si tu pouvais prendre un peu de temps pour regarder, je suis complètement perdu. J'ai compris la logique que tu viens de m'expliquer avec brio (j'insiste la dessus car en tant que formateur, il est rare de rencontrer des explications de ce type, donc chapeau !), mais je n'arrive pas à le retranscrire dans mon code. Qu'est ce que j'aurais manqué, oublié, mal mis /huh.gif' class='bbc_emoticon' alt='???' />
Bon, j'ai commenté un peu ton code pour essayer de corriger des choses, mais je t'avoue que, ne voyant pas trop l'interface, je ne sais pas trop quel est ton but. (Il y a 3 textFields ? quel est leur rôle ...)
C'est vrai que sans l'interface, mon exercice peut être abstrait.
Donc mon but est de pouvoir afficher grâce au bouton "Affiche A" la valeur de l'entier "a" récupéré ds Data. Le bouton "Modifier A" sert à modifier la valeur de "a" dans Data. Enfin, si je clique sur la fenêtre, donc sur ZoneClick, à droite s'affiche les coordonnées du click mais je voudrais que "a", dans data, prenne la valeur du x du click. Donc si je clique à nouveau sur "Affiche A" , il devrait s'afficher cette valeur.
Par exemple, au départ, je clique sur "Affiche A". Il me donne 120 (valeur que j'ai mis en initialisation). Si je rentre une valeur sous le bouton "Modifier A" comme 20 par exemple, il devrait s'afficher 20 sous le bouton "Affiche A". jusqu'à là , ça fonctionne.
Bon, maintenant, si je clique ds ma ZoneClick, je dois voir les coordonnées de mon click, par exemple {112,42} ET je voudrais que "a" de Data prenne la valeur 112. De ce fait, si je clique sur "Affiche A", je devrais voir s'afficher 112.
En fait, si je regarde le débugger, sur un point arrêt dans ZoneClick, mesData = 0x0, donc la valeur du pointeur n'est pas passé (?)
Je te fais passer le projet complet avec quelques remarques sur tes commentaires (placés en tabulation avancée d'un cran)
Ceci étant, il n'y a pas de moyen de passer sans l'IBOutlet ZoneClick ?
J'ai vu un peu de ton code. Un des soucis est que tu alloues une ZoneClick qui n'est pas celle qui est affichée !
Lorsque tu glisses une vue, lors du chargement du fichier xib, il y aura une instance créée. Pour avoir un pointeur sur cette instance, il faut un IBOutlet ! Toi, tu as créé une vue sans frame (init au lieu de initWithFrame), et qui n'a pas été ajoutée à la contentView de window. Autrement dit, la vue que tu as allouée est une vue "fantôme" ...
J'ai fait des modifs en gardant ta structure. (ajout d'un outlet qui pointe vers l'instance de ZoneClick créée dans IB, connection dans IB, suppression de ton allocation d'une ZoneClick "fantôme")
Ah oui, j'oubliais : lors de l'initialisation de MyDocument, on n'est pas sûr que les objets vers lesquels des outlet pointent soient réellement créés. Il faut donc envoyer des messages à ces objets plus tard. La méthode windowControllerDidLoadNib est prête à être implémentée pour cela dans une documentBased application et est justement appelée automatiquement après le chargement du fichier xib : c'est ici qu'on doit envoyer les éventuels messages à ces objets.
Ceci étant, il n'y a pas de moyen de passer sans l'IBOutlet ZoneClick ?
Oui, mais alors il faut instancier effectivement toi-même la vue ZoneClick ET NE PAS LA GLISSER DANS LE FICHIER XIB.
Dans windowControllerDidLoadNib...
<br />
<br />
- ([color=#c02d9d]void[/color])windowControllerDidLoadNib:([color=#743fa4]NSWindowController[/color] *) aController<br />
{<br />
[color=#40207c][color=#000000] [/color][color=#4b8186]maZone[/color][color=#000000]=[[[/color][color=#4b8186]ZoneClick[/color][color=#000000] [/color]alloc[color=#000000]] [/color]initWithFrame[color=#000000]:[/color]NSMakeRect[color=#000000]([/color][color=#2f2fd0]87[/color][color=#000000],[/color][color=#2f2fd0]132[/color][color=#000000],[/color][color=#2f2fd0]256[/color][color=#000000],[/color][color=#2f2fd0]200[/color][color=#000000])];[/color][/color]<br />
[color=#008324]//J'ai repris les valeurs du Nib (dans l'onglet règle de l'inspecteur, x, y, w, h. [/color]<br />
[color=#008324]//NSMakeRect permet de créer une structure C NSRect qui contient les coordonnées du coin inférieur gauche, la largeur et la hauteur du rectangle.[/color]<br />
[color=#008324]//Il faut retrouver la fenetre. Il n'y a qu'un windowController pour la gérer, on peut donc obtenir l'instance de la fenetre ainsi:[/color]<br />
[color=#743fa4]NSArray[/color] *windowControllers=[[color=#c02d9d]self[/color] [color=#40207c]windowControllers[/color]];<br />
[color=#008324][color=#743fa4]NSWindowController[/color][color=#000000] *windowController=[windowControllers [/color][color=#40207c]lastObject[/color][color=#000000]];[/color]//(il n'y a qu'un objet dans l'array, donc prenons... le dernier !)[/color]<br />
[color=#008324][color=#000000][[[windowController [/color][color=#40207c]window[/color][color=#000000]][/color][color=#40207c]contentView[/color][color=#000000]] [/color][color=#40207c]addSubview[/color][color=#000000]:[/color][color=#4b8186]maZone[/color][color=#000000]];[/color]//On ajoute la vue à la contentView de la fenêtre, sinon, la vue a beau exister en mémoire, elle ne sera jamais affichée ![/color]<br />
<br />
[color=#4b8186][color=#000000][[/color]maZone[color=#000000] [/color][color=#2e595d]setMesData[/color][color=#000000]:[/color]mesData[color=#000000]];[/color][/color]<br />
[color=#008324][color=#000000][[/color][color=#4b8186]maZone[/color][color=#000000] [/color][color=#40207c]release[/color][color=#000000]];[/color]//La vue a été ajoutée à la hierarchie, elle est donc retenue. Je peux lui envoyer un message release ici (ou au dealloc)[/color]<br />
<br />
[color=#008324]//Il faut encore écrire du code pour connecter l'outlet que tu avais créé et connecté dans IB :[/color]<br />
[color=#008324]//il faut donc non pas un outlet, mais bien une variable d'instance avec ses accesseur dans la classe ZoneClick[/color]<br />
[color=#008324]//Allons-y, créons le textField par le code ! (en supposant que tu a déclaré une property dans la classe ZoneClick @property (retain) NSTextField *afficheClick et le synthetize aui va avec, en lieu et place d'un IBOutlet[/color]<br />
[color=#008324]//J'ai bien sûr supprimé le textField dans le fichier XIB ds interface builder, ainsi que la ZoneClick que tu avais glissé dans la fenêtre, sinon, j'aurais des doubles ![/color]<br />
<br />
[color=#743fa4]NSTextField[/color] *afficheClick=[[[color=#743fa4]NSTextField[/color] [color=#40207c]alloc[/color]] [color=#40207c]initWithFrame[/color]:[color=#40207c]NSMakeRect[/color]([color=#2f2fd0]351[/color], [color=#2f2fd0]305[/color], [color=#2f2fd0]96[/color], [color=#2f2fd0]22[/color])];<br />
[afficheClick [color=#40207c]setEditable[/color]:[color=#c02d9d]NO[/color]]; //Pour ne pas qu'on puisse modifier la valeur, ce qui ne servirait à rien de toutes façon.<br />
[color=#40207c][color=#000000][afficheClick [/color]setBezelStyle[color=#000000]:[/color]NSTextFieldRoundedBezel[color=#000000]]; //Juste du style[/color][/color]<br />
[color=#40207c][color=#000000][afficheClick [/color]setAlignment[color=#000000]:[/color]NSCenterTextAlignment[color=#000000]]; //Idem, text centré[/color][/color]<br />
[[[windowController [color=#40207c]window[/color]][color=#40207c]contentView[/color]] [color=#40207c]addSubview[/color]:afficheClick]; //On ajoute à la hiérarchie en tant que subView de la contentView de la fenêtre.<br />
[color=#4b8186]maZone[color=#000000].[/color]afficheClick[color=#000000]=afficheClick; //On donne à maZone l'adresse de afficheClick qui sera stockée ds sa variable d'instance afficheClick (qui était un outlet auparavant)[/color][/color]<br />
[afficheClick [color=#40207c]release[/color]];<br />
<br />
[color=#40207c][color=#000000][[/color][color=#c02d9d]super[/color][color=#000000] [/color]windowControllerDidLoadNib[color=#000000]:aController];[/color][/color]<br />
<br />
[color=#008324]// Add any code here that needs to be executed once the windowController has loaded the document's window.[/color]<br />
}<br />
<br />
<br />
Comme tu le vois, créer des vues par le code, ça demande pas mal de ligne de code. Alors autant déclarer des IBOutlet, et dessiner notre interface avec une souris non ? Enfin, je dis ça.. c'est pour toi !
Edit : laZoneClick peut être aussi créée directement par la vue maZone : dans la méthode viewDidMoveToWindow de ZoneClick, appelée lorsqu'elle a été introduite dans la view hierarchy, tu peux écrire un code similaire au précédent. On retrouve la window non pas avec windowControllers ..., mais simplement avec un [self window] => une vue connaà®t sa fenêtre lorsqu'elle est dans la hierarchie. (Pas un NSDocument ! un document n'a pas de fenetre, ce n'est pas une vue...)
En fait, c'est ce qui me manquait : ces pu.. de liens. Bon, c'est vrai que lorsque l'on y regarde de plus près ça parait complètement logique. Mais sous Qt, on n'a pas ça donc changement de logique à acquérir. En tout cas tu me sorts une énorme épine du pied. Je vais donc maintenant pouvoir avancer ... notamment avec SQlite. Je vais donc très certainement recontacter le forum pour un autre coup de main. Ceci étant, si tu es toujours d'accord de m'aider, ça sera avec grand plaisir. Je pense que tu as compris mon mode de fonctionnement, donc beaucoup plus facile pour moi !!.
Merci en tout cas pour tout ce que tu as fais, de ta patience et de des compétences.
Ah ! voila la raison de ta compétence péda! super!
Comme tu l'as surement vu sur mon profil, je suis dans le sport (depuis au moins 35 ans, voir même 40), notamment dans l'expertise et la biomécanique sportive. D'où mon besoin de connaà®tre cocoa. As ce propos, j'ai refais passé une question, cette fois ci sur sqlite. Donc si ça te chante...
Réponses
Donc pour l'instant, j'essaye de m'approprier le MVC sous cocoa. Au fil des réponses, je crois comprendre qu'il faut que je crée une classe spécifique Model dans la quelle je "stocke" toutes mes variables, sachant que par la suite, elles seront importées d'une bd SQlite.
Donc mon exercice aujourd'hui est de récupérer la position d'un click, je suppose ensuite "stocker" ces valeurs x et y dans mon Model et tant qu'à faire, afficher ces valeurs dans ma fenêtre.
Mais actuellement, tu as raison, je ne suis pas encore du niveau pour comprendre tout les réponses, même si cela pas d'un bon sentiment et je trouve ça génial. Mais maintenant, à force d'essayer, j'ai l'impression de m'enliser.
Donc mpergand, si tu comprends mon objectif et que tu as quelque chose de simple pour moi, je suis preneur.
Merci d'avance
Ok, donc c'est bien à ta vue (sous classe de NSImageView ?) de mémoriser les points lors des clics.
Après pour le traitement, sauvegarde, etc, c'est dans cette vue qu'il faut définir les méthodes adéquates comme:
setPoints(NSArray* points);
-(NSArray*) points;
Et comme ça dans ton NSDocument tu récupères ces points et tu en fais ce que tu veux: sauvegarde dans un fichier, bdd, etc.
pas de KVO, bindings, NSCoding ou je ne sais quoi d'autre ...
Par ailleurs, ça me rappelle un question sur un autre forum où le but était de dessiner un rectangle par dessus une image, voir ICI
Il me semble que c'est cependant ce que j'ai fais : voici mes codes. En fait, j'arrive bien à stocker de Vue à ModelData mais si je veux récupérer les valeur sous My document, elles sont à nouveau égale à 0. Idem de MyDocument à ModelData et récupérer ces valeur sous Vue. J'ai le sentiment que lorsque je quitte soit Vue soit MyDocument, ModelData est remis à 0 /huh.gif' class='bbc_emoticon' alt='???' />
Si tu pouvais me dire ce qui ne fonctionne pas, même si , je sais, ce code doit certainement comprendre des trucs bizarre !!
Je me permets de rebondir sur ceci :
Cette phrase n'a aucun sens .. Qu'appelles-tu "quitter Vue" ?? On ne quitte pas une vue ! Une vue est un objet, c'est à dire un certain nombre d'octet en mémoire duquel tu as un pointeur, noté *lObjet. Celui-ci te permet d'accéder aux valeurs de ses variables d'instance (ou les pointeurs vers d'autres objets), et d'envoyer des messages à cet objet. Point. On ne "rentre pas", ni "quitte pas" une vue : On peut créer la vue, la détruire, modifier ses variables d'instances ...
Voici le guide qui te permettra d'avoir les bases.
Pour ton code, il y a des choses étranges. Par exemple, tu crées une instance de ton ModelData dont tu conserves un pointeur comme variable d'instance de MyDocument (tu remarqueras que je me force à utiliser le vocabulaire, et que mes phrases sont longues, de manière à SAVOIR DE QUOI ON PARLE)
Tu crées aussi une AUTRE instance de ModelData, A CHAQUE CLICK, et tu gardes le pointeur comme variable d'instance de le Vue ... sauf qu'au prochain click, ce sera une nouvelle instance de ModelData qui sera créée et le pointeur mesDatas pointera sur cette nouvelle instance, l'ancienne étant désallouée depuis longtemps (car autorelease au moment de l'allocation donc pas retenue) => Est-ce bien cela que tu voulais faire ?
Le pointeur monModel NE POINTE PAS sur la même instance de ModelData que la vue !!
Pour la classe ModelData, pas grand chose à dire, mis à part qu'il faudrait implémenter les fameuses méthodes encodeWithCoder et decodeWithCoder si tu veux pouvoir archiver les données pour enregistrement du fichier.
Essaye de corriger le tir en ne créant qu'une seule instance de ModelData, et de maintenir les pointeurs nécessaires comme variables d'instance des objets qui ont besoin d'y accéder. N'hésite pas à poser les questions nécessaires.
Dans Vue:
Quelle est le lien avec le ModelData de NSDocument ?
En plus en autorelease ...
Tu persistes à vouloir passer les data au document, je pense toujours que conceptuellement c'est pas terrible.
La bonne méthode pour le faire est d'utiliser un delegate (voir la doc Apple)
Dans ta vue tu ajoutes une variable delegate:
id delegate;
et la méthode:
dans mouseDown faire:
Enfin dans NSDocument :
Je me suis plongé ds vos conseils. Pas simple /crybaby.gif' class='bbc_emoticon' alt=' ' /> J'ai donc essayé de faire quelque chose de peut être plus précis : créer une classe réunissant toutes mes variables que j'importerai d'une base de données. Puis j'ai chercher à modifier des variables au sein de cette classe via myDocument et bien sur afficher la modif et enfin, via une 3e classe traitant du click souris, j'ai cherché à modifier la valeur de mes variable (seulement x) ds ma classe spécifique et afficher le changement. J'ai cru comprendre que puisque ma classe des data était instancier par ds MyDocument, il fallait "passer par là ". Donc mon cheminement est le suivant, récupérer la valeur du clique, la transférer ds MyDocument qui affectera la nouvelle valeur à data.
Bon le résultat fonctionne et donc je voulais avoir votre avis sur ce que j'ai fait, même si les termes ne sont pas exactes et sachant que j'essaye de réfléchir comme le curseur du déboggeur ! c'est à dire, "par où ça passe" ligne par ligne ...
merci d'avance
[font=arial, helvetica, sans-serif]Fonctionnellement, c'est pareil sauf que ta vue dépend strictement de MyDocument, si tu décides par la suite d'utiliser un autre controller, il te faudra modifier cette variable dans ta vue.[/font]
D'ailleurs, parlons de cette vue, que fait-elle ?
Pour moi c'est une "meta vue", c'est à dire qu'elle est composée de plusieurs autres vues et c'est à cette "meta vue" de les gérer.
Pour l'instant, je vois que tu as une vue ZoneClick et deux textFields val et nouveauVal,
alors, au lieu de passer par ton document, pourquoi ne pas le faire directement dans la vue ?
(c'est ce que tu fais déjà pour posClick !)
En effet, j'ai cherché à simplifier au plus le problème car pour moi, il y a des choses encore obscures, donc en cherchant à ne pas (ou peu) utiliser le builder, je comprends mieux ce que je fais.
Pour cette vue, il s'agissait plus d'un exercice visant à utiliser et "jongler" avec plusieurs classes, c'est donc la raison de cette démarche.
Du reste, si tu as un peu de temps à me consacrer, j'aimerais vérifier si ce que j'ai compris est juste. En effet, en lisant les doc et le bouquin de Hillegas, tout est limpide ! sauf que en face de mon écran, ce n'est pas du tout pareil !
Donc si j'ai bien compris, une classe fonctionne en "autonomie" (indépendante des autres classes) et comporte des objets que l'on déclare dans le .h. Prenons un exemple d'objets compris dans cette classe : NSTextField *a et NSTexteField *b. Toujour dans le .h, on "se donne les moyens" de pouvoir les lire et les modifier avec @property (readwrite, copy) NSTextField *a et idem pour b. @synthesize a (et b ) sont à placer dans le .m après @implementation (ce qui serait "l'équivalent" des getter et setter).
Déjà , est ce que j'écris est juste ??
Bon ensuite, j'ai compris que chaque classe est autonome. Pour pouvoir accéder à une classe depuis une autre classe, il faut (si j'ai bien compris le terme) l'instancier. Un exemple : ma classe Classe0 comprend les a et b précédent. Une 2e classe appelée Classe1 comprend les 2 mêmes types d'objet mais nommés c et d. Et une 3e classe appelée Classe2 comprend aussi les 2 mêmes types d'objets appelés e et f.
Si je veux accéder à Classe0 "depuis" Classe1, il faudrait que dans Classe1, (et dans init) je fasse quelque chose comme :
maClasse0 = [[Classe0 alloc]init]; (sachant que ds Classe1.h j'ai inscrit Classe0 *maClasse0;. maClasse0 devra aussi être supprimé ensuite via un -(void) dealloc { [maClasse0 dealloc]... .
A partir de cela, je pourrai accéder à a et b ([[maClasse0 a] intValue]), et les modifier ([[maClasse0 a] setIntValue : 3] par exemple);
Est ce que je me trompe ??
Encore une fois, j'essaye de comprendre à fond le système d'où cette simplification sans doute à l'extrême.
Si je veux accéder à nouveau à Classe0 mais cette fois-ci de Classe2 (sous un autre menu ou via un bouton spécifique par exemple); je devrai faire la même démarche.
Sauf que (sauf si j'ai fais une erreur et c'est fort probable), si je modifie a ou b via Classe1 (comme je viens de le faire) et que je veuilles consulter l'entier de a via cette fois-ci Classe2, je ne retrouve pas la valeur 3 (pour reprendre l'exemple précédent) mais 0, comme si Classe0 à été "initialisée" et remise à 0.
Pourtant, le pointeur de Classe0 crée ds Classe1devrait "m'écrire la valeur 3" dans Classe0 et donc, en faisant un [ [maClasse0 a] intValue] dans Classe2, je devrais retrouver 3 ?
Je ne sais pas si j'ai été clair dans mon explication mais si tu pouvais prendre un peu de temps pour m'éclairer, sachant que je suis encore débutant, que je cherche à comprendre la logique (donc sans l' IB) avec des termes que je puisse comprendre !!
En effet, si cela n'est pas limpide pour moi, je ne vois pas comment je pourrais avancer dans mon apprentissage.
Merci d'avance
Oui, tout au moins c'est le but ultime.
Pas tout à fait. Elle comporte des références (pointeurs) vers des objets (j'y reviens).
Je ne sais pas comment tu comprends la chose, mais c'est bien la référence qui est modifiable. C'est à dire que readwrite indique qu'on peut remplacer la référence vers un NSTextField par une référence vers un autre.
En fait, il synthétise automatiquement les accesseurs. C'est à dire que tu as tout à fait le droit d'écrire ta propre implémentation de - (void) setTextFieldA:(NSTextField *)textField ou - (NSTextField *)textFieldA.
Non, ça c'est faux. La classe est le moule pour un objet.
Instancier une classe signifie créer un objet (une instance) à partir de cette classe.
Ce qui nous ramène à cette histoire de référence: pour appeler les méthodes d'instance (celles qui commencent par un moins), il faut une référence vers un objet. Et ce n'est pas forcément ton objet qui a instancié cet objet, ça peut aussi une autre objet qui a passé la référence, ou ton objet qui a demandé une référence vers un objet.
Au niveau de la gestion mémoire, ceci amène deux questions:
1) Quand dois-je libérer la mémoire occupée par un objet ?
2) Combien de temps une référence reste-elle valide ?
C'est questions étant relativement complexes, je te conseille de lire attentivement le chapitre dans le livre d'Hillegass.
Pour ma part je préfère penser que la classe est le plan de fabrication d'un objet.
Instancier une classe, c'est donner un plan de fabrication à l'usine/iOS pour construire un objet dans l'espace mémoire.
Ceci étant Cérose, peux tu être plus clair dans l'explication suivante, quitte à l'appuyer d'un exemple très très simple:
" pour appeler les méthodes d'instance (celles qui commencent par un moins), il faut une référence vers un objet. Et ce n'est pas forcément ton objet qui a instancié cet objet, ça peut aussi une autre objet qui a passé la référence, ou ton objet qui a demandé une référence vers un objet."
Est que cela se traduirait concrètement au fait que si je veux lire un -(void) d'une autre classe, je ne suis pas obliger de créer l'objet via alloc init ? Donc comment est il possible de faire ?
Merci d'avance
Je pense que tu confonds un "objet", c'est à dire un certain nombre d'octets, avec un pointeur, qui contient lui l'adresse mémoire où se situe l'objet.
L'objet unDRH fait ceci : Secretaire *maSecretaire = [ [Secretaire alloc] initWithCouleurDeCheveu:blonde] => Ceci a pour effet de créer et initialiser un objet, donc de réserver un coin de mémoire où seront stockés toutes les choses nécessaires, et en plus d'assigner l'adresse de ce coin de mémoire à la variable maSecretaire qui est donc un pointeur. (comme indique *)
Imaginons que l'objet unDRH ait aussi un pointeur monPDG, qui pointe vers un objet de type PDG .
Les objets de la classe PDG peuvent avoir une variable d'instance Secretaire *maSecretaire. Si c'est la même secrétaire que le DRH, il faudra que unDRH envoie le message monPDG.maSecretaire=maSecretaire pour assigner l'adresse (N° du bureau...) de cette secrétaire du DRH à la variable d'instance maSecretaire du PDG.
Ainsi, unDRH et unPDG auraient tous les deux accès ici à la même INSTANCE de Secretaire, à savoir une blonde.
Si unPDG appelait la méthode de classe [Secretaire alloc] pour l'assigner à maSecretaire, cela créerait une nouvelle instance stockée à une autre adresse mémoire... pourquoi pas l'initialiser avec des cheveux bruns ?... Le PDG et le DRH n'auraient donc pas la même secrétaire ici.
Voici un exemple concret. Une objet Voiture possède une référence vers un Volant:
On considère que le Volant appartient à la Voiture, il est donc créé à l'initialisation de la Voiture:
Maintenant, un objet Garage, qui dispose de références vers des objets Voiture et Reparateur veut lancer des réparations. Il devra appeler la méthode -reparerVoiture: et passer un pointeur sur la voiture pour cela:
Dans l'implémentation, Reparateur n'a créé ni la Voiture, ni le Volant, mais ça ne l'empêche pas d'appeler les méthodes d'instance:
Tu as raison : tes excellentes explications confirment que j'ai tendance à confondre objet et pointeur.
Du coup, ton exemple m'a énormément éclairé notamment sur le rôle réel de alloc init, ce qui fait que le 1er et dernier paragraphe m'ont réellement aidés. Cela me permet de comprendre mon problème : j'ai créé 2 objets alors que j'aurais dû en créer 1 et utiliser que le pointeur ds mes 2 classes.
Cependant, le paragraphe du milieu est encore un peu flou. Je comprend qu'il n'est pas utile pour monPDG de créer l'objet Secretaire puisqu'il est déjà créé dans unDRH et qu'il suffit de faire appel à son pointeur *maSecretaire. Ca, maintenant c'est fluide (enfin je pense!)
Bon, maintenant, c'est le comment qui me bloque. Tu dis : "imaginons que unDRH a aussi un pointeur monPBG". Sous quelle forme ? : tu le créés comme maSecretaire ( PDG *monPDG [[PDG alloc]init]) dans unDRH ?
Et comment envoyer le message à monPDG pour qu'il récupère le pointeur *maSecretaire?
Merci d'avance
Merci encore
Le tout est d'assigner l'adresse de la bonne secrétaire à cette variable d'instance, c'est ton soucis je crois. C'est pour cela que dans mon exemple, j'ai supposé que le DRH avait un pointeur vers le PDG, ainsi, le DRH a pu assigner l'adresse de sa secrétaire à la variable d'instance maSecretaire du PDG avec le message monPDG.maSecretaire=maSecretaire : (Equivalent à :
).
De ce fait, le DRH et le PDG ont la même secrétaire. PDG n'a pas créé une nouvelle instance de secrétaire.
Bon, en même temps, c'est pas souvent que le PDG et de DRH ont la même secrétaire...
Attention par contre à la gestion mémoire... Si par exemple le setter setMaSecretaire du PDG ne fait qu'une simple assignation, alors si le DRH disparait, au moment de sa disparition il enverra le message release à la secrétaire qui sera donc virée sur le champ. le PDG n'aura alors plus de secrétaire, et si il ne le sait pas, il lui enverra des messages qui n'arriveront jamais (CRASH). Il vaut mieux alors que dans le setter de la secrétaire du PDG on retienne la secrétaire en lui envoyant un message retain. Du coup, même si le DRH est viré, la secrétaire ne l'est pas vu que le PDG a pris la responsabilité...
Voici le fichier de mes codes. Si tu pouvais prendre un peu de temps pour regarder, je suis complètement perdu. J'ai compris la logique que tu viens de m'expliquer avec brio (j'insiste la dessus car en tant que formateur, il est rare de rencontrer des explications de ce type, donc chapeau !), mais je n'arrive pas à le retranscrire dans mon code. Qu'est ce que j'aurais manqué, oublié, mal mis /huh.gif' class='bbc_emoticon' alt='???' />
Merci d'avance
C'est vrai que sans l'interface, mon exercice peut être abstrait.
Donc mon but est de pouvoir afficher grâce au bouton "Affiche A" la valeur de l'entier "a" récupéré ds Data. Le bouton "Modifier A" sert à modifier la valeur de "a" dans Data. Enfin, si je clique sur la fenêtre, donc sur ZoneClick, à droite s'affiche les coordonnées du click mais je voudrais que "a", dans data, prenne la valeur du x du click. Donc si je clique à nouveau sur "Affiche A" , il devrait s'afficher cette valeur.
Par exemple, au départ, je clique sur "Affiche A". Il me donne 120 (valeur que j'ai mis en initialisation). Si je rentre une valeur sous le bouton "Modifier A" comme 20 par exemple, il devrait s'afficher 20 sous le bouton "Affiche A". jusqu'à là , ça fonctionne.
Bon, maintenant, si je clique ds ma ZoneClick, je dois voir les coordonnées de mon click, par exemple {112,42} ET je voudrais que "a" de Data prenne la valeur 112. De ce fait, si je clique sur "Affiche A", je devrais voir s'afficher 112.
En fait, si je regarde le débugger, sur un point arrêt dans ZoneClick, mesData = 0x0, donc la valeur du pointeur n'est pas passé (?)
Je te fais passer le projet complet avec quelques remarques sur tes commentaires (placés en tabulation avancée d'un cran)
Ceci étant, il n'y a pas de moyen de passer sans l'IBOutlet ZoneClick ?
Merci encore
J'ai vu un peu de ton code. Un des soucis est que tu alloues une ZoneClick qui n'est pas celle qui est affichée !
Lorsque tu glisses une vue, lors du chargement du fichier xib, il y aura une instance créée. Pour avoir un pointeur sur cette instance, il faut un IBOutlet ! Toi, tu as créé une vue sans frame (init au lieu de initWithFrame), et qui n'a pas été ajoutée à la contentView de window. Autrement dit, la vue que tu as allouée est une vue "fantôme" ...
J'ai fait des modifs en gardant ta structure. (ajout d'un outlet qui pointe vers l'instance de ZoneClick créée dans IB, connection dans IB, suppression de ton allocation d'une ZoneClick "fantôme")
Ah oui, j'oubliais : lors de l'initialisation de MyDocument, on n'est pas sûr que les objets vers lesquels des outlet pointent soient réellement créés. Il faut donc envoyer des messages à ces objets plus tard. La méthode windowControllerDidLoadNib est prête à être implémentée pour cela dans une documentBased application et est justement appelée automatiquement après le chargement du fichier xib : c'est ici qu'on doit envoyer les éventuels messages à ces objets.
Oui, mais alors il faut instancier effectivement toi-même la vue ZoneClick ET NE PAS LA GLISSER DANS LE FICHIER XIB.
Dans windowControllerDidLoadNib...
Comme tu le vois, créer des vues par le code, ça demande pas mal de ligne de code. Alors autant déclarer des IBOutlet, et dessiner notre interface avec une souris non ? Enfin, je dis ça.. c'est pour toi !
Edit : laZoneClick peut être aussi créée directement par la vue maZone : dans la méthode viewDidMoveToWindow de ZoneClick, appelée lorsqu'elle a été introduite dans la view hierarchy, tu peux écrire un code similaire au précédent. On retrouve la window non pas avec windowControllers ..., mais simplement avec un [self window] => une vue connaà®t sa fenêtre lorsqu'elle est dans la hierarchie. (Pas un NSDocument ! un document n'a pas de fenetre, ce n'est pas une vue...)
En fait, c'est ce qui me manquait : ces pu.. de liens. Bon, c'est vrai que lorsque l'on y regarde de plus près ça parait complètement logique. Mais sous Qt, on n'a pas ça donc changement de logique à acquérir. En tout cas tu me sorts une énorme épine du pied. Je vais donc maintenant pouvoir avancer ... notamment avec SQlite. Je vais donc très certainement recontacter le forum pour un autre coup de main. Ceci étant, si tu es toujours d'accord de m'aider, ça sera avec grand plaisir. Je pense que tu as compris mon mode de fonctionnement, donc beaucoup plus facile pour moi !!.
Merci en tout cas pour tout ce que tu as fais, de ta patience et de des compétences.
A bientôt je l'espère
Comme tu l'as surement vu sur mon profil, je suis dans le sport (depuis au moins 35 ans, voir même 40), notamment dans l'expertise et la biomécanique sportive. D'où mon besoin de connaà®tre cocoa. As ce propos, j'ai refais passé une question, cette fois ci sur sqlite. Donc si ça te chante...
A+