How to "refresh" a JFrame inside an actionperformed method? - java

I try to design a GUI for a neural network I made recently. I am using the MNIST-dataset and want to display the handwritten digit using JPanels with the brightness-values written inside. By pressing the "train"-button the network gets trained and every new digit is displayed. However this happens in a for loop in the actionperformed method of the button and it seems that I can´t change the background of the labels or the text(at least it doesn´t display the changes) until the last one. I don´t know whether I´m right but it seems that only the last change gets displayed. That´s why my question is whether it is possible to "refresh" the JFrame inside the actionperformed method.
I already have tried revalidate(), invalidate() & validate(), SwingUtilities.updateComponentTreeUI(frame), but none of them worked.
Here is the relevant part of my code:
train.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
for (int i = 0; i < iMax; i++) {
...
digitRefresh(reader.getInputs()[i], (int) reader.getInputs()[i][0], 0);
}
}
});
.
public void digitRefresh(double[] pixelValue, int target, int result) {
for (int i = 0; i < 784; i++) {
double value = pixelValue[i + 1];
int brightness = (int) (value * 255);
l_digit[i].setText(String.valueOf(value));
l_digit[i].setBackground(new Color(brightness, brightness, brightness));
}
l_target.setText(String.valueOf(target));
l_result.setText(String.valueOf(result));
this.revalidate();
}
thank you for every awnser and sorry for my bad english.

