Swing thread communication - java

I am creating a small swing application which plots a set of points given in a file. Guidelines suggested me to invoke a new thread for the GUI, for which I used the following code.
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new View().setVisible(true);
}
});
One of the scenario in the application is to open a file (which is done using a JFileChooser).
private void openMenuItemActionPerformed(java.awt.event.ActionEvent evt) {
int returnVal = fileChooser.showOpenDialog(this);
if (returnVal == JFileChooser.APPROVE_OPTION) {
File file = fileChooser.getSelectedFile();
//some heavy operations
} else {
System.out.println("File access cancelled by user.");
}
}
There are some heavy operations that are needed to be done, before proceeding to plot the points.
My questions are, is it advisable to place heavy codes in the GUI thread ? Is it possible to send the file object to the main thread for processing and send the results to GUI thread ?
Please note that I have no idea about Java Threading API.

use SwingWorker
SwingWorker proposes a way to solve it by performing the time-consuming task on another background thread, keeping the GUI responsive during this time.
take a look at this
http://docs.oracle.com/javase/6/docs/api/javax/swing/SwingWorker.html
http://www.theeggeadventure.com/wikimedia/index.php/SwingWorker_Example
http://weblogs.java.net/blog/mkarg/archive/2010/01/03/did-you-know-swingworker-can-send-progress-status
http://www.javaworld.com/javaworld/jw-06-2003/jw-0606-swingworker.html

If the result of the long running task modifies a Swing component, you can use SwingWorker. For more information, please see Worker Threads and SwingWorker.

Related

Thread writting status update text on any components under mouse

This is a rather odd one. I am using a Swing button to launch a scan of a list of files. Because I want it to display updates on the status bar, I am using a Thread. Since Swing won't let anything draw until the button's code has finished, I am also using a Tread to allow me to change the 'Start Scan' button to a 'Stop Scan' button.
The problem is that if the wait cursor is placed over any other components, during the scan, the status messages are also being written onto those components, such as buttons (see sample button below code), check boxes, etc; which messes up the interface. Is this a major bug or is it not a good idea to do what I am doing? Is there a way around it?
private void jButton47ActionPerformed(java.awt.event.ActionEvent evt)
{
// Scan folders button.
//
this.getFrame().setCursor(new Cursor(Cursor.WAIT_CURSOR));
// If button is in stop mode then...
if (collection.isScanContinue())
{
collection.setScanContinue(false);
jButton47.setText(" Scan Folders For Files ");
jButton47.setBackground(view.getDefaultButtonCol());
}
else // in scan mode...
{
Thread t = new Thread(new Runnable()
{
#Override
public void run()
{
// Setup the stop scan process button (changes the scan button to a stop button).
//
collection.setScanContinue(true);
jButton47.setText(" Stop Scanning Folders ");
jButton47.setBackground(collection.getPrefs().getDeleteCol());
collection.scanSourceAndTargetFolders();
if(collection.isScanContinue())
{
// do scan
}
// Reset the stop scan button and flag.
//
collection.setScanContinue(false);
jButton47.setText(" Scan Folders For Files ");
jButton47.setToolTipText("Scans Source and, if required, Target folders.");
jButton47.setBackground(view.getDefaultButtonCol());
view.getFrame().setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
}
});
t.start();
}
}
It cleans up fine if I re-validate the main frame, but it looks terrible during the file scan.
Any action involving swing, such as changing button text etc, should be performed on event dispatch thread using SwingUtilities.invokeLater. Otherwise, you'll run into concurrency issues like you see here. See this question for more details about how event thread works: Java Event-Dispatching Thread explanation
Also, for doing background tasks like this, Swing provides a handy utility called SwingWorker: http://docs.oracle.com/javase/tutorial/uiswing/concurrency/worker.html

How to use a JProgressBar in a different Thread?

