我如何让多个线程绘制到AWT组件上? [英] How do I let multiple Threads paint onto an AWT component?

查看:122
本文介绍了我如何让多个线程绘制到AWT组件上?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

首先,这是我第一个问题,所以如果我犯了什么错误,请告诉我。

我正在尝试用Java编写一个Mandelbrot分形程序,用于培训目的。我想要的所有功能的理想选择是分形器( http://www.fractalizer.de/en/ ),但现在,我会很高兴在屏幕上绘制Mandelbrot Set的程序(而不是写入图像文件)。当然,我希望程序速度很快,所以我认为我可以将计算分成多个线程来利用我的多核处理器;例如,在四核系统上,图像将被分成2×2 = 4个图像,每个图像由一个单独的线程计算。所有这些线程都会得到一个Graphics对象,并在计算它们时绘制像素。



我第一次尝试这个工作是让线程在BufferedImage上绘制.getGraphics(),并且只要图像未完成,就让paint()方法不断地调用repaint():

  g.drawImage(tempImg,0,0,null); 
if(waiterThread.isAlive())
{
try
{
Thread.sleep(10);
} catch(InterruptedException e)
{
// do not
}
repaint(10);





$ b(waiterThread)一个接一个地加入所有计算线程,所以只要服务器线程至少有一个计算线程还没有完成。)



这可以起作用,但由于频繁的重新绘制会导致画布上的丑陋闪烁。 b
$ b

然后,通过一个小测试程序,我发现Graphics.draw *任何*在paint方法返回之前立即在屏幕上绘制,所以我现在的方法如下:


  • 一个包含2x2(在一个< 4核心系统,1x1)MandelbrotCanvas对象的GridLayout的面板

  • 每个MandelbrotCanvas对象将在第一次paint()调用时初始化一个计算线程,将它自己的Graphics对象传递给它(实际上,我使用的是一个自定义的GroupGraphics类,它将一个Graphics调用传递给几个图形,将图像备份到BufferedImage.getGraphics()中,但这并不重要),然后开始计算线程。
  • 面板将在其paint()方法中从每个MandelbrotCanvas中获取计算线程并加入()它们。 b
    $ b

    不幸的是,这只会产生一个黑屏。只有当计算完成后,图像才会显示出来。



    将多个线程绘制到一个组件上的正确方法是什么?



    编辑:



    我不知道的是:只允许Event Dispatch Thread在AWT组件上绘制(粗略地说),这意味着上面的方法不可能工作 - 显然,它应该抛出一个异常,但我没有得到一个。我的解决方案是使用第一种方法 - 将图像绘制到BufferedImage上并将其绘制到Canvas上 - 唯一的修改是我重载update()方法来调用paint()方法而不清除绘画区域

      public void update(Graphics g)
    {
    paint(g);





    $ b

    所以我想我的答案是一般问题(我如何让多个将线程绘制到AWT组件上?)将是:您不能,这是不允许的。让线程绘制到BufferedImage.getGraphics(),并重复绘制该图像。重载像上面那样的update()方法以避免闪烁。 (现在看起来非常棒。)另一个我不能用在我的例子中,但仍然不错的提示是有一个repaint()的变体,它使用矩形参数来指定必须重绘的区域,以及一个需要时间参数(以毫秒为单位)的变体,因此重绘不必立即发生。



    编辑2:此链接提供了非常有用的信息: http://java.sun.com/products/jfc/tsc/articles/ painting / index.html

    解决方案

    只有GUI线程可以直接在组件上绘制。 b
    $ b

    所以你必须调用repaint方法。



    当你有后台计算时,为了强制快速绘制,你应该使用<版本花费一些时间作为参数。



    一些细节fr 此处


    注意:如果在处理
    初始重绘请求之前在组件上发生了多次重绘repaint()调用,则多个请求可以是
    折叠为一次调用update()。
    确定何时应该折叠多个请求的算法是
    实现相关的算法。如果多个请求折叠,则
    结果更新矩形将等于折叠请求中包含的
    矩形的联合。



    EDIT: solved, look below for my solution.

    first of all, this is my very first question here, so if I make any mistakes, please tell me.

    I am trying to write a Mandelbrot Fractal Program in Java, for training purposes. The ideal for all the functionality I want to have is the Fractalizer (http://www.fractalizer.de/en/), but for now, I will be happy with a program that draws the Mandelbrot Set on the screen (instead of, for example, writing it to an image file). Of course, I want the program to be fast, so I thought that I could split the calculation into multiple threads to utilize my multi-core processor; for example, on a quad-core system, the image would be divided into 2x2=4 images, each to be calculated by a separate thread. All these threads get a Graphics object passed on which they draw the pixels as they are calculated.

    My first attempt to get this working was to have the threads draw on a BufferedImage.getGraphics() and to have the paint() method constantly call repaint() as long as the image isn't finished:

    g.drawImage(tempImg, 0, 0, null);
    if (waiterThread.isAlive())
    {
        try
        {
            Thread.sleep(10);
        } catch (InterruptedException e)
        {
            // do nothing
        }
        repaint(10);
    }
    

    (waiterThread joins all calculating threads one after another, so as long as the waiterThread is alive, at least one calculating thread is not yet finished.)

    This works, but causes ugly flickering on the canvas because of the frequent repainting.

    Then, by means of a small test program, I found out that Graphics.draw*anything* draws on the screen instantly, before the paint method returns, so my current approach is the following:

    • One Panel with a GridLayout that contains 2x2 (on a <4-core system, 1x1) MandelbrotCanvas objects
    • Each MandelbrotCanvas object will, on the first paint() call, initialize a calculating Thread, pass its own Graphics object to it (actually, I'm using a custom GroupGraphics class that passes one Graphics call to several graphics, to "backup" the image into a BufferedImage.getGraphics(), but that's not important), and start the calculating thread.
    • The panel will in its paint() method fetch the calculating threads from each of the MandelbrotCanvases and join() them.

    Unfortunately, this creates only a black screen. Only when calculation is finished, the image is displayed.

    What is the right way to have several threads paint onto one component?

    EDIT:

    What I didn't know: Only the Event Dispatch Thread is allowed to paint on AWT components (roughly spoken), which means that the last approach above can't possibly work - apparently, it's supposed to throw an exception, but I didn't get one. My solution is to use the first approach - draw the image onto a BufferedImage and draw that onto the Canvas - with the only modification that I overload the update() method to call the paint() method without clearing the painting area:

    public void update(Graphics g)
    {
        paint(g);
    }
    

    So I guess my answer to the general question ("How do I let multiple Threads paint onto an AWT component?") would be: You can't, it's not allowed. Let the Threads draw onto a BufferedImage.getGraphics(), and draw that image repeatedly. Overload the update() method like above to avoid flickering. (It looks really great now.) Another tip that I can't use in my case, but is still good, is that there is a variant of repaint() that takes rectangle arguments to specify the area that has to be redrawn, and a variant that takes a time argument (in milliseconds), so the repaint doesn't have to happen immediately.

    EDIT2: This link provides very helpful information: http://java.sun.com/products/jfc/tsc/articles/painting/index.html

    解决方案

    Only the GUI thread can paint directly on your component.

    So you must call the repaint method.

    When you have background computation, to force a fast drawing, you should use the version taking a time as parameter.

    Some details from here :

    NOTE: If multiple calls to repaint() occur on a component before the initial repaint request is processed, the multiple requests may be collapsed into a single call to update(). The algorithm for determining when multiple requests should be collapsed is implementation-dependent. If multiple requests are collapsed, the resulting update rectangle will be equal to the union of the rectangles contained in the collapsed requests.

    这篇关于我如何让多个线程绘制到AWT组件上?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