I have a java class called GameUpdater which extends JInternalFrame.
It used to extend JFrame when I ran the class as a program by itself, but I changed it to JInternalFrame to become part of a larger application - now accessible from a menu button.
The function being called when I press this menu button is as follows:
private void update(){
GameUpdater gu = new GameUpdater();
desktop.add(gu); //add to JDesktopPane
gu.setSize(400, 300);
gu.setVisible(true);
gu.readMatches();//this function takes ages
gu.setMatch("Updating database...");//this is some output to the user, displays info in the internal frame
//try and insert into database
for(Match m : gu.getMatches()){
db.insertMatch(m);
}
gu.setMatch("DONE"); //now it shows the frame, way too late
}
The method gu.readMatches() takes a long time to execute, so it periodically updates content in the JInternalFrame to display its progress. However the frame is not being shown until this update function is complete!
It's like setVisible(true) is waiting until the end of the function...
It worked absolutely fine when it was a JFrame. Is there any weird property of a JInternalFrame that would cause this?
Cheers
It sounds like you're executing a time consuming process inside the Event Dispatching Thread (EDT), this will prevent the event queue from process (amongst other things) repaint requests.
This will cause you program to appear as if it has "hung".
You need to off load this task to a background thread.
Have a read through Concurrency in Swing, especially the section on Worker Threads and SwingWorker
The problem is that you are blocking your EDT this can be taken care of by simply creating a new Thread/Runnable thar calls gu.readMatches(); the method:
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
gu.readMatches(); //gu will have to be declared `final`
gu.setMatch("Updating database...");//this is some output to the user, displays info in the internal frame
//try and insert into database
for(Match m : gu.getMatches()){
db.insertMatch(m);
}
}
});
ofcourse though you might want to implement a JProgressBar so the user can keep track of of how far the reading is.
Related
I'm creating a board game using a GUI and JFrames/JPanels where you can play against the computer. I have a method called showPieces() which updates board GUI by changing the image icons on an array of buttons (which are laid out in a grid format). Once the icons have been updated the revalidate() and repaint() methods to update the GUI.
The showPieces() method has a parameter that needs to be passed to it every time it is called.
The main issue I'm having is I want the human to make a move, update the GUI, wait 1 second, the computer makes it's move and then loop until someone wins.
My basic code is the following:
do{
human.makeMove();
gui.showPieces(data);
try {
Thread.sleep(1000);
} catch(InterruptedException ex) {
Thread.currentThread().interrupt();
}
computer.makeMove()
gui.showPieces(data);
}while(playing);
This cause the issue where when the human player makes their move, the GUI will freeze for one second and then after the delay, both moves are made at the same time.
I hope it makes sense, but I'm a novice with Java and may have to look more into threading as I don't understand it well enough.
Thread.sleep() is done on the Event Dispatch Thread which will lock the GUI.
So If you need to wait for a specific amount of time, don't sleep in the event dispatch thread. Instead, use a timer.
int delay = 1000; //milliseconds
ActionListener taskPerformer = new ActionListener() {
public void actionPerformed(ActionEvent evt) {
//...Perform a task...
}
};
new Timer(delay, taskPerformer).start();
As with most all similar Swing questions, you're putting your entire Swing GUI to sleep by calling Thread.sleep(...) on the GUI's event thread (the EDT or Event Dispatch Thread), and when during this period the GUI will not be able to update its images or interact with the user whatsoever. The solution here is not to use Thread.sleep(...) but rather to use a Swing Timer to cause your 1 second delay.
Swing Timer Tutorial.
i need to develop java code to have JFrame with a text filed and button.Using Threads,i need to update time for every one minute in the title bar of JFrame.Using Another Thread i need to display textbox value in the console when a button is clicked.I have code for performing both operations (updating time for every min and getting text box value)but i dont know how to add two threads in same class.if anyone knows pls help me out
What you are asking is a dangerous thing to do in Swing. Swing components are not thread-safe and should only be updated from the Event Dispatching Thread (also known as the EDT or Swing Thread). To do this, Swing has utility methods such as SwingUtilities.invokeLater(Runnable) which will execute the code in the Runnable (at some point in the future) on the EDT. The idea is that you place your code to do Swing-things (like update the Title of the JFrame with the time) inside of a separate Runnable and pass it to invokeLater().
To do this, you can create an anonymous Runnable class:
Runnable updateJFrame = new Runnable () {
public void run () {
myJFrame.setTitle("My New Title");
}
};
SwingUtilities.invokeLater(updateJFrame);
Using invokeLater() also ensures that the components get refreshed/repainted properly after they have been updated. (The behavior you are seeing when using statics may actually be a refresh/repaint issue.) The moral of this story is that if you manipulate Swing components on a non-EDT thread, all bets are off.
I'm re doing a specific application, just a basic text editor and I remember I had tabs and a JMenu so if you went File --> New it would add or 'Open' another tab on the JTabbedPane. But this time it's not doing it for me, could someone help? Here is how im doing it:
newFile.addActionListener(
new ActionListener(){
public void actionPerformed(ActionEvent event){
tabs.addTab("new file", text);
}
}
);
So when it's clicked it should add another tab but it's not for some reason...
If it matters there is a default tab open at the beginning and when you click new it wipes out the old one.
Thanks for any help! (Please ask if you need anymore explanation)
Here I uploaded my code here since the editor here kept saying I the way I was putting it in wasnt formatted correctly:
http://nardcake.com/java
There is 2 files there, one initializes it and the other is everything else
thanks!
try:
tabs.revalidate();
tabs.repaint();
I have removed these two lines (those two are anyhow called in the end by addTab() method), and rewritten your init.java like this:
public static void main(String[] args) {
System.out.println(SwingUtilities.isEventDispatchThread()); // 1
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
System.out.println(SwingUtilities.isEventDispatchThread()); //2
EBLFWE window = new EBLFWE();
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setExtendedState( window.getExtendedState()|JFrame.MAXIMIZED_BOTH );
window.setSize(1024, 728);
window.setVisible(true);
}
});
It works now. To quote myself:
Every usage of Swing components must be done thorugh the Event Dispatch Thread (abbreviated EDT) or you will probably get unwanted visual effects. See here for explanation.
EDIT:
All the GUI related code must be executed on the EDT. You can test if some part of your code is run by EDT like this:
System.out.println(SwingUtilities.isEventDispatchThread());
If it prints true you are safe to do a GUI update (e.g. call methods on Swing components instances) - like in 1 or anywhere in EBLFWE class. However 2 will print false - it is because the thread that runs your program is not EDT.
When calling SwingUtilities.invokeLater() you are actually placing that code to be executed (at some appropriate time the EDT sees fit) in the Event dispatch thread.
EDT does the actual painting, and a lot of other tasks, so when you call GUI update code from another thread you can mess up the order and get unwanted visual apperance.
I want to write a program, which search some HTML addresses. I assume that one search will be going more than 1 minute. When I print the results to console everything is OK, but when I make a frame the results don't appear in textpane. Now I have two classes one to search and one for frame. I write the simply function in the frame class to check if adding text working:
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
String Txt=(String)jComboBox1.getSelectedItem();
jTextPane1.setText("");
addText(Txt);
SecondClass.find(Txt);
}
public void addText(String text){
StyledDocument doc = jTextPane1.getStyledDocument();
try{
doc.insertString(doc.getLength(), text, null);
}catch(Exception e) { System.out.println(e); }
}
And in SecondClass I write the same line to addText, but it is working only from frame class. The second problem is that if the search is going I cannot do anything in program and the text from frame class is see after search is finished. I want see text immediately after finding it like in console which is working and I want to have possibility to click this link before search is ended ( I don't implement clickable links yet and don't know how to do this). I think that I must synchronize the processes, but I don't know how to do this.
It sounds like you are doing the main downloading task on the special Swing event thread, which handles all the work for Swing components. This will prevent the GUI from responding properly.
This will happen 'by accident' if you do long-running actions within event handlers (e.g. when you press a JButton).
You need to do long-running tasks in a separate thread, then 'hand over' the data to the Swing thread to update the GUI, using SwingUtilities.invokeLater(runnable); Edited: or see Max's answer about SwingWorker.
For example, your worker thread would do something like this when it has some data ready, using an anonymous Runnable:
final String text = getText();
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
appendText(text);
}
});
where getText() just indicates some means of acquiring the downloaded text, and appendtext() is a method you will need to write to update your textpane.
Check out SwingWorker. It should help you with handling lengthy operation and managing intermediate results as well. There is a sample in this manual that illustrates use of publish() and process() methods to append intermediate text results to JTextArea.
In a method of a class, I update the same label twice. The first time, it shows the user message to wait, but the second time shows the user the completed message. Something like the following:
MyClass{
myMethod(){
jLabel.setText("Please wait...");
//does calculation
jLabel.setText("Completed successfully!");
}
}
When I run the app, all I see is the "Completed successfully" message. Is the JLabel updating too quickly? How do I control it? I tried using the following but no luck :(
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
jLabel.setText("Please wait...");
}
});
If the calculation is done in the event dispatch thread, then it blocks the thread and prevents it from doing all its repaintings. You must do the computation in another thread, and have this thread change the label text when it ends (in the event dispatch thread, using SwingUtilities.invokeLater, or by using the SwingWorker mechanism). If the computation is really fast, it's not worth it, though, because the second text will appear so quickly after the first one that you won't even notice the first one.
Have a look at SwingWorker, which is designed for such use-cases. Its javadoc contains a useful example.
if you want to delay some Action/Event then use javax.swing.Timer, or wrap your code to the Runnable#Thread,
notice: never use Thread.sleep(int) durring EDT, your GUI freeze until Thread.sleep(int) ended
example for javax.swing.Timer & Runnable#Thread & Freeze GUI by implements Thread.sleep(int) durring EDT here
Possibly because your calculations are happening too fast. Did you try putting a delay after your calc.
Also as Nizet points out above if this is happening in EDT the component will not repaint until end of the thread which means it will take the last set value.