refresh gui in swt button listener - java

I have following class.
Why the btnDecorate is allways enabled? I wanted to disable the button when the loop is under processing.
Why text.redraw() works only in the end of loop? I wanted to see the box sequently on every character.
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.*;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.layout.*;
import org.eclipse.swt.widgets.*;
public class SampleRefreshStyledText {
public static void main(String[] args) {
final Display display = new Display();
Shell shell = new Shell(display);
shell.setLayout(new FillLayout(SWT.VERTICAL));
final Button btnDecorate = new Button(shell, SWT.NONE);
btnDecorate.setText("Decorate");
final StyledText text = new StyledText(shell, SWT.NONE);
text.setText("ABCDEFGHIJKLMNOPRQ\n1234567890");
btnDecorate.addSelectionListener(new SelectionListener() {
#Override
public void widgetSelected(SelectionEvent event) {
btnDecorate.setEnabled(false);
for (int i = 0; i < text.getText().length(); i++) {
StyleRange styleRange = new StyleRange();
styleRange.start = i;
styleRange.length = 1;
styleRange.borderColor = display.getSystemColor(SWT.COLOR_RED);
styleRange.borderStyle = SWT.BORDER_SOLID;
styleRange.background = display.getSystemColor(SWT.COLOR_GRAY);
text.setStyleRange(null);
text.setStyleRange(styleRange);
text.redraw();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
btnDecorate.setEnabled(true);
}
#Override
public void widgetDefaultSelected(SelectionEvent arg0) {}
});
shell.pack();
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) display.sleep();
}
display.dispose();
}
}

You can't write loops like this with SWT.
All UI operations occur on the single UI thread. Calling Thread.sleep puts the UI thread to sleep and nothing at all will happen.
The redraw call only requests that the text is redrawn, it will not actually happen until the next time the display.readAndDispatch() is run, so doing this repeatedly in a loop doesn't work.
What you have to do is run the first step of your loop once. You must then arrange to run the next step 500ms later without blocking the thread. You can do this using the Display.timerExec method to request that code is run at a later time:
display.timerExec(500, runnable);
where runnable is a class implementing Runnable that does the next step. At the end of this code you call timerExec again until you have worked your way through all the steps.

Related

How to know if a Thread has finished its task in SWT environment?