The simplest thing to do is start a new thread.
#Override
public void actionPerformed(ActionEvent e) {
new Thread( ()->{
for (int i = 0; i < iMax; i++) {
...
final int fi = i;
EventQueue.invokeLater( ()->{
digitRefresh(reader.getInputs()[fi], (int) reader.getInputs()[fi][0], 0);
});
}).start();
}
Now all of the work is being done on a separate thread, then as the ... work finishes, digit refresh method called from the EDT. Notice the final int fi part. There are caveats about going back and forth on threads so it is good to look into better controls than just using thread.
Swing worker for example:
How do I use SwingWorker in Java?

Related

Display delayed text on User's screen

Hello!
I am trying to display a text on the Screen (with Java), but I want it to be delayed, like, every 0.1 seconds, a letter of the text would appear on the screen. It's like Pokemons dialogs. Here's what I am talking about: https://www.youtube.com/watch?v=yUS1IcC5CBY
I don't want the fade and the acceleration of the text, I just want the text to appear letter-by-letter. Also, I would like the text to be a String. Please, can you help me?
Thanks a lot in advance!
You can use two methods:
One is Thread.sleep(), which is shown above:
private static String message = "Your Message";
private static JLable label = new JLabel();
private static String labelMessage = "";
for(int i = 0; i < message.length(); i++){
labelMessage += Character.toString(message.charAt(i));
label.setText(labelMessage);
try{
Thread.sleep(howManyMillisecondsYouShouldWait);//if you want to do it every .1
//seconds, just wait 100 milliseconds.
}catch(InterruptedException e){
Thread.currentThread().interrupt();
}
}
that will forever print it to the screen every 100 milliseconds. However, the only trouble with using Thread.sleep is (and I somehow just learned this the other day, even though I've been programming for a long while) it is not always accurate. It may sleep 100 ms, it may sleep 150, etc. Secondly, a slower computer may take longer to sleep through it.
The other method which you will use more often (probably) is to check the actual time of your system and see if it's been long enough since you last printed it to the screen, like this:
private static long timeOfLastWrite;//at what time did you last update the text?
private static long deltaTimeSinceLastWrite;//how long has it been since you last updated the text?
private static long timeOfFirstWrite;//when did you start?
private static long deltaTimeSinceFirstWrite;//how long has it been since you started?
private static String message = "Your Message";
private static JLabel label = new JLabel();
private static String labelMessage = "";
//print once here:
timeOfFirstWrite = System.currentTimeMillis();
timeOfLastWrite = System.currentTimeMillis();//every time you print to the screen, make
//sure that you make note of it by setting the timeOfLastWrite variable equal to the current time.
labelMessage += Character.toString(message.chatAt(0));
while(!labelMessage.equals(message)){
deltaTimeSinceLastWrite = System.currentTimeMillis() - timeOfLastWrite;
if(deltaTimeSinceLastWrite >= 100){
timeOfLastWrite = System.currentTimeMillis();
deltaTimeSinceFirstWrite = System.currentTimeMillis() - timeOfFirstWrite;
int currentIndexOfChain = (int) deltaTimeSinceFirstWrite / 100;
if(currentIndexOfChain >= message.length()){
currentIndexOfChain = message.length() - 1;
}
labelMessage = message.substring(0, currentIndexOfChain + 1);
label.setText(labelMessage);
}
}
This method isn't even slightly necessary for a program so simple as writing text to the screen 10 times a second. However, it's good to get into the practice of it. You'll learn that if you create a character and tell him to move 10 pixels, Thread.sleep(100), and move again and etc... that on a slower computer, the character will move slower. However, if you tell it to wait until a certain amount of time has passed according to your computer's time, if the user lags out and it takes 200 milliseconds before it tells the character to move again, you can account for that by simply making him move twice as far -- I think it's called framerate independence.
If I did anything wrong with the delta time management please let me now. Again, I just learned about this the other day even though I've been programming for awhile, so don't worry about it too much if you're just now learning to program.
And that's how you make an incredibly long (possibly too long) answer to an incredibly simple question. I hope you benefit from this response.
I'm unable to use Thread.sleep(x) or wait(): java.lang.InterruptedException; must be caught or declared to be thrown
try {
Thread.sleep(100);
} catch(InterruptedException ex) {
Thread.currentThread().interrupt();
}
You may use this code for doing so. Simply put a thread to print the text onto a jLabel.
new Thread(new Runnable() {
#Override
public void run() {
String x="";
String txt="Hello this is a sample text. Let us see how this works.";
for(int i=0;i<txt.length();i++){
try {
jLabel1.setText(x=x+txt.charAt(i));
Thread.sleep(100);
} catch(InterruptedException ex) {
Thread.currentThread().interrupt();
}
}
}
}).start();
How about this?
import javax.swing.Timer;
import java.awt.event.*;
import javax.swing.JFrame;
import javax.swing.JLabel;
public class DelayText
{
public String example = "As you can see, this sentence is being printed out a character at a time.";
public String transfer = "";
public Timer t;
public int i = 0;
public JFrame f;
public JLabel l;
public DelayText()
{
f = new JFrame("Example");
l = new JLabel();
f.add(l);
f.setSize(450, 200);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
TimerListener tl = new TimerListener();
t = new Timer(100, tl);
t.start();
}
public static void main(String[] args)
{
DelayText d = new DelayText();
}
private class TimerListener implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
if(i < example.length())
{
transfer += (example.charAt(i));
l.setText(transfer);
i++;
}
if(i >= example.length())
{
t.stop();
}
}
}
}
I am using the timer to create a delay between each character outputted on the JFrame. I noticed a lot of these other ones were a bit more complex, thought this might make things a bit easier to understand.

Displaying graphics as they paint in swing

