SwingWorker calls a JFrame Class ..window shows nothing - java

class class1{
public class1(){//here is my GUI commants}
#Override
public void actionPerformed(ActionEvent evt) //this is my action performed from a jframe window
{
worker = new SwingWorker<Void, Void>(){//ia m creating a worker
protected WaitWindow waitWindow;
#Override
protected Void doInBackground() throws Exception {
waitWindow= new WaitWindow();//i call waitWindow class to pop up my new window with the progressBar
return null;
}
#Override
protected void done(){
waitWindow.CloseWaitWindow();
}
};
try{
String option = (String)serversList.getSelectedItem();
if (evt.getSource().equals(Button1))//when client presses button1
{
if(option.equals("icsd Server"))
{//here is my connection
Registry registry = LocateRegistry.getRegistry("localhost",1080);
icsdserver = (ICSDinterface)registry.lookup("RmiCheckICSD");
worker.execute(); //i am calling execute until the server return 0 this might take a long time
if (icsdserver.RequestForEntry("icsd",0)==0)
{
worker.cancel(true); //when server tell its all ok (with 0) i call cancel(true)
AddGrade d = new AddGrade(icsdserver,"icsd");
}
}
}
}
catch (RemoteException ex) {System.out.println(ex);}
catch (NotBoundException ex) {System.out.println(ex);}
}}
The Wait Window class follows
class WaitWindow extends JFrame //my WaitWindow Class
{
private JProgressBar bar ;
public WaitWindow(){
super("Wait Until Connection Is ready");
setSize(100,200);
bar = new JProgressBar();
bar.setIndeterminate(true);
bar.setPreferredSize(new Dimension(300,330));
add(bar);
getContentPane();
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
}
public void CloseWaitWindow()
{
removeNotify();
}
}
What am I doing wrong here? I want the wait Window to shown until server's RequestForEntry method return 0 this might take some time. Also there is no error with RMI connection.

You're blocking the Event Dispathing Thread, with the call to RequestForEntry, which should be within the doInBackground method of the SwingWorker, for example
public void actionPerformed(ActionEvent ev) //this is my action performed from a jframe window
{
try {
final String option = (String) serversList.getSelectedItem();
if (evt.getSource().equals(Button1))//when client presses button1
{
final WaitWindow waitWindow = new WaitWindow();
worker = new SwingWorker<Void, Void>() {//ia m creating a worker
#Override
protected Void doInBackground() throws Exception {
if (option.equals("icsd Server")) {//here is my connection
Registry registry = LocateRegistry.getRegistry("localhost", 1080);
icsdserver = (ICSDinterface) registry.lookup("RmiCheckICSD");
worker.execute(); //i am calling execute until the server return 0 this might take a long time
if (icsdserver.RequestForEntry("icsd", 0) == 0) {
worker.cancel(true); //when server tell its all ok (with 0) i call cancel(true)
AddGrade d = new AddGrade(icsdserver, "icsd");
}
}
return null;
}
#Override
protected void done() {
waitWindow.CloseWaitWindow();
}
};
}
} catch (RemoteException ex) {
System.out.println(ex);
} catch (NotBoundException ex) {
System.out.println(ex);
}
}
Swing is a single threaded framework and isn't thread safe. This means that anything the blocks the Event Dispatching Thread will prevent it from processing new events, including paint requests.
Swing components should also only be updated from within the context of the EDT, which is where SwingWorker comes in.
See Concurrency in Swing and Worker Threads and SwingWorker for more details

Related

JavaSwing Update button based on Network Connection flag

I have a Java swing UI where I need to disable a button on the UI if I could not detect an active internet connection.
The code function properly so far, but when I get disconnected from the internet, it does not retrigger the method to update the boolean flag.
How can I add an event on that flag to let my button consume it during the lifetime of the application?
public class Main {
private static JButton button;
private static boolean testButtonEnabled;
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
/*
* function that returns true/false if connected to the internet
*/
if(Utils.isConnectedToInternet()) {
logger.debug("System is connected to the internet");
testButtonEnabled=true;
} else {
logger.debug("System is not connected to the internet");
testButtonEnabled=false;
}
Main window = new Main();
window.frame.setVisible(true);
button = new JButton("my button");
/*
* set the internet status
*/
button.setVisible(testButtonEnabled);
}
}
}
}
Again, this code:
SwingWorker<Void, String> worker = new SwingWorker<Void, String>()
{
public Void doInBackground()
{
while(true)
{
try {
isConnectedToInternet = Utils.isConnectedToInternet();
if (isConnectedToInternet) {
btn_online2.setEnabled(isConnectedToInternet);
} else {
btn_online2.setEnabled(isConnectedToInternet);
}
logger.debug("Internet connection status: " + isConnectedToInternet);
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
worker.execute();
is dangerous since it makes mutational changes to a Swing component from a background thread. While this code may work 95% of the time, it can fail in unpredicatable ways and at unpredicatable times. Better to only mutate Swing components on the event thread. For instance, even this would be better:
SwingWorker<Void, String> worker = new SwingWorker<Void, String>() {
#Override
public Void doInBackground() {
while(true) {
try {
isConnectedToInternet = Utils.isConnectedToInternet();
// note that there is no need for the if/else block
SwingUtilities.invokeLater(() -> {
btn_online2.setEnabled(isConnectedToInternet);
});
logger.debug("Internet connection status: " + isConnectedToInternet);
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
worker.execute();
or better still, using SwingWorker's publish/process:
SwingWorker<Void, Boolean> worker = new SwingWorker<Void, Boolean>() {
#Override
public Void doInBackground() {
while(true) {
try {
isConnectedToInternet = Utils.isConnectedToInternet();
// note that there is no need for the if/else block
publish(Utils.isConnectedToInternet());
logger.debug("Internet connection status: " + isConnectedToInternet);
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
#Override
protected void process(List<Boolean> chunks) {
for (Boolean chunk : chunks) {
btn_online2.setEnabled(chunk);
}
}
};
worker.execute();
I was able to resolve using the following method in the initialize() of the frame.
Adding that change part of the swingworker allowed the button to check the flag and assign it to itself whenever it changes.
Now whenever the internet is disconnected the button is disabled and whenever connected the button is enabled.
SwingWorker<Void, String> worker = new SwingWorker<Void, String>()
{
public Void doInBackground()
{
while(true)
{
try {
isConnectedToInternet = Utils.isConnectedToInternet();
if (isConnectedToInternet) {
btn_online2.setEnabled(isConnectedToInternet);
} else {
btn_online2.setEnabled(isConnectedToInternet);
}
logger.debug("Internet connection status: " + isConnectedToInternet);
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
worker.execute();

Get GUI of a different class to update continuously using java swing worker

I am creating a new window of the MainWindow class from within a different class.
The JFrame of the MainWindow class opens, and I can click my button, but the GUI does not update as it should after the button is clicked. I use SwingWorkers in this class, and if I run it on its own the GUI updates continuously.
However, creating it from the other class, it is not updating properly, even after I called the creating using a SwingWorker.
private void StartButtonActionPerformed(java.awt.event.ActionEvent evt) {
// TODO add your handling code here:
WindowEvent winClose = new WindowEvent(this,WindowEvent.WINDOW_CLOSING);
Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(winClose);
StartGame doGame = new StartGame();
doGame.execute();
}
class StartGame extends SwingWorker<Void, Void>
{
#Override
public Void doInBackground()
{
try {
MainWindow game = new MainWindow(num_respawns, hits, regen_secs, time, map);
game.setVisible(true);
} catch (IOException ex) {
Logger.getLogger(GameSetup.class.getName()).log(Level.SEVERE, null, ex);
}
return null;
}
#Override
public void done()
{
System.out.println("opening done");
}
}

pass JFrame to a thread

I have a class that creates a JFrame. When the start button is clicked, it calls my CoinCounterMechanism class. This class contains the following Thread:
Thread consumer = new Thread("CONSUMER"){
public void run ()
{
Integer coin;
while (producerFlag)
try
{
coin = queue.take();
System.out.println("Coin received: " + coin);
} catch (InterruptedException e)
{
e.printStackTrace();
}
}
};
When this thread gets called from my other class, I need to pass it the JFrame so I can modify the JFrame contents. How can I do this? This is for an intro level java course so the teacher gave us most of this code. Below is the code where the Thread gets called:
Button btnStart = new JButton("Start");
btnStart.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
cm = new CoinCounterMechanism();
cm.setConsumerFlag();
cm.setProducerFlag();
cm.producer.start();
cm.consumer.start();
}
});
Instead of an anonymous Thread, you create an actual Runnable class. You use the constructor to pass your JFrame and other fields.
public class Consumer implements Runnable {
private boolean producerFlag;
private JFrame frame;
private Queue<Integer> queue;
public Consumer(JFrame frame, Queue<Integer> queue, boolean producerFlag) {
this.frame = frame;
this.queue = queue;
this.producerFlag = producerFlag;
}
#Override
public void run() {
Integer coin;
while (producerFlag)
try {
coin = queue.take();
System.out.println("Coin received: " + coin);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Your JButton code contains higher level code than the code that actually starts the thread. In general, you would start a thread with the Runnable class above this way:
new Thread(new Consumer(frame, queue, true)).start();
The only thread that should be modifying anything on a JFrame is the event dispatch thread.
In order to have another thread modify a Swing component like a JFrame it needs to submit the change on the event dispatch thread, for example having the worker thread use SwingUtilities#invokeLater:
SwingUtilities.invokeLater(new Runnable() {
public void run() {
// modify your JFrame here
}
});

making pop up window by using SwingUtilities.invokeLater

I am writing a turn-based game on the internet. I try to pop up a window that should be in front until the input stream is ready. I created smth like this, but it seems that it does not work.
class CustomBlockerDialog extends JDialog {
/**
*
*/
private static final long serialVersionUID = 1L;
public CustomBlockerDialog(Frame owner, String text) {
super(owner, true);
setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
setSize(300, 100); // Adjust if needed
setTitle("");
add(new JLabel(text, SwingConstants.CENTER));
}
}
final CustomBlockerDialog block = new CustomBlockerDialog(null, "Not your turn");
SwingUtilities.invokeLater(new Runnable() {//A
#Override
public void run() {
System.out.println("show");
block.setVisible(true);
}
});
boolean one_write_only = true;
while(in.ready()){ /* C*/
if(one_write_only){
System.out.println("waiting server");
one_write_only = false;
}
};
System.out.println("suppose to hide");
SwingUtilities.invokeLater(new Runnable() {//B
#Override
public void run() {
System.out.println("hide");
block.setVisible(false);
}
});
It looks like "A" and "B" are executed after "C" and I have no idea why.
Your problem must be due to "C" being called on the Swing event thread and not in a background thread, since it sounds like "C" is blocking the event thread from running "A". Solution: be sure that "C" is not called on the Swing event thread. Also if this is the case, and this can be tested by running the SwingUtilities.isEventDispatchThread() method, then you don't need all those other runnables.
// note that this all must be called on the Swing event thread:
final CustomBlockerDialog block = new CustomBlockerDialog(null, "Not your turn");
System.out.println("show");
// block.setVisible(true); // !! no this will freeze!
final SwingWorker<Void, Void> worker = new SwingWorker<>() {
public void doInBackground() throws Exception {
boolean one_write_only = true;
while(in.ready()){ /* C*/
if(one_write_only){
System.out.println("waiting server");
one_write_only = false;
}
}
}
}
worker.addPropertyChangeListener(new PropertyChangeListener() {
public void propertyChanged(PropertyChangeEvent pcEvt) {
if (pcEvt.getNewValue() == SwingWorker.StateValue.DONE) {
System.out.println("hide");
block.setVisible(false);
// call worker's get() method here and catch exceptions
}
}
});
worker.execute();
// moved to down here since the dialog is modal!!!
block.setVisible(true);
Caveat: code not compiled nor tested. There may be errors present as it was typed off the cuff.
Thanks to Hovercraft Full Of Eels, I created a little different solution which works in my case:
final SwingWorker<Object,Object> worker2 = new SwingWorker<Object, Object>() {
public Object doInBackground() throws Exception {
boolean one_write_only = true;
while(!in.ready()){ /* C*/
if(one_write_only){
System.out.println("waiting server");
one_write_only = false;
}
}
return one_write_only;
}
protected void done() {
try {
block.setVisible(false);
} catch (Exception ignore) {}
}
};
worker2.execute();
block.setVisible(true);

ProgressMonitorDialog - Watching active thread to update monitor

In my GUI I have a PDF file creation operation. The operation can take up to 10-15 seconds to complete. When I start the operation, I attach a listener to it. The listener changes the cursor and disables the GUI, until the operation completes.
I would also like to add a progressbar, so the users will have a idea when it is going to complete.
Created a method startProgressBar() and called it from the start of the operation method.
See Below:
private void startSavePdfOperation() {
startProgressBar();
saveOp = new AplotSaveOperation(appReg.getString("aplot.message.SAVETOPDF"), "PDF", session);
saveOp.addOperationListener(new MyOperationListener(this) {
startProgressBar Method - See Below:
public void startProgressBar() {
Shell shell = new Shell(getShell());
shell.setSize(260, 120);
final ProgressBar bar = new ProgressBar(shell, SWT.SMOOTH);
bar.setBounds (20, 20, 200, 20);
shell.open();
final int maximum = bar.getMaximum();
new Thread(new Runnable() {
public void run() {
for (final int[] i = new int[1]; i[0] <= maximum; i[0]++) {
try {Thread.sleep (100);} catch (Throwable th) {}
if (Display.getDefault().isDisposed()) return;
Display.getDefault().asyncExec(new Runnable() {
public void run() {
if (bar.isDisposed ()) return;
bar.setSelection(i[0]);
}
});
}
}
}).start();
The code above created the ProgressBar. The issue is that the operation would end well before the progressbar indicator was close to ending.
Question: Is this because in the method I am creating a new thread and the indicator is updating according to the new thread and not the operation thread?
Question: Is it possible to create a new thread that watches the GUI thread and updates the progressbar accordingly?
Read a article suggesting using ProgressMonitorDialog with IRunnableWithProgress.
Method startProgressBar using ProgressMonitorDialog - see below:
public void startProgressBar() {
ProgressMonitorDialog dialog = new ProgressMonitorDialog(getShell());
try {
dialog.run(true, true, new IRunnableWithProgress(){
public void run(IProgressMonitor monitor) {
monitor.beginTask("Some nice progress message here ...", 100);
** getThread(); **
monitor.done();
}
});
}
catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void getThread() {
new Thread etc.. etc...
}
It seems that it will have the same issues with threading and updating as the code above.
Question: So now I am thinking can I just add or update the ProgressBar to my existing Listener
OperationListener Code - see below:
public abstract class MyOperationListener implements InterfaceAIFOperationListener {
AplotCreatePDFDialog w = null;
public MyOperationListener(AplotCreatePDFDialog win) {
w = win;
}
public void startOperation(String startMessage) {
Display.getDefault().asyncExec(new Runnable() {
public void run() {
w.getShell().setCursor(new Cursor(Display.getCurrent(), SWT.CURSOR_WAIT));
w.recursiveSetEnabled(getShell(), getShell().getEnabled());
w.getShell().setEnabled(!getShell().getEnabled());
}
});
}
public void endOperation() {
try {
endOperationImpl();
}
finally {
Display.getDefault().asyncExec(new Runnable() {
public void run() {
w.getShell().setCursor(new Cursor(Display.getCurrent(), SWT.CURSOR_ARROW));
w.recursiveSetEnabled(getShell(), true);
w.getShell().setEnabled(!getShell().getEnabled());
w.close();
}
});
}
}
abstract protected void endOperationImpl();
} // end class MyOperationListener
Thanks for any help you can give me with this.
EDIT
Baz, your answer below is exactly what the question asked, so thank you for answering.
But I am starting to think that what I am trying to do is not possible.
When my operation starts, I wanted the progress bar indicator to start and when my operation ended I wanted the indicator be at the end and the monitor would close.
I thought there might bee a way to use my listener to add the progressbar. Something like the following.
public void startOperation(String startMessage) {
Display.getDefault().asyncExec(new Runnable() {
public void run() {
->monitor.beginTask("Creating PDF File(s)", IProgressMonitor.UNKNOWN);<-
w.getShell().setCursor(new Cursor(Display.getCurrent(), SWT.CURSOR_WAIT));
w.recursiveSetEnabled(getShell(), getShell().getEnabled());
w.getShell().setEnabled(!getShell().getEnabled());
}
});
}
public void endOperation() {
try {
->monitor.worked(1);<-
endOperationImpl();
}
finally {
Display.getDefault().asyncExec(new Runnable() {
public void run() {
w.getShell().setCursor(new Cursor(Display.getCurrent(), SWT.CURSOR_ARROW));
w.recursiveSetEnabled(getShell(), true);
w.getShell().setEnabled(!getShell().getEnabled());
->monitor.done();<-
w.close();
}
});
}
}
abstract protected void endOperationImpl();
} // end class MyOperationListener
But I am starting to see that the ProgressBar has to have some sort of measurement to display the indicator correctly.
I would be happy if the indicator just went back and forth and the monitor would close at the end of the operation.
Why not use ProgressMonitorDialog?
Here is a related answer from me showing a simple example.
This is what it looks like:
If you are not sure about the workload, use this code:
monitor.beginTask("Copying files", IProgressMonitor.UNKNOWN);
It will show the idle bar while running.

Categories