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.
Related
I've tried to make a simple Swing example which uses SwingWorker to update the text of a Label. Whenever the SwingWorker finishes and tries to update the text, the UI will freeze. After closing the window it'll show a AWT-EventQueue-0 "Widget is disposed" exception.
I've tried to make sure it's running on the EDT etc, but I think I'm missing something very basic here. Could someone please look at my code and tell me what might be happening?
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.SWT;
public class MySwingWindow {
private Label mainText;
protected Shell shell;
/**
* Launch the application.
* #param args
*/
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
MySwingWindow window = new MySwingWindow();
window.open();
}
});
}
/**
* Open the window.
*/
public void open() {
Display display = Display.getDefault();
createContents();
shell.open();
shell.layout();
try {
SwingWorker<Boolean, Void> worker = new SwingWorker<Boolean, Void>() {
#Override
protected Boolean doInBackground() throws Exception {
for (int i = 0; i <= 2; i++) {
Thread.sleep(1000);
System.out.println("Running " + i);
}
return true;
}
#Override
protected void done() {
mainText.setText("Done");
}
};
worker.execute();
} catch (Exception e) {
e.printStackTrace();
}
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
}
/**
* Create contents of the window.
*/
protected void createContents() {
shell = new Shell();
shell.setSize(450, 300);
shell.setText("SWT Application");
mainText = new Label(shell, SWT.NONE);
mainText.setBounds(78, 84, 55, 15);
mainText.setText("Working");
}
}
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.
Can someone explain me why Window in this program freezes while I try to run 2 or more threads ?
I want to be able to click on Hello button while both of threads are running.
Here is my simple project.
Task class only prints out word "Testing".
public class Task extends Thread {
public static boolean keepRun = true;
public void run(){
while(keepRun == true){
System.out.println("Testing...");
try {
Thread.sleep(1000);
}catch(InterruptedException e){}};
}
}
Closing class stops thread when it is typed stop inside console.
import java.util.Scanner;
public class Closing implements Runnable{
Scanner s = new Scanner(System.in);
public void run() {
while (s.next().equals("stop")){
System.out.println("Threads down");
Task.keepRun =false;
try {
Thread.sleep(5000);
}catch(InterruptedException e){}
};
}
}
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
Here is Window class where main is also located.
public class Window {
protected Shell shell;
/**
* Launch the application.
* #param args
*/
public static void main(String[] args) {
try {
Window window = new Window();
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(192, 208);
shell.setText("SWT Application");
Button btnRun = new Button(shell, SWT.NONE);
btnRun.addSelectionListener(new SelectionAdapter() {
#Override
public void widgetSelected(SelectionEvent e) {
Task newTask = new Task();
Closing closing = new Closing();
newTask.start();
closing.run();
}
});
btnRun.setBounds(50, 32, 75, 25);
btnRun.setText("Run");
Button btnHello = new Button(shell, SWT.NONE);
btnHello.addSelectionListener(new SelectionAdapter() {
#Override
public void widgetSelected(SelectionEvent e) {
System.out.println("Hello");
}
});
btnHello.setBounds(50, 81, 75, 25);
btnHello.setText("Hello");
}
}
Rewrite this:
Closing closing = new Closing();
newTask.start();
closing.run();
to this:
Closing closing = new Closing();
newTask.start();
new Thread(closing).start();
Look at this code:
public class Closing implements Runnable{
Scanner s = new Scanner(System.in);
public void run() {
while (s.next().equals("stop")){
System.out.println("Threads down");
Task.keepRun =false;
try {
Thread.sleep(5000);
}catch(InterruptedException e){}
};
}
}
If you call simply run();, Thread.sleep(5000); will be affected to thread which called run, in the other hand, when you create a new Thread, sleep will be affected to this one.
Question:
Can you show any simple example or explanation of SWT display part(which updates the window/shell)? Or can you any sites that you think they are the best for SWT application development?
Background:
I am new to SWT application and currently building an application for running some tests.
It has a main display shell class with text area which keeps getting updated after user clicked on a run button.
The run button starts another thread process which updates public static object such as AtomicCounter in the StartView class.
Current Stage
The program seems running well, however, it does not update the text area in realtime.
Well, I can't say realtime but it shows a little bit delayed information.(I can say it's delayed because I print out on the console as well)
It seems like I don't understand displaying concept of SWT well enough to do whatever I am trying to do with it.
Goal
A. Main Display class which starts and stops C regardless of B running or not
B. Threaded process which updates text area of A class with public static object of A
C. Threaded process which does its job and updating public static object of A
Example Code (Working Code)
public class UnitTest {
public static Display display;
private Shell shell;
public static AtomicInteger counter = new AtomicInteger(0);
public static Text text;
private TestThread test1 = null, test2 = null;
public UnitTest()
{
display = Display.getDefault();
this.shell = new Shell(display, SWT.CLOSE);
this.shell.setSize(226, 120);
text = new Text(shell, SWT.BORDER);
text.setBounds(10, 10, 199, 19);
Button btnStart = new Button(shell, SWT.NONE);
btnStart.addSelectionListener(new SelectionAdapter() {
#Override
public void widgetSelected(SelectionEvent arg0) {
test1 = new TestThread();
test1.start();
test2 = new TestThread();
test2.start();
}
});
btnStart.setBounds(10, 54, 94, 28);
btnStart.setText("Start");
Button btnStop = new Button(shell, SWT.NONE);
btnStop.addSelectionListener(new SelectionAdapter() {
#Override
public void widgetSelected(SelectionEvent arg0) {
test1.interrupt();
test2.interrupt();
counter.set(0);
}
});
btnStop.setBounds(115, 54, 94, 28);
btnStop.setText("Stop");
this.shell.open();
this.shell.layout();
this.shell.addListener(SWT.Close, new Listener(){
public void handleEvent(Event event)
{
shell.dispose();
}
});
while(!this.shell.isDisposed())
{
if(!display.readAndDispatch())
{
//text.setText(""+counter.get());
display.sleep();
}
}
}
public static void main(String[] args)
{
new UnitTest();
}
}
class TestThread extends Thread
{
public void run()
{
try
{
int i = 0;
while(i++ < 1000 && !this.isInterrupted() )
{
UnitTest.counter.getAndIncrement();
try {
TestThread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
break;
}
if(UnitTest.display.isDisposed())
return;
UnitTest.display.asyncExec(new Runnable() {
public void run() {
if (UnitTest.text.isDisposed())
return;
UnitTest.text.setText(""+UnitTest.counter.get());
}
});
}
}
catch(Exception e)
{
e.printStackTrace();
}
finally
{
System.out.println("Existing thread...");
}
}
}
You should carefully use UI updates from separate threads. Please, read this:
http://goo.gl/At8hC
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");