JProgressBar instantiate it self multiple time - java

I'm trying to add a JProgressBar to a simple program just to learn. So far i can display it, but it adds multiple instance while i want just one to show.
Here's the code :
package package1;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
public class Opening extends JPanel {
private JProgressBar loadingBar;
private Thread t;
public void paintComponent(Graphics g)
{
super.paintComponent(g);
//Loading bar
bar();
}
private void bar()
{
loadingBar = new JProgressBar();
t = new Thread(new LoadMyBar());
this.add(loadingBar).setLocation(25, 600);
loadingBar.setSize(625, 25);
loadingBar.setStringPainted(true);
loadingBar.setValue(0);
loadingBar.setMinimum(0);
loadingBar.setMaximum(100);
t.start();
}
class LoadMyBar implements Runnable
{
public void run(){
for(int i = loadingBar.getMinimum(); i <= loadingBar.getMaximum(); i++)
{
loadingBar.setValue(i);
try
{
t.sleep(1000);
}catch(InterruptedException e)
{
e.printStackTrace();
}
}
}
}
}
Any idea on what i'm doing wrong?

You're calling bar() inside of the paintComponent(Graphics g) method. This method is responsible for drawing the component and can be called potentially many times and not in your control. Don't do that, but rather call it once in a constructor or some other location where it can be called just once.
You're also setting the JProgressBar's value off of the Swing event thread, something that can be dangerous to do. Use a Swing Timer instead or use a SwingWorker's progress property together with a PropertyChangeListener.
e.g.,
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
#SuppressWarnings("serial")
public class Opening extends JPanel {
private static final int TIMER_DELAY = 1000;
private JProgressBar loadingBar;
private Timer t = new Timer(TIMER_DELAY, new TimerListener());
public Opening() {
bar();
}
private void bar() {
loadingBar = new JProgressBar();
this.add(loadingBar);
loadingBar.setStringPainted(true);
loadingBar.setValue(0);
loadingBar.setMinimum(0);
loadingBar.setMaximum(100);
t.start();
}
private class TimerListener implements ActionListener {
int value = 0;
#Override
public void actionPerformed(ActionEvent e) {
if (value <= 100) {
loadingBar.setValue(value);
value++;
} else {
// value > 100
((Timer) e.getSource()).stop(); // stop timer
}
}
}
private static void createAndShowGui() {
JFrame frame = new JFrame("Opening");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(new Opening());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}

You are creating the JProgressBar and adding it to your class Opening in the method paintComponent().
paintComponent() is called every time, the component needs to draw itself.
This can be after resizing the window (JFrame) or some other application overlapping your application.
You should move the initialization to the constructor of Opening.
See http://docs.oracle.com/javase/tutorial/uiswing/painting/closer.html

Related

Changing a label text at the start and the end of an ActionListener

I have a process to be performed in an ActionListener call. This process can take some time. So I have to inform that is processing during the time of the execution in a label.
My problem is that Swing doesn't repaint the label during the thread of ActionListener, so I can't show the "Processing" message.
What is the best way to solve that?
package test;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class MyTestPane extends JPanel{
/**
*
*/
private static final long serialVersionUID = 340444475514103360L;
private JLabel myLabel;
private JButton myButton;
public MyTestPane() {
initComponents();
}
private void initComponents() {
myLabel = new JLabel("Ready");
myButton = new JButton("Start");
myButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
myActionListenter();
}
});
add(myLabel);
add(myButton);
}
private void myActionListenter(){
myLabel.setText("Starting count ...");
try{
Thread.sleep(10000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
myLabel.setText("Finishing count ...");
}
public static final void main(String argv[]) {
JFrame frame = new JFrame();
frame.add(new MyTestPane());
frame.pack();
frame.setVisible(true);
}
}
When you want to do something heavy (in terms of time required to be finished), like Thread.sleep, you can't simply do it in the same thread the GUI runs. When your heavy task starts on the same thread as the GUI, the GUI thread is busy calculating your heavy task and therefore it cannot receive any events. That's why the whole GUI freezes. Because it is busy.
Now in order to solve that read concurrency in Swing.
To sum it up for you, all Swing applications must run on their single-per-application thread. This thread is called the Event Dispatch Thread. Because all GUI events (mouse pointers moving, buttons clicked, windows resizing, etc) happen there.
So, when you want to do something heavy, you do it in another thread and since they are invisible to the user(GUI) they are called background threads. Swing has its own API to create background threads. They are called SwingWorkers. You can find how to use one in the doc.
Here is an example of doing something heavy (Thread.sleep) and at the same time publishing the progress (I don't know if you are interested in that). Also, I try to keep things some sort of abstract, in order to keep the concerns separated.
I suggest you to run it and have some experiments with it.
public class WorkerExample {
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
MyPanel myPanel = new MyPanel();
myPanel.setPreferredSize(new Dimension(400, 300));
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(myPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
});
}
private static class MyPanel extends JPanel implements ProgressableView {
private JLabel countLabel;
private JProgressBar progressBar;
public MyPanel() {
super(new FlowLayout());
countLabel = new JLabel("Progress: 0");
add(countLabel);
progressBar = new JProgressBar();
progressBar.setVisible(false);
add(progressBar);
JButton startHeavyTaskButton = new JButton("Start Heavy Task");
startHeavyTaskButton.addActionListener(e -> {
startHeavyTaskButton.setEnabled(false);
startHeavyTaskButton.setText("Please wait..");
progressBar.setValue(0);
progressBar.setVisible(true);
Runnable restoreButtonAvailability = () -> {
startHeavyTaskButton.setEnabled(true);
startHeavyTaskButton.setText("Start Heavy Task");
};
new DoSomethingHeavyAndShowProgressWorker(this, restoreButtonAvailability).execute();
});
add(startHeavyTaskButton);
}
#Override
public void setProgress(int progress) {
if (progress >= 99) {
countLabel.setText("Finished!!");
progressBar.setVisible(false);
} else {
progressBar.setValue(progress);
countLabel.setText("Progress: " + progress);
}
}
}
private static interface ProgressableView {
void setProgress(int progress);
}
private static class DoSomethingHeavyAndShowProgressWorker extends SwingWorker<Void, Integer> {
private ProgressableView view;
private Runnable onDone;
public DoSomethingHeavyAndShowProgressWorker(ProgressableView view, Runnable onDone) {
this.view = view;
this.onDone = onDone;
}
#Override
protected Void doInBackground() throws Exception {
for (int i = 0; i < 100; i++) {
Thread.sleep(200);
publish((i + 1)); // Publish will call process() method in the GUI thread
}
return null;
}
#Override
protected void process(List<Integer> chunks) {
if (chunks.isEmpty())
return;
int progress = chunks.get(chunks.size() - 1);
view.setProgress(progress);
}
#Override
protected void done() {
try {
get(); // To catch exceptions happend in background
if (onDone != null)
onDone.run();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
}
}
Here is a preview:
I put the counter code in a separate thread so it would run separately from the Event Dispatch Thread that all Swing components must be created and executed.
Here's the complete runnable code.
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class TimerTestPanel extends JPanel {
private static final long serialVersionUID = 340444475514103360L;
private JLabel myLabel;
private JButton myButton;
public TimerTestPanel() {
initComponents();
}
private void initComponents() {
myLabel = new JLabel("Ready");
myButton = new JButton("Start");
myButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent event) {
myActionListenter();
}
});
add(myLabel);
add(myButton);
}
private void myActionListenter() {
myLabel.setText("Starting count ...");
new Thread(new Counter()).start();
}
public static final void main(String argv[]) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TimerTestPanel());
frame.pack();
frame.setVisible(true);
}
});
}
public class Counter implements Runnable {
#Override
public void run() {
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
myLabel.setText("Finishing count ...");
}
});
}
}
}
I think I can use an invokeLater to perform the process, so the ActionListener can change the label without waiting de long process to be finished, but I don't if it is the best solution.
package test;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class MyTestPane extends JPanel{
/**
*
*/
private static final long serialVersionUID = 340444475514103360L;
private JLabel myLabel;
private JButton myButton;
public MyTestPane() {
initComponents();
}
private void initComponents() {
myLabel = new JLabel("Ready");
myButton = new JButton("Start");
myButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
myActionListenter();
}
});
add(myLabel);
add(myButton);
}
private void myActionListenter(){
myLabel.setText("Starting count ...");
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
try{
Thread.sleep(10000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
myLabel.setText("Finishing count ...");
}
});
}
public static final void main(String argv[]) {
JFrame frame = new JFrame();
frame.add(new MyTestPane());
frame.pack();
frame.setVisible(true);
}
}

