【问题标题】:Custom NSButton not working from CoreMIDI callback function自定义 NSButton 在 CoreMIDI 回调函数中不起作用
【发布时间】:2014-01-08 03:05:13
【问题描述】:

我正在创建一个钢琴应用程序(用于 OSX),它有一个屏幕键盘,可以显示用户在他们的合成器键盘上演奏的内容。一切都使用 CoreMIDI 框架连接。我通过在一个名为PianoKey 的类中继承NSButton 创建了一些自定义按钮。该类可以在下面看到:

#import "PianoKey.h"

@implementation PianoKey

//updates the core animation layer whenever it needs to be changed
- (BOOL)wantsUpdateLayer {
    return YES;
}

- (void)updateLayer {
    //tells you whether or not the button is pressed
    if ([self.cell isHighlighted]) {
        NSLog(@"BUTTON PRESSED");
        self.layer.contents = [NSImage imageNamed:@"buttonpressed.png"];
    }
    else {
        NSLog(@"BUTTON NOT PRESSED");
        self.layer.contents = [NSImage imageNamed:@"button.png"];
    }
}

@end

“钢琴键”是以编程方式创建的。未按下时,钢琴键为蓝色,按下时为粉红色(只是临时颜色)。如果我单击屏幕上的按钮,颜色开关可以正常工作,但是当我尝试通过我的 MIDI 键盘弹奏时它们不会改变。

注意事项: 1) MIDI 键盘工作 2) 我成功地从键盘获取 MIDI 数据。

a) 这个 MIDI 数据被传递给一个名为 midiInputCallback 的回调函数,如下所示: [来自 AppController.m 类]

void midiInputCallback(const MIDIPacketList *list, void *procRef, void *srcRef) {
    PianoKey *button = (__bridge PianoKey*)procRef;

    UInt16 nBytes;
    const MIDIPacket *packet = &list->packet[0]; //gets first packet in list

    for(unsigned int i = 0; i < list->numPackets; i++) {
        nBytes = packet->length; //number of bytes in a packet

        handleMIDIStatus(packet, button);
        packet = MIDIPacketNext(packet);
    }
}

对象 button 是对我以编程方式创建的按钮的引用,在 AppController.h 中定义为:

@property PianoKey *button; //yes, this is synthesized in AppController.m

b) 回调函数调用一堆处理 MIDI 数据的函数。如果检测到正在播放一个音符,这就是我将自定义按钮设置为突出显示的地方...这可以在下面的函数中看到:

void updateKeyboardButtonAfterKeyPressed(PianoKey *button, int key, bool keyOn) {
    if(keyOn)
        [button highlight:YES];
    else
        [button highlight:NO];

    [button updateLayer];

}

[button updateLayer] 从 PianoKey 调用我的 updateLayer() 方法,但它不会更改图像。有什么我想念的吗?似乎视图或窗口没有被更新,即使我的按钮说它是“突出显示的”。请帮忙!在此先感谢:)

【问题讨论】:

    标签: cocoa nsbutton coremidi


    【解决方案1】:

    只是一个猜测(但一个很好的猜测......;-) 回调函数是在主线程上调用的吗?如果不是,那可能就是问题所在——至少它在 iOS 中的工作方式是这样的:UI(屏幕绘图)仅在主线程中更新。

    在回调中放置一个日志语句或断点以查看它是否被调用。如果是,那么问题是由于这个线程问题。


    更新:

    所以告诉按钮更新自身 - 但在主线程上(我认为我的语法是正确的 - 至少对于 iOS - OSX 可能会有所不同)

    [button performSelectorOnMainThread:@selector(updateLayer) 
                             withObject:nil 
                          waitUntilDone:FALSE];
    

    【讨论】:

    • 我不完全确定...我不认为这是我一直在阅读的内容的主线程。再说一次,CoreMIDI 上的信息并不多,所以有时我不知道应该把什么当作事实:/ 你想让我尝试在回调方法中调用什么?
    • 我只在 iOS 中使用过 CoreMidi,但据我所知,GUI 更新的线程问题是相同的。首先,只需在您的 updateLayer 方法中添加一个 NSLog(或 Cocoa 等效项)语句,看看它是否被调用。如果是,那么它是一个线程问题。在这种情况下,您想使用performSelectorOnMainThread。一个警告:由于键盘更新可能比刷新率更快,因此每次调用更新可能毫无意义。在 iOS 中,有一个方法可以在每次屏幕刷新之前调用。在 OSX 中不知道。也许试试performSelectorOnMainThread第一个广告看看是否有效
    • 好的,我知道你现在在说什么了。我目前有一个指向我的按钮实例的指针,因为我将它传递给了我的回调函数。如果我没有这个引用,它将对我的按钮对象一无所知。所以你是对的,这是一个线程问题。你如何使用performSelectorOnMainThread?我是这一切的菜鸟,但我学得很快(我认为)。我应该将此作为新问题发布吗?顺便说一句,如果我有一个我的按钮实例并且我可以调用updatelayerperformSelectorOnMainThread 将如何提供帮助?
    • @02fentym - 很高兴它成功了! (我只知道它,因为我在 iOS 上遇到了完全相同的事情。)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-22
    • 1970-01-01
    • 2015-05-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多