Eclipse Community Forums
Forum Search:

Search      Help    Register    Login    Home
Home » Eclipse Projects » Standard Widget Toolkit (SWT) » GC operations not propagated to viewport on OS X
GC operations not propagated to viewport on OS X [message #1058858] Wed, 15 May 2013 08:31 Go to next message
Marko Topolnik is currently offline Marko Topolnik
Messages: 42
Registered: July 2009
Member
I'm developing an Eclipse plug-in on OS X Lion. It has a view with a live histogram and a slider beside it, which regulates the quantity visualized by the histogram. In technical detail, I've got a Scale and a Canvas in a ViewPart and I repeatedly paint the live histogram on the Canvas (from a scheduled task that uses Display#asyncExec). To improve performance I use a single instance of GC across all repaints. Everything works well until the user engages the Scale slider with a mouse-down: as long as the mouse is down, the Canvas is not getting updated at all.

If I use a new GC for each histogram update and dispose it at the end of the update, the Canvas refreshes properly, but in this case performance is severely hampered by the overhead of CG construction/disposal.

Is there an unspecified requirement to dispose the GC in order to guarantee its operations are flushed to the viewport?

Is there a way to keep both the performance and proper refreshing of the Canvas?

BTW On Windows this problem is not manifested.
Re: GC operations not propagated to viewport on OS X [message #1058969 is a reply to message #1058858] Thu, 16 May 2013 01:08 Go to previous messageGo to next message
Vijay Raj
Messages: 608
Registered: July 2009
Senior Member
why not move your paint code to paint event instead of
Display.asyncExec
.

you just update the model and trigger a repaint on the canvas(or control).

again a snippet would help a lot in understanding, what actually is wrong.


---------------------
why, mr. Anderson, why, why do you persist?
Because I Choose To.
Regards,
Vijay
Re: GC operations not propagated to viewport on OS X [message #1059022 is a reply to message #1058969] Thu, 16 May 2013 05:11 Go to previous messageGo to next message
Marko Topolnik is currently offline Marko Topolnik
Messages: 42
Registered: July 2009
Member
Yes, I should prepare a minimalist snippet...

About the paint event, it would be the same thing: I would still need to generate the paint event inside Display.asyncExec because the decision when to do it happens in another thread, the one that does the histogram calculation. A paint event just creates its own GC and disposes it in the end, so the performance issue is back.

This is my current assessment: SWT/Cocoa prevents GUI flicker by postponing screen updates until there is a slight pause in the incoming GUI operations. Also, when a GC is disposed, all operations it has performed are immediately flushed to the viewport.

This goes well with the typical usage where several paint operations done in succession partially overlap and would cause flicker if each individual operation was immediately rendered. Indeed, on Windows there is flicker, and on Mac there never is; Windows doesn't suffer from my issue and Mac does.

The downside of this manifests itself when the user engages the Scale slider: apparently Scale generates a constant stream of events regardless of whether the user is actually moving the slider. This prevents my GC operations to propagate to the screen.
Re: GC operations not propagated to viewport on OS X [message #1059052 is a reply to message #1059022] Thu, 16 May 2013 08:39 Go to previous messageGo to next message
Vijay Raj
Messages: 608
Registered: July 2009
Senior Member
Yep there may be an issue of mac,but what i was suggesting should work there too.

i am suggesting on similar lines as below
import org.eclipse.swt.widgets.Composite;

public class UpdateCanvasSnippet extends Composite
{

    class ScaleModel
    {
        private int value;

        public ScaleModel(int value)
        {
            this.value = value;
        }

        public ScaleModel()
        {
            this.value = 20;
        }

        public int getValue()
        {
            return value;
        }
    }

    private ScaleModel scaleModel = new ScaleModel();

    /**
     * Create the composite.
     * 
     * @param parent
     * @param style
     */
    public UpdateCanvasSnippet(Composite parent, int style)
    {
        super(parent, style);
        setLayout(new GridLayout(1, false));

        final Scale scale = new Scale(this, SWT.NONE);
        GridData gd_scale = new GridData(GridData.FILL_HORIZONTAL);
        scale.setLayoutData(gd_scale);

        scale.setMinimum(20);
        scale.setMaximum(100);
        final Canvas canvas = new Canvas(this, SWT.NO_BACKGROUND | SWT.DOUBLE_BUFFERED);
        canvas.setLayoutData(new GridData(GridData.FILL_BOTH));
        scale.addSelectionListener(new SelectionListener()
        {

            @Override
            public void widgetSelected(SelectionEvent e)
            {      
                final int selection = scale.getSelection();
                Thread thread = new Thread(new Runnable()
                {
                    
                    @Override
                    public void run()
                    {                       
                        //Actual logic to calculate                        
                        scaleModel = new ScaleModel(selection);   
                        Display.getDefault().syncExec(new Runnable()
                        {                            
                            @Override
                            public void run()
                            {
                                canvas.redraw();     
                            }
                        });        
                    }
                });
                thread.start();   
            }

            @Override
            public void widgetDefaultSelected(SelectionEvent e)
            {
                // TODO Auto-generated method stub

            }
        });
        canvas.addPaintListener(new PaintListener()
        {

            @Override
            public void paintControl(PaintEvent e)
            {
                e.gc.setLineWidth(5);
                e.gc.setForeground(e.display.getSystemColor(SWT.COLOR_RED));
                e.gc.drawRectangle(e.width / 2 - scaleModel.getValue()/2,
                    e.height / 2 - scaleModel.getValue()/2, scaleModel.getValue(),
                    scaleModel.getValue());;
            }
        });

    }

    @Override
    protected void checkSubclass()
    {
        // Disable the check that prevents subclassing of SWT components
    }

    public static void main(String[] args)
    {
        Display display = new Display();
        Shell shell = new Shell(display, SWT.SHELL_TRIM | SWT.DOUBLE_BUFFERED);
        shell.setLayout(new FillLayout());
        UpdateCanvasSnippet testSWTDesigner = new UpdateCanvasSnippet(shell, SWT.None);
        shell.setSize(600, 400);
        shell.open();
        while (!shell.isDisposed())
        {
            if (!display.readAndDispatch())
                display.sleep();
        }
        display.dispose();
    }
}


you perform your operations and update a model which in turn is used by the paint even to draw. all you have to do is update the model and call canvas.redraw()

Note this call can be inside display.asyncExec too. like in your case you perform operations and update the model and call canvas.redraw inside display.syncExec or asyncExec as per your requirement.


EDIT: Updated the snippet as per your scenario.you can test this on mac,if this works then your code also should work.


---------------------
why, mr. Anderson, why, why do you persist?
Because I Choose To.
Regards,
Vijay

[Updated on: Thu, 16 May 2013 10:33]

Report message to a moderator

Re: GC operations not propagated to viewport on OS X [message #1059130 is a reply to message #1059052] Thu, 16 May 2013 17:45 Go to previous messageGo to next message
Marko Topolnik is currently offline Marko Topolnik
Messages: 42
Registered: July 2009
Member
Thanks for the snippet; you beat me to it Smile I have adapted it to better reflect my issues. I have added more canvases (i have more histograms like that), so the performance becomes more critical. The line in code that says "draw(gc)" is commented out; if we uncomment it and comment "canvas.redraw()" above it, we can compare the two behaviors. Indeed, on my Mac the canvas.redraw() technique works much better. In my real code I was using notifyListeners(SWT.Paint), which is worse than redraw. I am now working on the redraw technique.

import static java.lang.Math.sin;
import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.eclipse.swt.layout.GridData.FILL_HORIZONTAL;

import java.util.concurrent.ScheduledExecutorService;

import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Scale;
import org.eclipse.swt.widgets.Shell;

public class UpdateCanvasSnippet extends Composite
{
  static final Display display = new Display();
  static final ScheduledExecutorService sched = newSingleThreadScheduledExecutor();
  double phi, phase;
  GC gc;

  public UpdateCanvasSnippet(Composite parent, int style) {
    super(parent, style);
    setLayout(new GridLayout(1, false));
    final Scale scale = new Scale(this, SWT.NONE);
    final GridData gd_scale = new GridData(FILL_HORIZONTAL);
    scale.setLayoutData(gd_scale);
    scale.setMinimum(100);
    scale.setMaximum(600);
    final Canvas canvas = new Canvas(this, SWT.NONE);
    gc = new GC(canvas);
    canvas.setLayoutData(new GridData(GridData.FILL_BOTH));
    sched.scheduleAtFixedRate(new Runnable() { public void run() {
      display.asyncExec(new Runnable() { public void run() {
        phi = System.currentTimeMillis();
        if (!canvas.isDisposed())
          canvas.redraw();
//          draw(gc);
      }});
    }}, 0, 10, MILLISECONDS);
    scale.addSelectionListener(new SelectionAdapter() {
      @Override public void widgetSelected(SelectionEvent e) {
        phase = scale.getSelection();
      }
    });
    canvas.addPaintListener(new PaintListener() { public void paintControl(PaintEvent e) {
      draw(e.gc);
    }});
  }
  void draw(GC gc) {
    gc.setForeground(display.getSystemColor(SWT.COLOR_RED));
    gc.setBackground(display.getSystemColor(SWT.COLOR_WHITE));
    final Rectangle area = gc.getClipping();
    gc.fillRectangle(area);
    for (int x = 0; x < area.width; x++)
      gc.drawLine(x, 0, x, (int) (area.height/2 - 30*sin((phi/20-phase-x)/10d)));
  }

  public static void main(String[] args) throws InterruptedException {
    final Shell shell = new Shell(display, SWT.SHELL_TRIM | SWT.DOUBLE_BUFFERED);
    shell.setLayout(new GridLayout(3,false));
    for (int i = 0; i < 9; i++) {
      final Composite c = new UpdateCanvasSnippet(shell, SWT.None);
      gridData().grab(true, true).applyTo(c);
    }
    shell.setSize(1000, 800);
    shell.open();
    while (!shell.isDisposed()) if (!display.readAndDispatch()) display.sleep();
    sched.shutdown();
    sched.awaitTermination(1, SECONDS);
    display.dispose();
  }

  static GridDataFactory gridData() { return GridDataFactory.fillDefaults(); }

  @Override protected void checkSubclass() { }
}
Re: GC operations not propagated to viewport on OS X [message #1059166 is a reply to message #1059130] Fri, 17 May 2013 02:06 Go to previous messageGo to next message
Vijay Raj
Messages: 608
Registered: July 2009
Senior Member
A small change in your snippet and no performance issue should happen.
import static java.lang.Math.sin;
import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.eclipse.swt.layout.GridData.FILL_HORIZONTAL;

import java.util.concurrent.ScheduledExecutorService;

import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Scale;
import org.eclipse.swt.widgets.Shell;

public class UpdateCanvasSnippet extends Composite
{
    static final Display display = new Display();

    static final ScheduledExecutorService sched = newSingleThreadScheduledExecutor();

    double phi, phase;

    GC gc;

    public UpdateCanvasSnippet(Composite parent, int style)
    {
        super(parent, style);
        setLayout(new GridLayout(1, false));
        final Scale scale = new Scale(this, SWT.NONE);
        final GridData gd_scale = new GridData(FILL_HORIZONTAL);
        scale.setLayoutData(gd_scale);
        scale.setMinimum(100);
        scale.setMaximum(600);
        final Canvas canvas = new Canvas(this, SWT.NO_BACKGROUND | SWT.DOUBLE_BUFFERED);
        // gc = new GC(canvas);
        canvas.setLayoutData(new GridData(GridData.FILL_BOTH));
        sched.scheduleAtFixedRate(new Runnable()
        {
            public void run()
            {
                display.asyncExec(new Runnable()
                {
                    public void run()
                    {
                        phi = System.currentTimeMillis();
                        if (!canvas.isDisposed())
                            canvas.redraw();
                        // draw(gc);
                    }
                });
            }
        }, 0, 10, MILLISECONDS);
        scale.addSelectionListener(new SelectionAdapter()
        {
            @Override
            public void widgetSelected(SelectionEvent e)
            {
                phase = scale.getSelection();
            }
        });
        canvas.addPaintListener(new PaintListener()
        {
            public void paintControl(PaintEvent e)
            {
                draw(e.gc);
            }
        });
    }

    void draw(GC gc)
    {
        gc.setForeground(display.getSystemColor(SWT.COLOR_RED));
        gc.setBackground(display.getSystemColor(SWT.COLOR_WHITE));
        final Rectangle area = gc.getClipping();
        gc.fillRectangle(area);
        for (int x = 0; x < area.width; x++)
            gc.drawLine(x, 0, x, (int) (area.height / 2 - 30 * sin((phi / 20 - phase - x) / 10d)));
    }

    public static void main(String[] args) throws InterruptedException
    {
        final Shell shell = new Shell(display, SWT.SHELL_TRIM | SWT.DOUBLE_BUFFERED);
        shell.setLayout(new GridLayout(3, false));
        for (int i = 0; i < 9; i++)
        {
            final Composite c = new UpdateCanvasSnippet(shell, SWT.None);
            gridData().grab(true, true).applyTo(c);
        }
        shell.setSize(1000, 800);
        shell.open();
        while (!shell.isDisposed())
            if (!display.readAndDispatch())
                display.sleep();
        sched.shutdown();
        sched.awaitTermination(1, SECONDS);
        display.dispose();
    }

    static GridDataFactory gridData()
    {
        return GridDataFactory.fillDefaults();
    }

    @Override
    protected void checkSubclass()
    {
    }
}



just create the canvas using following styles
Canvas canvas = new Canvas(this, SWT.NO_BACKGROUND | SWT.DOUBLE_BUFFERED);


first one tells the canvas that you dont have your own background,second one tell it to draw on an image first and then draw it to the canvas.which increases the drawing performance.By the way this can be also done in the paint event by first drawing on an image and then drawing that to gc.
refer to canvas section and various others in the snippets page of SWT
http://www.eclipse.org/swt/snippets/

The issue here is not canvas.redraw or notifyListeners(SWT.Paint)(in your case this call was useless)

The issue is creating your own gc and drawing,all major paint operations should be performed in the paint event and only when it is unavoidable or a minor paint operation is required should you use the creating gc and drawing method.

many things are taken care of in the paint event, which would fail in GC method,in your case it is not visible due to periodic updates.

There can be some other ways to achieve this more efficiently like creating a GC using an empty image and drawing on it and on each paint event drawing the image on the canvas.but in this case finding the size of the image will be tricky.


---------------------
why, mr. Anderson, why, why do you persist?
Because I Choose To.
Regards,
Vijay
Re: GC operations not propagated to viewport on OS X [message #1059193 is a reply to message #1059166] Fri, 17 May 2013 04:24 Go to previous messageGo to next message
Marko Topolnik is currently offline Marko Topolnik
Messages: 42
Registered: July 2009
Member
Thanks for all the good information. In my earlier versions I did indeed have NO_BACKGROUND style, but then I read the documentation on Canvas, which says "Styles: none", so I removed it. Are some style bits always applicable even when the documentation says none apply? Or is the documentation just incomplete?
Re: GC operations not propagated to viewport on OS X [message #1059207 is a reply to message #1059193] Fri, 17 May 2013 05:58 Go to previous messageGo to next message
Vijay Raj
Messages: 608
Registered: July 2009
Senior Member
See parent class Composite. ideally you can draw on any control which is the superclass of all controls,
Canvas is a specialized control for drawing only.


---------------------
why, mr. Anderson, why, why do you persist?
Because I Choose To.
Regards,
Vijay
Re: GC operations not propagated to viewport on OS X [message #1059225 is a reply to message #1059207] Fri, 17 May 2013 07:46 Go to previous messageGo to next message
Marko Topolnik is currently offline Marko Topolnik
Messages: 42
Registered: July 2009
Member
Thanks, I'll keep in mind to always study the complete inheritance chain of each component class that I'm working with.

The cause of my issues on Mac is demonstrated with the updated snippet below. Even with NO_BACKGROUND the entire Canvas is being cleared out with grey on each repaint. The key method that demonstrates this is drawRect, which draws a blue rectangle only on first invocation. The rectangle immediately disappears from the Canvas even though it is in the area unaffected by any other draw operations.

On my production app I have a lot of expensive-to-draw static content like meter scales, chart title, etc, which for performance reasons I must not redraw each time. If I rely on my own GC, the content is untouched, but whenever a repaint event comes in, everything is erased.

import static java.lang.Math.sin;
import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.eclipse.swt.layout.GridData.FILL;
import static org.eclipse.swt.layout.GridData.FILL_HORIZONTAL;

// type imports elided

public class UpdateCanvasSnippet extends Composite
{
  static final Display display = new Display();
  static final ScheduledExecutorService sched = newSingleThreadScheduledExecutor();
  double phi, phase;
  boolean rectDrawn;

  public UpdateCanvasSnippet(Composite parent, int style) {
    super(parent, style);
    setLayout(new GridLayout(1, false));
    final Scale scale = new Scale(this, SWT.NONE);
    final GridData gd_scale = new GridData(FILL_HORIZONTAL);
    scale.setLayoutData(gd_scale);
    scale.setMinimum(100);
    scale.setMaximum(600);
    final Canvas canvas = new Canvas(this, SWT.NO_BACKGROUND | SWT.DOUBLE_BUFFERED);
    canvas.setBackground(display.getSystemColor(SWT.COLOR_WHITE));
    gridData().align(FILL, FILL).grab(true, true).applyTo(canvas);
    sched.scheduleAtFixedRate(new Runnable() { public void run() {
      display.asyncExec(new Runnable() { public void run() {
        phi = System.currentTimeMillis();
        if (!canvas.isDisposed())
          canvas.redraw();
      }});
    }}, 0, 10, MILLISECONDS);
    scale.addSelectionListener(new SelectionAdapter() {
      @Override public void widgetSelected(SelectionEvent e) {
        phase = scale.getSelection();
      }
    });
    canvas.addPaintListener(new PaintListener() { public void paintControl(PaintEvent e) {
      draw(e.gc);
    }});
  }
  void drawRect(GC gc) {
    if (rectDrawn) return;
    gc.setForeground(display.getSystemColor(SWT.COLOR_BLUE));
    final Rectangle area = gc.getClipping();
    gc.drawRectangle(0, area.height-51, 50, 50);
    rectDrawn = true;
  }
  void draw(GC gc) {
    drawRect(gc);
    gc.setForeground(display.getSystemColor(SWT.COLOR_RED));
    gc.setBackground(display.getSystemColor(SWT.COLOR_WHITE));
    final Rectangle area = gc.getClipping();
    for (int x = 0; x < area.width; x++)
      gc.drawLine(x, 0, x, (int) (area.height/2 - 30*sin((phi/20-phase-x)/10d)));
  }

  public static void main(String[] args) throws InterruptedException {
    final Shell shell = new Shell(display, SWT.SHELL_TRIM | SWT.DOUBLE_BUFFERED);
    shell.setLayout(new GridLayout(3,false));
    for (int i = 0; i < 9; i++) {
      final Composite c = new UpdateCanvasSnippet(shell, SWT.None);
      gridData().grab(true, true).applyTo(c);
    }
    shell.setSize(1000, 800);
    shell.open();
    while (!shell.isDisposed()) if (!display.readAndDispatch()) display.sleep();
    sched.shutdown();
    sched.awaitTermination(1, SECONDS);
    display.dispose();
  }

  static GridDataFactory gridData() { return GridDataFactory.fillDefaults(); }

  @Override protected void checkSubclass() { }
}
Re: GC operations not propagated to viewport on OS X [message #1059242 is a reply to message #1059225] Fri, 17 May 2013 09:00 Go to previous messageGo to next message
Vijay Raj
Messages: 608
Registered: July 2009
Senior Member
This is exactly where you will use GC method to draw the static content, the trick being you will create the GC using an empty image, you will draw whatever you want on this and on paint you will first draw this image and then your normal paint operations.

The above case only work when the static content is on non painted region.so you have to define a clipping region in which only
your dynamic content will be painted.

Another way is to draw the whole thing on an empty image and on paint event draw this image.

import static java.lang.Math.sin;
import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.eclipse.swt.layout.GridData.FILL;
import static org.eclipse.swt.layout.GridData.FILL_HORIZONTAL;

import java.util.concurrent.ScheduledExecutorService;

import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Scale;
import org.eclipse.swt.widgets.Shell;

// type imports elided

public class UpdateCanvasSnippet extends Composite
{
    static final Display display = new Display();

    static final ScheduledExecutorService sched = newSingleThreadScheduledExecutor();

    double phi, phase;

    boolean rectDrawn;

    Image drawable = null;

    private Canvas canvas;

    public UpdateCanvasSnippet(Composite parent, int style)
    {
        super(parent, style);
        setLayout(new GridLayout(1, false));
        final Scale scale = new Scale(this, SWT.NONE);
        final GridData gd_scale = new GridData(FILL_HORIZONTAL);
        scale.setLayoutData(gd_scale);
        scale.setMinimum(100);
        scale.setMaximum(600);
        canvas = new Canvas(this, SWT.NO_BACKGROUND | SWT.DOUBLE_BUFFERED);
        // canvas.setBackground(display.getSystemColor(SWT.COLOR_WHITE));
        gridData().align(FILL, FILL).grab(true, true).applyTo(canvas);
        sched.scheduleAtFixedRate(new Runnable()
        {
            public void run()
            {
                display.asyncExec(new Runnable()
                {
                    public void run()
                    {
                        phi = System.currentTimeMillis();
                        paintImage(canvas);
                        if (!canvas.isDisposed())
                            canvas.redraw();
                    }

                });
            }
        }, 0, 10, MILLISECONDS);
        scale.addSelectionListener(new SelectionAdapter()
        {
            @Override
            public void widgetSelected(SelectionEvent e)
            {
                phase = scale.getSelection();
            }
        });
        canvas.addPaintListener(new PaintListener()
        {
            public void paintControl(PaintEvent e)
            {
                draw(e.gc);
            }
        });
    }

    private void paintImage(final Canvas canvas)
    {
        if (rectDrawn)
            return;
        if (drawable != null)
        {
            drawable.dispose();
        }
        if (!canvas.isDisposed())
        {
            drawable = new Image(display, canvas.getBounds().width, canvas.getBounds().height);
            final GC gc = new GC(drawable);
            drawRect(gc);
            gc.dispose();
        }
        rectDrawn = true;
    }

    void drawRect(GC gc)
    {
        gc.setForeground(display.getSystemColor(SWT.COLOR_BLUE));
        final Rectangle area = gc.getClipping();
        gc.drawRectangle(0, area.height - 51, 50, 50);
    }

    void draw(GC gc)
    {
        if (drawable == null)
        {
            paintImage(canvas);
        }
        gc.drawImage(drawable, 0, 0);
        gc.setForeground(display.getSystemColor(SWT.COLOR_RED));
        gc.setBackground(display.getSystemColor(SWT.COLOR_WHITE));
        final Rectangle area = gc.getClipping();
        for (int x = 0; x < area.width; x++)
            gc.drawLine(x, 0, x, (int) (area.height / 2 - 30 * sin((phi / 20 - phase - x) / 10d)));
    }

    public static void main(String[] args) throws InterruptedException
    {
        final Shell shell = new Shell(display, SWT.SHELL_TRIM | SWT.DOUBLE_BUFFERED);
        shell.setLayout(new GridLayout(3, false));
        for (int i = 0; i < 9; i++)
        {
            final Composite c = new UpdateCanvasSnippet(shell, SWT.None);
            gridData().grab(true, true).applyTo(c);
        }
        shell.setSize(1000, 800);
        shell.open();
        while (!shell.isDisposed())
            if (!display.readAndDispatch())
                display.sleep();
        sched.shutdown();
        sched.awaitTermination(1, SECONDS);
        display.dispose();
    }

    static GridDataFactory gridData()
    {
        return GridDataFactory.fillDefaults();
    }

    @Override
    protected void checkSubclass()
    {
    }
}


Note:you have take lot of care about image and GC dispose, also resize events should accordingly compensated.


---------------------
why, mr. Anderson, why, why do you persist?
Because I Choose To.
Regards,
Vijay
Re: GC operations not propagated to viewport on OS X [message #1059377 is a reply to message #1059242] Fri, 17 May 2013 14:52 Go to previous messageGo to next message
Marko Topolnik is currently offline Marko Topolnik
Messages: 42
Registered: July 2009
Member
I see your point there. So generally I can't rely on Canvas to maintain a persistent image and only change when explicitly painted to? To overcome this you basically cache the image with the rectangle so you can paint it again every time.

I also have a few digital readouts on the canvas, which refresh at a lower rate, say 5 times per second, to promote easier reading of the numbers. With my private GC it was no problem to just leave the numbers untouched until it is time to update them; with this PaintEvent's GC approach the numbers must be printed every time, so there's a performance penalty. There is little I can do about that, correct?

[Updated on: Fri, 17 May 2013 16:37]

Report message to a moderator

Re: GC operations not propagated to viewport on OS X [message #1059385 is a reply to message #1059377] Fri, 17 May 2013 15:51 Go to previous messageGo to next message
Vijay Raj
Messages: 608
Registered: July 2009
Senior Member
Everything can be as it was,you can have your own GC ,just create the Gc using an image instead of a canvas and in paint event draw that image.

similar to previous snippet,rectangle gc.



---------------------
why, mr. Anderson, why, why do you persist?
Because I Choose To.
Regards,
Vijay
Re: GC operations not propagated to viewport on OS X [message #1059388 is a reply to message #1059385] Fri, 17 May 2013 17:04 Go to previous messageGo to next message
Marko Topolnik is currently offline Marko Topolnik
Messages: 42
Registered: July 2009
Member
Alright, that's what I needed to hear: so there's nothing wrong with keeping a long-lived GC, just don't do it for the Canvas itself, ignoring the paint event's GC. Understood.
Re: GC operations not propagated to viewport on OS X [message #1059448 is a reply to message #1059388] Sat, 18 May 2013 13:04 Go to previous messageGo to next message
Marko Topolnik is currently offline Marko Topolnik
Messages: 42
Registered: July 2009
Member
After reworkng my code, now I've got a problem on Windows: I redraw and update the parent composite which contains all histograms, but nothing happens. The documentation clearly states that the whole bounds of the control will be redrawn.

It only works if I redraw each individual histogram. I am trying to measure the refresh time in order to adapt the refresh rate to it, but if each histogram redraws on its own, I can't easily measure the time to redraw the whole window.

It was easy to reproduce this in our snippet. On Cocoa this works as expected.

import static java.lang.Math.sin;
import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.eclipse.swt.layout.GridData.FILL;

public class UpdateCanvasSnippet extends Composite
{
   static final Display display = new Display();
   static final ScheduledExecutorService sched = newSingleThreadScheduledExecutor();
   static double phi, phase;
   boolean rectDrawn;
   Image drawable;
   private Canvas canvas;

   public UpdateCanvasSnippet(Composite parent, int style)
   {
      super(parent, style);
      setLayout(new GridLayout(1, false));
      canvas = new Canvas(this, SWT.NO_BACKGROUND | SWT.DOUBLE_BUFFERED);
      gridData().align(FILL, FILL).grab(true, true).applyTo(canvas);
      canvas.addPaintListener(new PaintListener()
      {
         public void paintControl(PaintEvent e)
         {
            draw(e.gc);
         }
      });
      canvas.addListener(SWT.Resize, new Listener()
      {
         public void handleEvent(Event event)
         {
            paintImage();
         }
      });
   }

   void paintImage()
   {
      if (drawable != null)
         drawable.dispose();
      drawable = new Image(display, canvas.getBounds().width, canvas.getBounds().height);
      final GC gc = new GC(drawable);
      gc.setForeground(display.getSystemColor(SWT.COLOR_BLUE));
      final Rectangle area = gc.getClipping();
      gc.drawRectangle(0, area.height - 51, 50, 50);
      gc.dispose();
   }

   void draw(GC gc)
   {
      if (drawable == null)
         paintImage();
      gc.drawImage(drawable, 0, 0);
      gc.setForeground(display.getSystemColor(SWT.COLOR_RED));
      gc.setBackground(display.getSystemColor(SWT.COLOR_WHITE));
      final Rectangle area = gc.getClipping();
      for (int x = 0; x < area.width; x++)
         gc.drawLine(x, 0, x, (int) (area.height / 2 - 30 * sin((phi / 20 - phase - x) / 10d)));
   }

   public static void main(String[] args) throws InterruptedException
   {
      final Shell shell = new Shell(display, SWT.SHELL_TRIM | SWT.DOUBLE_BUFFERED);
      shell.setLayout(new GridLayout(3, true));
      final Scale scale = new Scale(shell, SWT.NONE);
      gridData().align(GridData.FILL, GridData.FILL).applyTo(scale);
      scale.setMinimum(100);
      scale.setMaximum(200);
      scale.addSelectionListener(new SelectionAdapter()
      {
         @Override
         public void widgetSelected(SelectionEvent e)
         {
            phase = scale.getSelection();
         }
      });
      for (int i = 0; i < 8; i++)
      {
         final Composite c = new UpdateCanvasSnippet(shell, SWT.None);
         gridData().grab(true, true).applyTo(c);
      }
      sched.scheduleAtFixedRate(new Runnable()
      {
         public void run()
         {
            display.asyncExec(new Runnable()
            {
               public void run()
               {
                  if (shell.isDisposed())
                     return;
                  phi = System.currentTimeMillis();
                  shell.redraw();
                  shell.update();
               }
            });
         }
      }, 0, 10, MILLISECONDS);
      shell.setSize(1000, 800);
      shell.open();
      while (!shell.isDisposed())
         if (!display.readAndDispatch())
            display.sleep();
      sched.shutdown();
      sched.awaitTermination(1, SECONDS);
      display.dispose();
   }

   static GridDataFactory gridData()
   {
      return GridDataFactory.fillDefaults();
   }

   @Override
   protected void checkSubclass()
   {
   }
}
Re: GC operations not propagated to viewport on OS X [message #1059641 is a reply to message #1059448] Tue, 21 May 2013 01:17 Go to previous messageGo to next message
Vijay Raj
Messages: 608
Registered: July 2009
Senior Member
I dont think refresh call will propagate to children,i may be wrong.

mean while you can do this.
import static java.lang.Math.sin;
import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.eclipse.swt.layout.GridData.FILL;

import java.util.concurrent.ScheduledExecutorService;

import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Scale;
import org.eclipse.swt.widgets.Shell;

public class UpdateCanvasSnippet extends Composite
{
    static final Display display = new Display();

    static final ScheduledExecutorService sched = newSingleThreadScheduledExecutor();

    static double phi, phase;

    boolean rectDrawn;

    Image drawable;

    private Canvas canvas;

    public UpdateCanvasSnippet(Composite parent, int style)
    {
        super(parent, style);
        parent.addPaintListener(new PaintListener()
        {

            @Override
            public void paintControl(PaintEvent e)
            {
                canvas.redraw();
            }
        });
        setLayout(new GridLayout(1, false));
        canvas = new Canvas(this, SWT.NO_BACKGROUND | SWT.DOUBLE_BUFFERED);
        gridData().align(FILL, FILL).grab(true, true).applyTo(canvas);
        canvas.addPaintListener(new PaintListener()
        {
            public void paintControl(PaintEvent e)
            {
                draw(e.gc);
            }
        });
        canvas.addListener(SWT.Resize, new Listener()
        {
            public void handleEvent(Event event)
            {
                paintImage();
            }
        });
    }

    void paintImage()
    {
        if (drawable != null)
            drawable.dispose();
        drawable = new Image(display, canvas.getBounds().width, canvas.getBounds().height);
        final GC gc = new GC(drawable);
        gc.setForeground(display.getSystemColor(SWT.COLOR_BLUE));
        final Rectangle area = gc.getClipping();
        gc.drawRectangle(0, area.height - 51, 50, 50);
        gc.dispose();
    }

    void draw(GC gc)
    {
        if (drawable == null)
            paintImage();
        gc.drawImage(drawable, 0, 0);
        gc.setForeground(display.getSystemColor(SWT.COLOR_RED));
        gc.setBackground(display.getSystemColor(SWT.COLOR_WHITE));
        final Rectangle area = gc.getClipping();
        for (int x = 0; x < area.width; x++)
            gc.drawLine(x, 0, x, (int) (area.height / 2 - 30 * sin((phi / 20 - phase - x) / 10d)));
    }

    public static void main(String[] args) throws InterruptedException
    {
        final Shell shell = new Shell(display, SWT.SHELL_TRIM);
        shell.setLayout(new GridLayout(3, true));
        final Scale scale = new Scale(shell, SWT.NONE);
        gridData().align(GridData.FILL, GridData.FILL).applyTo(scale);
        scale.setMinimum(100);
        scale.setMaximum(200);
        scale.addSelectionListener(new SelectionAdapter()
        {
            @Override
            public void widgetSelected(SelectionEvent e)
            {
                phase = scale.getSelection();
            }
        });
        for (int i = 0; i < 8; i++)
        {
            final Composite c = new UpdateCanvasSnippet(shell, SWT.None);
            gridData().grab(true, true).applyTo(c);
        }
        sched.scheduleAtFixedRate(new Runnable()
        {
            public void run()
            {
                display.asyncExec(new Runnable()
                {
                    public void run()
                    {
                        if (shell.isDisposed())
                            return;
                        phi = System.currentTimeMillis();
                        shell.redraw();
                    }
                });
            }
        }, 0, 10, MILLISECONDS);
        shell.setSize(1000, 800);
        shell.open();
        while (!shell.isDisposed())
            if (!display.readAndDispatch())
                display.sleep();
        sched.shutdown();
        sched.awaitTermination(1, SECONDS);
        display.dispose();
    }

    static GridDataFactory gridData()
    {
        return GridDataFactory.fillDefaults();
    }

    @Override
    protected void checkSubclass()
    {
    }
}


Also explore Draw2D and GEF,that may solve lots of your problems, instead of extensive custom drawing.


---------------------
why, mr. Anderson, why, why do you persist?
Because I Choose To.
Regards,
Vijay

[Updated on: Tue, 21 May 2013 01:22]

Report message to a moderator

Re: GC operations not propagated to viewport on OS X [message #1060064 is a reply to message #1059641] Wed, 22 May 2013 15:37 Go to previous message
Marko Topolnik is currently offline Marko Topolnik
Messages: 42
Registered: July 2009
Member
I see: catch a paint request and issue a redraw on children. In the meantime I found out that the other overload, namely redraw(int, int, int, int, boolean), does cascade to the children, but is awkward to use because the canvas bounds must be calculated just to pass the "obvious" values which the simple redraw() assumes.

I will take your suggestion to study higher-level APIs seriousy; I do think I'm working at a too low level right now.

Thank you for all the help.
Previous Topic:SWT.XXX widget cross-reference
Next Topic:Merging cells in a Word table through OleAutomation
Goto Forum:
  


Current Time: Fri Aug 22 21:52:07 EDT 2014

Powered by FUDForum. Page generated in 0.04848 seconds