Using SwingWorker to update the progress of a thread from another class

Value is updated by the end, but not during the process. I guess the reason is that the publish method locates outside the loop.
As to invoke PropertyChangeListener, can it be achieved by defining the class without extends SwingWorker<Void, Void> ?
To address the question in another way, I have two threads locate in two different classes. Is it possible to build communication between the two using SwingWorker?
Class Application
import javax.swing.JFrame;
public class Application {
public static void main(String[] args){
javax.swing.SwingUtilities.invokeLater(new Runnable(){
#Override
public void run() {
JFrame frame = new Progress();
frame.setSize(200, 300);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
});
}
}
Class Progress
import java.awt.BorderLayout;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.SwingWorker;
#SuppressWarnings("serial")
public class Progress extends JFrame{
private JProgressBar progressbar;
public Progress(){
JButton button = new JButton("Run");
progressbar = new JProgressBar(0, 100);
progressbar.setValue(0);
progressbar.setStringPainted(true);
JPanel panel = new JPanel();
panel.add(button);
panel.add(progressbar);
add(panel, BorderLayout.PAGE_START);
button.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent event){
start();
}
});
}
private void start(){
progressbar.setValue(0);
SwingWorker<Void, Integer> worker = new SwingWorker<Void, Integer>(){
#Override
protected Void doInBackground() throws Exception {
Simulation sim = new Simulation();
publish(sim.getCount());
return null;
}
#Override
protected void process(List<Integer> chunks) {
Integer progress = chunks.get(chunks.size()-1);
progressbar.setValue(progress);
progressbar.repaint();
progressbar.update(progressbar.getGraphics());
}
#Override
protected void done() {
Toolkit.getDefaultToolkit().beep();
}
};
worker.execute();
}
}
Class Simulation
public class Simulation {
private int count;
public Simulation(){
for(int i=0; i<100; i++){
count++;
System.out.println(count);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public int getCount(){
return count;
}
}
How can I update the value during the process?
You need to implement PropertyChangeListener on your SwingWorker and listen to the Progress property changes and then from overridden propertChange() method update your JProgressBar.
Here is what you are looking.
Hope this helps.

using a jpanel in another class? (noob issue)

I have the classes:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class MyTimer extends JFrame {
static JButton quitButton;
static JButton quit_2Button;
public MyTimer() {
initUI();
}
public static void random(){
int x = (int)(Math.random() * 100 + 1);
int y = (int)(Math.random() * 150 + 1);
System.out.println(x);
System.out.println(y);
quitButton = new JButton("Press this to quit?");
quitButton.setBounds(x, y, 200, 20);
}
public void initUI() {
random();
JPanel panel = new JPanel();
getContentPane().add(panel);
panel.setLayout(null);
JButton quit_2Button = new JButton("Or maybe press this to quit!");
quit_2Button.setBounds(50, 100, 200, 20);
quit_2Button.setRolloverEnabled(true);
quit_2Button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent event) {
System.exit(0);
}
});
quitButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent event) {
finder.main(null);
}
});
panel.add(quitButton);
panel.add(quit_2Button);
setTitle("Java");
setSize(300, 200);
setLocationRelativeTo(null);
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
}
, and
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class timer_run {
ActionListener al = new ActionListener() {
public void actionPerformed(ActionEvent e) {
panel.remove(); //error here
}
};
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
MyTimer ex = new MyTimer();
ex.setVisible(true);
new timer_run();
}
});
}
public timer_run() {
Timer timer = new Timer(1000, al);
timer.start();
}
}
and I am trying to make it so that when actionlistener al is activated, jpanel.remove(quit_2Button) is called. The problem is, that whenever i try and do this it gives me the error "panel cannot be resolved". I assume the reason this is happening is because timer_run does not have access to the JPanel frame, but through various trial and error have not been able to fix this. Does anybody have any suggstions about what I could possibly do to let timer_run see the JPanel frame? thanks in advance, and please note I am a java noob, so make the answer as simple as you can, if possible?
Recommendations:
One class should not try to directly manipulate variables of another class.
Instead have one class call public methods of the other class.
To allow your second class to do that, you need to pass a reference of the first class's object into that of the second class. I suggest you do this via a constructor parameter.
For example
// class name should begin with an upper case letter
public class TimerRun {
private MyTimer myTimer;
public TimerRun(MyTimer myTimer) {
this.myTimer = myTimer;
}
//....
}
Now the TimerRun class can call public methods on the MyTimer object held by myTimer by calling, myTimer.someMethod()