I'm using POI in a function to fill the content of my excel document (it takes 10 seconds) and when i call my function I want to use a JProgressBar to see the progress, but the button and the program is block, i need to to make in other thread? and how can I do it? an example of my code:
btnEjecutar.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent arg0) {
//the function
generarDocumento(nombre);
}
Try to use an invokeLater, like the example bellow:
SwingUtilities.invokeLater(new Runnable() {
public void run() {
generarDocumento(nombre);
});
Event listeners are executed in the UI thread. If an event listener takes a long time, the UI will stop working/lock up/block/hang.
My guess is that the method generarDocumento() takes a long time. If you want the UI to continue working, you must run it in a worker thread. The Java documentation contains several examples how to do that: https://docs.oracle.com/javase/tutorial/uiswing/concurrency/worker.html
The last example in the tutorial contains demo code how to update a progress bar: https://docs.oracle.com/javase/tutorial/displayCode.html?code=https://docs.oracle.com/javase/tutorial/uiswing/examples/components/ProgressBarDemoProject/src/components/ProgressBarDemo.java
Note: The linked content is copyrighted; therefore I can't copy it here.

How can I check if this check box is enabled from another method in Java?

What I am trying to do is check if a check box is enabled from another method that is also running on another thread. I am fairly new to Java so I apologise in advanced if my code isn't how Java is usually written (or if it is written badly).
So I've made a method that created a iframe then adds a check box to it. I've removed the part which creates the jframe just to keep my code minimal - you can see it below:
private void initialize() {
chckbxEnabled.setHorizontalAlignment(SwingConstants.LEFT);
chckbxEnabled.setForeground(Color.WHITE);
chckbxEnabled.setBounds(98, 123, 81, 23);
frame.getContentPane().add(chckbxEnabled);
}
I've then created a new method in a new thread and called it from another method which I haven't shown here.
static Thread thread = new Thread(new Runnable() {
public void getPing() throws IOException, InterruptedException {
while (true) {
System.out.println(chckbxEnabled.isEnabled());
if(chckbxEnabled.isEnabled()) {] // Part I am having trouble with
String apiKey = "exmapleApiKey";
URL url = new URL("http://example.com/ping.php?mode=get&apikey="+apiKey);
URLConnection yc = url.openConnection();
BufferedReader in = new BufferedReader(
new InputStreamReader(
yc.getInputStream()));
String inputLine;
inputLine = in.readLine();
}
Thread.sleep(1000);
}
}
public void run() {
try {
getPing();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
As you can see I am trying to access chckbxEnabled.isEnabled(). This worked because just after I made my main class I added private static JCheckBox chckbxEnabled = new JCheckBox("Enabled");. So I can access it but it always returns true when I print it even though the check box is sometimes checked.
So my question is what is a better way of doing this as I expect that way I have experimented with is 'messy' and not the way it should be done?
You are mixing up methods:
isEnabled is for checking if the user can interact with the user (think greyed out elements)
isSelected is what you are looking for (check for the checkmark inside the box). You might want to take a look at ItemListener (example)
Also: keep in mind that you can draw to the api only form the gui thread.
Update:
So: most gui frameworks allow only one thread to draw the gui, since synchronisation is very complex. Therefore most of swing is not thread safe. A good answer outlining this (actually related to swing), can be found here
Quote:
A note on thread safety: It may seem strange that such an important
part of the Java platform is not thread safe. It turns out that any
attempt to create a thread-safe GUI library faces some fundamental
problems. For more on this issue, see the following entry in Graham
Hamilton's blog: MultiThreaded toolkits: A failed dream?
Given that we have to draw the gui from a single thread, what does this mean?
Update operations, i.e. changing a labels text should not be done from a thread you spawned, but should be done from the main thread that runs your gui code (in swing it is called event dispatch thread(in the swing trail).
A tutorial on how to have worker threads that update the gui can be found i.e. here. the basic idea is to have a backgound thread do the calculations and return the result. There is SwingWorker to help with this. The gui thred will then update the gui.
A introduction to concurreny and swing is i.e. here

showing progress of a task while a button is executing in java

I am executing several SQL queries in the function evoked by a button in java. I wish to show the status of the same, and I am using a jProgressBar for the same. But the problem is it will only update after the button has finished executing itself, making it pointless to show the progress. How can I display the actual progress of the executing button.
Make a thread dispatcher like this
public class ThreadDispatcher implements Runnable {
public ThreadDispatcher() {
}
public void run() {
//call the method related to query here
}
}
When button pressed call this class and let this class evoke your query related function.
It may be like this when you press the button.
Thread thread = new Thread(new ThreadDispatcher());
thread.start();
sleep(100);
catch the InterruptedException ex.
Link for Thread example
You need to do the computation on a background thread, not the main thread.
Take a look at the Java SwingWorker Tutorial.

Initiating a commandline tool from GUI

I want to fire a command line executible with the parameters entered in GUI.
Process class can be used to fork my required command line process from the Java application, and I used the getInputStream() method of Process object to get the result and got it displayed in the GUI.
private void confirmActionPerformed(java.awt.event.ActionEvent evt) {
String output;
try {
Process p = Runtime.getRuntime().exec("my command line exe with parameters");
BufferedReader stdInput = new BufferedReader(new InputStreamReader(p.getInputStream()));
while ((output = stdInput.readLine()) != null) {
TextField.setText(TextField.getText()+output+"\n");
}
} catch (IOException e) {
System.out.println("Exception in Process");
e.printStackTrace();
}
}
This is my code which is an event listener of a button pressed event and I attempted to display the result of the process in the text field (java swing component).
This process actually runs for quite a long time and shows some statistics as and when it runs when run in command line, but when i attempt it in GUI with the above code I'm getting the entire consolidated result only after the process finish running. I'm not getting the TextField updated as and when it is executing.
What would be the cause of this issue?
This is because the whole thing is done by the Swing event-handling thread.
Perhaps should you consider creating a separate thread and update the TextField with a SwingUtilities.invokeLater().
As Maurice already pointed out, you shouldn't be doing intensive processing on the UI thread. The SwingWorker class helps with that. It's part of the standard API since 1.6. There is also a backport to 1.5 if you can't use 1.6.
This class makes it easier to put some processing task on another thread and display the result later so that your GUI doesn't block in the meantime. You can also use SwingUtilities.invokeLater() directly, of course, as suggested by Maurice, but for me SwingWorker is the way to go as it is quite easy and apparently the new standard way of doing this kind of thing.

Categories