2010-11-03 7 views
1

Je souhaite placer une barre de progression animée dans une vue personnalisée NSMenuItem. Ceci est démontré dans l'exemple de MenuItemView d'Apple, mais il ne s'anime pas (du moins pas dans 10.5, et l'échantillon est apparemment de 10.4).Barre de progression animée dans NSMenuItem

J'ai essayé de régler un temporisateur qui appelle setNeedsDisplay:YES, programmé comme NSEventTrackingRunLoopMode comme le disent les docs. Cela fonctionne, mais seulement pour une barre de progression déterminée si je change la valeur, et seulement la première fois que le menu s'ouvre. La deuxième fois et successivement, la barre redessine deux fois et reste ensuite figée. Pour une barre de progression indéterminée, les rayures du barbier ne s'animent jamais.


Modifier: extrait de code. Je viens d'ajouter l'appel itemChanged, qui ne semble pas avoir d'effet. La mise à jour de l'élément textuel fonctionne correctement.

class AppDelegate(NSObject): 
    barItem = None 
    menuProgressBar = None 
    progressItem = None 

    def applicationDidFinishLaunching_(self, sender): 
    statusbar = NSStatusBar.systemStatusBar() 
    self.statusitem = statusbar.statusItemWithLength_(
     NSSquareStatusItemLength) 
    self.statusitem.setHighlightMode_(True) 
    image = NSImage.imageNamed_("menubar.png") 
    self.statusitem.setImage_(image) 
    self.statusitem.retain() 

    menu = NSMenu.alloc().init() 

    AppDelegate.barItem = NSMenuItem.alloc(). \ 
     initWithTitle_action_keyEquivalent_('progress', None, '') 
    itemView = NSView.alloc().initWithFrame_(NSMakeRect(0, 0, 50, 20)) 
    itemView.setAutoresizingMask_(NSViewWidthSizable) 
    AppDelegate.menuProgressBar = \ 
     NSProgressIndicator.alloc().initWithFrame_(NSMakeRect(16, 5, 22, 10)) 
    AppDelegate.menuProgressBar.setAutoresizingMask_(NSViewWidthSizable) 
    AppDelegate.menuProgressBar.setControlSize_(NSSmallControlSize) 
    AppDelegate.menuProgressBar.setUsesThreadedAnimation_(True) 
    itemView.addSubview_(AppDelegate.menuProgressBar) 
    AppDelegate.menuProgressBar.setIndeterminate_(False) 
    AppDelegate.menuProgressBar.setMaxValue_(100) 
    AppDelegate.menuProgressBar.startAnimation_(self) 
    timer = NSTimer.timerWithTimeInterval_target_selector_userInfo_repeats_(
     0.1, self, 
     objc.selector(self.animateProgress, signature='[email protected]:'), 
     None, True) 
    NSRunLoop.currentRunLoop().addTimer_forMode_(
     timer, NSEventTrackingRunLoopMode) 
    AppDelegate.barItem.setView_(itemView) 
    menu.addItem_(AppDelegate.barItem) 

    AppDelegate.progressItem = NSMenuItem.alloc(). \ 
     initWithTitle_action_keyEquivalent_('Progress', None, '') 
    menu.addItem_(AppDelegate.progressItem) 

    self.statusitem.setMenu_(menu) 

    def animateProgress(self): 
    time = NSDate.timeIntervalSinceReferenceDate() 
    AppDelegate.menuProgressBar.setDoubleValue_(time%100) 
    AppDelegate.menuProgressBar.display() 
    AppDelegate.progressItem.setTitle_('Progress: %d'%(time%100)) 
    AppDelegate.barItem.menu().itemChanged_(AppDelegate.barItem) 

Répondre

1

L'approche du temporisateur devrait être complètement inutile. Avez-vous dit à NSProgressIndicator de -setUsesThreadedAnimation: YES l'a alors dit à -startAnimation:?

+0

Oui, je l'ai fait aussi. Aucun effet. – Uncommon

+0

Probablement le moment de poster votre code. –

+0

Exemple de code posté. – Uncommon

1

Pour un indicateur indéterminé, j'envoie le message startAnimation: dans la méthode menuWillOpen: delegate du menu en utilisant performSelector: ... pour l'envoyer en mode NSEventTrackingRunLoopMode.

- (void)menuWillOpen:(NSMenu *)menu 
{ 
    [[progressIndicator performSelector:@selector(startAnimation:) 
          withObject:self 
          afterDelay:0.0 
           inModes:[NSArray arrayWithObject:NSEventTrackingRunLoopMode]]; 
} 

Pour l'indicateur déterminé j'ai essayé votre code (mais dans l'objectif-C) et cela a fonctionné. Je ne connais pas Python ou PyObjC mais en regardant le code de la variable time je pense que vous envoyez l'appel timeIntervalSinceReferenceDate() à la classe NSDate. Alors peut-être que le temps est toujours zéro? Sur la base des alloc() appelle Je me demande si elle ne devrait pas être ...

temps = NSDate.date() timeIntervalSinceReferenceDate()


Mise à jour:. Pour le dossier ici est le code que j'ai utilisé pour tester l'indicateur de progrès déterminé. Juste la version obj-c du code OP. L'indicateur est mis à jour correctement.

- (void)menuWillOpen:(NSMenu *)menu 
{ 
    NSTimer *timer = [NSTimer timerWithTimeInterval:0.1 target:self selector:@selector(animateProgress:) userInfo:nil repeats:YES]; 
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSEventTrackingRunLoopMode]; 
} 

- (void)animateProgress:(NSTimer *)timer 
{ 
    NSTimeInterval time = [[NSDate date] timeIntervalSinceReferenceDate]; 
    [progressIndicator setDoubleValue:fmod(time, 100)]; 
} 
+0

L'appel NSDate fonctionne correctement. La barre de progression déterminée fonctionne la première fois que le menu s'ouvre et l'élément textuel fonctionne toujours. Je vais essayer de sous-classer NSView pour pouvoir remplacer menuWillOpen. – Uncommon

+0

Je veux dire, je vais essayer de faire le truc du délégué. – Uncommon