Swing JProgressBar doesn't repaint as I'd expect it to - java

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.

Related

Constant updating of Conway's Game of Life

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();

Using thread.sleep in a for loop

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
}
}

Reset Progress bar to zero after method completion java swing

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...

How to wait for input in a text field?

I'm converting a console application to one that uses Swing. At the moment I want my program to do a similar thing to this .nextInt(); how can I achieve this by using .getText(); or something similar?
In short;
How can I hold the execution of the program till the user has entered something in the text field and pressed enter.
Update: So you want to wait for the user to to input something from the GUI. This is possible but needs to be synchronized since the GUI runs in another thread.
So the steps are:
Create an "holder" object that deligates the result from GUI to "logic" thread
The "logic" thread waits for the input (using holder.wait())
When the user have entered text it synchronizes the "holder" object and gives the result + notifies the "logic" thread (with holder.notify())
The "logic" thread is released from its lock and continues.
Full example:
public static void main(String... args) throws Exception {
final List<Integer> holder = new LinkedList<Integer>();
final JFrame frame = new JFrame("Test");
final JTextField field = new JTextField("Enter some int + press enter");
frame.add(field);
field.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
synchronized (holder) {
holder.add(Integer.parseInt(field.getText()));
holder.notify();
}
frame.dispose();
}
});
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
// "logic" thread
synchronized (holder) {
// wait for input from field
while (holder.isEmpty())
holder.wait();
int nextInt = holder.remove(0);
System.out.println(nextInt);
//....
}
}
Console application and GUI application are quite different in the behaviour. Console application takes input from command line arguments or wait for user entered input from keyboard while GUI application is driven by event mechanism in order to perform a task.
For example, you add a TextField object to your Frame and add a keyListener to your text field object. The listener is invoked when the key event has ben notified. There are lots of example out there, e.g. official java example http://download.oracle.com/javase/tutorial/uiswing/events/keylistener.html

Multithreading in Java

Hey all
I have a problem with multithreading in java. This is my scenario. I have a class,which name is LocalTime. This class should catch the current time of system, and via a method,which name is getCurrentTime, return this value to a Form. This is the code of this class
public class LocalTime implements Runnable {
private String currentTime=null;
Thread t;
private long time;
private long h;
private long m;
private long s;
public LocalTime() {
t=new Thread(this,"current Time");
t.start();
}
public synchronized void run() {
try {
for(;;){
Thread.sleep(1000);
time = System.currentTimeMillis()/1000;
h = (time / 3600) % 24;
m = (time / 60) % 60;
s = time % 60;
this.currentTime=h+":"+m+":"+s;
}
} catch (InterruptedException ex) {
Logger.getLogger(LocalTime.class.getName()).log(Level.SEVERE, null, ex);
}
}
public String getCurrentTime(){
return this.currentTime;
}
}
and this is the code of main form:
public MainForm1() {
initComponents();
LocalTime l=new LocalTime();
this.jLabel1.setText(l.getCurrentTime());//show the current time
//The rest of code
}
now my major problem is the jLabel1 text which shows current system time does not update each 1 second. If I insert for(;;) before the line of jLabel1.setText(l.getCurrentTime()) the rest of code won't be reacheable. what should I to correct this error? please suggest me solution with these assumptions. I know that I can run a thread in MainForm1, too but I want some solution to this problem.
The problem is that you just read the time once. You should either start a worker thread that queries LocalTime or add a listener to LocalTime that is informed of time changes and triggers the label update.
As a side note: you might want to use new Date() in conjunction with a SimpleDateFormat in order to get a nicely formatted representation.
Unless you are not including crucial pieces of code, there is no way for the label to be updated more than once.
See, your custom thread is kicking every second, updating it's own this.currentTime field.
But your label (at least with the meager amount of code that you are showing), it only gets updated once (via this.jLabel1.setText(l.getCurrentTime())) with whatever l had in it's time field at that moment.
Just because the local time thread runs, that doesn't mean it will magically go out and update the label. Why would it magically do it if you don't code that yourself? Think about it.
What you need to do is to have the thread update the label itself within it's run method. Moreover, this should be done within the Swing thread rules (and in the event queue) How you refactor that so that you get clean code, that's a topic for another thread.
// java pseudocode
public void run() {
Runnable labelUpdater = new Runnable()
{
public void run()
{
someForm.jLabel1.setText(yourUpdatedTimeFormattedAsStringOrWhatever);
}
}
try {
for(;;){
Thread.sleep(1000);
// will cause label update to occur in the awt event queue
SwingUtils.invokeLater(labelUpdater);
}
} catch (InterruptedException ex) {
Logger.getLogger(LocalTime.class.getName()).log(Level.SEVERE, null, ex);
}
}
Some notes:
You don't need the synchronized keyword in this specific instance of your run() method.
Use java.util.Calendar and java.util.Format to get string representations of the current time. What you are doing there with all that string concatenation and dividing the output of System.currentTimeMillis() is just bad. Have you looked at the Java API to see what's already there for you????????
Stuff for you to read:
Concurrency in Swing
http://java.sun.com/products/jfc/tsc/articles/threads/threads1.html#event_dispatching
Concurrency (the Java Tutorial)
http://download.oracle.com/javase/tutorial/essential/concurrency/
Please, please use a Timer for that kind of task... Why go to all that extra complexity?
Try something like this:
int delay = 1000; //milliseconds
ActionListener taskPerformer = new ActionListener() {
final LocalTime locTime = new LocalTime();
public void actionPerformed(ActionEvent evt) {
jLabel1.setText(locTime.getCurrentTime());
}
};
new Timer(delay, taskPerformer).start();
Create a TimerTask and call the setText in timer's run method. Then schedule the task to be executed every second.
There is only one thing that u are misssing is to update the label after each second. the thread which is calculating the time by using system date is working fine. What u can do is raise an event after each second and change the value of textfield in the listener of the event.

Categories