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.
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 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.
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");
In the below code the JProgressBar displays correctly when the doSomething() is called from within main() but not when called as a result of an ActionEvent - the interface seems to freeze. What is the problem?
import java.awt.BorderLayout;
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import java.util.Vector;
public class ThreadedDialog extends JFrame implements ActionListener{
private JDialog dlg;
private JButton button;
private void buildInterface(){
button = new JButton("do stuff;");
button.addActionListener(this);
this.getContentPane().setLayout(new BorderLayout());
this.getContentPane().add(BorderLayout.CENTER, button);
dlg = new JDialog(this, "Progress Dialog", true);
JProgressBar dpb = new JProgressBar(0, 500);
dlg.getContentPane().setLayout(new BorderLayout());
dlg.getContentPane().add(BorderLayout.CENTER, dpb);
dlg.getContentPane().add(BorderLayout.NORTH, new JLabel("Progress..."));
dlg.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
dlg.setSize(300, 75);
dlg.setLocationRelativeTo(this);
dpb.setIndeterminate(true);
}
public void doSomething(){
Thread t = new Thread(new Runnable(){
public void run() {
dlg.show();
}
});
t.start();
try {
for (int i=0; i<100; i++){
System.out.println("wtf is going on here?");
Thread.sleep(5000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
dlg.hide();
}
public static void main(String[] args) {
ThreadedDialog me = new ThreadedDialog();
me.buildInterface();
me.pack();
me.setVisible(true);
me.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//me.doSomething();
}
public void actionPerformed(ActionEvent event) {
doSomething();
}
}
Thanks
Everything you do with Swing components should be done on the event dispatch thread (EDT) (i.e. the thread used by Swing to call your events). You should launch threads to perform lengthy background operations.
In your code, you do the reverse : you try showing the dialog in another thread, and perform the long operation in the EDT.
Here's the fixed code :
package fr.free.jnizet.stackoverflow;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JProgressBar;
import javax.swing.SwingUtilities;
public class ThreadedDialog extends JFrame implements ActionListener{
private JDialog dlg;
private JButton button;
private void buildInterface(){
button = new JButton("do stuff;");
button.addActionListener(this);
this.getContentPane().setLayout(new BorderLayout());
this.getContentPane().add(BorderLayout.CENTER, button);
dlg = new JDialog(this, "Progress Dialog", true);
JProgressBar dpb = new JProgressBar(0, 500);
dlg.getContentPane().setLayout(new BorderLayout());
dlg.getContentPane().add(BorderLayout.CENTER, dpb);
dlg.getContentPane().add(BorderLayout.NORTH, new JLabel("Progress..."));
dlg.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
dlg.setSize(300, 75);
dlg.setLocationRelativeTo(this);
dpb.setIndeterminate(true);
}
public void doSomething(){
// create a thread for the background task
Thread t = new Thread(new Runnable(){
public void run() {
try {
for (int i=0; i<100; i++){
System.out.println("wtf is going on here?");
Thread.sleep(5000);
}
}
catch (InterruptedException e) {
e.printStackTrace();
}
// when the background task is finished, hide the dialog in the EDT.
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
dlg.setVisible(false);
}
});
}
});
t.start();
// show the dialog in the EDT
dlg.setVisible(true);
}
public static void main(String[] args) {
// create the GUI in the EDT
SwingUtilities.invokeLater(new Runnable() {
public void run() {
ThreadedDialog me = new ThreadedDialog();
me.buildInterface();
me.pack();
me.setVisible(true);
me.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
});
}
public void actionPerformed(ActionEvent event) {
doSomething();
}
}
You should read this tutorial, and learn to use SwingWorker for background tasks.
The problem here is that you are doing dlg.show() on the new thread that blocks that thread and you have Thread.sleep() on your main thread so pretty much everything is blocked. Besides JDialog.show() and hide() are deprecated and you might want to be using setVisible(true/false), but that was not causing any issue in your case. If you are trying to display a dialog with a progress bar for some time and then close it, here's the fixed code. There is a second thread now that takes care of hiding the dialog after some time leaving the main thread to do it's thing.
import java.awt.BorderLayout;
import javax.swing.*;
import java.awt.event.*;
public class ThreadedDialog extends JFrame implements ActionListener {
private JDialog dlg;
private JButton button;
private void buildInterface() {
button = new JButton("do stuff;");
button.addActionListener(this);
this.getContentPane().setLayout(new BorderLayout());
this.getContentPane().add(BorderLayout.CENTER, button);
dlg = new JDialog(this, "Progress Dialog", true);
JProgressBar dpb = new JProgressBar(0, 500);
dlg.getContentPane().setLayout(new BorderLayout());
dlg.getContentPane().add(BorderLayout.CENTER, dpb);
dlg.getContentPane().add(BorderLayout.NORTH, new JLabel("Progress..."));
dlg.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
dlg.setSize(300, 75);
dlg.setLocationRelativeTo(this);
dpb.setIndeterminate(true);
}
public void doSomething() {
Thread t1 = new Thread(new Runnable() {
public void run() {
dlg.setVisible(true);
}
});
Thread t2 = new Thread(new Runnable() {
public void run() {
try {
System.out.println("wtf is going on here?");
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
dlg.setVisible(false);
}
});
t1.start();
t2.start();
}
public static void main(String[] args) {
ThreadedDialog me = new ThreadedDialog();
me.buildInterface();
me.pack();
me.setVisible(true);
me.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// me.doSomething();
}
public void actionPerformed(ActionEvent event) {
doSomething();
}
}