I'm in trouble in getting result from some threads.
I explain the environment, I have a SWT shell with a button. The listener for this button calls a Runnable that inside its run() calls a method that instantiate N threads for performing some operations. The problem is: how can I display a message dialog or something on the screen, when all the computation is terminated? The code I have is something similar
public void widgetSelected(SelectionEvent event) {
Runnable t = new MyThread(params);
executor.execute(t);
}
And inside MyThread class I have
public void run(){
myMethod();
}
public void myMethod(){
for(int i =0; i<queue.length; i++){
Runnable thread = new AnotherThread();
executor.execute(thread);
}
executor.shutdown();
if(executor.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS){
//here I know all thread of kind AnotherThread have finished
}
}
So inside the widgetSelected method of the button listener, I want to put something that alerts the user that all threads called by the listener have successfully terminated.
Is there a way to know that? If I put the awaitTermination statement inside the Listener, the shell becomes not responsive and freezes on the screen.
Hope someone could help me. If something was not clear, please tell me.
Thanks all.
This is "straight forward" - but a bit of work: you "simply" have to enhance your Runnables to somehow signal their progress.
In other words: the ExecutorService interface doesn't offer any means to figure how many of scheduled "tasks" completed. So - if you need that information, you have to "bake" it into your Runnable.
One idea: create a Map<Runnable, Integer> upfront. The keys are your Runnable objects, and the value could represent some progress information. You start with all values at 0. And then you pass that map to each Runnable - and the Runnable simply updates its value at certain, defined points in time. Maybe ten steps, or maybe just 4. And as soon as all map values are at say 100, you know that you are done! That implies that your main thread simply loops and checks the map content every other second/minute/... and of course: this extra thread should not be the event dispatcher thread. You don't want to stall your UI while doing this.
( of course: you should use a ConcurrentHashMap to implement this ).
Long story short: this information is available already - the code in your Runnable knows what it is doing, right?! So you "only" have to make this information accessible to the outer world somehow. There are many options to do that; the above is just one way to get there.
I would recommend taking a look at using FutureCallbacks which are part of the Guava library.
What this will allow you to do is create a ListenableFuture for each task that you fire off. In your case, it sounds like this would be represented by a Runnable, but you can just as easily use a Callable. When these tasks are all fired off, you will end up with a list of these ListenableFuture objects, which can be "flattened" into a single ListenableFuture which represents the completion of ALL of the tasks. This is accomplished with the method Futures.allAsList(...):
final List<ListenableFuture<T>> futures = ...
final ListenableFuture<List<T>> combinedFuture = Future.allAsList(futures);
Now that you have a single ListenableFuture which represents the completion of all of your tasks, you can easily listen for its completion by adding a FutureCallback to be invoked upon completion:
Futures.addCallback(future, new FutureCallback<List<String>>() {
#Override
public void onFailure(final Throwable arg0) {
// ...
}
#Override
public void onSuccess(final List<String> arg0) {
// ...
}
}
Now, once these tasks are completed, we need to update the UI to notify users. To do so, we must be sure that the UI updates happen back on the SWT UI thread:
Display.getCurrent().asyncExec(new Runnable() {
#Override
public void run() {
// Update the UI
}
});
Note that this can easily be done within the onSuccess() method above so that the result of the tasks can be used.
Putting it all together, we can easily loop through a handful of ListeningExecutorService.submit(...) calls for background execution (so as not to block the UI thread - In my example below, you can freely type in the text box while the tasks are running in the background), grab all the ListenableFutures, and add a callback to be invoked upon completion, which will hop back to the UI thread to make the UI updates.
Full example:
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.Executors;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
public class CallbackExample {
private final Display display;
private final Shell shell;
private final Text output;
private final ListeningExecutorService executor;
public CallbackExample() {
display = new Display();
shell = new Shell(display);
shell.setLayout(new FillLayout());
executor = MoreExecutors.listeningDecorator(Executors
.newFixedThreadPool(20));
final Composite baseComposite = new Composite(shell, SWT.NONE);
baseComposite
.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
baseComposite.setLayout(new GridLayout());
output = new Text(baseComposite, SWT.MULTI | SWT.BORDER | SWT.V_SCROLL);
output.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
final Button button = new Button(baseComposite, SWT.PUSH);
button.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
button.setText("Start tasks");
button.addSelectionListener(new SelectionAdapter() {
#SuppressWarnings("synthetic-access")
#Override
public void widgetSelected(final SelectionEvent e) {
// Start tasks when the button is clicked
startTasks();
}
});
}
private void startTasks() {
// Create a List to hold the ListenableFutures for the tasks
final List<ListenableFuture<String>> futures = new ArrayList<ListenableFuture<String>>();
// Submit all the tasks for execution (in this case 100)
for (int i = 0; i < 100; i++) {
final ListenableFuture<String> future = executor
.submit(new Callable<String>() {
#Override
public String call() throws Exception {
// Do the work! Here we sleep to simulate a long task
Thread.sleep(2000);
final long currentMillis = System
.currentTimeMillis();
System.out.println("Task complete at "
+ currentMillis);
return "Task complete at " + currentMillis;
}
});
// Add the future for this task to the list
futures.add(future);
}
// Combine all of the futures into a single one that we can wait on
final ListenableFuture<List<String>> future = Futures
.allAsList(futures);
// Add the callback for execution upon completion of ALL tasks
Futures.addCallback(future, new FutureCallback<List<String>>() {
#Override
public void onFailure(final Throwable arg0) {
System.out.println("> FAILURE");
}
#SuppressWarnings("synthetic-access")
#Override
public void onSuccess(final List<String> arg0) {
System.out.println("> SUCCESS");
// Update the UI on the SWT UI thread
display.asyncExec(new Runnable() {
#Override
public void run() {
final StringBuilder sb = new StringBuilder();
for (final String s : arg0) {
sb.append(s + "\n");
}
final String resultString = sb.toString();
output.setText(resultString);
}
});
}
});
}
public void run() {
shell.setSize(200, 200);
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
executor.shutdownNow();
display.dispose();
}
public static void main(final String... args) {
new CallbackExample().run();
}
}

SWT: Layout broken when refilling a shell with components

I want to periodically clear everything from a shell and recreate the components in it. This is a minimal Test case of what I am trying to do. At first, the labels are put side by side as the layout dictates, but when the updateThread triggers after five seconds, the newly created labels are all put over each other, completely ignoring the layout.
package testpkg;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.*;
public class Test {
public static void main(String[] args){
Display display = new Display();
Shell testShell = new Shell(display);
FillLayout testLayout = new FillLayout();
testShell.setLayout(testLayout);
for(int i = 1; i<20; i++){
Label label = new Label(testShell, SWT.BORDER);
label.setText("HELLO"+i);
label.pack();
}
testShell.open();
Thread updateThread = new Thread(new Runnable(){
public void run(){
try {Thread.sleep(5000);} catch (Exception e){};
display.getDefault().syncExec(new Runnable(){
public void run(){
for(Control control : testShell.getChildren()){
control.dispose();
}
for(int i = 1; i<20; i++){
Label label = new Label(testShell, SWT.BORDER);
label.setText("HELLO"+i);
label.pack();
}
}
});
}
});
updateThread.start();
while (!testShell.isDisposed()) {
if (!display.readAndDispatch())
display.sleep();
}
display.dispose();
}
}
I want to update the (variable amount of) labels periodically but the layout breaks completely when I do it this way.
you can put this line after the for loop:
testShell.layout(true);
so the final run() method will look like:
public void run() {
for (Control control : testShell.getChildren()) {
control.dispose();
}
for (int i = 1; i < 20; i++) {
Label label = new Label(testShell, SWT.BORDER);
label.setText("HELLO"+i);
label.pack();
}
testShell.layout(true);
}
I noticed that resizing the window fixed everything. So if I make the window larger and then smaller for a split second, it works.

SWT StackLayout topControl apparently not working

I am testing a simple SWT StackLayout example to learn how it works but things are not working as I expected.
I created a StackLayout with two buttons on them, both set to cycle the top control between the two of them five times when they are selected, with a 2-second pause every time the top control changes. However, when I run the problem I do not see anything happen.
Any ideas on what I am missing?
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StackLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
public class MyApp {
protected Shell shlMyFirstSwt;
Button btnOne;
Button btnTwo;
/**
* Launch the application.
* #param args
*/
public static void main(String[] args) {
try {
MyApp window = new MyApp();
window.open();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Open the window.
*/
public void open() {
Display display = Display.getDefault();
createContents();
shlMyFirstSwt.open();
shlMyFirstSwt.layout();
while (!shlMyFirstSwt.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
}
/**
* Create contents of the window.
* #throws InterruptedException
*/
protected void createContents() {
shlMyFirstSwt = new Shell();
shlMyFirstSwt.setSize(621, 416);
shlMyFirstSwt.setText("My First SWT Application");
StackLayout layout = new StackLayout();
shlMyFirstSwt.setLayout(layout);
Button btnOne = new Button(shlMyFirstSwt, SWT.NONE);
btnOne.addSelectionListener(new SelectionAdapter() {
#Override
public void widgetSelected(SelectionEvent e) {
for (int i = 0; i != 10; i++) {
layout.topControl = i % 2 == 0? btnOne : btnTwo;
shlMyFirstSwt.layout();
try {
Thread.sleep(2000);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}
});
btnOne.setText("One");
Button btnTwo = new Button(shlMyFirstSwt, SWT.NONE);
btnTwo.addSelectionListener(new SelectionAdapter() {
#Override
public void widgetSelected(SelectionEvent e) {
for (int i = 0; i != 10; i++) {
layout.topControl = i % 2 == 0? btnOne : btnTwo;
shlMyFirstSwt.layout();
try {
Thread.sleep(2000);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}
});
btnTwo.setText("Two");
}
}
Elaborating after first answer:
Trying a simpler approach without delaying. Now I modified the event handler to simply have one button switch the top control to be the other button, as shown below. I expected the two buttons to alternate as top control, but instead when I click on the first button, the window turns blank. Any idea why?
Button btnOne = new Button(shlMyFirstSwt, SWT.NONE);
btnOne.addSelectionListener(new SelectionAdapter() {
#Override
public void widgetSelected(SelectionEvent e) {
layout.topControl = btnTwo;
shlMyFirstSwt.layout();
}
});
btnOne.setText("One");
Button btnTwo = new Button(shlMyFirstSwt, SWT.NONE);
btnTwo.addSelectionListener(new SelectionAdapter() {
#Override
public void widgetSelected(SelectionEvent e) {
layout.topControl = btnOne;
shlMyFirstSwt.layout();
}
});
btnTwo.setText("Two");
Your Thread.sleep calls are blocking the user interface thread so the GUI does not get updated. You must never block the user interface thread like this. Calls to methods like layout do not update instantly - they require that display.readAndDispatch runs to dispatch the various updates that are generated.
If you want to delay something use Display.timerExec:
Display.getDefault().timerExec(2000, new Runnable() {
#Override
public void run()
{
... code to be run after the delay
}
});
So you will have to rework your code to use this to do the timed updated.
Figured it out: just the silly mistake of including btnTwo in the first event handler before it was initialized, even though it is used after initialization.

How can i open a shell without making it active?

I need to know if there is any way to open a shell and not make it active, even if i click a control in it. The best way to explain what i need is to show you this little example. I need to keep the first shell active, even if i click the second one, or any widget that it contains.
public class TestMeOut
{
public static void main(final String[] args)
{
final Display display = new Display();
final Shell shell = new Shell(display);
shell.setLayout(new GridLayout(1, false));
final Shell shell2 = new Shell(shell);
shell2.setLayout(new GridLayout());
final Button btn = new Button(shell, SWT.PUSH);
final Button btn2 = new Button(shell2, SWT.PUSH);
btn.setText("Test me");
btn2.setText("I steal focus");
btn.addSelectionListener(new SelectionAdapter()
{
#Override
public void widgetSelected(final SelectionEvent e)
{
shell2.setVisible(true);
}
});
btn2.addSelectionListener(new SelectionAdapter()
{
#Override
public void widgetSelected(final SelectionEvent e)
{
shell2.setVisible(false);
}
});
shell.pack();
shell.open();
shell.addShellListener(new ShellListener()
{
public void shellIconified(final ShellEvent e)
{
}
public void shellDeiconified(final ShellEvent e)
{
}
public void shellDeactivated(final ShellEvent e)
{
System.out.println("Deactivated! This isn't supposed to happen.");
}
public void shellClosed(final ShellEvent e)
{
}
public void shellActivated(final ShellEvent e)
{
System.out.println("Activated!");
}
});
shell2.pack();
shell2.open();
shell2.setVisible(false);
while (!shell.isDisposed())
{
if (!display.readAndDispatch())
display.sleep();
}
display.dispose();
}
}
Thank you in advance!
You can change the style of your second Shell and use SWT.APPLICATION_MODAL which will make sure that you cannot interact with the parent shell unless you close the child shell:
final Shell shell2 = new Shell(shell, SWT.SHELL_TRIM | SWT.APPLICATION_MODAL);
The modality of an instance may be specified using style bits. The modality style bits are used to determine whether input is blocked for other shells on the display. The PRIMARY_MODAL style allows an instance to block input to its parent. The APPLICATION_MODAL style allows an instance to block input to every other shell in the display. The SYSTEM_MODAL style allows an instance to block input to all shells, including shells belonging to different applications.
Even SWT.PRIMARY_MODAL would suffice in this case.
UPDATE
If on the other hand you don't want the parent to loose focus when the child is clicked, just disable the child:
shell2.setEnabled(false);

an indeterminate progressbar in new window in swt

I am designing interface of java application using window builder. What I need to do is..
Click on a button does two things 1. do some background task
2. while this is in progress, display an indeterminate progressbar n new window.
I know I need multithreading to accomplish this.
I tried taking the help of some tutorials but could not implement this.
Can anyone help?
code:
Function from where I want to open the progress bar window
public void mouseDown(MouseEvent e) {
pbar p=new pbar();
p.caller();
dowork();
p.closeprogress();
}
Progressbar class
import org.eclipse.swt.widgets.Display;
public class pbar {
protected Shell shell;
public void pcaller() {
try {
//System.err.println("Error: " + bod);
//System.err.println("Error: " + lines);
pbar window = new pbar();
window.open();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Open the window.
*/
public void open() {
Display display = Display.getDefault();
createContents();
shell.open();
shell.layout();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
}
/**
* Create contents of the window.
*/
protected void createContents() {
shell = new Shell();
shell.setSize(315, 131);
shell.setText("Updating!!! Please Wait");
ProgressBar progressBar = new ProgressBar(shell, SWT.INDETERMINATE);
progressBar.setBounds(47, 34, 195, 17);
// ProgressBar pb2 = new ProgressBar(shell, SWT.HORIZONTAL |
SWT.INDETERMINATE);
// pb2.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
}
public void close()
{
shell.close();
}
}
I want when I call p.caller(), progress bar will appear. Then, control should come to original program and execute dowork() method. when i am done with this method, it will call p.progress.close().
I Don't know why isn't understanding this simple program, forget about answering.
Is use of JFace acceptable?
If so you can use ProgressMonitorDialog class.
ProgressMonitorDialog dialog = new ProgressMonitorDialog(parent.getShell());
dialog.run(true, true, new SomeTask());
...
class SomeTask implements IRunnableWithProgress {
#Override
public void run(IProgressMonitor monitor) throws InvocationTargetException,
InterruptedException {
monitor.beginTask("Doing some task", IProgressMonitor.UNKNOWN);
for(int i = 0; i < 1000; i++) {
if (!monitor.isCanceled())
Thread.sleep(10);
}
monitor.done();
}
}
You can find complete example of usage here.
Try using.....
JProgressBar p = new JProgressBar();
p.setStringPainted();
Now where the value needs to be set.
p.setValue(val);
To display a message when done.
p.setString("done");

Categories