代码之家  ›  专栏  ›  技术社区  ›  Everett Kaser

在事件处理程序中绘制到NSView

  •  0
  • Everett Kaser  · 技术社区  · 6 年前

    我想尽了一切办法,但运气不好。

    我有一个项目,本质上是一种编程语言的“解释器”。因此,事件被传递到解释器循环,被解释的代码执行任何操作(在本例中,更新内存位图),然后解释器循环返回,事件处理程序返回。最终会调用应用程序的drawRect,并将内存位图绘制到NSView。大多数时候这一切都很好。

    但是在一些情况下,解释的“代码”想要产生一个短动画,并通过更新内存位图、usleep()'ing几毫秒、更新内存位图、usleep()'ing等来实现。动画只需要不到一秒钟的时间,因此线程阻塞不应成为问题。

    问题是没有任何动画显示,直到解释的“代码”结束,事件返回后,屏幕才会更新。

    当解释的代码指示要睡眠时,调用的睡眠函数如下所示:

    void KSleep(DWORD tm) {
    
        if( [pView lockFocusIfCanDraw] ) {
            inSleep = true;
            [pView setNeedsDisplay:YES];
            [pView display];
            [pView drawRect:NSMakeRect(0, 0, pView.frame.size.width, pView.frame.size.height)];
            [pView unlockFocus];
        }
        usleep(tm*1000);
    }
    

    “inSleep”是为测试目的而设置的全局变量,“pView”是窗口唯一视图的全局NSView*。注意:是的,上面的一些代码是多余的,我只是将其包括在内,以表明我尝试了多次组合,试图向操作系统指示视图是脏的,并对其进行更新。他们都没有成功。

    drawRect代码(删除所有执行位图常规blitting的代码)如下所示:

    -(void)drawRect:(CGRect)rect {
    
        CGContextRef context = [[NSGRaphicsContext currentContext] graphicsPort];
        CGContextSaveGState(context);
        if( inSleep ) CGContextSetRGBFillColor(context, 0.0, 0.0, 1.0, 1.0);
        else CGContextSetRGBFillColor(context, 1.0, 1.0, 0.0, 1.0);
        CGContextFillRect(context, CGRectMake(0,0,200,200));
        CGContextRestoreGState(context);
    }
    

    因此:
    1) -mouseDown()事件发生,它用该事件调用解释器。
    2) 解释后的代码绘制到位图(我在这里忽略了这一点,因为它对屏幕更新的工作与否并不重要),并调用“睡眠”
    3) 解释器看到“sleep”调用,调用Ksleep()。
    4) Ksleep锁定焦点,它似乎创建了一个上下文,若并没有它,调试器会在drawRect()函数期间发出0x0上下文的警告,而使用lockFocus则不会并且似乎有一个有效的上下文值。
    5) Ksleep将视图标记为需要更新,并调用(varyingly)“display”和/或“drawRect”等。
    6) drawRect例程确实获得了控制(断点表示在这方面一切正常)。'inSleep'设置正确。它按预期逐步完成drawRect中的所有内容。但是显示屏上什么也没有显示,直到。。。
    7) drawRect返回到Ksleep,睡眠超时,解释器继续解释,解释的代码进行更多的绘制和睡眠,大约10次(因此重复步骤2-7大约10次)。

    从程序启动到鼠标操作导致“动画”尝试,视图中会绘制一个黄色矩形。一旦发生导致鼠标点击的“动画”,在动画完全完成之前,窗口中不会更新任何内容(即使在整个动画尝试中多次执行drawRect),然后矩形变为蓝色。但断点表明,每次调用KSleep()例程时,执行都会通过drawRect(inSleep为true)。

    这是线的东西吗?(程序没有显式创建任何线程。)

    我并不是特别想寻求关于如何避免动画/KSleep结构的建议,我意识到这不是Macos的首选方法,但这是一种从其他地方移植旧项目的尝试,修改“解释”代码以避免这种情况是不可能的。

    感谢您的任何想法或建议。

    1 回复  |  直到 6 年前
        1
  •  0
  •   Everett Kaser    6 年前

    我找到的解决方案是将所有解释器代码放在第二个线程中,释放主线程来更新显示。尽管drawRect例程正在运行和绘制,但直到当前的“命令解释”周期完成并返回后,它才刷新到显示。本页包含大量关于该主题的信息: https://developer.apple.com/library/content/technotes/tn2133/_index.html

    此外,所有“invalidate”调用/语句都必须保存/缓冲到一个公共的“包含所有invalidate的最小矩形”中,然后在返回“命令解释”后和/或在执行sleep()之前执行invalidate。

    皮塔。