I am trying to use progress bar in my swing code. It works well but i am enable to reset to zero after the execution is finished. Here is my code logic to get user inputs and call respective methods.
final JProgressBar progressBar = new JProgressBar();
btnRun = new JButton("Run");
btnRun.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
btnRun.setEnabled(false);
if (textStartDate.getText().length() == 0 ||textEndDate.getText().length() == 0 ||textField.getText().length() == 0
|| textField_1.getText().length() == 0) {
JOptionPane.showMessageDialog(frame,"Please Enter all fields");
}
// else if(minDate.compareTo(maxDate)>0 ){
// JOptionPane.showMessageDialog(frame,"Starting Date should be lesser then end Date");
// }
else{
ArrayList<String> ss = list.listFiles(fSource,
textStartDate.getText(), textEndDate.getText());
for (String string : ss) {
i++;
progressBar.setMaximum(i);
System.out.println(i);
progressBar.setValue(i);
System.out.println(textField.getText().replace('\\', '/'));
list.writeToFolder(ftarget, string);
}
btnRun.setEnabled(true);
}
}
});
Set values to 0 like next:
progressBar.setValue(0);
progressBar.setMaximum(0);
progressBar.setString("");
The main problem you're having is your running a loop within the context of the Event Dispatching Thread, which will prevent it from process, amongst other things, paint requests.
This means the the progress bar won't actually update to until you exit the actionPerformed method.
There are a number of possible solutions, but the easiest would be to use a SwingWorker, which will allow you to run the loop in a background thread, but has the ability to provide both progress updates as well as re-sync updates back to the EDT.
Take a look at Concurrency in Swing for more details
For example...
java swingworker thread to update main Gui
Progress Bar Java
JProgressBar isn't progressing
I would, also, focus on maintaining the maximum value as a static value, for example...
progressBar.setValue(0);
progressBar.setMaximum(100);
//... Within the SwingWorker...
ArrayList<String> ss = list.listFiles(fSource,
textStartDate.getText(), textEndDate.getText());
for (String string : ss) {
i++;
int progress = (int)(((float)i / (float)ss.size()) * 100f);
setProgress(progress);
//...
}
For example. It will make the progress bar actually progress, otherwise it will always appear to be 100% (because i is both the value and the maximum).
This will automatically re-seed the progress bar the next time you create a new instance of the SwingWorker and execute it...
Related
I have this code sample
public static class BlinkMe extends Thread {
int counter = 0;
protected boolean stop = true;
public void run() {
while (stop) {
counter++;
if (counter % 2 == 0) {
jLabel4.setVisible(true);
jLabel7.setVisible(true);
jLabel8.setVisible(true);
counter = 0;
} else {
jLabel4.setVisible(false);
jLabel7.setVisible(false);
jLabel8.setVisible(false);
if (jButton4.isEnabled() == false) {
stop = false;
jLabel4.setVisible(true);
jLabel7.setVisible(true);
jLabel8.setVisible(true);
if (jButton2.isEnabled() == false) {
stop = true;
jButton2.setEnabled(false);
}
}
}
}
}
}
I need to stop this Thread when I press my Stop Button...
Here's the code I'm using for the Button's function but it is not working. ***The Thread is not working at ll*
Here is the Button's function
private void jButton4ActionPerformed(java.awt.event.ActionEvent evt) {
BlinkMe b=new BlinkMe();
b.stop(); //here I have even used b.interrupt(); but doesn't stop the
}
There are many, many things wrong in this code.
you're accessing Swing components from a background thread. That's forbidden. Only the event dispatch thread is allowed to access Swing components
You're trying to stop() a thread, although this method is deprecaed and should never, ever be used, as the documentation explains
Instead of stopping the actual thread, you create a new instance of that thread class, and call stop() on that new instance.
You "blink" without any delay between the blink.
Your thread uses a stop variable, but this variable is never modified anywhere. Even if it was, it's not volatile, so you have a big chance of not seeing the modification, and thus not stopping the thread.
Read the Swing tutorial abount concurrency. And use a Swing Timer, which is designed to do that kind of thing, safely.
You are creating a new thread in actionPerformed and trying to stop the same, which was not started so far. Try calling stop in actual thread.
The initial value of your stop is "true". This means that when the thread starts, the run method executes but will not execute the while block because the condition will result to false right away.
First, you need to change your while loop into like this:
while(!stop) { /* the rest of your code */ }
Next, you need to create a method in your BlinkMe thread that would allow other objects in your program that would make it stop. The method would look something like this:
public void stopBlinking() {
stop = true;
}
Calling the above method will stop the infinite loop in the run method.
I don't think you will see a blinking effect when you run your program. It is because the loop executes very fast. I suggest you put a Thread.sleep(1000) somewhere in the loop so that there is time to reflect the blink effect visually.
I have trouble trying to click on a JButton and constantly updating the Conway's Game of Life. So what I have is firstly to give the rules from the Game of Life and simulate and calculate the position for the counters. Then update the frame by setting the background colour to the JButton, and then delay and repeat. But the problem is when I press the start button, it gets stuck due to the fact I was trying to use while loop.
There is a separate package called the AI_Processor which is just the simulation and calculation which is all done correctly, just the updating got some problems.
Code Parts:
public void updateFrame() {
AI.AI_Movement_Update();
addColour();
}
public void addColour() {
for (int i = 0; i < fieldHeight; i++) {
for (int j = 0; j < fieldWidth; j++) {
if (AI.getPosB(j, i) == true) {
testMolecules[i][j].setBackground(Color.green);
} else {
testMolecules[i][j].setBackground(Color.black);
}
}
}
}
Timer tm = new Timer(1000,this);
if (ae.getSource() == start) {
while(true) {
updateFrame();
tm.start();
}
}
You state:
But the problem is when I press the start button, it got stucked due to the fact i was trying to use while loop.
Then get rid of the while (true) loop since all this does is tie up the Swing event thread rendering your GUI useless. You have a Swing Timer, and you could call the model's update method in the timer's ActionListener so that it is called with each tick of the timer, and then you would not need the while loop. Other options include keeping the while (true) loop, but calling it in a background thread, but if you do this, take care to update your GUI on the Swing event thread only.
...Sorry for the formatting though...
I have formatted your code for you, but for future reference you will want to read the help section of this site regarding how to format questions and containing code. Also have a look here.
Other random thoughts:
Regarding Timer tm = new Timer(1000,this);, I try to avoid having my GUI classes implement listener interfaces as it forces the class to do too much, breaking the Single Responsibility Principle. Better to use either a separate listener class, a Control class that assigns listeners, or an anonymous inner class.
For more information on Swing threading issues, please see Lesson: Concurrency in Swing
For more on anonymous inner classes, again, get rid of the while (true) bit and instead try something like:
// note that TIMER_DELAY is a constant, and needs to be smaller than 1000, perhaps 20?
Timer tm = new Timer(TIMER_DELAY, new ActionListener() {
#Override
public void actionPerformed(ActionEvent evt) {
updateFrame();
}
});
// the start call below can only be called inside of a method or a constructor
tm.start();
EDIT:
sorry, previous solution was bad :-(
EDIT:
you could use anonymous inner class for this
see http://docs.oracle.com/javase/tutorial/java/javaOO/anonymousclasses.html
see http://docs.oracle.com/javase/7/docs/api/javax/swing/Timer.html
If you use Timer, then you should pass an instance of ActionListener
Timer creates a new Thread, so while is not neccessary...
Not tested:
public void updateFrame(){
AI.AI_Movement_Update();
addColour();
}
public void addColour() {
for (int i = 0; i < fieldHeight; i++) {
for (int j = 0; j < fieldWidth; j++) {
if (AI.getPosB(j, i) == true) {
testMolecules[i][j].setBackground(Color.green);
} else {
testMolecules[i][j].setBackground(Color.black);
}
}
}
}
if(ae.getSource() == start)
new Timer(1000,new ActionListener(){
updateFrame();
}).start();
I am using eclipse if it would make any difference. I am trying to update a label 10 times at the press of a button, and I want it to wait between updates. I am trying to use thread.sleep in a for loop, but it does not update the label until the for loop reaches an end.
The code is close to. It also has much more code in it to specify what to change the label to.
for (int i = 0; i < 10; i++) {
try{
thread.sleep(250);
}catch(InterruptedException ie) {
return;
}
panel.repaint();
}
Thanks, it really helped!
For the label to be updated, the main GUI event loop has to get its turn. But I'm guessing your code is running in the main thread, so the redrawing can't occur until your code is completely finished.
What you need to do is put your sleeping loop into a separate thread.
For this task, the SwingWorker class might be useful.
Swing has a single thread (commonly called the Swing thread) and all button presses, redraws, processing, updates, etc happen in that thread.
This means that if you block that thread (such as for example by sleeping in a loop) then it cannot redraw the screen until you finish.
You need to farm out the work to another thread such as by using SwingWorker or user a Timer to schedule the updates. Swing has a Timer class you can use that is designed for exactly this case, just tell it to call you back every 250ms and make the change in that callback.
May be i am not getting your exact problem otherwise below is the solution:
for (int i = 0; i < 10; i++) {
try{
panel.repaint();
thread.sleep(250);
// Or here if you want to wait for 250ms before first update
}catch(InterruptedException ie) {
return;
}
}
Thoguh SwingWorker is better option. Move above logic to SwingWorker thread. Sample code is below:
class Task extends SwingWorker<Void, Void> {
#Override
public Void doInBackground() {
for (int i = 0; i < 10; i++) {
try{
panel.repaint();
thread.sleep(250);
// Or here if you want to wait for 250ms before first update
}catch(InterruptedException ie) {
}
}
return null;
}
/*
* Executed in event dispatching thread
*/
#Override
public void done() {
// Do something if you want at the end of all updates like turn off the wait cursor
}
}
Hey all, I have a pretty simple problem someone should be able to help me with. All I want is a small frame with a progress bar that updates, right now it's not updating:
final JProgressBar bar = new JProgressBar(0,250000);
bar.setValue(1000);
bar.setIndeterminate(false);
JOptionPane j = new JOptionPane(bar);
final JDialog d = j.createDialog(j,"Expierment X");
d.pack();
d.setVisible(true);
bar.setValue(40000);
The 40,000 value doesn't show up, only the measly 1000. I'd prefer to not have to write any classes to handle repaint calls or whatever is involved in doing that (haven't used Swing in forever).
Thanks!
This is because createDialog blocks so bar.setValue will not be called until you hit OK on the dialog.
You should update the progress bar in a different thread.
For example:
final JProgressBar bar = new JProgressBar(0,250000);
bar.setValue(1000);
bar.setIndeterminate(false);
JOptionPane j = new JOptionPane(bar);
Thread t = new Thread(){
public void run(){
for(int i = 1000 ; i < 250000 ; i+=10000){
bar.setValue(i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
}
}
};
t.start();
final JDialog d = j.createDialog(j,"Expierment X");
d.pack();
d.setVisible(true);
You need to make sure that the setValue method gets called from the Event Dispatch Thread. You can use SwingUtilities.invokeLater for that.
Have you ever heard about a GUI freezing because of repeated calls to the method javax.swing.Document.insertString?
There is my code:
private int insertNotation(GameNode startNode, StyledDocument doc, int pos) {
String s = "";
int startPos = pos;
boolean isContinuous = false;
Style boldStyle, regularStyle, commentStyle, currentNodeStyle, nagStyle, grayStyle;
grayStyle = notationTextPane.getStyle("gray");
GameNode currentNode = history.getCurrentGameNode();
if ((currentNode.isLeaf() && startNode == currentNode.getParent()) || startNode == currentNode) {
try {
if (startNode.getComment().length() > 0) {
s = startNode.getComment() + " ";
commentStyle.addAttribute("gameNode", startNode);
doc.insertString(pos, s, commentStyle);
pos += s.length();
}
for (int n = 0; n < startNode.getChildCount(); n++) {
GameNode node = (GameNode) startNode.getChildAt(n);
boolean isCurrentNode = (node == currentNode);
if (node.isLeaf()) {
if (node.isWhiteMove()) {
s = node.getFullMoveNumber() + ". ";
boldStyle.addAttribute("gameNode", node);
doc.insertString(pos, s, boldStyle);
pos += s.length();
s = node.getMove();
Style style = isCurrentNode ? currentNodeStyle : regularStyle;
style.addAttribute("gameNode", node);
doc.insertString(pos, s, style);
pos += s.length();
isContinuous = true;
} else {
if (isContinuous) {
s = node.getMove();
Style style = isCurrentNode ? currentNodeStyle : regularStyle;
style.addAttribute("gameNode", node);
doc.insertString(pos, s, style);
pos += s.length();
} else {
isContinuous = true;
s = node.getFullMoveNumber() + "... ";
boldStyle.addAttribute("gameNode", node);
doc.insertString(pos, s, boldStyle);
pos += s.length();
s = node.getMove();
Style style = isCurrentNode ? currentNodeStyle : regularStyle;
style.addAttribute("gameNode", node);
doc.insertString(pos, s, style);
pos += s.length();
}
}
doc.insertString(pos++, " ", regularStyle);
}
} catch (BadLocationException e) {
e.printStackTrace();
}
return pos - startPos;
}
I simplified it a lot but as you can see, there are many calls to the insertString() method in my 'doc' StyledDocument variable.
This StyledDocument object is added in a JTabbedPane.
I have read here (in Performance Analysis section) that javax.swing.Document.insertString method is extremely slow (here over 1 ms per call).
Can repeated calls to it freeze the GUI?
Whenever you do something slow in the main GUI thread, you will freeze the GUI. It redraws based on processing events. Imagine that your event handling code is in a while loop pulling events off a queue -- if, you don't return from your function, the next event can't be processed.
Consider doing long-running or slow processing in a background thread.
See this article: http://java.sun.com/products/jfc/tsc/articles/threads/threads2.html
Consider using a background thread to throttle the addition of text to your document. This is best accomplished using a SwingWorker.
First we define queue for throttling. Requests to insert text will simply add to this queue. These requests do not have to be on the Event Dispatch thread.
BlockingQueue<String> toAdd = new LinkedBlockingQueue<String>();
toAdd.add("Some text");
toAdd.add("Some more text");
Next we invoke SwingWorker where background thread continuously polls the queue and publishes results back to the Event Dispatch thread in chunks.
new SwingWorker<Void, String>() {
// Implementation of 'process' and 'doInBackground' methods to go here.
}.execute();
Now we implement doInBackground to poll until input queue is empty and then publish back to the Event Dispatch thread in one go for more efficient throttling.
public String doInBackground() {
while (!Thread.interrupted()) {
List<String> l = new LinkedList<String>();
String s = toAdd.poll();
if (s == null) {
publish(l.toArray(new String[l.size()]));
l.clear();
} else {
l.add(s);
}
}
// Thread interrupted but publish anything pending before returning.
if (!l.isEmpty()) {
publish(l.toArray(new String[l.size()]));
}
return null;
}
Finally we implement process. This is called on Swing thread following a call to publish on the background thread. We join chunks together using a StringBuilder to avoid the need for multiple inserts into document (this the main advantage with this approach).
public void process(String... chunks) {
StringBuilder sb = new StringBuilder();
for (String chunk : chunks) {
sb.append(chunk);
}
// Insert sb.toString() into buffer HERE
}
Are you actually seeing a freeze (no forward progress), or just very severe slowdown?
We don't have your entire program code, so it's not clear, but is your document already being displayed when you start changing it? Each call to insertString not only modifies the document, but creates a cascade of events, to which the GUI reacts.
If you are essentially building the document and you only want to display the final state, you may want to consider building a new document before adding it to the text component widget, and then setting the ready document. It'll be a little more flicker, but much faster, since until you add your document to the widget with setDocument, you're not really triggering events and updates.
You can then build the document on a different thread.
The problem is gone as I used a SwingWorker object to alleviate the GUI thread: the calls to javax.swing.Document.insertString was in this SwingWorker.
I also had an other resolved bug: there was too many graphics overloaded treatment process.
In this case too I used a SwingWorker.
Here is the code:
new SwingWorker<Void, Void>()
{
#Override
protected Void doInBackground() throws Exception
{
visualBoard.update(); // here your method call, deported from the GUI thread.
return null;
}
}.execute();
And thank you all for your so fast answers!!
This can actually happen. It's not about doing too much either; the method just hangs (CPU=0%).
Here's an example to test: http://tinybrain.de/1000816
When the bug occurs, you will not see a frame at all, and the last line on the console is "inserting".
It's kind of a severe bug that speaks against using JTextPane at all...
Edit: I have now reproduced the freezing with JTextArea.append too! http://tinybrain.de/1000822
It seems to revolve around always creating the elements in an AWT thread. Previously, I created them just in main(), and calling SwingUtilities.invokeLater there with the appends. THAT seems to be wrong.
If I create the whole GUI within an "awt {}" block (JavaX shorthand for SwingUtilities.invokeLater), everything is fine, as you can see here: http://tinybrain.de/1000823.
Cheers
You should look into running your repeated call within a Thread. That should do it for you.