Java Swing Thread changing UI - complications - java

I tried this for many hours.. I have a thread that changes a JTextField of my UI, which completely destroys the UI. The Thread (lets call it Thread A) is generated by an ActionListener. The .setText() function call is in a extra thread (B) created by Thread A. Thread B is the Parameter of SwingUtilitis.invokeAll() and/or SwingUtilities.invokeAndWait(). I tried them both. Here's some code to make it more clear.
This is my ActionListener which creates Thread A - shortened of course:
public void actionPerformed(ActionEvent evt) {
Object source = evt.getSource();
if (source == window.getBtn_Search()) {
Refresher refresh = new Refresher();
refresh.start();
}
}
This is my Thread A, which later puts Thread B into the EDT Queue:
public class Refresher extends Thread implements Runnable {
private int counter = 0;
private UI window = null;
private int defRefresh = 0;
#Override
public void run() {
while(true){
-bazillion lines of code-
do {
try {
Refresher.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(window.canceled()) break;
UI.updateCounter(window.getLbl_Status(), (Configuration.getRefreshTime()-counter));
counter++;
} while (counter <= Configuration.getRefreshTime());
- more code-
}
}
}
The UI.updateCounter(...) will queue Thread B into the EDT.
public static void updateCounter(final JLabel label, final int i) {
try {
SwingUtilities.invokeAndWait(
new Runnable() {
public void run() {
label.setText("Refreshing in: " + i + " seconds.");
}
}
);
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Now when the last function gets called, everything gets messed up. I tried different stuff for hours and nothing worked. I also tried using SwingWorker, but the some or nothing at all happened.

The invokeAndWait() tried allows to post a Runnable task to be executed on the EDT, but it blocks the current thread and waits until the EDT is done executing the task.
But there is deadlock potential in invokeAndWait(), as there is in any code that creates a thread interdependency.
If the calling code holds some lock (explicitly or implicitly) that the code called
through invokeAndWait() requires, then the EDT code will wait for the non-
EDT code to release the lock, which cannot happen because the non-EDT code
is waiting for the EDT code to complete, and the application will hang.
As we can see here, modifying the JLabel component passed by the waiting non-
EDT code.
Instead we can use
invokeLater() takes
care of creating and queuing a special event that contains the Runnable. This event is processed on the EDT in the order it was received, just like any other event.
When its time comes, it is dispatched by running the Runnable’s run() method.
SwingUtilities.invokeLater(new Runnable() {
public void run() {
label.setText("Refreshing in: " + i + " seconds.");
}
});
OR
isEventDispatchThread() that returns true if the calling code is currently being executed on the EDT, false otherwise.
Runnable code= new Runnable() {
public void run() {
label.setText("Refreshing in: " + i + " seconds.");
}
}
);
if (SwingUtilities.isEventDispatchThread()) {
code.run();
} else {
SwingUtilities.invokeLater(code);
}

In general, labels are not very good at displaying text which change: their width change, and the layout with it.
Using a read-only JTextField, perhaps with proper changes in style, could be a better solution.

I think the intermediate JPanels you've created may count as validation roots. Therefore the revalidate() that automagically happens when you call setText() does not cause any layout changes higher than the level of the JPanel parent.
I don't think you actually need the panels, since a JLabel can contain both an Icon and text. See the tutorial.
So my advice is to remove the panels or, if they serve a purpose, make sure isValidateRoot() on the panels returns false.

When changing the label's text you should at least call repaint()/revalidate() on the label's topmost container, triggering a relayout, assuming the label calls invalidate()/revalidate() correctly on text change.

Related

Continue executing a thread while another thread is running in java

tl, dr;
I have a GUI thread that creates an object of another class (the seconds class has implemented Runnable, but here we don't execute the run() method, instead, we call a normal method) and calls a method. In that method, the first thread (current thread) is called again (to show sth on the LCD), then sends some data to the Internet, and waits 3 seconds for the server response. The problem is that the information is printed after 3 seconds. I know about the stack and program counter, but I wonder if there is another option that I can do my job.
I have the main method, which runs 3 threads (for short, I just write the requisite code. Tell me to add more, if needed):
public static void main(String[] args) throws UnknownHostException, InterruptedException {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
GUI.getInstance().setVisible(true); //GUI is singleton, using swing and JFrame
} catch (Exception e) {
e.printStackTrace();
}
}
});
MQTTConnection.getInstance().tryToConnect(); //It's the connection class, which has a thread (the thread is handled by a library that keeps the connection alive. I have no threads there) and is a singleton too.
Thread t1 = new Thread(new SendDataThread()); //A thread which sends some data every 20 seconds.
t1.start();
}
And in SendDataThread, I have a function that creates some random data and sends them (using the MQTTConnection class).
This is the SendDataThread:
public class SendDataThread implements Runnable {
public void sendLog() {
boolean serverOnline = false;
StringBuilder data = new StringBuilder();
data.append(createData());
GUI.getInstance().printNeutral(data.toString()); //Prints BLACK on a `JTextPane`
if(MQTTConnection.getInstance().publishLog(MQTTConnection.getInstance().MQTT_PUBLISH_ESP_SEND_LOG, data.toString())) //This line has a 3 second timeout. If the server doesn't respond, it will return false. I've added the 3 seconds timeout too. Please continue reading.
serverOnline = true;
if(serverOnline)
GUI.getInstance().printOK("Server Submitted"); //Prints in GREEN
else
GUI.getInstance().printProblem("Check your connection!"); //Prints in RED
GUI.getInstance().printNeutral("-------------------------------------------------");
}
#Override
public void run() {
while(true) {
sendLog();
try {
Thread.sleep(20000); //sleeps 20 about seconds
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//.....
}
And this is the 3 seconds timeout method, in MQTTConnection:
boolean publishLog(String topic, String data){
mqtt_responds = false;
publish(topic, data);
System.out.println("MQTT is connected");
long lastTime = System.currentTimeMillis();
while(System.currentTimeMillis() - lastTime < callback_timeout) {
if(mqtt_responds){
mqtt_responds = false;
System.out.println("Server submitted");
return true;
}
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("Timeout");
return false;
}
Till now, everything work right. The problem starts where I have a button in the GUI class, which the user can manually send random logs:
JButton sendLogBtn = new JButton("Send Log");
sendLogBtn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
SendDataThread sdt = new SendDataThread();
sdt.sendLog();
}
});
sendLogBtn.setBounds(10, 331, 89, 23);
panel.add(sendLogBtn);
This button creates an object of SendDataThread and calls the sendLog() method. The issue happens here: after sendLog() is called, sendLog(), calls this GUI thread again:
--> GUI.getInstance().printNeutral(data.toString()); //Prints BLACK on a `JTextPane`
But the log is printed after 3 seconds (After the sendLog() method has finished working, the timeout!)
How can I fix this?
In the button's actionPerformed you are calling sendLog. sendLog does exactly what you said, ie reports some logs and waits about 3 seconds (assuming callback_timeout is about equal to 3000).
To fix this, you need to make sure that the 3sec blocking is not on the EDT and also to make sure that the logs are instead posted on the EDT.
As a quick workaround you can do:
sendLogBtn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
new Thread(() -> new SendDataThread().sendLog()).start();
}
});
and then, as always, post your logs in the EDT like for example:
SwingUtilities.invokeLater(() -> GUI.getInstance().printNeutral(...));
AND
SwingUtilities.invokeLater(() -> GUI.getInstance().printProblem(...));
AND
SwingUtilities.invokeLater(() -> GUI.getInstance().printOk(...));
As for the question in your comment, I don't really understand what you are asking, but I should say that (as far as I know) the EDT is a Thread where all the Swing code is (and should be) posted on for execution. This way the Swing code does not have to be synchronized, because all GUI related stuff is executed sequentially (on the EDT). AWT for example was not intended to be single threaded as far as I know. Swing is however single threaded.