Calling one JFrame from another using Timer without any buttons

Calling one JFrame from another using Timer without any buttons: time is decreased then open another JFrame without any buttons. Please help. Used in netbeans
Your question is very unclear, but the use of multiple frames is not recommended. As an alternative, consider a modeless dialog, shown below. The dialog's enclosed JOptionPane listens for a PropertyChangeEvent, while counting down from TIME_OUT using javax.swing.Timer. The JOptionPane button is convenient but not required.
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.Timer;
/**
* #see https://stackoverflow.com/a/12451673/230513
*/
public class JOptionTimeTest implements ActionListener, PropertyChangeListener {
private static final int TIME_OUT = 10;
private int count = TIME_OUT;
private final Timer timer = new Timer(1000, this);
private JDialog dialog = new JDialog();
private final JOptionPane optPane = new JOptionPane();
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
new JOptionTimeTest().createGUI();
}
});
}
private void createGUI() {
JFrame frame = new JFrame("Title");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
timer.setCoalesce(false);
optPane.setMessage(message());
optPane.setMessageType(JOptionPane.INFORMATION_MESSAGE);
optPane.setOptionType(JOptionPane.DEFAULT_OPTION);
optPane.addPropertyChangeListener(this);
dialog.add(optPane);
dialog.pack();
frame.add(new JLabel(frame.getTitle(), JLabel.CENTER));
frame.pack();
frame.setVisible(true);
dialog.setLocationRelativeTo(frame);
dialog.setVisible(true);
timer.start();
}
public void propertyChange(PropertyChangeEvent e) {
String prop = e.getPropertyName();
if (JOptionPane.VALUE_PROPERTY.equals(prop)) {
thatsAllFolks();
}
}
public void actionPerformed(ActionEvent e) {
count--;
optPane.setMessage(message());
if (count == 0) {
thatsAllFolks();
}
timer.restart();
}
private String message() {
return "Closing in " + count + " seconds.";
}
private void thatsAllFolks() {
dialog.setVisible(false);
dialog.dispatchEvent(new WindowEvent(
dialog, WindowEvent.WINDOW_CLOSING));
}
}

