Part of my function looks like this
jLabel2.setBackground(Color.YELLOW);
jLabel2.setText("Status : Idle");
boolean ok=cpu21.RestartSSH();
if(ok){
jLabel2.setBackground(Color.GREEN);
jLabel2.setText("Status : Run");
}
Before I enter in function label is Green and Run, but when I come in function it doesn't chabge color to Yellow ( function RestartSSH is executing 5-6 sec, but during that time labels doesn't change colors and captures ). Where I make mistake in painting ?
Make your JLabel opaque so you can set its background colour.
Perform RestartSSH in a separate thread, or your GUI won't respond to events.
Example:
final JLabel jLabel2 = new JLabel("HELLO");
jLabel2.setOpaque(true);
jLabel2.setBackground(Color.YELLOW);
jLabel2.setText("Status : Idle");
//perform SSH in a separate thread
Thread sshThread = new Thread(){
public void run(){
boolean ok=cpu21.RestartSSH();
if(ok){
//update the GUI in the event dispatch thread
SwingUtilities.invokeLater(new Runnable() {
public void run() {
jLabel2.setBackground(Color.GREEN);
jLabel2.setText("Status : Run");
}
});
}
}
};
sshThread.start();
(Update: added call to SwingUtilities.invokeLater)
JLabels is opaque by default, so their's background isn't painted by default. Try with:
jLabel2.setOpaque(true);
or maybe you have to call repaint after changing the color:
jLabel2.repaint();
I suspect that your restartSSH() method is blocking the event dispatch thread. One approach is to use a SwingWorker, as suggested in this example. This would allow you to show the progress of the restart process and set the label appropriately when done.
Related
I originally was attempting to update a JFrame and JPanel several times while in a Java Action Listener, but both would only update when the Action Listener completed all its tasks. Here is the link to my original question (Refreshing a JFrame while in an Action Listener).
I was told in the feedback to that question that Swing Worker should solve my problems. However, when I implemented Swing Worker (as seen below), nothing changed. The JFrame and JPanel still updated only when the Action Listener completed all tasks. My question is, am I missing something below? If not, how can I implement this in an Action Listener to properly update the Frame and Panel timely?
#Override
protected Integer doInBackground() throws Exception{
//Downloads and unzips the first video.
if(cameraBoolean==true)
panel.add(this.downloadRecording(camera, recording));
else
panel.add(new JLabel("Could not contact camera "+camera.getName()));
panel.repaint();
jframe.repaint();
return 1;
}
private JLabel downloadRecording(Camera camera, Recording recording){
//does a bunch of calculations and returns a jLabel, and works correctly
}
protected void done(){
try{
Date currentTime = new Timestamp(Calendar.getInstance().getTime().getTime());
JOptionPane.showMessageDialog(jframe, "Camera "+camera.getName()+" finished downloading at "+currentTime.getTime());
}catch (Exception e){
e.printStackTrace();
}
}
You have a misundertanding about how SwingWorker works. This class is intended to provide a way to update the GUI while heavy tasks are being performed. All of this is because Swing components updates take place in the Event Dispatch Thread (a.k.a. EDT) which is a particular thread.
For instance, if you click a button and perform a time consuming task all in the EDT, then this thread will block untill this task finishes. Consequently, you'll see your GUI is frozen.
Keeping this in mind, doInBackground() method runs in another different thread that's not the EDT which is ok. So don't call any Swing method in there:
protected Integer doInBackground() throws Exception{
//Downloads and unzips the first video.
if(cameraBoolean==true) // just use if(cameraBoolean), since this is a boolean
panel.add(this.downloadRecording(camera, recording)); // NO!
else
panel.add(new JLabel("Could not contact camera "+camera.getName())); //NO!
panel.repaint(); //NO, never!
jframe.repaint();//NO, never!
return 1;
}
Add a JLabel to this panel before executing your SwingWorker and update its text using publish() and process() methods instead:
JPanel panel = new JPanel();
final JLabel progressLabel = new JLabel("Some text before executing SwingWorker");
panel.add(progressLabel);
SwingWorker<Integer, String> worker = new SwingWorker<Integer, String>() {
#Override
protected Integer doInBackground() throws Exception {
if(cameraBoolean){
pubish("Starting long process...");
//Some processing here
publish("Intermediate result to be published #1");
//Some other processing stuff
publish("Intermediate result to be published #2");
//And so on...
return 0;
} else {
publish("Could not contact camera "+camera.getName());
return -1;
}
}
#Override
protected void process(List<String> chunks) {
for(String string : chunks){
progressLabel.setText(string);
}
}
#Override
protected void done() {
progressLabel.setText("Finished!!!");
}
};
worker.execute();
Both process() and done() methods take place in the EDT so it's safe make GUI updates there. Take a look to this excelent example: Swing Worker Example for more details.
Maybe because you repaint your panel/frame just when the synchronous call this.downloadRecording(camera, recording) is finished?
Try to only put this call into the doInBackground() method, because (so I guess) that's the one that takes a long time and for all this time the JFrame gets not refreshed.
You can't update UI in next way:
panel.repaint();
jframe.repaint();
In your doInBackground method you must to call publish(V... chunks) method, that Sends data chunks to the process(java.util.List<V>) method.(according docs) and than in method process(List<V> chunks) you can update your UI(according docs process method - Receives data chunks from the publish method asynchronously on the Event Dispatch Thread.). SwingWorker docs.
So, override process method for updating, and call publish method.
Also you can use Executors for background processes. In this case your UI will be working in EDT and your background process in another thread. Example:
Executors.newSingleThreadExecutor().execute(new Runnable() {
#Override
public void run() {
// run background process
}
});
EDIT: good example of SwingWorker
I have a Window Listener on the main JFrame of my application. I also have a button listener on a button within the application. I used this as the pattern for the button listener:
good.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ev) {
// We're going to do something that takes a long time, so we
// spin off a thread and update the display when we're done.
Thread worker = new Thread() {
public void run() {
// Report the result using invokeLater().
SwingUtilities.invokeLater(new Runnable() {
public void run() {
for(int i=0; i<1000000; i++){
System.out.println("foo");
}
}
});
}
};
worker.start(); // So we don't hold up the dispatch thread.
}
});
When I click on the button, I see the printout for each iteration, but the window listener is not triggered until after the loop has finished executing. The print 'foo' is just there to simulate something that takes a while, and I want the listener to be triggered as soon as the window event occurs (which could be somewhere in the middle of the run() method execution), but it seems like it's not being triggered until the end of the for loop.
Any idea why?
Swing is single-threaded - the Event Dispatch Thread (EDT) manages painting and events for Swing. Any long running tasks placed on the EDT will prevent the EDT from doings tasks until the long running task is complete (in other words, the UI will seem like it has locked up). Here, your code dispatches the long running process (the for loop) onto the EDT using SwingUtilities - if you have a long running task and want a responsive Swing UI during the process, then place your long running task into it's own Thread, or use a SwingWorker.
So, all this...
// Report the result using invokeLater().
SwingUtilities.invokeLater(new Runnable() {
public void run() {
for(int i=0; i<1000000; i++){
System.out.println("foo");
}
}
});
Will do is, is cause the loop to be executed within the context of the Event Dispatching Thread, which will prevent the EDT from processing the event queue, including paint request.
In your case, it might be easier to use a SwingWorker which you can use to publish/process information from the background thread to the EDT as well as supports progress notification
See How to use SwingWorker for more details
I am making my first GUI, for a project of mine. I made the first page, login for it.
Now I have a login button which should let the controller know when it's clicked and pass the username and password, but I didn't design the LoginButtonListener for that yet, just the clicking.
Now I have this MouseListener I made named LoginButtonListener which implements MouseListener, and this is my overwritten mouseEntered method, which should fadeIn the login button:
#Override
public void mouseEntered(MouseEvent arg0) {
Timer timer = new Timer();
final LoginButtonListener b = this;
timer.schedule(new TimerTask(){
private int alpha = 0;
#Override
public void run() {
alpha += 5;
synchronized(b) {
b.button.setBackground(new Color(255, 255, 255, alpha));
}
if (alpha >= 255) {
this.cancel();
}
}
}, 0, 50);
}
as you see I synchronize the b variable because the timer I am using is java.util.Timer, Timer is a own thread, so I think synchronization is required here.
Did I synchronize it correctly? or is what I am doing is wrong?
This is what happens in mouseEntered:
(source: gyazo.com)
You did not do it right.
Never ever touch swing components from thread other than message dispatcher thread.
To do so, put code that access swing in SwingUtilities.invokeLater(Runnable) (or invokeAndWait).
You don't need (explicit) synchronization to govern acces to swing components, because you actually work with them ONLY through one thread. Other threads just register to some queue (done through SwingUtilities) task you want to perform with GUI.
Swing is a single threaded environment, you've got the right idea, but you've implemented slightly incorrectly.
Instead of java.util.Timer, you really should be using javax.swing.Timer, which will raise it's ticks within the context of the Event Dispatching Thread, making it safe to update the UI components from within, for example...
public void mouseEntered(MouseEvent arg0) {
javax.swing.Timer timer = new javax.swing.Timer(50, new ActionListener() {
public void actionPerformed(ActionEvent evt) {
alpha += 5;
if (alpha >= 255) {
alpha = 255;
((javax.swing.Timer)evt.getSource()).stop();
}
b.button.setBackground(new Color(255, 255, 255, alpha));
}
});
}
Take a look at How to use Swing Timers and Concurrency in Swing for more details
You should also be careful changing the alpha of the background color of any opaque component, Swing only knows how to paint opaque or transparent components, it can't deal with translucent components. To do that correctly, you need to take over the control of the background painting...
I have a problem while creating a JProgressBar which is set to indeterminate.
The following code is my implementation of the JProgressBar and is called/constructed from another class:
public class Progress implements Runnable
{
private JFrame frameProgress;
private JProgressBar progressBar;
public Progress(String title, String message)
{
try
{
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
}
catch (Exception e)
{
GlobalVariables.LOGGING_logger.error("Error instatiating progress bar.",
e);
}
UIManager.put("ProgressBar.selectionForeground", Color.black);
UIManager.put("ProgressBar.selectionBackground", Color.black);
this.frameProgress = new JFrame(title);
this.frameProgress.setIconImage(GlobalVariables.GUI_icon.getImage());
this.frameProgress.setSize(300, 60);
this.frameProgress.setLocation(16, 16);
this.progressBar = new JProgressBar();
this.progressBar.setStringPainted(true);
this.progressBar.setString(message);
this.progressBar.setIndeterminate(true);
this.frameProgress.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.frameProgress.add(this.progressBar);
this.frameProgress.setResizable(false);
this.frameProgress.setVisible(true);
}
public void start()
{
new Thread(this).start();
}
public void close()
{
this.frameProgress.dispose();
this.frameProgress = null;
this.progressBar = null;
}
#Override
public void run()
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
// do nothing, because progress bar is indeterminate
}
});
}
}
The caller of this JProgressBar is the following code snippet:
Progress p = new Progress("bla", "blub");
p.start();
boolean successfull = xmlWriter.writeCommonSettingsFromGUI(this);
p.close();
And now i want, while the xmlWriter.writeCommonSettingsFromGUI(this); is doing something, that the JProgressBar is shown to the user and is working while the algorithm is running.
How can I achieve this? I don't know so much about threading and searched in many other forums, but I don't found any answer for my question.
Please help me and thank you in advance ;)
EDIT:
The Progress JFrame opens up with no content for that time, the algorithm is running.
You are probably facing concurrency issues with Swing. Assuming that the following code runs on the EDT (Event Dispatching Thread):
Progress p = new Progress("bla", "blub");
eventually, this will open a JFrame with a progress bar in it.
I would consider using a JDialog instead of a JFrame
I would not force the size of the JFrame, but rather call pack()
Then, still running on the EDT (and thus blocking all UI-events such as repaint, mouse clicks, etc...), you call p.start() which starts a new Thread() which will invoke run() which itself calls
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
// do nothing, because progress bar is indeterminate
}
});
This basically won't do anything except push an additional event on the EventQueue and it will run after all currently pending events. This event will run... "nothing" since your Runnable is just empty. The new Thread dies almost immediately. So all this code is useless.
Still pursuing on the EDT, you call boolean successfull = xmlWriter.writeCommonSettingsFromGUI(this); (btw, "successful" ends with only one 'l'). This will continue on blocking the EDT, preventing repaints from occurring and preventing the JProgressBar from painting itself. Eventually you will dispose the JFrame but since all this code is running on the EDT, the user will not see much of the progress bar and the UI will look frozen.
Consider reading the Swing tag wiki (especially the very last part with 3 important links).
Using a SwingWorker should help you out in this.
I am making a game having squares in it (a grid of panels) and when the game ends there is an algorithm that changes the color of the panels one by one in a "live" fashion where the user watches the squares change color slowly. I try something like:
Thread.sleep(1000);
grid.getComponent(boxNumber).setBackground(Color.YELLOW);
Thread.sleep(1000);
grid.getComponent(boxNumber).setBackground(Color.ORANGE);
Although the color of a box changes to Yellow, it does not change to Orange afterwards. Anyone have any ideas? Hope I was able to be clear.
Read the section from the Swing tutorial on Concurrency to understand why you should not be using the sleep() method.
One solution is to use a SwingWorker, then you can 'publish' the color of the component so it can be updated properly on the EDT and you can invoke the sleep() method in the worker as well.
These need to happen on the Swing event thread. call set background via:
SwingUtilities.invokeLater(new Runnable() {
public void run() {
grid.getComponent(boxNumber).setBackground(Color.ORANGE);
}
});
Note, your Thread.sleep() should not be in the event thread (or directly from within a Swing event listener (ActionListener, WindowListener, etc).
It would also be prudent to look the Swing Timing Framework which is specifically for things like this.
-Generally its not a good idea to do Thread.sleep(1000); in the EDT. You should try using Timers.
-You also need to call revalidate()/validate() and repaint() afterward.
So maybe something like this:
Timer yellowTimer = new Timer(1000,new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
jtp.setBackground(Color.YELLOW);
//call revalidate()/validate() and repaint() afterward
jtp.revalidate();
jtp.repaint();
}
});
yellowTimer.setRepeats(false);
Timer orangeTimer = new Timer(2000,new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
jtp.setBackground(Color.ORANGE);
//call revalidate()/validate() and repaint() afterward
jtp.revalidate();
jtp.repaint();
}
});
orangeTimer.setRepeats(false);
yellowTimer.start();
orangeTimer.start();