Updating JPanel/JFrame not working - java

I have a class that creates a JFrame and needs to have one image displayed in the JFrame. Images must rotate every few seconds.
I've tried using repaint(), revalidate(), and validate() on both the JFrame and JPanel and none seem to work. Following is my current code to update the JFrame/JPanel. This code will wait 4 seconds and then display the fourth image, but I would like it to update every second with a new image.
public void startSlideshow(){
int i = 0;
Long oldTime = System.currentTimeMillis();
do {
Long currentTime = System.currentTimeMillis();
if( currentTime - oldTime >= 1000 ) {
img = new JLabel(new ImageIcon("Albums/" + album + "/" + imageNames.get(i)));
panel.removeAll();
panel.setBackground(Color.BLACK);
panel.add(img);
panel.revalidate();
panel.repaint();
i++;
oldTime = System.currentTimeMillis(); // reset reference time
}
} while(i<imageNames.size());
panel.removeAll();
panel.setBackground(Color.BLACK);
panel.add(img);
panel.getRootPane().revalidate();
panel.repaint();
}
Any help is greatly appreciated as I've been banging my head for a while now. Thanks.

You're blocking the Event Dispatching Thread, preventing it from process new repaint requests.
Instead of using a loop and Thread.sleep, try using a javax.swing.Timer
Take a look at How to Use Swing Timers and Concurrency in Swing
You may also find it simpler to change the JLabel's icon instead of creating a new JLabel on each time through...

Related

How to do image rendering using bufferedimage in a jframe?

I am trying to render a complicated set of objects, and instead of trying to render each object individually, I thought it would be faster to render them as a BufferedImage. The only way I could figure out how to do that was to turn the BufferedImage into an ImageIcon, and set that to a JLabel, and add the JLabel to the JFrame. To update the image, I remove the JLabel, set it with the new BufferedImage, and re-add it to the JFrame. This makes the screen flash rapidly as you can see an empty frame in between each rendering. If I don't remove the label, the program runs extremely slowly. How do I fix this? Should I even be using JLabels or is there a better way?
public static void createWindow() {
frame = new JFrame("PlanetSim");
frame.setPreferredSize(new Dimension(WINDOW_X, WINDOW_Y));
frame.setMaximumSize(new Dimension(WINDOW_X, WINDOW_Y));
frame.setMinimumSize(new Dimension(WINDOW_X, WINDOW_Y));
foregroundLabel = new JLabel(new ImageIcon(foreground));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
frame.add(foregroundLabel);
frame.pack();
frame.setVisible(true);
}
public static void setForeground() {
frame.getContentPane().remove(foregroundLabel);
foregroundLabel = new JLabel(new ImageIcon(foreground));
frame.getContentPane().add(foregroundLabel);
frame.pack();
}
I doubt the problem has to do with the loop itself, but I'm including it anyway just in case.
public void run() {
long lastTime = System.nanoTime(), now; //keeps track of time on computer in nanoseconds
double amountOfTicks = 60.0; //number of ticks per rendering
double delta = 0; //time step
long timer = System.currentTimeMillis(); //timer to display FPS every 1000 ms
int frames = 0;
while (running) {
now = System.nanoTime();
delta += (now - lastTime) * amountOfTicks / 1000000000;
lastTime = now;
while(delta >= 1) {
tick();
delta--;
}
if (running)
render();
frames++;
if (System.currentTimeMillis() - timer > 1000) {
timer += 1000;
System.out.println("FPS: " + frames);
frames = 0;
}
}
}
public void render() {
populateWindow();
Window.setForeground();
}
frame.getContentPane().remove(foregroundLabel);
foregroundLabel = new JLabel(new ImageIcon(foreground));
frame.getContentPane().add(foregroundLabel);
frame.pack();
I would suggest there is no need to remove/add the label or pack the frame since I would assume the Icon will be the same size every time.
All you need to do is replace the Icon of the label.
//frame.getContentPane().remove(foregroundLabel);
foregroundLabel.setIcon( new ImageIcon(foreground) );
//frame.getContentPane().add(foregroundLabel);
//frame.pack();
I am trying to render a complicated set of objects, and instead of trying to render each object individually, I thought it would be faster to render them as a BufferedImage
Otherwise just do the rendering in your paintComponent() method. Swing is double buffered by default so it essentially removes the need to create the BufferedImage.
See: get width and height of JPanel outside of the class for an example of custom painting. Just update the ball count from 5 to 100 to make it more complex.