how to update a jLabel every time with a while loop with a delay

private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
int count = jSlider1.getValue();
int delay = jSlider2.getValue();
int valueOfSlider = jSlider2.getValue();
int valueOfSlider2 = jSlider1.getValue();
while (count > 0)
{
count--;
String count2 = ""+count;
jLabel3.setText(count2);
try {Thread.sleep(delay); }
catch (InterruptedException ie) { }
}
It will eventually show the final number on the jLabel but it does not incrementally update the number. any help
Swing is single-threaded. Therefore, long-running tasks should never take place in the EDT. This includes sleeping. Instead, use a javax.swing.Timer. This will delay in a background thread, and then post an action to be executed in the EDT.
See also:
How to Use Swing Timers
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public final class JLabelUpdateDemo {
public static void main(String[] args){
SwingUtilities.invokeLater(new Runnable(){
#Override
public void run() {
createAndShowGUI();
}
});
}
private static void createAndShowGUI(){
final JFrame frame = new JFrame("Update JLabel Demo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().setLayout(new FlowLayout());
frame.getContentPane().add(JTimerLabel.getInstance());
frame.setSize(new Dimension(275, 75)); // used for demonstration purposes
//frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
Timer t = new Timer(1000, new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
int val = Integer.valueOf(JTimerLabel.getInstance().getText());
JTimerLabel.getInstance().setText(String.valueOf(++val));
}
});
t.start();
}
private static final class JTimerLabel extends JLabel{
private static JTimerLabel INSTANCE;
private JTimerLabel(){
super(String.valueOf(0));
setFont(new Font("Courier New", Font.BOLD, 18));
}
public static final JTimerLabel getInstance(){
if(INSTANCE == null){
INSTANCE = new JTimerLabel();
}
return INSTANCE;
}
}
}
This SSCCE imitates a counter that will count up from 0 every second (i.e. update the JLabel instance) until the application is terminated.
Your problem is that your doing something time consuming in an ActionPerformed callback, which executes in the event thread. In callbacks, you should do something quickly and return, even if that "something" is spawning a thread. The GUI can't update while you're occupying the event thread, it will only update after your callback returns.

Categories

Resources