I have a JFrame with a CardLayout component. I am using the CardLayout to switch between different JPanel's at different moments of the application execution. At some point I am using a SwingWorker Object to generate some XML files. In this time I want to display another JPanel in my window to tell the user to wait. On this JPanel I want to switch between 3 labels.
JLabel 1 would be : "Please wait."
JLabel 2 would be : "Please wait.."
JLabel 3 would be : "Please wait..."
Right now the code looks like this:
ConvertersWorker.execute();
CLayout.show(Cards, WAIT_PANEL);
Timer t =new Timer(500, new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e) {
WaitPanel.SwitchLabels();
}
});
t.start();
while (!this.finishedConverting)
{
}
//After this im am executing other methods based on the generated XML files
The SwingWorker code:
SwingWorker<Boolean, Void> ConvertersWorker = new SwingWorker<Boolean, Void>() {
public Boolean doInBackground() {
Boolean result = RunConverters();
return result;
}
public void done() {
try {
finishedConverting = get();
} catch (InterruptedException ex) {
ex.printStackTrace();
} catch (ExecutionException ex) {
ex.printStackTrace();
}
}
} ;
The second JPanel is not even displayed because the JFrame blocks. It blocks because of the while loop but I don't know how to implement it differently. And also the method done() from the SwingWorker is never executed. If it were executed then the finishedConverting variable would have been set to true and the while loop would have stopped. Can anyone help me to find a better solution?
I know you solved this but that's happening because you are using just one thread, and it blocked because of the While, so you need to create a new thread to handle this
new Thread(){
public void run() {
//code here
}
}.start();
and to refresh the content of a JPanel you can use
myJpanel.doLayout();
or
myJpanel.repaint();
I removed the while loop and moved the code which was after the loop in another method which is executed in the done() method of the SwingWorker so now it works.
Related
So I wrote a little program which will move circles around and when they collide they will move opposite direction, however when I'm trying to delay the execution so they won't move around stupidly fast I get java.lang.IllegalMonitorStateException
Lock in canvasRender.java, creating an instance:
ReentrantLock renderLock = new ReentrantLock();
Method which will pause execution for a moment, so circles won't move around super fast.
publlic void delay(){
renderLock.unlock();
try { Thread.sleep(10); } catch (Exception e) {} ;
renderLock.lock();
}
then from another class where I create a window and add actionListener
public static void main(String[] args){
//Buttons and other elements
// ...
JButton start = new JButton("Start!");
createAndShowGUI();
}
In createAndShowGUI():
static void createAndShowGUI(){
//adding elements to panels
start.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
start(); //this will set gameIsRunning variable to true and create models
while (gameIsRunning) {
//update(); //which has delay(); at the end of frame drawing
//but even if just put delay()
delay(); //still says exception
start.setEnabled(false); //while game is running button is unavailable
}
start.setEnabled(true);
}
});
}
In this case my lock is owned by the Thread main, but at the time when I click button 'Start!' current is Thread AWT-EventQueue-0, and so the program crashes. How to fix this issue? (or where am I silly?)
The problem is that you're calling renderLock.unlock() from AWT-EventQueue-0 after renderLock.lock() was called by main. The thread AWT-EventQueue-0 isn't allowed to call unlock() it, since it's not the thread that called lock() it in the first place.
You could probably simplify things by dropping ReentrantLock and just using synchronized.
I don't know the design of the rest of your program, but it seems to me that the contents of the while loop belong in a separate thread. You generally don't want to loop in a UI listener method (such as actionPerformed() in ActionListener) because it will freeze up the GUI.
One thing you could do is add an Object to synchronize on:
private static final Object LOCK = new Object()
Then move the game-updating logic to its own thread — something like this:
private static class GameThread extends Thread {
public GameThread() {
super("GameThread");
}
public void run() {
synchronized (LOCK) {
start();
while (gameIsRunning) {
update();
try {
// Try to sleep for 10 millis:
LOCK.wait(10);
} catch (InterruptedException ignored) { }
}
}
// Re-enable the button:
javax.swing.SwingUtilities.invokeLater(() -> start.setEnabled(true));
}
}
And you can change your ActionListener to simply disable the button and start a GameThread:
start.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
start.setEnabled(false);
synchronized(LOCK) {
if(!gameIsRunning) {
new GameThread().start();
}
}
}
});
Any other code that checks or modifies the state of the game should also be enclosed within a synchronized (LOCK) block. And if update() modifies the GUI as well as the game state, then it probably needs to do so with SwingUtilities.invokeLater().
It might also make things more clear to rename start() to setupGame() and JButton start to JButton startButton.
What I am trying to do is an Appled which throws 2 threads, each running a counter which increases itself via an infinite loop
I then use a while(true) in the Applet's paint() method, which continuously paints the counters, the problem is that I have also 2 buttons, each intended to stop each thread, but the infinite loop in the paint() method doesn't let me neither click none of them nor close the Applet's window nor anything
Here a screenshot followed by the code
btw I'm certain the problem is the paint() loop as if I disable the loop I can interact with the buttons, but the counters are obviously not updated, and weird thing is that I put the mouse cursor over the buttons to show it took the form like when you want to resize a windows but the imprpant didn't capture it :/
http://i.imgur.com/PJnDI4u.png
public class MainApplet extends Applet implements ActionListener {
private static final long serialVersionUID = -2500043816999861110L;
private Font fuente;
private Button bUno, bDos;
private HiloContador hUno, hDos;
public void init() {
setBackground(Color.LIGHT_GRAY);
fuente = new Font("Verdana",Font.BOLD,26);
bUno = new Button("Parar");
bUno.addActionListener(this);
bDos = new Button("Parar");
bDos.addActionListener(this);
bUno.setSize(40,20);
add(bUno);
bDos.setSize(40,20);
add(bDos);
hUno = new HiloContador(20);
hUno.start();
hDos = new HiloContador(40);
hDos.start();
}
#SuppressWarnings({ "deprecation", "static-access" })
public void actionPerformed(ActionEvent e) {
if(e.getSource().equals(bUno)){
hUno.parar();
bUno.setLabel("1 parado");
}else if (e.getSource().equals(bDos)){
hDos.parar();
bDos.setLabel("2 parado");
}
}
public void paint(Graphics g) {
while (true){
g.clearRect(1,1,getSize().width,getSize().height); //dibuja la ventana
g.setFont(fuente);
g.drawString(hUno.getContador()+"",40,60);
g.drawString(hDos.getContador()+"",100,60);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
in case it helps anyone, solved deleting the infinite loop and adding this method
Timer timer = new Timer();
timer.schedule( new TimerTask() {
public void run() {
repaint();}
}, 0, 1000);
Alright, so I have a null layout JPanel with a single JLabel in it. The JLabel is positioned at (0,0). What I'm trying to do is use a while loop in a new Thread to sleep the new Thread and then shift the JLabel 10px down by using SwingUtilities.invokeLater . The problem is that the UI gets updated in a laggy sort of way. It doesn't update every time it should, but skips lots of updates and shifts in big chunks. I know I can easily do it with Timer, but the point is understanding Threads better. Thanks in advance!
Code:
private void start(){
Updater up = new Updater();
up.start();
}
public void updatePosition(){
int y = label1.getLocation.y;
label.setBounds(0,y+10, 10,10);
}
private class Updater extends Thread{
public void run(){
while(!shouldQuit){
try{
Updater.sleep(100);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
updatePosition();
}
});
}catch(Exception e){
System.out.println(e);
}
}
}
}
EDIT:
I got it to work by replacing the while loop with a call to a new Thread instance in the updatePosition() method, to settle things down a bit. And also, it wasn't only the Thread that was causing the problem, so I had to force the panel to re-layout it's subviews by calling revalidate() on it.
Here's how it looks (the fully working one):
private void start(){
new Updater().start();
}
public void updatePosition(){
int y = label1.getLocation.y;
label.setBounds(0,y+10, 10,10);
panel.revalidate();
if(!shouldQuit) new Updater().start();
}
private class Updater extends Thread{
public void run(){
try{
Updater.sleep(100);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
updatePosition();
}
});
}catch(Exception e){
System.out.println(e);
}
}
}
You should try to use visibility and GridLayout to maximize movement. You can use a int var to count threads and reciprocate that to the label. As well, you should be using your ability o create Updaters, more and smoother. Just do the start() mwthod while trolling for threads :-)
You could have something besides an infinity call to start. I think you've lost the inheritance from the class, itself. The object label1 must ave been lost in tbe fray. If that's not it, then I'm pretty sure I'm not really able to answer this one.
I know the basics behind trying to write code that terminates a thread, but I've run into a bit of a problem.
I've got a JButton in a JFrame GUI that launches the simulation I'm trying to animate. It is called in the ActionPerformed code of the JButton by
new AnswerWorker().execute();
The AnswerWorker class, in turn, extends SwingWorker so that the animation frame can be drawn while the GUI is still active.
public class AnswerWorker extends SwingWorker<String, Integer> {
protected String doInBackground() throws Exception
{
Threading threading = new Threading();
return null;
}
protected void done()
{
try {
JOptionPane.showMessageDialog(InputGUI.this, AMEC.unsuccesfulpercentage + "% of iterations had trucks that had to sleep over");
AMEC.unsuccesfulpercentage = 0;
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
To create a way of stopping the simulation thread, I created the class Threading, that calls the function that runs the simulation.
public class Threading extends Thread {
#Override
public void run() {
while (!Thread.currentThread().isInterrupted()) {
try {
AMEC.runsimulation();
} catch (IOException ex) {
Logger.getLogger(InputGUI.class.getName()).log(Level.SEVERE, null, ex);
} catch (InterruptedException ex) {
Logger.getLogger(InputGUI.class.getName()).log(Level.SEVERE, null, ex);
}
}
return;
}
}
Now, in the runsimulation() function, I initialize a JFrame, and I want to terminate the thread running the simulation if the JFrame is closed by clicking on its close button. How do I do this?
EDIT: all of the above code is called in a file InputGUI.java that contains all my GUI elements. The runsimulation function is in my main project file, AMEC.java
You could override the dispose() method on the JFrame to include a call to stop the thread
#Override
dispose(){
stopThread();
super.dispose();
}
How do I run a terminate command on JFrame exit?
add WindowListener to JFrame, from windowClosing event you have to call JFrame.setVisible(false) and then to call SwingWorker
after SwingWorker ended to terminate current JVM, to show a JOptionPane in the case that any exception raised, or to show JFrame again back to screen, required to change DefaultCloseOperation, to HIDE_ON_CLOSE
I execute the task in this class and the Dialog pops up as a white box. The print statement IS printing out the progress values I'm expecting, but nothing shows up on the Dialog until after the operation is complete. I can see the progress bar flash visible for a millisecond before the dialog is closed at the end. Absolutely no clue what's going on :\
public class ProgressDialog extends JDialog {
private JProgressBar pb;
private SwingWorker<Boolean, Void> task;
public SwingWorker<Boolean, Void> getTask(){
return task;
}
public ProgressDialog(final String call){
setTitle("Working...");
setLayout(new BorderLayout());
setBounds(300,300,300,100);
pb = new JProgressBar(0, 100);
pb.setValue(0);
pb.setVisible(true);
pb.setStringPainted(true);
add(pb, BorderLayout.CENTER);
setVisible(true);
task = new SwingWorker<Boolean, Void>(){
public Boolean doInBackground(){
switch(call){
case "Category": pb.setValue(Category.getProgress());
while(pb.getValue()<99){
try{
Thread.sleep(500);
} catch (InterruptedException e){
Thread.currentThread().interrupt();
}
pb.setValue(Category.getProgress());
System.out.println(pb.getValue());
repaint();
revalidate();
}
break;
}
return true;
}
public void done(){
dispose();
}
};
}
}
EDIT: tried this change. no dice. Why am I not even getting a progress bar at 0%? It only appears once it is at 100%
public class ProgressDialog extends JDialog {
private JProgressBar pb;
private SwingWorker<Boolean, Integer> task;
public SwingWorker<Boolean, Integer> getTask(){
return task;
}
public ProgressDialog(final String call){
setTitle("Working...");
setLayout(new BorderLayout());
setBounds(300,300,300,100);
pb = new JProgressBar(0, 100);
pb.setValue(0);
pb.setStringPainted(true);
add(pb, BorderLayout.CENTER);
setVisible(true);
task = new SwingWorker<Boolean, Integer>(){
public Boolean doInBackground(){
switch(call){
case "Category": setProgress(Category.getProgress());
while(pb.getValue()<99){
try{
Thread.sleep(500);
} catch (InterruptedException e){
Thread.currentThread().interrupt();
}
setProgress(Category.getProgress());
}
break;
}
return true;
}
public void done(){
//dispose();
}
};
task.addPropertyChangeListener(new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent evt) {
if ("progress".equals(evt.getPropertyName())) {
System.out.println((Integer)evt.getNewValue());
pb.setValue((Integer)evt.getNewValue());
pb.revalidate();
pb.repaint();
}
}
});
}
}
You're trying to set the progress bar's state from within the SwingWorker's doInBackground method, from a background thread -- which makes no sense. The whole reason for using a SwingWorker is to allow you to do a background process in a Swing GUI, so you don't make Swing calls from a background thread, and so that you don't tie up the Swing thread with a long-running bit of code.
You should not make Swing calls from this background process. Instead use the publish/process methods as the tutorials will show you. Or perhaps better, set the SwingWorker's progress field, and use a PropertyChangeListener on the SwingWorker to allow the progress bar to react to it.
Regardless, the bottom line:
Use the SwingWorker to do background work.
Do not make Swing calls from within the SwingWorker's doInBackground method.
Use publish to push data from the background method into the Swing thread realm.
Use the process method to handle this data being pushed.
SwingWorker has a progress property that is also handy to use for allowing Swing code to respond to changes in background states.
If you go this route, use a PropertyChangeListener.
You almost never want to use setBounds(...) or null layout. Trust me as someone who has written hundreds of Swing programs, this one will bite you in the end.
It looks as if your Category is using a static method for getting its progress. Again, this is something you almost never want to do. A progress field suggests state, and this should be part of the instance fields of an object, never static.
Here's an SSCCE to demonstrate how you should be updating your JProgressBar. Copy/paste this and run it.
Notice how we update the progress bar by calling publish(i) which sends the integer to the process() method. The SwingWorker sends results to the process() method in chunks, but we are only using an Integer to update the JProgressBar so all we care about it the LAST chunk. In this SSCCE, we go from 1-1000. If you examine the console, you'll see that a lot of numbers between 1-1000 are being skipped because we are updating too fast for the SwingWorker to catch up (but that's ok. That's why it delivers results in chunks).
NOTE: the process() method was originally designed for programmers to return real-time results from their long-running processes and update the GUI. So, if you were doing a database fetch, you could update a JTable with the results you return. I hate doing things that way, though. So 99% of the time I just use an "indeterminate" JProgressBar and wait till the done() method to publish my results. Occaisionally, however, I'll use a "determinate" JProgressBar and update like we do in this SSCCE. Never have I used process() to return and publish actual data. :) But, that's what it was originally designed to do.
import java.util.List;
import java.util.concurrent.ExecutionException;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JProgressBar;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
/**
*
* #author Ryan
*/
public class Test {
public static void main(String args[]) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
go();
}
});
}
public static void go() {
JFrame frame = new JFrame();
JProgressBar jpb = new JProgressBar();
jpb.setIndeterminate(false);
int max = 1000;
jpb.setMaximum(max);
frame.add(jpb);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
new Task(jpb, max).execute();
}
static class Task extends SwingWorker<Void, Integer> {
JProgressBar jpb;
int max;
public Task(JProgressBar jpb, int max) {
this.jpb = jpb;
this.max = max;
}
#Override
protected void process(List<Integer> chunks) {
jpb.setValue(chunks.get(chunks.size()-1)); // The last value in this array is all we care about.
System.out.println(chunks.get(chunks.size()-1));
}
#Override
protected Void doInBackground() throws Exception {
for(int i = 0; i < max; i++) {
Thread.sleep(10); // Sleep for 1/10th of a second
publish(i);
}
return null;
}
#Override
protected void done() {
try {
get();
JOptionPane.showMessageDialog(jpb.getParent(), "Success", "Success", JOptionPane.INFORMATION_MESSAGE);
} catch (ExecutionException | InterruptedException e) {
e.printStackTrace();
}
}
}
}
EDIT: I created a diagram that should be a helpful reference when handling SwingWorker so you know where to place your code.