Application avec plusieurs types de documents
misiu91
Membre
Bonjour à tous,
Je suis débutant sur Cocoa et malgré toute la littérature que j'ai pu consulter, je ne trouve pas la réponse à ma question.
Je veux développer une application (Cocoa document-based application) qui me permette de créer des documents type texte (jusque là je sais faire).
La barre de menu de mon application comprend également 4 ou 5 commandes de lancement de calculs dont le résultat est affiché sous forme de table dans un autre type de document que je veux sauvegarder en format CSV.
Je voudrais que l'enregistrement et l'ouverture de ce deuxième type de document soient gérés par la même commande que celle des documents texte (Ouvrir, Enregistrer, Enregistrer sous du menu fichier).
Ceci est-il réalisable ?
D'autre part, pour pouvoir lancer les calculs à partir de mes 4 ou 5 commandes de la barre de menu, faut-il que je place les instances des classes correspondantes dans le fichier MainMenu.nib ou puis-je les créer dynamiquement ?
Je patauge un peu et j'aimerai de l'aide.
Merci d'avance pour votre compréhension.
Edit modération Titre de sujet plus en rapport avec la demande.
Je suis débutant sur Cocoa et malgré toute la littérature que j'ai pu consulter, je ne trouve pas la réponse à ma question.
Je veux développer une application (Cocoa document-based application) qui me permette de créer des documents type texte (jusque là je sais faire).
La barre de menu de mon application comprend également 4 ou 5 commandes de lancement de calculs dont le résultat est affiché sous forme de table dans un autre type de document que je veux sauvegarder en format CSV.
Je voudrais que l'enregistrement et l'ouverture de ce deuxième type de document soient gérés par la même commande que celle des documents texte (Ouvrir, Enregistrer, Enregistrer sous du menu fichier).
Ceci est-il réalisable ?
D'autre part, pour pouvoir lancer les calculs à partir de mes 4 ou 5 commandes de la barre de menu, faut-il que je place les instances des classes correspondantes dans le fichier MainMenu.nib ou puis-je les créer dynamiquement ?
Je patauge un peu et j'aimerai de l'aide.
Merci d'avance pour votre compréhension.
Edit modération Titre de sujet plus en rapport avec la demande.
Connectez-vous ou Inscrivez-vous pour répondre.
Réponses
Comma-separated values (CSV) est un format informatique ouvert représentant des données tabulaires sous forme de " valeurs séparées par des virgules ".
Comma-Separated Values
Extension de fichier : .csv
Type MIME : text/csv
Type de format : fichier texte
Standard(s) : (en) RFC 4180
Spécification :
Format ouvert
Ce format n'a jamais vraiment fait l'objet d'une spécification formelle. Toutefois, la RFC 4180 décrit la forme la plus courante et établit son type MIME "text/csv", enregistré auprès de l'IANA.
Un fichier CSV est un fichier texte (par opposition aux formats dit " binaires "). Chaque ligne correspond à une rangée du tableau et les cellules d'une même rangée sont séparées par une virgule.
Par exemple :
Sexe, Prénom, Année de naissance
M,Alphonse,1932
F,Béatrice,1964
F,Charlotte,1970
On ne voit pas bien dans ton post ce qu'est ton modèle : un fichier, une NSString via un NSTextView, une NSTableView où les données sont déjà séparées ?
Si ce que tu appelles CSV est ce qu'il y a au dessus, je ne vois pas où il pourrait y avoir problème
La barre de menu envoie un message à la chaà®ne des Responder, qui débute par .. le FirstResponder. Voir ce post
De plus, je sais pas si les NSArchiver sont capables de faire du CSV.
Mon problème est effectivement d'avoir une application multi-documents de plusieurs formats.
J'ai beaucoup programmé en C sous OS9 et j'ai vraiment du mal à me mettre à l'objet !!!
J'ai notamment du mal à voir comment doit s'articuler la boucle principale de mon programme. Comment se fait l'appel des différentes fonctions à partir de ma barre de menu.
Dans toute la documentation que j'ai pu ingurgiter, les exemples se limitent à la gestion d'un type de document et je n'ai pas trouvé d'exemples de gestion multi-documents de différents formats.
Je ne sais pas si je suis suffisamment clair.
Merci
-(IBAction) saveDocument:(id) sender
{
NSLog(@Saving Document);
}
dans le code de MyDocument, tu verras que l'instance de cette classe répond aux actions déclenchées par le menu.(item File>Save)
Après comme le dit Psycho, reste à savoir si il existe un traitement prédéfini pour le type de fichier CSV .. lecture de Doc, ou un pénible switch() pour traiter les différents types de documents à traiter.
Je vais te donner un exemple plus concret.
Imaginons que la barre de menu de mon application contienne 2 items :
- File avec la commande New
- Toto avec la commande Titi
Dans mon application, lorsque je fais File>New, j'ouvre un document de type "texte". La gestion de ce doc est contenue dans le fichier MyDocument1.m.
Lorsque je fais Toto>Titi, je crée un document contenant une NSTableView. La gestion de gestion de ce doc est contenue dans le fichier MyDocument2.m.
Ce que je veux c'est que lorsque je fais File>Save, le document actif soit enregistré avec son modèle de données. Si j'ai bien compris, à l'envoi de cette commande, c'est le FirstResponder du document actif qui va être appelé et déclencher la bonne méthode d'enregistrement.
Suis-je dans le vrai ou n'ai-je rien compris au film ???
Merci de ton indulgence.
Oui
Là deux possibilités:
Soit tes documents sont assez différents et dans ce cas, tu dois faire une sous-classe de NSDocument par type de document.
Soit tes documents ne sont pas très différents et dans ce cas, tu peux utiliser la même classe pour les 2. Pour différencier les comportements pour les 2 types de documents à la sauvegarde/couverture, tu as toujours un argument spécifiant le type dans les méthodes à utiliser. Exemple:
[tt]- (id)initWithContentsOfURL:(NSURL *)absoluteURL ofType:(NSString *)typeName error:(NSError **)outError[/tt]
Avec ça, les méthodes d'ouverture et de sauvegarde devraient fonctionner de manière transparente. Pour les nouveaux documents, la commande "nouveau" crée par définition un document du type principal, si tu veux un autre type, tu dois rajouter un article dans le menu. Pour le "Save As", c'est un peu particulier. Un document est toujours créé avec un type (le type par défaut sauf si le cas contraire est spécifié). Donc si tu veux faire une conversion d'un type vers un autre, il faudra sans doute coder un peu.
Oui et non. Le message est envoyé dans la "chaine du first responder", qui commence par le first responder de la fenêtre active. Les détails ici
La philosophie des Cocoa-Based document est plutôt mono-document.
En débutant en Cocoa, avec le besoin de plusieurs interfaces de représentations, j'essaierais d'abord de le faire en changeant la contentView de la fenêtre principale ou une View remplissant complètement cette contentView.
En mettant une variable d'instance correspondant au type de données traitées dans le document.
Ceci dit je n'ai jamais essayé de faire des multi-documents, je m'en vais de ce pas faire un petit essai ...
Et si le fisrtResponder n'est pas en mesure de répondre, le message est transmis au suivant
Dans mon post de départ, il y avait une question subsidiaire pour laquelle je n'ai pas encore de réponse.
Pour la commande Toto>Titi que j'évoquai dans mon dernier post, faut-il que je mette dans le MainMenu.nib une instance de la classe qui crée le type de document associé ou puis-je créer dynamiquement différentes instances dans le contrôleur de mon application ?
Dans ce cas, comment est-ce que je gère ces différentes instances ?
Mon programme effectuera différents calculs de statistiques dont les résultats seront présentés dans une table surmontée probablement d'une représentation graphique. Le document de type texte permettra de rédiger des comptes-rendus sur ces résultats.
Ta proposition de changer la contentView de la fenêtre principale est certainement un bonne idée mais je ne vois (pas encore mais ça viendra) comment est-ce que l'on réalise cela.
Comment détermine-t-on ou spécifie-t-on le type par défaut ?
NSWindow propose la méthode
- (void)setContentView:(NSView *)view
et on peut mettre autant de custom view que l'on veut dans le fichier MyDocument.nib par drag/drop
Un double-clic dessus dans IB l'ouvre, et permet de la remplir comme on veutÂ
Après il suffit de répertorier les views par IBOutlet.
Mais n'ayant jamais essayé des appli multi-documents, ... Renaud qu'en penses-tu ?
Tu as aussi plusieurs autres options attachés à chaque types de document (MIME, extension, icone, classe principale, si c'est un bundle ou pas, etc.)
Il y a au point de vue interface des solutions encore plus simples :
NSTabView, sorte de container de vue à onglets.
NSSplitView , container de vue comme dans XCode ou Grapher
2008-01-27 17:48:51.545 essai document[2047:10b] test from <NSWindow: 0x15b410>
2008-01-27 17:49:00.674 essai document[2047:10b] create new secondDocument
2008-01-27 17:49:07.081 essai document[2047:10b] test from <NSWindow: 0x181630>
2008-01-27 17:49:12.736 essai document[2047:10b] test from <NSWindow: 0x15b410>
Voir une solution plus étoffée plus loin dans le post par Renaud
Merci pour ce petit bout de code. Je l'ai décortiqué et cela me parait très clair.
Je l'ai fait tourné avec ObjectAlloc et j'ai constaté les choses suivantes :
- Lorsque je crée des docs avec File>New et que les ferme, je vois bien le nombre d'instances courantes de MyDocument s'incrémenter et se décrémenter. Il en est de même avec les instances de NSWindowController.
- En revanche, lorsque je crée des "seconds docs" avec File>NewSecond, je ne vois pas d'instances de MySecondDocument (ce que je ne comprends pas) et les instances de NSWindowController s'incrémentent mais ne se décrémentent pas. Il y a donc une fuite mémoire potentielle.
Est-ce que je me trompe ?
Si non, que faut-il faire pour que les instances de NSWindowController ne restent pas en mémoire après fermeture ?
Merci
Rectifié
MySecondDocument pourra ainsi avoir ses propres méthodes d'archivage, de désarchivage, ...
J'ai donc apporté les évolutions suivantes :
Tout cela fonctionne parfaitement bien et il n'y a plus aucune fuite mémoire. Il me reste à appliquer tout cela à mon application.
Qu'en penses-tu ?
Avec mes remerciements pour ton aide.
C'est normal, dans ce code ils sont retenus par AppController
Ca ne suit pas les recommandations qu'Apple a mis en commentaires dans windowNibNameÂ
C'est plus logique en effet mais il me semble que c'est fait par addWindowController
Il suffit de mettre un log dans le code :
 [self addWindowController:wController]; //
 NSLog(@The document associated with the windowController is : %@ \nThe document owning this window controller is : %@",[wController document],self);
Tout cela fourni (SGDG) sans garantie du gouvernement car je n'ai personnellement jamais vu ou fait d'appli multi-doc
Un petit tour sur Internet permettrait de confirmer/infirmer
Oups, sorry pour le délai.
Remplacer la contentView me semble pas des plus approprié. Soit les types de documents ont une même UI, mais ne diffèrent que par le stockage et dans ce cas on ne fait la différenciation que dans les méthodes de sauvegarde. Si l'UI est différente, alors dans ce cas, il y a des chances qu'à mesure que l'interface se développe les différences s'accentuent et que le code devienne un beau schmilblick. Autant utiliser les possibilités offertes par la POO et faire une sous classe du document initial pour vraiment différencier les comportements.
Alors ça, surtout pas! On est pas censer libérer des objets qu'on a pas instancié/retenu soi-même! Les instances des documents doivent être créées par NSDocumentController, donc c'est à lui de releaser les instances (à moins que tu aies fait un [self retain] ailleurs dans cette classe, mais ça n'a pas de sens). Sans compter que surcharger une méthode comme celle-là sans appeler [tt][super close][/tt] peut être source de problème.
Pour le code que tu as posté, les corrections:
1. Info.plist non complété
Seul le premier type de document était mentionné, il faut mentionner les 2.
2. MySecondDocument.nib
Si tu sous-classes [tt]-windowNibName[/tt], le file's owner du nib doit être le document.
Si tu sous-classes [tt]-makeWindowControllers[/tt], le file's owner du nib doit être autre chose. Le autre chose est laissé à ton appréciation, mais vu que tu utilises la méthode [tt]-[NSWindowController initWithWindowNibName:][/tt], le file's owner doit être une instance de NSWindowController (et les outlets correctement reliés).
3. Création du second document
Il est beaucoup plus judicieux de passer par les méthodes ad hoc de NSDocumentController (puisque le Info.plist est remplit correctement).
Au lieu de:
[tt]MySecondDocument * doc=[[MySecondDocument alloc] init];
[doc makeWindowControllers];
[doc showWindows];[/tt]
-
Tu mets donc:
[tt]NSError *error = nil;
[[MyDocumentController sharedDocumentController] setDefaultType:@SecondDocument];
id doc = [[MyDocumentController sharedDocumentController] openUntitledDocumentAndDisplay:YES error:&error];
[[MyDocumentController sharedDocumentController] setDefaultType:nil];
if ( !doc ) {
[[NSDocumentController sharedDocumentController] presentError:error];
}[/tt]
Tu remarqueras que j'ai du sous-classer NSDocumentController, c'est une nouveauté du 10.4, la méthode [tt]-openUntitledDocumentOfType:display:[/tt] étant dépréciée.
4. Définir la fenêtre principale d'un document
Pour qu'un document soit fermé (et libéré) lorsque la fenêtre principale est fermée, il faut utiliser la méthode [tt]-[NSWindowController setShouldCloseDocument:][/tt] lorsque tu instancies le window controller de la fenêtre principale. C'est fait automatiquement dans le cas de MyDocument car c'est assuré par l'implémentation par défaut de [tt]-makeWindowController[/tt].
5. Autorelease dans [tt]-[MySecondDocument close][/tt]
Voir plus haut. Si tu me réponds que tu l'as mis là parce tu as mis toi-même le [tt]init[/tt] dans AppController, je prends un règle et je te tape sur les doigts: faire les [tt]retain/init[/tt] dans une classe et les [tt]release[/tt] dans une autre, c'est une belle façon de se créer des maux de têtes en cas de problème.
J'ai mis le projet corrigé en pièce jointe (note: MyDocumentController est déclaré dans AppController.m)
Ce qu'il faut comprendre ici, c'est qu'il n'y a qu'un seul sharedDocumentController pour toutes les sous-classes de NSDocumentController :
[NSDocumentController sharedDocumentController]=[MyDocumentController sharedDocumentController]
D'ou l'utilité de la définition de defaultType, qui permet de lire la classe du document créé via la plist .
openUntitledDocumentAndDisplay:YES
The default implementation of this method invokes defaultType to determine the type of new document to create, invokes makeUntitledDocumentOfType to create it, then invokes addDocument: to record its opening. If displayDocument is YES, it then sends the new document makeWindowControllers and showWindows messages.
Effectivement ! c'est mal.
Dans ton schéma c'est qui envoie le message de "fin de vie au document" : le windowController ? le documentController ?
Je ne sais plus où c'est écrit dans la doc, mais la première instance créée de NSDocumentController (ou d'une de ses sous-classes) devient le sharedDocumentController. D'où l'appel [tt][MyDocumentController sharedDocumentController][/tt] dans [tt]+[AppController initialize][/tt] pour s'assurer que c'est bien la bonne sous-classe de NSDocumentController qui sera utilisée.
ça dépend de la manière dont tu définis "fin de vie". Si je te prends au mot, c'est à partir d'un [tt]-release[/tt] et là c'est le NSDocument lui même (pas taper).
Bon, le principe c'est que la méthode [tt]-close[/tt] est envoyée lorsqu'un document est fermé, pas pour demander la fermeture d'un document. Donc je reviens sur un point que j'avais oublié de mentionner dans ma correction: pas besoin de demander de confirmation pour dans [tt]-close[/tt]. Si cette méthode est appelée c'est trop tard.
Pour demander la fermeture d'un document, si tu veux le faire de la manière la plus simple, il faut juste que tu aies au moins une instance de NSWindowController qui renvoie YES à [tt]-shouldCloseDocument[/tt]. De cette manière quand tu fermes la fenêtre qu'il gère, l'implémentation par défaut est telle qu'il vérifie que le document est bien enregistré, si c'est pas le cas, il demande une confirmation,.... pour au final envoyer le [tt]-close[/tt]. Le menu File->Close utilise en fait ce mécanisme: il ne fait que demander de fermer la fenêtre et puis le window controller fait le reste.
Donc dans ce schéma, c'est le window controller si la demande se fait suite à une fermeture de fenêtre et le document controller si le but est de faire suite à une demander de quitter l'appli.
EDIT: misiu91: petit détail, j'ai renommé le titre de la conversation. Pour la prochaine fois, lorsque tu poses une question, utilise un terme moins générique (accessoirement, celui que tu as mis a tendance à me faire fuire d'habitude - dans neuf cas sur dix, sous ce genre de titre se cachent des questions auxquelles la réponse a été donnée plusieurs fois).