Swing is very slow with long strings

I built a simple Java program that logs in a JTextArea component.
JTextArea _log = new JTextArea();
_log.setEditable(false);
JScrollPane scrollLog = new JScrollPane(_log);
scrollLog.setPreferredSize(getMaximumSize());
add(scrollLog);
The problem is that logging like this takes 15ms on average:
public void log(String info) {
_log.append(info + "\n");
}
This is far(!) slower than logging using System.out.println. Logging takes more time than the whole running time of the algorithm!
Why is the JTextArea is so slow? Is there a way to improve it?
EDIT 1:
I am using separate thread for the algorithm, and using SwingUtilities.invokeLater to update the log in the UI.
The algorithm tread finish his work after 130ms on average, but the JTextArea finish his appends after 6000ms on avarage.
EDIT 2:
I tried to test this by use setText of string that contains 2500 charaters. In that case the operation took 1000ms on average.
I tried to use another controller then JTextArea and I get same results.
Is it hard for Swing components to deal with large strings? What can I do about it?
EDIT 3:
I just test with this code:
public class Test extends JFrame {
public Test() {
final JTextArea log = new JTextArea();
log.setEditable(false);
log.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
JScrollPane scrollLog = new JScrollPane(log);
scrollLog.setPreferredSize(getMaximumSize());
JButton start = new JButton("Start");
start.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
long start = System.nanoTime();
for (int i = 0; i < 2500; i++) {
log.append("a\n");
}
long end = System.nanoTime();
System.out.println((end - start) / 1000000.0);
}
});
JPanel panel = new JPanel();
panel.setLayout(new GridLayout(2, 1));
panel.add(scrollLog);
panel.add(start);
add(panel);
}
public static void main(String[] args) {
Test frame = new Test();
frame.setSize(600,500);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
The time of that for loop is 1870ms on avarage.
This is the only code that I ran (include the declaration of _log at the top of the question)
A JTextArea is not slow.
Far(!) away from System.out.println.
System.out.println() executes on a separate Thread.
The log takes more time then the hole running time of the algorithm!
So your algorithm is probably executing on the Event Dispatch Thread (EDT) which is the same Thread as the logic that appends text to the text area. So the text area can't repaint itself until the algorithm is finished.
The solution is to use a separate Thread for the long running algorithm.
Or maybe a better choice is to use a SwingWorker so you can run the algorithm and "publish" results to the text area.
Read the section from the Swing tutorial on Concurrency for more information and a working example of a SwingWorker.
Edit:
//log.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
The above line is causing the problem. I get 125 for the first test and 45 when I keep clicking the button.
That property is not needed. The text is still displayed on the left side of the text pane. If you want right aligned text then you need to use a JTextPane and set the attributes of the text pane to be right aligned.
That is why you should always post an MCVE. There is no way we could have guessed from your original question that you were using that method.
Edit2:
Use the alignment feature of a JTextPane:
SimpleAttributeSet center = new SimpleAttributeSet();
StyleConstants.setAlignment(center, StyleConstants.ALIGN_CENTER);
textPane.getStyledDocument().setParagraphAttributes(0, doc.getLength(), center, false);
Now any new text you add to the document should be center aligned. You can change this to right.

Java Swing ignores repaint()?

Basically I need one of my panels to be repainted at least 60 times per second. However I noticed, that if I don't move my mouse, FPS drops to ~5. I wrote program to test it.
package test;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class Test extends JFrame implements ActionListener {
Timer t;
JLabel l;
JPanel p;
long lastT;
public static void main(String[] args){
new Test();
}
public Test(){
add(p = new JPanel());
p.add(l = new JLabel("0000000000000000000000000000000"));
pack();
lastT = System.nanoTime();
t = new Timer(10, this);
t.setRepeats(true);
t.start();
setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
#Override
public void actionPerformed(ActionEvent e) {
long time = System.nanoTime();
l.setText(String.valueOf((time - lastT)));
repaint();
lastT = time;
}
}
It shows the interval between frames. If my mouse is inside the window, it changes much faster, if it is outside of window, it changes much slower, similar to my original problem. But the thing is, the number itself is not much different no matter if mouse is inside or outside, which means that Timer is shooting event at same interval. So it means that the repaint() is ignored? How can it be fixed?
Please note that repaint is never guaranteed to work as the Swing repaint manager will ignore stacked repaint requests -- that is if repaint requests build up and are not able to be handled in a timely fashion due to code being run from the Swing event queue, only the last one is called. Please read Painting in AWT and Swing for more.
Note however that there is no need to call repaint() in your code above, since changing the state of the JLabel's model will trigger a repaint on its own, and this would be a repaint of just the label itself, something that should be run more efficiently than calling repaint on the entire GUI. Also note that 10 mSec is a very short time slice and a Swing Timer may not be accurately or reliably called at 10 msec.

How do I implement timer using JLabel on JPanel?

I would like to get a set variable of the object inst, then set it to a JLabel and set that to the current JLabel that's already on the panel. But I would like for the variable (inst.Time (which represents seconds)) of the object 'inst' to decrement by one and remove the current JLabel and add to the panel the updated, decremented inst.Time.
I would like to decrement by 1 second as countdown timer until it reaches 0 and come out of the method WaitAndEnterIntoWorkArea. Could someone please show how to do so with the given code? Help please. Thank you
int.Time is an integer
private int WaitAndEnterIntoWorkArea(Instruction inst) // 'inst' is an object of Instruction arrayList
{
int index = 0;
int nFirstYDirectionToMove = 1;
JLabel busy = new JLabel(String.valueOf(inst.Time) + "s"); //inst.Time is a time that was set for that particular 'inst' and set the busy JLabel to show time + s, e.g. 5s or 3s (s represents seconds)
busy.setFont(font3);
busy.setForeground(Color.RED);
if(inst.WorkArea.startsWith("anvil")) //If inst.WorkArea is set to "anvil" Go into the block of code
{
nFirstYDirectionToMove = 1; //Move over one (Not important)
//I would like to decrement inst.Time 1 at a time to 0 and add to JPanel every second
while(true)
{
synchronized("row1" + String.valueOf(index)){
if(row1[index].getText().equalsIgnoreCase("Open")) //If JLabel row1[index].getText() is already set as text "Open" execute the if statement;
{
//I would like to remove the current JLabel (row1[index]) and add the 1 second decremented JLabel to the panel
panel.remove(row1[index]); //Remove the JLabel of row1[indexOfArea] (row1 is an array of JLabels) from the panel
row1[index] = busy; //Set JLabel text of row1[index] to the busy JLabel //Set the 1 second decremented JLabel to the current JLabel
panel.add(row1[index]); //Add the JLabel with the new text label with something like e.g. 5s or 4s, etc. to the panel
revalidate();
repaint();
break;
}
}
}
}
If I'm not mistaken, simply calling busy.setText(String.valueOf(inst.Time) + "s"); should be all you need. But as always, I would highly recommend using JavaFX instead of Swing.
As for the timing try
Timer timer = new Timer();
timer.scheduleAtFixedRate(()-> busy.setText(...), 0, 1000);

How to implement a count down timer in swing

I would like to know how should I implement a count down timer in swing.
I'm currently developing a net game with count down timer, and I need the Timer to be shown all the time in the corner.
The problem is I don't know how to implement it in away that the game screen will be responsive all the time for the user in the same time the timer is on..
1.I'm asking it because I know every update to the view is being held through the event dispatch thread but it can also freeze my game if there is something which run there consistently (like timer).
2.Design aspect :Do you know some nice ways of how to show the timer in an attractive and maybe animated way?
Thank you very much in advance
Use the javax.swing.Timer class. This will fire action events at fixed intervals, which will prevent the GUI from becoming unresponsive.
As for animation, see the 2D Graphics tutorial.
int j=10;
final Timer t = new Timer(1000, new ActionListener() {
private long time = 10 * 1000; //10 seconds, for example
public void actionPerformed(ActionEvent e) {
if (time > 0) {
time -= 1000;
j--;
jLabel1.setText(Long.toString(j));
}
}
});
t.start();

Categories

Resources