Reputation: 1335
I am currently trying to make the UI of our application lightweight, and as such am trying to move the heavyweight server calls to their own threads. I then update the widget using Display.getDefault.asyncExec()
, which works perfectly.
I want to display a animation to the user while the server call returns. To that point, I am using another thread, which shows the animation in another Display.getDefault.asyncExec()
. That also works... but in a weird way. When I run the app, the widget flickers very heavily, and sometime the screen stays unupdated. The thing is, when I run a prototype version,i.e without the main app, just a bare bones SWT app, it works perfectly. Any ideas why this happens?
Basic workflow ::
showAnimation = new Thread(new Runnable() {
@Override
public void run() {
while(true){
synchronized (this) {
try {
Thread.sleep(1000);
drawCircles(); // basically the animation, also called in a Display.getDefault().asyncExec()
}
catch (InterruptedException e)
{
// do nothing, because this thread is meant to be interrupted!
// and then, break out of the infinite loop
System.out.println("Interrupted");
break;
}
}
}
}
});
invokeLater = new Thread(new Runnable(){
@Override
public void run(){
// do the data base call here
System.out.println("Got data!");
if(showAnimation.isAlive())
{
showAnimation.interrupt();
}
}
});
and in my view -->
showAnimation.start();
invokeLater.start();
I briefly wondered if I should increase the priority of the animation thread, just to see if that works.It didn't (also doesn't make sense to increase the priority of the thread which is not bottle-necking the application).
Any ideas?
P.S: The drawCircles()
code -->
public void drawCircles(){
Display.getDefault().asyncExec(new Runnable() {
@Override
public void run(){
int radius = 100;
int maxX = radius ;
int maxY = radius ;
int direction=0;
int centerX = table.getBounds().width/2;
int centerY = table.getBounds().height/2;
System.out.println("Drawing");
Image image = new Image(Display.getDefault(),table.getBounds().width,table.getBounds().height);
GC gc = new GC(image);
gc.setAntialias(SWT.ON);
gc.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_BLACK));
//gc.drawLine(0, centerY, shell.getBounds().width, centerY);
//gc.drawLine(centerX, 0, centerX, shell.getBounds().height);
// 1st circle
gc.drawOval(centerX-radius/2, centerY-radius - radius/2, maxX, maxY);
// 2nd circle
gc.drawOval(centerX - 2*radius + radius/2, centerY - radius/2, maxX, maxY);
// 3rd circle
gc.drawOval(centerX+radius/2, centerY-radius/2, maxX, maxY);
// 4th circle
gc.drawOval(centerX-radius/2, centerY + radius/2, maxX, maxY);
direction++;
direction %= 4;
switch(direction){
case 0:
gc.setBackground(Display.getDefault().getSystemColor(SWT.COLOR_BLACK));
gc.fillOval(centerX - 2*radius + radius/2, centerY - radius/2, maxX, maxY);
break;
case 1:
gc.setBackground(Display.getDefault().getSystemColor(SWT.COLOR_BLACK));
gc.fillOval(centerX-radius/2, centerY-radius - radius/2, maxX, maxY);
break;
case 2:
gc.setBackground(Display.getDefault().getSystemColor(SWT.COLOR_BLACK));
gc.fillOval(centerX+radius/2, centerY-radius/2, maxX, maxY);
break;
case 3 :
gc.setBackground(Display.getDefault().getSystemColor(SWT.COLOR_BLACK));
gc.fillOval(centerX-radius/2, centerY + radius/2, maxX, maxY);
break;
}
table.setBackgroundImage(image);
table.redraw();
table.update();
image.dispose();
gc.dispose();
}
});
}
Upvotes: 0
Views: 147
Reputation: 1335
To solve the issue, I ended up using double buffering, by drawing the image in a GC and then updating the paintEvent gc later. This greatly reduced the flickering , even with anti-aliasing turned on.
Basically, create a PaintListener::
final PaintListener paint = new PaintListener() {
@Override
public void paintControl(PaintEvent e) {
int radius = 100;
int maxX = radius ;
int maxY = radius ;
int centerX = e.width/2;
int centerY = e.height/2;
Image image = new Image(Display.getDefault(),e.width,e.height);
GC gc = new GC(image);
gc.setAntialias(SWT.ON);
gc.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_BLACK));
//gc.drawLine(0, centerY, shell.getBounds().width, centerY);
//gc.drawLine(centerX, 0, centerX, shell.getBounds().height);
// 1st circle
gc.drawOval(centerX-radius/2, centerY-radius - radius/2, maxX, maxY);
// 2nd circle
gc.drawOval(centerX - 2*radius + radius/2, centerY - radius/2, maxX, maxY);
// 3rd circle
gc.drawOval(centerX+radius/2, centerY-radius/2, maxX, maxY);
// 4th circle
gc.drawOval(centerX-radius/2, centerY + radius/2, maxX, maxY);
direction++;
direction %= 4;
switch(direction){
case 0:
gc.setAlpha(255);
gc.setBackground(Display.getDefault().getSystemColor(SWT.COLOR_BLACK));
gc.fillOval(centerX - 2*radius + radius/2, centerY - radius/2, maxX, maxY);
break;
case 1:
gc.setAlpha(170);
gc.setBackground(Display.getDefault().getSystemColor(SWT.COLOR_BLACK));
gc.fillOval(centerX-radius/2, centerY-radius - radius/2, maxX, maxY);
break;
case 2:
gc.setAlpha(80);
gc.setBackground(Display.getDefault().getSystemColor(SWT.COLOR_BLACK));
gc.fillOval(centerX+radius/2, centerY-radius/2, maxX, maxY);
break;
case 3 :
gc.setAlpha(20);
gc.setBackground(Display.getDefault().getSystemColor(SWT.COLOR_BLACK));
gc.fillOval(centerX-radius/2, centerY + radius/2, maxX, maxY);
break;
}
e.gc.drawImage(image, 0, 0);
image.dispose();
gc.dispose();
}
};
The animation thread can then be written as ::
showAnimation = new Thread(new Runnable() {
@Override
public void run() {
while(true){
synchronized (this) {
try {
Thread.sleep(100);
Display.getDefault().asyncExec(new Runnable() {
@Override
public void run() {
table.addPaintListener(paint);
table.redraw();
table.update();
table.removePaintListener(paint);
}
});
}
catch (InterruptedException e)
{
// do nothing, because this thread is meant to be interrupted!
// and then, break out of the infinite loop
break;
}
}
}
}
});
I added and removed the listeners because notify was giving some issues (drawing more times than required!). Then, the database call can be made in another thread like this::
invokeLater = new Thread(new Runnable() {
@Override
public void run() {
//Fetching records from Service
try
{
// database call here
if(showAnimation.isAlive())
{
showAnimation.interrupt();
}
}
}
});
This ended up working for me If anyone can improve upon this, please tell me :)
Upvotes: 1