I created a maze generator in swing for a class, and it works great. The only thing is, I want to show the maze being created in realtime, but the way I have everything set, it only updates after all the calculations have completed. I am using paintComponent and repaint in my code. How do I have it show the JFrame and draw lines immediately, rather than doing the algorithm and showing them all at the end?
Here is the relevant code:
public void generateMaze() {
Stack<Box> stack = new Stack<>();
int totalCells = Finals.numCol * Finals.numRow, visitedCells = 1;
Box currentCell = boxes[0][0];
Box nextCell;
stack.add(currentCell);
while (visitedCells < totalCells) {
nextCell = checkNeighbors(currentCell.xCoord, currentCell.yCoord);
if (nextCell != null) {
knockWalls(currentCell, nextCell);
stack.add(currentCell);
currentCell = nextCell;
visitedCells++;
} else {
currentCell = stack.pop();
}
}
repaint();
}
Here is my paintComponent method override
public void paintComponent(Graphics g) {
for(int x = 0; x < Finals.numRow; x++) {
for(int y = 0; y < Finals.numCol; y++) {
if(boxes[y][x].top != null)
boxes[y][x].top.paint(g);
if(boxes[y][x].bottom != null)
boxes[y][x].bottom.paint(g);
if(boxes[y][x].left != null)
boxes[y][x].left.paint(g);
if(boxes[y][x].right != null)
boxes[y][x].right.paint(g);
}
}
}
The knockWalls method sets certain walls equal to null, which causes them to not be drawn in the paintComponent method. I'm still fairly new at a lot of this, so I apologize if some of the code isn't super high quality!
Thanks everyone.
As MadProgrammer already pointed out in the comments, you are almost certainly blocking the Event Dispatch Thread. This is the thread that is responsible for repainting the GUI, and for handling the interaction events (like mouse clicks and button presses).
So presumably, you start the computation via a button click, roughly like this:
// The actionPerformed method of the button that
// starts the maze solving computation
#Override
void actionPerformed(ActionEvent e)
{
generateMaze();
}
That means that the event dispatch thread will be busy with executing generateMaze(), and not be able to perform the repainting.
The simplest solution would be to change this to something like
// The actionPerformed method of the button that
// starts the maze solving computation
#Override
void actionPerformed(ActionEvent e)
{
Thread thread = new Thread(new Runnable()
{
#Override
public void run()
{
generateMaze();
}
});
thread.start();
}
However, some care has to be taken: You may not modify Swing components from this newly created thread. If you have to modify Swing components, you have to put the task that performs the actual modification of the Swing component back on the EDT, using SwingUtilities.invokeLater(task). Additionally, you have to make sure that there are no other synchronization issues. For example, the lines
if(boxes[y][x].top != null)
boxes[y][x].top.paint(g);
are still (and have to be!) executed by the event dispatch thread. In this case, you have to make sure that no other thread can set the boxes[y][x].top to null after the EDT has executed the first line and before it executes the second line. If this may be an issue in your case, you might have to provide a bit more code, e.g. the code that is showing where and how the boxes[y][x] are modified.

JLabel Icon not updating with setIcon (or other redundancies)

Two different versions of code I'm using, as noted, the first one works fine, without any problems what so ever, the second one, the only thing that doesn't happen is the updating of the image (have verified through step debugging and debug printing to verify all values and conditionals by hand)
/* properly updates dice[] JLabel icons */
for (int i = 0; i < game.getToRoll(); i ++){
//sets rolled dice to actual values
dice[i].setIcon(dicePic[(game.getDice(i).getFaceValue())]);
}
/* loops properly, generates properly, does not update icons */
Die x = new Die();
int animate = 0;
while(animate < 10){
for (int i = 0; i < 6; i++ ){
x.roll();
if (i <= (game.getToRoll() -1))
dice[i].setIcon(dicePic[x.getFaceValue()]);
else
dice[i].setIcon(dicePic[0]);
}
panel[1].repaint();
panel[1].validate();
animate++;
try{
Thread.sleep(100);
}
catch(Exception e){
e.printStackTrace();
}
}
I've been looking around for some kind of idea of what it is that's causing the problem, and I've not run into anything other than that "sometimes repaint and validate will fix things that don't work."
As stated above, debug is giving me the greenlight on code flow working entirely as expected, just null image icons in the second example.
The problem is the Thread.sleep(100); The icon does change but you don't see the change because you blocked the UI thread.
So the rule is: never sleep the EventDispatchThread!
My advice is to use a Timer:
new javax.swing.Timer(200, new ActionListener() {
#Override
public void actionPerformed(final ActionEvent e) {
//do an icon change
}
}).start();

Fish Eye effect on a JLabel

