Reputation: 313
I've written this code to update my progress bar progress message. Basically, when I have length processes that run on for() loops, I throw this in the loop, and have it update with prog = i and max = an array max.
for(int i = 1; i <= array.length-1; i++){
//Process
setPrg(this.lbl, ths.prg, i, array.length-1, "Doing so process");
}
-
public void setPrg(JLabel lbl, JProgressBar prg, int prog, int max, String msg) {
prg.setMaximum(max);
if (prog <= max) {
prg.setValue(prog);
}
prg.update(prg.getGraphics());
if (prog >= max) {
setMessage(lbl, "");
} else {
setMessage(lbl, msg);
}
}
public void setMessage(JLabel lbl, String msg) {
lbl.setText(msg);
lbl.update(lbl.getGraphics());
}
I do this with multiple consecutive for() loops, which each have unique messages describing the process.
The problem is, for some reason the label text overlaps over itself. For example, if I have one loop that runs this code:
setPrg(this.lbl, ths.prg, i, array.length-1, "This is process 1");
And one that runs this code:
setPrg(this.lbl, ths.prg, i, array.length-1, "The second process this is");
Then the text from the two messages will overlap as soon as the second process starts, instead of "This is process 1" disappearing.
On the other hand, if I don't include this line:
lbl.update(lbl.getGraphics());
No text will show up at all during the loop, and only the last text set for the label will show up after the loop.
Help much appreciated!
Here's the full program:
package test;
import javax.swing.JLabel;
import javax.swing.JProgressBar;
public class Main extends javax.swing.JFrame {
public Main() {
initComponents();
}
public void setPrg(JLabel lbl, JProgressBar prg, int prog, int max, String msg) {
prg.setMaximum(max);
if (prog <= max) {
prg.setValue(prog);
}
prg.update(prg.getGraphics());
if (prog >= max) {
setMessage(lbl, "");
} else {
setMessage(lbl, msg);
}
}
public void setMessage(JLabel lbl, String msg) {
lbl.setText(msg);
lbl.update(lbl.getGraphics());
}
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">
private void initComponents() {
jButton1 = new javax.swing.JButton();
jProgressBar1 = new javax.swing.JProgressBar();
jLabel1 = new javax.swing.JLabel();
jTextField1 = new javax.swing.JTextField();
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
jButton1.setText("Button");
jButton1.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jButton1ActionPerformed(evt);
}
});
jLabel1.setText("jLabel1");
jTextField1.setText("jTextField1");
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGap(246, 246, 246)
.addComponent(jLabel1))
.addGroup(layout.createSequentialGroup()
.addGap(226, 226, 226)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
.addComponent(jTextField1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(jButton1)))
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(jProgressBar1, javax.swing.GroupLayout.PREFERRED_SIZE, 540, javax.swing.GroupLayout.PREFERRED_SIZE)))
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addContainerGap(108, Short.MAX_VALUE)
.addComponent(jButton1)
.addGap(22, 22, 22)
.addComponent(jTextField1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jLabel1, javax.swing.GroupLayout.PREFERRED_SIZE, 31, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(18, 18, 18)
.addComponent(jProgressBar1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(40, 40, 40))
);
pack();
}// </editor-fold>
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
for (int i = 1; i <= 10000; i++) {
this.setPrg(jLabel1, jProgressBar1, i, 10000, "Message 1");
}
for (int i = 1; i <= 10000; i++) {
this.setPrg(jLabel1, jProgressBar1, i, 10000, "Second Message");
}
}
public static void main(String args[]) {
//<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
/* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel.
* For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html
*/
try {
for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
if ("Nimbus".equals(info.getName())) {
javax.swing.UIManager.setLookAndFeel(info.getClassName());
break;
}
}
} catch (ClassNotFoundException ex) {
java.util.logging.Logger.getLogger(Main.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
} catch (InstantiationException ex) {
java.util.logging.Logger.getLogger(Main.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
} catch (IllegalAccessException ex) {
java.util.logging.Logger.getLogger(Main.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
} catch (javax.swing.UnsupportedLookAndFeelException ex) {
java.util.logging.Logger.getLogger(Main.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
}
//</editor-fold>
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new Main().setVisible(true);
}
});
}
// Variables declaration - do not modify
public javax.swing.JButton jButton1;
public javax.swing.JLabel jLabel1;
public javax.swing.JProgressBar jProgressBar1;
public javax.swing.JTextField jTextField1;
// End of variables declaration
}
Upvotes: 1
Views: 452
Reputation: 285403
Your problem is a classic one of running long-running code on the Swing event thread, as well as making invalid Swing graphics calls. The solution is to do long-running code in a background thread, such as the doInBackground method of a SwingWorker, and to take care to make all Swing calls on the Swing event thread.
All of this is well explained in tutorials that are easy to find, including
For example, compile and run this:
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.concurrent.ExecutionException;
import javax.swing.*;
@SuppressWarnings("serial")
public class Main4b extends JPanel {
private static final int MAX = 10000;
private static final int PREF_W = 400;
private static final int PREF_H = 200;
private ButtonAction buttonAction = new ButtonAction();
private JButton button = new JButton(buttonAction);
private JTextField jTextField1 = new JTextField("jTextField1", 10);
private JLabel jLabel1 = new JLabel("jLabel1");
private JProgressBar jProgressBar1 = new JProgressBar(0, MAX);
public Main4b() {
jProgressBar1.setStringPainted(true);
setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
add(new JPanel() {{add(button);}});
add(new JPanel() {{add(jTextField1);}});
add(new JPanel() {{add(jLabel1);}});
add(jProgressBar1);
setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
}
@Override
public Dimension getPreferredSize() {
Dimension superSz = super.getPreferredSize();
if (isPreferredSizeSet()) {
return superSz;
}
int prefW = Math.max(superSz.width, PREF_W);
int prefH = Math.max(superSz.height, PREF_H);
return new Dimension(prefW, prefH);
}
private class ButtonAction extends AbstractAction {
public ButtonAction() {
super("Button");
}
@Override
public void actionPerformed(ActionEvent e) {
setEnabled(false); // make the button non-pressable
// create background thread worker
MyWorker myWorker = new MyWorker();
// add propertychange listener to it
myWorker.addPropertyChangeListener(new WorkerListener());
// run the worker thread
myWorker.execute();
}
}
// background thread
private static class MyWorker extends SwingWorker<Void, Integer> {
private static final String MY_PROGRESS = "my progress";
private int myProgress = 0;
@Override
protected Void doInBackground() throws Exception {
// all this code is run in a background thread
// do this twice
for (int j = 0; j < 2; j++) {
// iterate from 0 to 10,000
for (int i = 0; i < MAX; i++) {
Thread.sleep(1); // small delay so we can see what we're doing
setMyProgress(i + j * MAX); // send output to listeners
}
}
return null;
}
public int getMyProgress() {
return myProgress;
}
// myProgress is a "bound" field, one that will notify listeners
// if it is changed
public void setMyProgress(int myProgress) {
int oldValue = this.myProgress;
int newValue = myProgress;
this.myProgress = myProgress;
// notify all listeners
firePropertyChange(MY_PROGRESS, oldValue, newValue);
}
}
private class WorkerListener implements PropertyChangeListener {
private String message = "Message %d: %05d";
public WorkerListener() {
jLabel1.setText(String.format(message, 1, 0));
}
@Override
public void propertyChange(PropertyChangeEvent evt) {
// all this code is run on the Swing event thread
// listen for changes to my progress bound field
if (MyWorker.MY_PROGRESS.equals(evt.getPropertyName())) {
int value = (int) evt.getNewValue(); // get value
int newMsgIndex = 1 + value / MAX; // message number
jProgressBar1.setValue(value % MAX); // set value on progress bar
jLabel1.setText(String.format(message, newMsgIndex, value % MAX));
} else if (evt.getNewValue() == SwingWorker.StateValue.DONE) {
// worker is done
jLabel1.setText(""); // reset JLabel
buttonAction.setEnabled(true); // re-enable JButton's Action
MyWorker myWorker = (MyWorker) evt.getSource();
try {
// always call this to catch and handle any exceptions that
// may have been thrown from within the worker
myWorker.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
}
private static void createAndShowGui() {
Main4b mainPanel = new Main4b();
JFrame frame = new JFrame("Main");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
Upvotes: 2
Reputation: 1421
When you are using Swing you are automatically in a multi threaded environment. One is the main-Thread and Swing uses the EDT for UI repainting.
Use SwingUtilities.invokeLater() to execute JProgressBar.setValue() and JLabel.setValue() on the EventDispatchThread. This ensures they will be updated accordingly.
An example snippet of how to use SwingUtilities.
final JProgressBar prg = new JProgressBar();
final JLabel lbl = new JLabel();
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
prg.setValue(70);
lbl.setText("Text");
}
});
And you will see, that your calls to lbl.update(lbl.getGraphics()); are no longer needed.
Upvotes: 4