JProgressBar updating on one thread, not on another

I created a popup window to show progress of something, and it is working fine with my downloader, everything is being updated.
private void downloadFile(String link, String directory, String name) throws IOException {
task = new Downloader(link, directory, name);
task.start();
}
and in the Downloader class:
public void run() {
try {
while ((length = is.read(b)) != -1) {
downloaded += length;
bout.write(b, 0, length);
int percent = (int) ((downloaded * 100) / fileLength);
window.modify1("Downloading " + name + ".jar");
window.modify2((int) downloaded, "Progress: " + percent + "% [" + String.valueOf(downloaded).subSequence(0, String.valueOf(downloaded).length() - 1) + "kb/" + String.valueOf(fileLength).subSequence(0, String.valueOf(fileLength).length() - 1) + "kb]");
}
is.close();
bout.close();
window.exit();
Creator c = new Creator(directory, name);
c.create();
this.join();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
However, when I attempt to do almost the same in another thread, it does not work. Nothing in the popup window is being shown until the thread finishes.
LauncherThread t = new LauncherThread();
t.start();
try {
t.join();
} catch (InterruptedException e2) {
e2.printStackTrace();
}
and in the LauncherThread class:
public void run() {
window.modify1("Fetching data...");
window.modify2(0, "Progress: 0% [0/10]");
Main.trust();
window.modify2(1, "Progress: 10% [1/10]");
Main.bukkitVersions = Finder.findBukkitVersions();
window.modify2(2, "Progress: 20% [2/10]");
Main.spigotVersions = Finder.findSpigotVersions();
window.modify2(3, "Progress: 30% [3/10]");
Main.vanillaVersion = Finder.findVanillaVersion();
window.modify2(4, "Progress: 40% [4/10]");
Main.bukkitLinks = new String[3];
Main.bukkitLinks[0] = Finder.findDownloadLink("bukkit", "rb");
window.modify2(5, "Progress: 50% [5/10]");
Main.bukkitLinks[1] = Finder.findDownloadLink("bukkit", "beta");
window.modify2(6, "Progress: 60% [6/10]");
Main.bukkitLinks[2] = Finder.findDownloadLink("bukkit", "dev");
window.modify2(7, "Progress: 70% [7/10]");
Main.spigotLinks = new String[2];
Main.spigotLinks[0] = Finder.findDownloadLink("spigot", "lastStable");
window.modify2(8, "Progress: 80% [8/10]");
Main.spigotLinks[1] = Finder.findDownloadLink("spigot", "lastBuild");
window.modify2(9, "Progress: 90% [9/10]");
Main.vanillaLink = Finder.findDownloadLink("vanilla", null);
window.modify2(10, "Progress: 100% [10/10]");
window.exit();
}
I'm still quite new to Java so I apologize for my ignorance.
Edit: I encased the t.start() method inside a SwingUtilities.invokeLater(), and it works now. But the new problem is that the main class is no longer waiting for the LauncherThread to finish.
I went with the easy (and possibly bad) way. the initialize method is called in the LauncherThread after it finishes.
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
new Main();
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the application.
*/
public Main() {
launch();
}
/**
* Run launcher.
*/
private void launch() {
LauncherThread t = new LauncherThread(this);
t.start();
}
I pass the Main instance to the LauncherThread class through its constructor, so I can call the initialize method from there without having to use static access.
main.initialize();
You are in the process of running into serious problems and I'm afraid your current solution doesn't solve the problem but just hides it.
The root problem is that Swing (as most comparable libs) isn't thread safe. So there must only one thread that accesses Swing components. That thread isn't arbitrary, but a special thread named EDT (Event Dispatch Thread).
The consequences of that are:
The initialization of your UI must be wrapped in a call to SwingUtilities.invokeLater or SwingUtilities.invokeAndWait
If you do work in a separate thread that code must not touch your Progressbar directly. Instead it must dispatch updates to the progress bar through the two methods mentioned above.
If you don't do this right now and your code seams to work, it does not mean it will work tomorrow, or on a different machine, or when the weather changes or the government in china changes.
If you want, one thread to wait on another you should look into stuff like CountDownLatch just make sure you don't block the EDT, because when it is blocked nothing will get painted in the UI, so the application seems to be dead for the user. Again executing some code through invokeLater might solve that problem.
When i have to update Swing components from another Thread, then i'm doing it in
SwingUtilites.invokeLater(new Runnable() {
//set new values
});
from Javadoc ot method:
Causes doRun.run() to be executed asynchronously on the AWT event dispatching thread. This will happen after all pending AWT events have been processed. This method should be used when an application thread needs to update the GUI.
One more thing,.
You might consider doing your application via SwingWorker, those object are friendly with EDT. But remember, that they are executed in limited ThreadPool. I tried once to launch 50+ swing workers to update components of panel, however it worked only for first 10 of them. Probably could be changed to have them all running, but I've changed my approach.

How to make the main method wait for input on GUI without using Listener as a direct trigger?

I am working on a webscraping tool that should perform various operations with the scraped data.
Because of this, I need various different GUIs to work in an orderly manner and because of that, I need the main method to wait before each has completed it's purpose.
After searching for a while, I have found the following StackOverflow questions that provided some clues on how to solve the problem, but that I could not implement because they have some differences to my case:
How to wait for input in a text field
How to make main thread wait a different thread to finish
I know I can trigger code using a Listener to a/the GUI's components (a button, for example), but i'm having a hard time making the main-thread wait for that listener to wake it up, while the code for the GUI's thread (when there is one) is initialized by the main thread...
This is an simplified code to demonstrate how the program is supposed to work:
public class Main {
/*
* Waiter is a simple GUI with just an "Start" button in it. Here in place of my actual GUIs.
*/
private static Waiter auth; //Represents my NTLM-authentication form.
private static Waiter status; //Represents a status-feedback GUI that will be displayed during processing.
private static Waiter operation; //Represents a GUI in with the user choses what to do with the gathered data.
public static void main(String[] args) throws InterruptedException {
auth = new Waiter();
auth.setVisible(true);
System.out.println("NTLM Authentication form. Should wait here until user has filled up the GUI and clicked \"Start\".");
System.out.println("Authenticates WebClient's NTLM using data inputed to the GUI...");
auth.dispose();
Thread srt = new Thread(status = new Waiter());
srt.start();
status.setVisible(true);
//Performs webscraping operations...
System.out.println("Prepares the webscraped data here...Things like downloading files and/or parsing text...");
System.out.println("Keeps the user aware of the progress using the \"status\" GUI.");
status.setVisible(false);
//Clears the status GUI.
operation = new Waiter();
operation.setVisible(true);
System.out.println("Operation selection form. Should wait here until user selects an option.");
System.out.println("Starts performing the operation(s)...");
operation.dispose();
status.setVisible(true);
System.out.println("Performs the operation(s), while giving status-feedback to the user.");
status.setVisible(false);
System.out.println("Displays a file-save dialog to save the results.");
System.out.println("And finally, displays a \"End of operations\" dialog before ending.");
}
}
UPDATE 1:
The main difficulty I'm having is to implement something like this (this is what I want to do):
//Main method...code...
Thread srt = new Thread(status = new Waiter());
//Before "srt.start();"...
status.startButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
main.continueExecution();
}
});
//Thread's run() being something like "status.setVisible(true); main.waitGUI();"
srt.start();
//continues here after the Listener is triggered...more code...
Instead of this (what is being the solution to most other people, if I'm understanding it right...) (this is what I don't want to do, if possible):
//GUI before this one...
//code...
Thread srt = new Thread(status = new Waiter());
status.startButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
/*
* Code that should come after this GUI.
*/
}
});
//Thread's run() being something like "status.setVisible(true);"
srt.start();
//"ends" here...(Initial code or GUI before this "status")
In other words, I'm having trouble implementing the GUIs and Listeners in a way to trigger main's thread's "sleep" and "wake up" actions, instead of triggering actual processing code.
UPDATE 2:
Following #JB_Nizet 's tip on SwingUtilities.invokeLater(), I took a good look at the SwingUtilities docs, and after I found out about how the SwingUtilities.invokeAndWait() method works, and I think I've found how to do it, using a combination of Semaphore and invokeAndWait().
I need someone with a better understanding of multi-threading and/or GUIs to confirm if it's a safe, valid solution or not. (I'll then edit the question and clean it up, and if confirmed, post this in proper "answer format")
Anyways, here goes the modified code, which seems to be working for me:
public class Main_Test {
//Semaphore:
public static Semaphore semaphore;
//GUIs:
private static Waiter auth; //Represents my NTLM-authentication form.
public static void main(String[] args) {
try {
semaphore = new Semaphore(1);
// semaphore.acquire();
auth = new Waiter() {
#Override
public void run() {
try {
System.out.println(Main_Test.getThread() + this.getName() + " has been created and is now running.");
semaphore.acquire(); //Makes main pause.
this.setVisible(true);
} catch (InterruptedException ex) {
Logger.getLogger(Main_Test.class.getName()).log(Level.SEVERE, null, ex);
}
}
};
auth.jButton1.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println(getThread() + "NTLM has been hypothetically authenticated.");
semaphore.release(); //Makes main continue after GUI is done.
auth.dispose();
}
});
// semaphore.release();
SwingUtilities.invokeAndWait(auth);
semaphore.acquire(); //<- Where the main effectively gets paused until the permit is released.
/*
* GUI's run() will accquire the semaphore's permit.
* The invokeAndWait() garantees (?) it will happen before main's acquire().
* This causes the main to pause when trying to acquire the permit.
* It stays paused until the actionListener release() that permit.
*/
System.out.println(getThread() + "This message represents the processing, and should come only after the hypothetical NTLM authentication.");
} catch (InterruptedException ex) {
Logger.getLogger(Main_Test.class.getName()).log(Level.SEVERE, null, ex);
} catch (InvocationTargetException ex) {
Logger.getLogger(Main_Test.class.getName()).log(Level.SEVERE, null, ex);
}
}
public static String getThread() {
return String.format("%-32s --- ", Thread.currentThread().toString());
}
}
I'm not sure I have completely understood what you want to do, but it seems to me that you have a consumer thread (the main thread, waiting for events from the event dispatch thread), and a producer thread (the event dispatch thread).
The typical way to implement this is to use a blocking queue as a communication mechanism:
Create a blocking queue
Create your GUI and pass it the blocking queue
start a loop which gets data from the queue. Since the queue is blocking, the main thread will be blocked untile there is something in the queue
Have your event listeners, running in the EDT, post data to the blocking queue