I am trying to implement a Fish Eye Image Menu in a JavaBean. As a start, I created a JLabel and put this code on the mouseEntered event. But when I run this, the output is shaky and doesn't re-size the JLabel.
This is my code.
new Thread() {
public void run() {
for (int i = 0; i < 30; i++) {
int x = imgLabel.getWidth()+1;
int y = imgLabel.getHeight()+1;
imgLabel.setSize(x , y );
// sets the icon to the label
imgLabel.setIcon(new ImageIcon(new ImageIcon(getClass().getResource("/pics/icon.png")).getImage().getScaledInstance(x , y, Image.SCALE_DEFAULT)));
repaint();
try {
Thread.sleep(10);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}
}.start();
If I comment that line where I set the image to the JLabel, the label gets re-sized perfectly.
Where has this gone wrong?
The problem is that getScaledInstance() together with resource loading is slow. Do these calculations once and cache them (in an array of 30 items). Not every time in the for loop.
Another thing: make sure you use a Swingworker for your animation, that helps in the timing and avoids setting icons outside the Event Dispatch Thread (EDT).

How To Reset JFrame (Restart Game)?

This is a java game that I have started working on.I have been trying to add a button that says "RESTART" which on clicking resets the whole program the way it was when it was at the beginning( i mean at the start of the game).
Here is my code:
There are 2 buttons namely "PLAY" & "CHECK WHO WON!"
For "PLAY" This is the code:
int delay = 1000;
final Timer timer = new Timer();
timer.schedule(new TimerTask(){
public void run(){
String b = "C:\\Users\\COMPUTER\\Desktop\\deck\\.png";
Random r = new Random();
r1 = r.nextInt(upplim)+lolim;
String a = Integer.toString(r1);
String c = "C:\\Users\\COMPUTER\\Desktop\\deck\\"+a+".png";
l1.setIcon(new ImageIcon(c));
}
},delay, 50);
For "CHECK WHO WON!" This is the code:
final int p = h;
System.out.println("ANSWER IS:"+p);
int delay2 = 1000;
for (int i = 1; i < 53; i++)
{
while(true)
{
next = rng.nextInt(Ulim) + Llim;
if (!generated.contains(next))
{
generated.add(next);
break;
}
}
if ( i % 2 == 0 )
{count++;
deck1[e] = next;deck1count++;
e++;
}
else {count++;
deck2[f] = next;deck2count++;
f++;
}
System.out.println(""+next);
if(next==p)
{break;}
}
if(deck1count==deck2count)
{
count=count-2;
fcard=99;}
final Timer timer2 = new Timer();
timer2.schedule(new TimerTask(){
public void run(){
do
{
System.out.println("dec2 "+deck2[z]);
String a = Integer.toString(deck2[z]);
String c = "C:\\Users\\COMPUTER\\Desktop\\deck\\"+a+".png";
l3.setIcon(new ImageIcon(c));
System.out.println("dec1 "+deck1[z]);
String b = Integer.toString(deck1[z]);
String d = "C:\\Users\\COMPUTER\\Desktop\\deck\\"+b+".png";
l4.setIcon(new ImageIcon(d));
System.out.println("count"+count);
z++;
count=count-2;
if(fcard==99&&count<0)
{l3.setIcon(new ImageIcon("C:\\Users\\COMPUTER\\Desktop\\deck\\99.png"));
}
}while(count>0&&z==p);
if(count<0)
{timer2.cancel();
reschk=11;
timer2.purge();
}
}
},delay2, 1000);
There is also another set of code which is written on the MouseClicked event of a label but I don't think it would be of much help here.
I have tried:
classname.this.dispose();
classname classname = new classname();
But it just shuts the whole program down.Is there any other way to reset the game?
Thanks for reading.
Any help would be appreciated.
You obviously have values which update so that you can represent your game during different states. If you want to restart your game, you simply need to set all of these values back to the original start values. You can write a Restart method to this.
Also, dispose() is designed to close the window.
edit: There is no magic method you can call to reset your program.
For Swing based container use Swing Timer rather than util.Timer, otherwise output from util.Timer should be out of EDT.
You don't need to dispose old container and recreate a new for fresh game, you can remove its contents.
anything else isn't clear from code posted here, nor question
Keep the state of the game in a distinct class and replace that with a fresh instance.

Categories

Resources