actually i have called the swing worker from a frame (Suppose) A.. in the swing worker class in do-in-Background method i have certain db queries and i am calling frame B too.. in the done() method however i want to dispose the frame A.. how can i do that..? i cannot write dispose() in frame A class because that results in disposing of frame before the new frame(frame B) is visible... Please help!!
class frameA extends JFrame{
public frameA(){
//done some operations..
SwingWorker worker=new Worker();
worker.execute();
}
public static void main(string[] args){
new frameA();
}
}
and in worker class
class Worker extends SwingWorker<Void, String> {
public Worker() {
super();
}
//Executed on the Event Dispatch Thread after the doInBackground method is finished
#Override
protected void done() {
//want to dispose the frameA here..
}
#Override
protected Void doInBackground() throws Exception {
// some db queries
new frameB().setVisible(true);
// call to frameb
}
The done() method of the SwingWorker is usually overridden to display the final result. Upon
completion of doInBackground() , the SwingWorker automaticlly invokes
done() in the EDT. So put your frame's invisible and visible code in this function.
The doInBackground() is not meant to do any GUI rendering task. You can invoke publish(V) from doInBackground() function which in turn invokes The process(V) method to run inside the EDT and performing GUI rendering task.
So a sample solution would be:
class Worker extends SwingWorker<Void, String> {
JFrame frameA;
public Worker(JFrame frameA) {
this.frameA = frameA;
}
#Override
protected void done() {
frameA.dispose();
new frameB().setVisible(true);
}
//other code
}
Now, create you SwingWorker instance by passing the target frame to it's constructor: new Worker(frame); For your context you probably could make use of this
However, you should not really design your application to be dependent on multiple JFrame. There are reasons for not to use multiple JFrame window. For more, see The Use of Multiple JFrames, Good/Bad Practice?. A general work around with use case where multiple frame would be needed is explained here.
Related
Let's say i have a listener attached to a button. When i press this button, actionPerformed is called and i set a label as visible. Then the calculate() method runs(which has some really long calculations inside it and it takes time). Then i wanna print the results with the show() method.
Thing is that i know for a fact that the label will be set as visible after all the code inside actionPerformed will be executed.
So my question is : How should i set the calculate method to run on background? Threads? SwingTimer? SwingWorker? I haven't found an ideal way yet.
class ButtonListener implements ActionListener{
#Override
public void actionPerformed(ActionEvent e) {
calculateLbl.setVisible(true);
calculate();
show();
}
}
Your problem is one of Swing concurrency: When calculate() is called on the Swing event thread, the long-running code hampers the event thread, preventing it from painting to the JLabel. The solution is to run calculate in a background thread, and then be notified when it is done. When notification occurs, call show(). A SwingWorker would work great for this since it comes with its own notification mechanism.
e.g.,
#Override
public void actionPerformed(ActionEvent e) {
calculateLbl.setVisible(true);
new SwingWorker<Void, Void>() {
public Void doInBackground() throws Exception{
calculate(); // this is run in a background thread
// take care that calculate makes no Swing calls
return null;
}
protected void done() {
show(); // this is run on the Swing event thread
}
}.execute();
}
Caveat: code not tested/compiled/nor run.
A problem with the above code is that it does not handle any exceptions that might be thrown within the calculate method, and a cleaner better way to do this is to create a SwingWorker variable, attach a PropertyChangeListener to it, and when its SwingWorker.StateValue is SwingWorker.StateValue.DONE, call get() on the SwingWorker and handle any possible exceptions there.
I have a code with two methods.
public void fondo() { ... } //Gathers JFrame Background and system time
public void recuperarDatosInternet() {...} //Connects to a URL and gets data.
When the JFrame is running, at the beginning it takes four or five seconds to perform all the operations of those methods.
While it's loading, the frame displays totally empty for 3 or 4 seconds until all the methods are complete, then the frame shows up and it's all right.
How can I make a Progress Bar that shows the user that something it's loading?
I don't mean a ProgressBar that are predetermined to take "4000 ms". I am referring to a progressbar that can take whatever it takes, and the bar doesn't reach the 100% until the methods are complete.
You could use a SwingWorker for this. This class enables allows the time-consuming work to be done in background thread and does not hold up the user-interface in the meantime. It also has the facility to divide the work up into 'chunks' and to update the user-interface on the completion of these chunks of work. This is what you would need for a progress bar, although it depends on your task being 'chunkable'. The link above takes you to the JavaDoc for this class which contains an example for both the simple and the 'chunked' usage.
If you run heavy task in The Event Dispatch Thread it's gonna to freeze until finish to avoid that you can execute the download in another thread using SwingWorker.
Follow this link to see a complete example with progressBar , special attention to setProgress() publish() and process().
Example:
public class MyWorker extends SwingWorker<Integer, String> {
#Override
protected Integer doInBackground() throws Exception {
// Start
publish("Start Download");
setProgress(1);
// More work was done
publish("More work was done");
setProgress(10);
// Complete
publish("Complete");
setProgress(100);
return 1;
}
#Override
protected void process(List< String> chunks) {
// Messages received from the doInBackground() (when invoking the publish() method)
}
}
and in client code:
SwingWorker worker = new MyWorker();
worker.addPropertyChangeListener(new MyProgressListener());
worker.execute();
class MyProgressListener implements PropertyChangeListener {
#Override
public void propertyChange(final PropertyChangeEvent event) {
if(event.getPropertyName().equalsIgnoreCase("progress")) {
downloadProgressBar.setIndeterminate(false);
downloadProgressBar.setValue((Integer) event.getNewValue());
}
}
}
All my sample programs create a big JFrame which contains all the code for its components, instead of creating separate classes for components and then adding these components to the big JFrame. This big JFrame is run or displayed by instantiating it inside an anonymous Runnable as shown below.
public static void main(String args[]) {
// Create the frame on the event dispatching thread.
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new StopWatch(); // Or any other JFrame
}
});
}
Class StopWatch implements ActionListener. Would it make any difference if I make it class StopWatch implements ActionListener, Runnable and then pass it as a runnable to the invokeLater method ? Is there is any other way of doing what the above code does ?
I have been struggling with JTree. I cannot refresh it after I add a new tree node (DefaultMutableTreeNode). I am able to refresh it when the code that adds the tree node is called from within the GUI class, but not outside it. Here is the code that actually adds the node to the JTree:
public class TreeViewer extends JPanel implements TreeSelectionListener {
JTree tree;
DefaultMutableTreeNode rootNode;
DefaultTreeModel treeModel;
public void modifyJTree(String name) {
DefaultMutableTreeNode childNode = new DefaultMutableTreeNode(name);
treeModel.insertNodeInto(childNode, rootNode, rootNode.getChildCount());
}
}
When it is called in the main method, the GUI failed to refresh itself after the node is added. I experimented several ways to put it on the Event-Dispatching Thread, but it does not work. I also tried it on the main thread, and it also failed. The code examples are provided below:
public static void main(String[] args) throws Exception {
final TreeViewer viewer = new CaseViewer();
// I omit the code that sets up the GUI and displays it
// This calls modifyJTree on the Event-Dispatching Thread
// And it does not work
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
viewer.modifyJTree("InvokeLater");
}
});
// This also calls modifyJTree on the Event-Dispatching Thread
// And it still does not work
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
viewer.modifyJTree("InvokeLater");
}
});
// Using a SwingWorker. Still no luck.
SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
#Override
protected Void doInBackground() throws Exception {
Thread.sleep(5000);
return null;
}
#Override
protected void done() {
viewer.modifyJTree("SwingerWorker");
}
};
// Now I tried to call it on the main thread, but this cannot work
viewer.modifyJTree("main thread");
}
However, if the call is from within the class, it works. For example, in the constructor of my TreeViewer class, as shown below:
public TreeViewer() {
rootNode = new DefaultMutableTreeNode("Root Node");
treeModel = new DefaultTreeModel(rootNode);
treeModel.addTreeModelListener(new MyTreeModelListener());
tree = new JTree(treeModel);
tree.setEditable(false);
tree.getSelectionModel().setSelectionMode(
TreeSelectionModel.SINGLE_TREE_SELECTION);
tree.setShowsRootHandles(true);
// Listen for when the selection changes.
tree.addTreeSelectionListener(this);
// omitting other initialization stuff
// Using a SwingWorker. It is the same SwingWorker, but this one works!
SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
#Override
protected Void doInBackground() throws Exception {
Thread.sleep(5000);
return null;
}
#Override
protected void done() {
viewer.modifyJTree("InsideSwingerWorker");
}
};
}
So my impression is that the call must be coming from inside the TreeViewer class. However, if the method cannot be called from outside the class, my TreeViewer is basically useless.
I can't help but suspect this is a bug of JVM. Or am I ignorant of some best practices regarding JTrees that caused this weird error?
Update: Problem solved. It actually has nothing to do with JTrees. The JTree instance I was modifying was not correctly added into the GUI that I was looking at.
Settle down. First, if you haven't already, read the How to Use Trees tutorial. Once you feel comfortable with the material, you can focus your attention on the Dynamically Changing a Tree partition. Therein you'll find sample code that will hopefully make things a little bit more clear for you.
But remember, you're absolutely right in what you're trying to do. That is, respect Swing's single-thread model. Using mechanisms, such as SwingUtilities and SwingWorker to modify a Swing component when in another thread is absolutely correct, although this does not seem to be applicable in your case. I think you're just a little misguided, or overwhelmed.
I'm wondering if SwingWorker has to be a nested class within my main GUI. I'd rather make it an external class to keep the GUI clear from any of my programs logic.
I tried to make the SwingWorker class external, which works fine for the process, unfortunately I can't access any of my GUI fields from the SwingWorker class.
Whenever I try to access an attribute, such like a label or whatever from within SwingWorker's done() method I get a nullPointer exception.
Any advice would be much appreciated!
First of all thank you very much Jeff! Works fine so far, even though I could not follow you on the second option you presented.
One of my background tasks calculates a certain size (long value), so it would be nice to get that value from my GUI.
You suggested to work with getters and setters but unfortunately I've got no idea on how to implement them in the SwingWorker class.
I tried it like this:
public void setSize(long totalSize) {
this.totalSize = totalSize;
}
public long getTotalSize() {
return totalSize;
}
The setter is invoked at the end of the doInBackground() method. Unfortunately I can't use the get() method from my GUI.
final MySwingWorker w = new MySwingWorker();
Runnable r = new Runnable() {
public void run() {
// do something with w.get()
}
};
w.setRunnable(r);
w.execute();
The object creation of "w" does not work in my case as the constructor requires an object of Runnable.
Am I missing something?
Please go easy on me, it's the first time I work with SwingWorker. :)
Again, thank you very much for your help!
You can make the SwingWorker an external class. However, just like any other class, if it can't see the variables (e.g. the label you want to set), of course it won't be able to set it. One thing you could do is pass the worker a Runnable that it executes when it is complete.
public class MySwingWorker extends SwingWorker {
private final Runnable r;
public MySwingWorker(Runnable r) {
this.r = r;
}
public void doInBackground() {...}
public void done() { r.run(); }
}
Now from the GUI, you might do something like
Runnable updateLabel = new Runnable() {
public void run() {
label.setText("myValue");
}
};
SwingWorker w = new MySwingWorker(updateLabel);
w.execute();
This gets a bit trickier if you want to use the result of the SwingWorker, though it is possible. Rather than passing the Runnable to the swing worker's constructor, you would have a setter method and then it would be something like:
final MySwingWorker w = new MySwingWorker();
Runnable r = new Runnable() {
public void run() {
// do something with w.get()
}
};
w.setRunnable(r);
w.execute();
In either case, the Runnable is functioning similarly to a closure that is executed when the worker is finished.