My code as below is not working, can anyone tell me why? Please also correct my code, I am very new to Java. Besides that, I am searching for the "loading panel component", something like ProgressMonitor but maybe more attractive and which animates better. Please suggest me if anyone has used such things before.
public class Main extends JFrame {
private JPanel contentPane;
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
Main frame = new Main();
frame.setVisible(true);
ProgressMonitor pm = new ProgressMonitor(frame, "Loading...",
"waiting...",
0, 100000);
for (int i = 0 ; i < 100000 ; i ++){
pm.setProgress(i);
pm.setNote("Testing");
System.out.println(i);
Thread.sleep(1000);
}
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the frame.
*/
public Main() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 450, 300);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
contentPane.setLayout(new BorderLayout(0, 0));
setContentPane(contentPane);
}
}
As #Mark Rotteveel already indicated, you are keeping the EDT (Event Dispatch Thread) occupied. The tutorial on 'How to show progress bars/monitors' contains valuable information and code samples.
But basically it comes down to moving your calculations to a worker Thread (e.g. using a SwingWorker), and showing the ProgressMonitor on the EDT. It is up to the worker thread to indicate to the ProgressMonitor what progress has already been made.
And here is a direct link to the sample code of that tutorial which clearly shows how the work is done in the SwingWorker extension (the Task class in that example), and how the ProgressMonitor gets updated by adding a PropertyChangeListener to the SwingWorker, where the listener passes the progress to the ProgressMonitor.
I would also suggest to read the Concurrency in Swing tutorial which contains more information on how to handle Threads in combination with Swing, and why you can't/shouldn't do heavy calculations on the EDT
In Swing, the event-thread is what modifies and updates the GUI. You are keeping the event-thread busy with that for-loop and sleep, so it cannot update the GUI. All things you do on the event-thread should be short-lived. Everything else should be moved off the event-thread.
So you need to move that for-loop out of the event-thread.
1. Consider this as the rule of thumb, UI work on UI thread, and Non-UI work on Non-UI thread.
2. Event Dispatcher Thread (EDT) is the UI thread here, and so you should keep your Non-UI process intensive work on a separate thread OUT of the EDT.
3. You can do this in 2 ways.....
i. Create a separate Thread to do this.
ii. Use SwingWorker to synchronize the Non-UI and the UI thread.
4. Always keep the main() method only for making the JFrame visible in the EDT.
eg:
public static void main(String[] args){
EventQueue.invokeLater(new Runnable(){
public void run(){
myframe.setVisible(true);
}
}
}
Take a look at this site for the working example of SwingWorker:
http://www.kodejava.org/examples/381.html
Related
When I try to use any kind of delay on my code the program gets delayed but the JLabel that was put before the delay gets updated after the program ends.
I want the program to:
update the JLabel on the GUI
wait for 5 seconds
update the JLabel again with diferent text
wait another 5 seconds
I have tried with timers, invokelater, invokeandwait, thread.sleep and others.
The problems is that the GUI does get delayed at the right spot but the GUI does not update the JLabel ath place where the code is located. The JLabel gets updated after the program ends.
I want the user to be able to read the text for 5 seconds then read another text for another 5 seconds in order. I do not want the program to run the gui pause at a cetain spot then at end just update the JLabel. I want the gui to get updated before the delay. I do not want the same thing that when I used a timer to happen where I type in a setText for the JLabel before the Timer is typed and then when I run the program the timer works but the JLabel gets updated after the delay(It is not what I want).
The way to achieve that is either using a Swing Worker, either a a Swing Timer. A Swing worker runs a task in background and at the same time it is capable of publishing GUI changes in the Event dispatch thread (the thread where the GUI runs). A SwingTimer can be considered as a simplified version of a Swing Worker, that only runs a task after some time in the Event Dispatch Thread. (Consider a worker that sleeps the thread in background, and after the sleep, it runs the task in the Gui thread).
There are a lot of examples online, like this one and this one.
If you do not want to do something in background, a Timer solution sounds "simpler". Take a look at How can I pause/sleep/wait in a java swing app?
An example where it is closer to what you need (with a worker):
public class WorkerExample extends JFrame {
private static final long serialVersionUID = 6230291564719983347L;
private JLabel label;
public WorkerExample() {
super("");
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLayout(new FlowLayout());
SwingWorker<Void, String> worker = new SwingWorker<Void, String>() {
#Override
protected Void doInBackground() throws Exception {
publish("Started....");
Thread.sleep(1500);
for (int i = 0; i < 5; i++) {
publish("Number of iterations: " + i);
//Do something
Thread.sleep(3500);
}
return null;
}
#Override
protected void process(List<String> chunks) {
String chunk = chunks.get(0);
label.setText(chunk);
}
#Override
protected void done() {
label.setText("Done.");
}
};
JButton button = new JButton("Start");
button.addActionListener(e -> worker.execute());
add(button);
label = new JLabel("Nothing yet.");
add(label);
setSize(400, 400);
setLocationByPlatform(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
new WorkerExample().setVisible(true);
});
}
}
You can experiment with these in order to understand how a SwingWorker works, but I strongly recommend you to read concurrency in Swing before doing that.
I understand that EventQueue.invokeLater() is a function called to make the Java Swing components Thread-Safe.
Also, I know that the argument to this function is an object with implements Runnable.
However, I am unable to understand the syntax for this function call, i.e. this call -
EventQueue.invokeLater(()-> {
new Screen();
});
Here, Screen() is a class that extends JFrame.
public class Screen extends JFrame
{
Screen()
{
setSize(1000, 1000);
JPanel j1 = new Board();
j1.setBounds(0,0,500, 500);
JPanel j2 = new DiceModel();
j2.setBounds(500, 0, 500, 500);
add(j1);
add(j2);
setLayout(null);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLocationRelativeTo(null);
setVisible(true);
}
public static void main(String[] args)
{
EventQueue.invokeLater(()-> {
new Screen();
});
}
}
This code runs as expected.
Board and DiceModel are two classes I have defined that which extend JPanel.
The invocation
EventQueue.invokeLater( new Screen() );
gives the expected error that Screen is not an object of type Runnable.
So,my question is, what is the meaning of the syntax for the function call for invokeLater() ?
Is it a kind of anonymous function call in Java ?
The complete Swing processing is done in a thread called EDT (Event Dispatching Thread). Therefore you would block the GUI if you would compute some long lasting calculations within this thread.
The way to go here is to process your calculation within a different thread, so your GUI stays responsive. At the end you want to update your GUI, which have to be done within the EDT. Now EventQueue.invokeLater comes into play. It posts an event (your Runnable) at the end of Swings event list and is processed after all previous GUI events are processed.
Also the usage of EventQueue.invokeAndWait is possible here. The difference is, that your calculation thread blocks until your GUI is updated. So it is obvious that this must not be used from the EDT.
Still there is Java code out there that starts a JFrame simple from the main thread. This could cause issues, but is not prevented from Swing. Most modern IDEs now create something like this to start the GUI
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new NewJFrame().setVisible(true);
}
});
}
NOTE: I work a lot of hours and research google and stackoverflow but I cannot find answer.
I use Thread.sleep() in a JDialog and it freezes all other JDialog, JFrame and threads.
My example code:
public Guitest()
{
setSize(300,300);
// create a JDialog that make guitest wait
MyDialog dlg = new MyDialog();
dlg.setSize(100,100);
dlg.setVisible(true);
while(dlg.isWait())
{
try
{
Thread.sleep(1000);
} catch (InterruptedException ex)
{
Logger.getLogger(Guitest.class.getName()).log(Level.SEVERE, null, ex);
}
System.out.println("waiting mydialog");
}
}
class MyDialog extends JDialog
{
boolean wait = true;
JButton btn = new JButton("OK");
public MyDialog()
{
setSize(50,50);
btn.addActionListener(new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
wait=false;
}
});
add(btn);
}
public boolean isWait()
{
return wait;
}
}
In this situation JDialog does not appear correctly:
inccorect appear jdialog
But it must be apper this:
true appear jdialog
How can I solve this problem. I want to make main thread wait another thread.And someone can correct my sample code or share a sample code with this situation.
IMHO, it appears like you have just one running thread. At first, we draw our JDialog, after that, you sleep your main thread because of the wait flag.
ie. you can't execute your button action listener, thus you can't awake your thread.
Hope it helps understanding.
Thread.Sleep() just sleeps the current thread (i.e. stops it from doing anything, such as redrawing, processing clicks etc), which in your case is the UI thread.
You need to use a worker thread. Any main work that needs to be done that may take a larger amount of time needs to be done in its own thread, and this is the thread that you will want to sleep. It is currently ran alongside the UI components and so this is why you're seeing them freezing.
A good reference is the documentation for concurrency for swing http://docs.oracle.com/javase/tutorial/uiswing/concurrency/
The following may be useful too:
http://java.sun.com/developer/technicalArticles/Threads/swing/
http://java.sun.com/products/jfc/tsc/articles/threads/threads1.html
I'm reading a media with vlcj and I would like to display the time elapsed and the remaining time in some JLabels. I wrote some code but it seems my JLabel.setText don't refresh more that 2 times per second.
To make some more try and being sure that it's not the vlcj's thread that would have some troubles, I wrote a very code with a JLabel. The aim of this simple code is to update the JLabel 10 times per seconds.
Here is my code :
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import javax.swing.JFrame;
import javax.swing.JLabel;
public class TestLabel extends JFrame implements Runnable{
JLabel label = new JLabel("0");
int i=0;
TestLabel() {
this.setTitle("Test");
this.setSize(200, 200);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setContentPane(label);
this.setVisible(true);
}
public static void main(String[] args) {
TestLabel tLabel = new TestLabel();
Thread t1 = new Thread(tLabel);
t1.start();
}
#Override
public void run() {
ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
scheduler.scheduleAtFixedRate(new Runnable() {
#Override
public void run() {
i+=1;
System.out.println(i);
label.setText(String.valueOf(i));
}
}, 0, 100, TimeUnit.MILLISECONDS);
}
}
Result : in the console, I get 1, 2, 3, 4... But in the JLabel, I have something like : 1, 2, 3 (...) 32, 37, 42, 47. It seems that the System.out.println write each "i", but the JLabel don't. Why do I have this artefact ?
Thank you for all your reply. Regards.
Thou shalt not use swing components from a thread other than the event dispatch thread.
So, either use a Swing Timer rather than a ScheduledExecutorService, or wrap the label change into SwingUtilities.invokeLater().
BTW, the call to new TestLabel(); should also be wrapped into SwingUtilities.invokeLater(). Read the swing concurrency tutorial.
You need to call the SwingUtilities.invokeLater method to properly update your GUI text while using Swing (i.e. JFrame, JLabel, etc).
public void run() {
i+=1;
System.out.println(i);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
label.setText(String.valueOf(i));
}
});
}
For more information about SwingUtilities.invokeLater, check out this SO post.
Don't use a ScheduledExecutorService. Swing components need to be updated on the Event Dispatch Thread (EDT).
Instead you should be using a Swing Timer. Read the section from the Swing tutorial on How to Use Timers for more information.
Here is a simple example using a Timer: How to make JScrollPane (In BorderLayout, containing JPanel) smoothly autoscroll. Just change the interval to 100, instead of 1000.
When a user clicks a button, a long task of approximately 10 seconds will run. During this time I want to show a progress bar to the user. But the main thread has to wait for the worker thread to finish because the worker thread will set a variable that the main thread will use. If I don't wait the worker thread I will get a NullPointerException when using the variable. So after the worker thread finishes, I will also close the progress bar dialog.
When I wait for the worker thread using join() the progress bar dialog shows (interestingly without the progress bar though) and hangs there.
Thread runnable = new Thread() {
public void run() {
try {
System.out.println("thread basladi");
threadAddSlaveReturnMessage = request.addSlave(
ipField.getText(), passField.getText(),
nicknameField.getText());
System.out.println("thread bitti");
} catch (LMCTagNotFoundException e) {
e.printStackTrace();
}
}
};
Thread runnable_progress = new Thread() {
public void run() {
JTextArea msgLabel;
JDialog dialog;
JProgressBar progressBar;
final int MAXIMUM = 100;
JPanel panel;
progressBar = new JProgressBar(0, MAXIMUM);
progressBar.setIndeterminate(true);
msgLabel = new JTextArea("deneme");
msgLabel.setEditable(false);
panel = new JPanel(new BorderLayout(5, 5));
panel.add(msgLabel, BorderLayout.PAGE_START);
panel.add(progressBar, BorderLayout.CENTER);
panel.setBorder(BorderFactory.createEmptyBorder(11, 11, 11, 11));
dialog = new JDialog(Frame.getFrames()[0], "baslik", true);
dialog.getContentPane().add(panel);
dialog.setResizable(false);
dialog.pack();
dialog.setSize(500, dialog.getHeight());
dialog.setLocationRelativeTo(null);
dialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
dialog.setAlwaysOnTop(false);
dialog.setVisible(true);
msgLabel.setBackground(panel.getBackground());
}
};
runnable.start();
System.out.println("runnable start");
runnable_progress.start();
System.out.println("progress start");
runnable.join();
System.out.println("runnable join");
runnable_progress.join();
System.out.println("progress join");
if (threadAddSlaveReturnMessage.equalsIgnoreCase("OK")) {
fillInventoryTable(inventoryTable);
JOptionPane.showMessageDialog(this, messages.getString("centrum.addslavepanel.SUCCESS"), null, JOptionPane.INFORMATION_MESSAGE);
}
"progress join"
doesn't get printed.
You can use a SwingWorker here. A short example :
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package org.godel.nio;
import java.awt.BorderLayout;
import java.util.List;
import javax.swing.*;
/**
*
* #author internet_2
*/
public class Test {
public static void main(String[] args) {
new Test().doJob();
}
public void doJob() {
JTextArea msgLabel;
JProgressBar progressBar;
final int MAXIMUM = 100;
JPanel panel;
progressBar = new JProgressBar(0, MAXIMUM);
progressBar.setIndeterminate(true);
msgLabel = new JTextArea("deneme");
msgLabel.setEditable(false);
panel = new JPanel(new BorderLayout(5, 5));
panel.add(msgLabel, BorderLayout.PAGE_START);
panel.add(progressBar, BorderLayout.CENTER);
panel.setBorder(BorderFactory.createEmptyBorder(11, 11, 11, 11));
final JDialog dialog = new JDialog();
dialog.getContentPane().add(panel);
dialog.setResizable(false);
dialog.pack();
dialog.setSize(500, dialog.getHeight());
dialog.setLocationRelativeTo(null);
dialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
dialog.setAlwaysOnTop(false);
dialog.setVisible(true);
msgLabel.setBackground(panel.getBackground());
SwingWorker worker = new SwingWorker() {
#Override
protected void done() {
// Close the dialog
dialog.dispose();
}
#Override
protected void process(List chunks) {
// Here you can process the result of "doInBackGround()"
// Set a variable in the dialog or etc.
}
#Override
protected Object doInBackground() throws Exception {
// Do the long running task here
// Call "publish()" to pass the data to "process()"
// return something meaningful
return null;
}
};
worker.execute();
}
}
Edit : "publish()" should be called in "doInBackground()" to pass the data to "process()".
you have the issue with Concurency is Swing, your GUI is visible after all thread are done
is possible to moving with JProgressBar (I'm talking about you code) but you have to
create and show JDialog, create once time and reuse this container
then to start Thread,
better could be from Runnable#Thread
output to the Swing GUI must be wrapped into invokeLater()
this is exactly job for using SwingWorker and with PropertyChangeListener
As the previous answers already mentioned, SwingWorker is the way to go, if use want to use concurrency with Swing.I found this SwingWorker and ProgressBar tutorial quite useful in understanding how it all works together.
Coming back to your actual problem: I assume you use a GUI, because you stated the user has to click a button. The question is, what is your "main thread" doing? Does it really have to run all the time? Doesn't look like it. Because you said the thread needs a variable which is set by another thread, which is a result of a user interaction. In short: Why does it need to run if it's dependent on a user interaction anyway? The usual way would be to first get all the data you need and then run the calculations or whatever. In your case, either run everything in a single background thread(set the variable first then do the rest), started by the ActionListener of your button or run the other thread after the thread where you set the variable has completed.You could for example use the method done(), provided by SwingWorker, to launch the next task. Or if you really have to, you could wait in a loop for task.isDone()to return true. But don't forget to check for isCancelled() too.Anyway, I think you should rethink your design. Because what I can see from the limited information provided looks overly complicated to me.