Preventing GUI From Freezing When Calling SwingWorker.get( )

I have a program where I am loading a file while at the same time I am displaying a window to inform the user that the file is being loaded. I decided to make a FileLoader class that was a SwingWorker which actually handled loading the file and a ProgressWindow that implements PropertyChangeListener to inform the user about the status of the SwingWorker that was passed into it.
My code currently looks like this:
FileLoader loader = new FileLoader(filePath);
new ProgressWindow(loader, "Loading File", "Loading File");
//ProgressWindow's constructor calls loader.execute() inherited from SwingWorker
doc = loader.get(); //GUI Freezes when called
The problem is that whenever I call loader.get(), it freezes the GUI, thus the progress bar in the Progress Window doesn't run and the whole thing is pointless. As far as I can tell, this is because the thread controlling the GUI is the same thread that calls loader.get(), which goes on hold while loader.execute() is running.
So far, I've tried creating a new thread for either the loader.get() command or the loader.execute() method, and calling SwingUtilities.invokeLater() on the thread, but then the whole program freezes.
I've considered creating a ChangeListener for when SwingWorker.isDone() and then running loader.get(), but this would require some reworking of my code that I would rather not do.
Could anyone tell me what the best way is to get this to work?
get() is like join() in that it will block until called, and will wait for the SwingWorker to finish before being called. Using it wrongly can completely nullify all the advantages of using a SwingWorker in the first place.
Solution: Don't call get() until you know that the SwingWorker is done with its processing, by either calling it in the SwingWorker's done() method, or if you need to call it from the calling code, then in a PropertyChangeListener that has been added to the SwingWorker when the SwingWorker's "state" property is SwingWorker.StateValue.DONE.
Something like:
final FileLoader loader = new FileLoader(filePath);
loader.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
if ("state".equals(evt.getPropertyName())) {
// since DONE is enum, no need for equals(...) method
if (evt.getNewValue() == SwingWorker.StateValue.DONE) {
try {
loader.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
}
});
new ProgressWindow(loader, "Loading File", "Loading File");
Note: code not compiled nor tested
Edit: try/catch added.
So far, I've tried creating a new thread for either the loader.get() command or the loader.execute() method, and calling SwingUtilities.invokeLater() on the thread, but then the whole program freezes.
If you call SwingUtilities.invokeLater() on the thread that will execute the thread in the EDT which freezes the GUI. Instead, run the thread by calling it's start() method and only use SwingUtilities.invokeLater() when you need to update the progress bar in the PropertyChangeListener.
I have create a WorkerThread class which take care of Threads and GUI current/main thread .
i have put my GUI application in construct() method of WorkerThread when an event fire to start XXXServer then all threads are activate and GUI work smoothlly wihout freeze. have a look.
/**
* Action Event
*
* #see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
*/
public void actionPerformed(ActionEvent ae) {
log.info("actionPerformed begin..." + ae.getActionCommand());
try {
if (ae.getActionCommand().equals(btnStart.getText())) {
final int portNumber = 9990;
try {
WorkerThread workerThread = new WorkerThread(){
public Object construct(){
log.info("Initializing the Server GUI...");
// initializing the Server
try {
xxxServer = new XXXServer(portNumber);
xxxServer.start();
btnStart.setEnabled(false);
} catch (IOException e) {
// TODO Auto-generated catch block
log.info("actionPerformed() Start button ERROR IOEXCEPTION..." + e.getMessage());
e.printStackTrace();
}
return null;
}
};workerThread.start();
} catch (Exception e) {
log.info("actionPerformed() Start button ERROR..." + e.getMessage());
e.printStackTrace();
}
} else if (ae.getActionCommand().equals(btnStop.getText())) {
log.info("Exit..." + btnStop.getText());
closeWindow();
}
} catch (Exception e) {
log
.info("Error in ServerGUI actionPerformed==="
+ e.getMessage());
}
}

Delay is affecting entire Block

I have a code right below...take a look.
enter.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (enter.getText().length()>0){
addToChat("You: "+enter.getText());
enter.setText("");
delay(1000);
addToChat("oie");
}
}
});
And here is the delay void.
public static void delay(int delayTime){
try
{
Thread.sleep(delayTime);
} catch (InterruptedException ie)
{
}
}
The problem is whoever I type something into the text box and hit enter, it takes one second for not only the one to show up in the text area, but also the "You: " text block to show up, which is before the delay. Why is this delay affecting things BEFORE it and how can I fix this?
The UI does not get a chance to update before your action listener is finished. If you would like to change something after the delay, you should schedule it on a different thread, rather than wait inside the event handler:
addToChat("You: "+enter.getText());
enter.setText("");
new Thread(
new Runnable() {
public void run() {
delay(1000);
addToChat("oie");
}
}
).start();
You're sleep()ing in the Event Dispatch Thread, which means your UI is frozen and can't repaint itself, or accept input, or anything. You should only perform very quick actions in the EDT to avoid this effect. Check out the Graphical User Interfaces and following tutorial trails for the basics of UI programming.

Categories

Resources