Est ce possible de "sous sous classer" une classe déjà  sous classée

 

Bonjour

 

 

Ma question va peut être paraitre bête mais je cale sur un souci. 

J'ai actuellement un QTMovieView que j'ai sous classé pour pouvoir utiliser les doubles clicks à  des fins spécifiques (récupérer le currentTime au moment du double clic). J'ai fait en parallèle une application pour dessiner des traits sur une NSView. J'aimerais maintenant utiliser cette 2e application sous forme de classe afin de pouvoir dessiner des traits sur ma video. Je pensais sous classer ma QTMovieView avec cette classe mais elle est déjà  sous classéey  pour utiliser le double click. 

Du coup comment dois je faire pour utiliser ma classe de dessin (donc les mouseDown, drawRect ...) dans ma classe QTMovieView (qui j'utilise déjà  le mouseDown mais pour le double click) ?

 

Merci d'avance

Réponses

  • Bonjour,


     


    Je ne sais pas si ça va t'aider, mais j'ai déjà  fait quelque chose du genre, mais j'en suis venu plutôt à  utiliser les layer d'une vue perso.


    => sous-classe de NSView (tu gères tes clicks et tes clacks)


    => Tu ajoutes un layer pour dessiner "tes traits" (cf doc CALayer : j'avais utilisé un delegate pour le dessin)


    => Tu ajoutes un QTMovieLayer.


     


    Du coup, tu as une sous-classe de NSView qui te permets de répondre aux événements, et des layers qui permettent l'affichage de la vidéos et des "décorations"...


     


    C'est ainsi que j'avais fait une petite appli pour l'étude du mouvement d'un mobile : on pointe à  la souris les positions successives d'un objet, et on en déduit la vitesse, l'accélération ... Après, tu peux sûrement faire autrement, mais sous-classer QTMovieView, pas sûr du résultat. 


  • CéroceCéroce Membre, Modérateur

    Contrairement à  d'autres langages, Objective-C ne permet pas l'héritage multiple, c'est à  dire hériter de plusieurs parents à  la fois. (Je ne vais pas discuter des raisons, mais c'est plutôt sage comme approche).


     


    De fait, il faut aller dans le sens du langage: préférer la composition à  l'héritage. Aussi la méthode de Mick est elle bonne: dessiner avec une autre classe. Mick te propose d'utiliser une CALayer, mais tu peux aussi ajouter une NSView en subview à  QTMovieView.


  • Comme l'a dit Céroce, tu utilise la composition. Cet à  dire que si ta classe de déssin se nomme NSViewDraw, tu crée une property du style nSViewDrawObject : NSViewDraw et dans ta méthode mouseDown() de ta classe QTMovieView tu appel [nSViewDrawObject


     mouseDown] par exemple. Ainsi mouseDown de chaque classe est appelé.


  • Merci à  tous de vos réponses. Je vais essayer tout ça. Bon ça va me prendre un peu de temps car je ne connais pas bien CALayer mais je vous tiens au courant de mes résultats

  • Bon je viens d'essayer les 2 solutions (layer et view) mais pour l'instant de façon très simple : à  partir de ma MSView, je rajoute une autre NSView. Voici le code (en commentaire la partie layer):



    // [self setLayer:[CALayer layer]];
    // [self setWantsLayer:YES];

    NSView *uneAutreVue = [[NSView alloc] initWithFrame:NSMakeRect(40, 40, 200, 200)];

    [uneAutreVue setLayer:[CALayer layer]];
    [uneAutreVue setWantsLayer:YES];

    CALayer *layer = [CALayer layer];
    layer.backgroundColor = [NSColor orangeColor].CGColor;

    [uneAutreVue setLayer:layer];

    // [[self layer] insertSublayer:[uneAutreVue layer] above:[self layer]];
    [self addSubview:uneAutreVue positioned:-1 relativeTo:self];

    Les soucis :   (1)  dans le cas des layer (mais pas dans addView), je n'accède plus au drawRect de ma 1ere vue (le fond devait être dessiné en bleu, ce qui n'est plus le cas et je n'y passe plus dans le débugger), et (2), ma 2e vue reste au 1er plan et je n'arrive pas à  la mettre en fond.


     


    Comment modifier cela ?


     


    Merci d'avance


  • CéroceCéroce Membre, Modérateur

    Vaste sujet que Core Animation... Quelques pistes:


    - Le moment où on appelle -setWantsLayer: importe, regarde la doc. Il y a deux modes (layer-hosting, layer-backed), et dans ton exemple, tu es en layer-hosting.


    - En mode layer-hosting, la vue héberge la layer, et donc ne se dessine plus dans -drawRect:. C'est maintenant la layer qui doit se dessiner.


     


    Pour dessiner une layer, il y a deux choix possibles:


    - hériter de CALayer et surcharger -renderInContext:


    - ou dessiner dans la méthode -drawLayer:inContext: de CALayerDelegate


     


    Je te conseille cette seconde approche, plus centralisée; n'oublie pas de mettre la vue en déléguée de sa layer.


  • oui comme tu dis, vaste sujet le CA...


     


    Bon, je crois comprendre les pistes. Je vais les tester. 


     


    Merci Céroce. Je reposterai quand j'aurai avancé

  • ok, j'ai testé le drawLayer: inContext et je pense avoir compris le fonctionnement. En fait, si j'ai bien compris, il dessine une layer puis une autre et ainsi de suite. Dans mon cas, j'ai 2 layers : voici le code



    [self setLayer:[CALayer layer]];
    [self setWantsLayer:YES];

    uneAutreVue = [[NSView alloc] initWithFrame:NSMakeRect(0, 0, self.bounds.size.width, self.bounds.size.height)];

    [uneAutreVue setLayer:[CALayer layer]];
    [uneAutreVue setWantsLayer:YES];

    uneAutreVue.layer.delegate = self;

    [[self layer]addSublayer:[uneAutreVue layer]];

    [uneAutreVue.layer display];
    [self.layer display];

     et voici le code du draw layer : 



    -(void) drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx
    {
    if (layer == uneAutreVue.layer) {
    // layer.backgroundColor = [NSColor orangeColor].CGColor;
    CGContextSetRGBFillColor (ctx, 1, 0, 0, 1);
    CGRect rectFond = CGRectMake(0,60, 50, 50);
    CGContextFillRect (ctx, rectFond);


    }

    if (layer == self.layer) {
    CGContextSetRGBFillColor (ctx, 0, 1, 0, 1);
    CGRect rectFond = CGRectMake(50,60, 50, 50);
    CGContextFillRect (ctx, rectFond);
    }

    }

    LE  souci, c'est que je ne dessine que le rectangle rouge (celui de uneAutreVue) mais je ne passe jamais dans l'autre qui doit être en bleu en dessus  (celui du self)


     


    Je suppose que j'ai oublié quelque chose mais je ne sais pas quoi


     


    Merci de votre patience

  • CéroceCéroce Membre, Modérateur
    juillet 2013 modifié #10

    En fait, il existe plusieurs layer associées (model layer, representation layer). Il faut juste dessiner dans la layer passée en argument. Je précise qu'il ne s'agit pas de la layer d'une autre vue: elle concerne bien ta vue.


  • Justement, je pensais avoir créé 2 layera : celle de ma vue principale (self) et une autre que j'ai ajouté (uneAutreVue). Du coup, en faisant un display sur une layer puis sur l'autre je devais passer dans le bon layer sous drawLayer et donc dessiner le carré rouge puis vert. Or, je ne passe jamais dans if(layer == self.layer) (dans drawLayer).


    Pourquoi ??


  •  if([layer isEqualToObject: self.layer]) ?


  • isEqualToObject ne semble pas exister. J'ai donc essayé avec isEqualTo mais pas mieux


  • CéroceCéroce Membre, Modérateur


    Justement, je pensais avoir créé 2 layera : celle de ma vue principale (self) et une autre que j'ai ajouté (uneAutreVue). Du coup, en faisant un display sur une layer puis sur l'autre je devais passer dans le bon layer sous drawLayer et donc dessiner le carré rouge puis vert. Or, je ne passe jamais dans if(layer == self.layer) (dans drawLayer).


    Pourquoi ??




    OK, je n'avais pas compris le problème, mais tu as entièrement raison.


    Pour le pourquoi, c'est à  cause de ce que je t'explique plus haut: tu as une Model layer et une Representation layer.


     


    Maintenant, pour ce qui est de la solution, j'en vois deux:


    - utiliser la vue parente en mode layer-backed plutôt que layer-hosting, et dessiner dans -drawRect: comme avant.


    - ou sous-classer CALayer et dessiner dans -drawInContext pour être sûr de la classe de la layer.

  • Merci beaucoup Céroce


     


    Ceci dit : 2 choses


    - comment passe-t-on en mode layer-backed ? le Core Animation comment réellement à  m'intéresser à  force de fouiner !!


    - entre temps, j'ai touvé une autre solution à  mon problème initiale qui consiste à  travailler non pas sur les layer mais sur les vues en donnant la possibilité de cliquer au travers des vues dans les zones transparentes. Voici le code :




    - (NSView *)findNextSiblingBelowEventLocation:(NSEvent *)theEvent {
    // Translate the event location to view coordinates
    NSPoint location = [theEvent locationInWindow];
    NSPoint convertedLocation = [self convertPointFromBase:location];

    // Find next view below self
    NSArray *siblings = [[self superview] subviews];
    NSView *viewBelow = nil;
    for (NSView *view in siblings) {
    if (view != self) {
    NSView *hitView = [view hitTest:convertedLocation];
    if (hitView != nil) {
    viewBelow = hitView;
    }
    }
    }
    return viewBelow;
    }



    -(void) mouseDown:(NSEvent *)theEvent
    {

    NSView *viewBelow = [self findNextSiblingBelowEventLocation:theEvent];
    if (viewBelow) {
    [[self window] makeFirstResponder:viewBelow];
    }
    [super mouseDown:theEvent];

    }

    Cependant, Céroce, si tu peux me donner cette info (mode layer-backed) que je progresse ds ce domaine


    Merci à  toi et à  tous


     


    Dans l'attente de cette réponse


  • CéroceCéroce Membre, Modérateur


     


    - comment passe-t-on en mode layer-backed ? le Core Animation comment réellement à  m'intéresser à  force de fouiner !!


     




    Je te l'ai dit, il faut lire la doc concernant -setWantsLayer: ;-)


    En gros, il faut juste que tu n'écrives pas self.layer = [CALayer layer];


    Dans ce cas, la layer sera créée quand setWantsLayer: est appelée, et mise à  jour lors des appels à  -drawRect:.

  • Grosse confusion de ma part


     


    Dans la doc, j'avais compris qu'il fallait automatiquement les 2 ... bon, j'ai progressé en anglais depuis le début mais j'ai encore beaucoup de lacunes ::)


     


    Merci encore Céroce


Connectez-vous ou Inscrivez-vous pour répondre.