In the following application I have put a button ,clicking on which makes the GlassPane visible and a Thread starts which updates the Progress bar value.Below is the code:-
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.LinearGradientPaint;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
public class GlassPaneDownload extends JFrame implements Runnable{
Thread t;
CustomGlassPane jp;
public static void main(String args[])
{
SwingUtilities.invokeLater(new Runnable(){public void run(){new GlassPaneDownload();}});
}
public GlassPaneDownload(){
super("Glass Pane Download Simulation");
setSize(400,400);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new FlowLayout());
jp=new CustomGlassPane();
jp.setOpaque(false);
setGlassPane(jp);
JButton btn=new JButton("Click Here");
btn.addActionListener(new ActionListener(){public void actionPerformed(ActionEvent e){
//Make Glass Pane visible
jp.setVisible(true);
//Start Thread to update Progress Bar
t=new Thread();
t.start();
}});
add(btn);
setVisible(true);
}
public void run(){
for(int i=1;i<=100;i++)
{
jp.setProgress(i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
t=null;
}
}
class CustomGlassPane extends JComponent
{
private final float pattern[]=new float[]{0.0f,0.499f,0.50f,1.0f};
private final Color color[]=new Color[]{Color.WHITE,Color.GRAY,Color.BLACK,Color.LIGHT_GRAY};
private int progress,oldProgress;
public void paintComponent(Graphics g1)
{
super.paintComponent(g1);
g1.setColor(new Color(1.0f,1.0f,1.0f,0.7f));
g1.fillRect(0, 0, getWidth(), getHeight());
g1.setColor(new Color(200,200,255));
g1.drawRect(100,100,200,20);
LinearGradientPaint p=new LinearGradientPaint(100,100,200,20,pattern,color);
Graphics2D g2=(Graphics2D)g1;
g2.setPaint(p);
g2.fillRect(100, 100,progress*2, 20);
}
public void setProgress(int prog)
{
progress=prog;
repaint(100,100,200,20);
//repaint();
}
}
But,though the GlassPane gets visible but the ProgressBar is not updating.
Need Help friends.
This is a classical mistake: you are blocking the EDT (Event Dispatching Thread) with a loop over a Thread.sleep(). The EDT dispatches all GUI events: paint-events, mouse-events, key-events, action-events etc... Since you are looping and sleeping in the EDT (during an ActionEvent), it prevents the display from refreshing (I bet that your GUI becomes unresponsive after you click the button). So
Rule #1: do not block the EDT
Rule #2: DO NOT BLOCK THE EDT, which leads directly to
Rule #3: do not sleep()in the EDT, and
Rule #4: do not perform any lengthy-operation in the EDT
To work around this problem, use SwingWorker or javax.swing.Timer. This can also work with traditional Thread's and Executors (thread-pools) but you have to make sure that all your attempts to modify the GUI are performed in the EDT.
You may want to have a look at this example which shows how to combine a JProgressBar and a SwingWorker
Related
Pretty much title. The code is supposed to draw one box, wait 1 second, then draw a new one at a different location and repaint. Instead, it will wait for 1 second then paint both boxes. Thanks for the help and sorry if I messed up on formatting.
import javax.swing.*;
import java.awt.*;
public class GameRunner extends JPanel{
#Override
public void paintComponent (Graphics g){
int x = 0;
boolean directionRight = true;
g.setColor(Color.blue);
g.fillRect(300,400,100,100);
repaint();
try{
Thread.sleep(1000);
}
catch (Exception ex){}
g.fillRect(600,400,100,100);
repaint();
}
public static void main (String[] args){
JFrame frame = new JFrame("Submarine");
GameRunner gameRunner = new GameRunner();
frame.add(gameRunner);
frame.setSize(1200,700);
frame.setVisible(true);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
}
}
Thread.sleep(1000); will block the current running thread
paintComponent is called from within the context of the Event Dispatching Thread.
Swing won't update the state of the UI until it's finished processing the current (in this case "paint") event, meaning that while it's blocked at Thread.sleep, nothing will be updated on the UI and no new events will be processed.
Swing is a single threaded framework. You should never perform any blocking or long running operations from within the context of the Event Dispatching Thread.
Have a look at Concurrency in Swing for more details and How to use Swing Timers for a possible solution.
As a side note, you should NEVER modify the state if the UI or any variable the UI relies on from within any paint method. Painting should only paint the current state of the component, never modify it, this includes calling repaint directly or indirectly
For example...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class GameRunner extends JPanel {
private int xPos = 300;
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.blue);
g.fillRect(xPos, 400, 100, 100);
repaint();
}
public GameRunner() {
Timer timer = new Timer(1000, new ActionListener() {
private boolean state = false;
#Override
public void actionPerformed(ActionEvent e) {
if (state) {
xPos = 300;
} else {
xPos = 600;
}
state = !state;
repaint();
}
});
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(700, 500);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new GameRunner());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}
How can i attach the OpenGl display to a JFrame and so that when i close the JFrame is destroys the display? Here is my code so far:
package test.core;
import java.awt.BorderLayout;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Component;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import org.lwjgl.LWJGLException;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.GL11;
import static org.lwjgl.opengl.GL11.*;
public class Main {
private static CreateCanvas canvas;
private static CreateFrame frame;
private static int width = 800;
private static int height = 600;
public static void main(String[] args) throws InterruptedException {
startFrames();
startDisplay();
}
public static void cleanUp() {
Display.destroy();
}
private static void startDisplay() {
try
{
Display.setParent(canvas);
Display.create();
}catch(LWJGLException ex)
{
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
}
}
private static void startFrames()
{
Runnable r = new Runnable(){
#Override
public void run(){
frame = new CreateFrame();
JButton button = new JButton("BUTTON");
canvas = new CreateCanvas();
JPanel panel = frame.panel;
panel.add(canvas);
panel.add(button);
frame.add(panel);
canvas.setSize(300, 300);
frame.setSize(width, height);
frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
WindowListener listen = new WindowAdapter(){
#Override
public void windowClosing(WindowEvent we){
int result = JOptionPane.showConfirmDialog(frame, "Do you want to quit the Application?");
if(result == JOptionPane.OK_OPTION){
frame.setVisible(false);
cleanUp();
frame.dispose();
}
}
};
frame.addWindowListener(listen);
frame.setVisible(true);
}
};
SwingUtilities.invokeLater(r);
}
}
I had the opengl display attach to the JFrame before i did the runnable. But after adding the runnable the display now shows up the same size as my screen size. I have tried rearranging the
canvas.setSize();
and the
frame.setSize();
but nothing changes the opengl display is still the same size and when i try to close the JFrame first rather then close the display first i get this error:
Exception in thread "AWT-EventQueue-0" java.lang.IllegalStateException: From thread Thread[AWT-EventQueue-0,6,main]: Thread[main,5,main] already has the context current
which points me to my
Display.destroy();
which im guessing is telling me that i am not properly disposing the display? Can anyone help me attach the opengl display to the JFrame and fix the error above?
It appears that you started the Display in the "main" thread (which gives the main thread the current OpenGL context), but you're trying to destroy the display from a different thread, which in this case is the Event Dispatch Thread (EDT).
However, only one thread can have the current OpenGL context at a given time.
Although it is possible to change which thread has the current context, I don't think that's what you want to do here.
What we want to do here then is to destroy the display on the same thread that we created it on (the thread with the current OpenGL context). An approach I've seen is to use the Canvas's addNotify() and removeNotify() methods, which are run on the EDT, to set a flag that is checked on the OpenGL thread to determine when to destroy the display.
Also, the question mentioned problems about setting the size of the display. Your JFrame display size and Display size were not being set to what you desired because of how setSize() and LayoutManager's work. Please see the Java tutorials and documentation for details. In the following example, I have use one approach to address this issue.
So here is an example trying to stay close to the intent of the code posted in the question:
import java.awt.BorderLayout;
import java.awt.Canvas;
import java.awt.Dimension;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import org.lwjgl.LWJGLException;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
public class LWJGLTester {
private volatile boolean isRunning = false;
/*
* The question asker seemed to desire that the JFrame be 800x600 and
* that the Display be 300x300. Regardless of the desired sizes,
* I think the important thing is to set the Canvas and Display to the same sizes.
*/
private int frameWidth = 800;
private int frameHeight = 600;
private int displayWidth = 300;
private int displayHeight = 300;
private Thread glThread;
public static void main(String[] args) {
new LWJGLTester().runTester();
}
private void runTester() {
final JFrame frame = new JFrame("LWJGL in Swing");
frame.setSize(frameWidth, frameHeight);
frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
frame.addWindowListener(new WindowAdapter() {
#Override
public void windowClosing(WindowEvent we){
int result = JOptionPane.showConfirmDialog(frame, "Do you want to quit the Application?");
if(result == JOptionPane.OK_OPTION){
frame.setVisible(false);
frame.dispose(); //canvas's removeNotify() will be called
}
}
});
JPanel mainPanel = new JPanel(new BorderLayout());
JButton button = new JButton("BUTTON");
JPanel buttonPanel = new JPanel();
buttonPanel.add(button);
mainPanel.add(buttonPanel, BorderLayout.NORTH);
Canvas canvas = new Canvas() {
#Override
public void addNotify() {
super.addNotify();
startGL();
}
#Override
public void removeNotify() {
stopGL();
super.removeNotify();
}
};
canvas.setPreferredSize(new Dimension(displayWidth, displayHeight));
canvas.setIgnoreRepaint(true);
try {
Display.setParent(canvas);
} catch (LWJGLException e) {
//handle exception
e.printStackTrace();
}
JPanel canvasPanel = new JPanel();
canvasPanel.add(canvas);
mainPanel.add(canvasPanel, BorderLayout.SOUTH);
frame.getContentPane().add(mainPanel);
//frame.pack();
frame.setVisible(true);
}
private void startGL() {
glThread = new Thread(new Runnable() {
#Override
public void run() {
isRunning = true;
try {
Display.setDisplayMode(new DisplayMode(displayWidth, displayHeight));
Display.create();
} catch (LWJGLException e) {
//handle exception
e.printStackTrace();
}
// init OpenGL here
while(isRunning) {
// render OpenGL here
Display.update();
}
Display.destroy();
}
}, "LWJGL Thread");
glThread.start();
}
private void stopGL() {
isRunning = false;
try {
glThread.join();
} catch (InterruptedException e) {
//handle exception
e.printStackTrace();
}
}
}
Note: This is example was tested using lwjgl version 2.9.1, since that seemed to be the latest version available at the time the question was originally posted.
I am trying to make a JFrame and see the progress of the counter.
int i = 1;
while (i < 100000){
textField.setText(String.valueOf(i));
System.out.println(i);
i++;
}
When I start it I can see the progress at the console but the value of the textField does not change. It changes to 100000 when the loop ends.
How can I make it show the progress like in console?
There are a number of important differences between and other GUI toolkits like C#.
Firstly, Swing components SHARE a common native peer. In many other GUI frameworks, components have their own native peer, this affects the context in which how you can access these components.
Secondly, because Swing components share a common native peer, there are inherently un-thread safe (they all share the same message queue for example), this means you should never modify a UI component out side of the context of the Event Dispatching Thread.
Thirdly, you should never block the Event Dispatching Thread, this will prevent it from process new events, including paint requests.
In this context, you should should probably use s a javax.swing.Timer, which will allow you to schedule a callback (which will occur within the context of the EDT) at a regular interval, making it safe to use within the context of the Swing framework, for example...
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Counter {
public static void main(String[] args) {
new Counter();
}
public Counter() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JLabel label;
private Timer timer;
private int count;
public TestPane() {
label = new JLabel("...");
setLayout(new GridBagLayout());
add(label);
timer = new Timer(500, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
count++;
if (count < 100000) {
label.setText(Integer.toString(count));
} else {
((Timer)(e.getSource())).stop();
}
}
});
timer.setInitialDelay(0);
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
}
}
Take a look at Concurreny in Swing and How to Use Swing Timers for more details...
I am trying to make my JButton flicker red for this game I am creating. All the solutions on this website suggest using a thread and putting it to sleep or using a timer, however, the pause allays seems to come after the color change
Here is my code:
Color cb = board[Y1][X1].getBackground();
board[Y1][X1].setBackground(Color.RED);
//Pause
board[Y1][X1].setBackground(cb);
If I put a thread and put it to sleep on line 3 and comment out line 4 the pause will come before the JButton is turned red. (Note board is just a 2D array of JButtons)
There are any number reasons why this might be occurring and equally, any number of ways it might be fixed.
Based on your description, it sounds like you're trying to update the UI from outside of the Event Dispatching Thread.
Swing is a single thread environment, it's also not thread safe. Basically what this means is, there is an expectation that all interactions/changes to the UI are carried out within the context of the EDT. Failing to following this rule can lead to all sorts of weird and wonderful behaviour.
The simplest solution is to use a javax.swing.Timer, which allows you to schedule regular timed events which are guaranteed to be executed within the EDT, for example
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class FlashyButton {
public static void main(String[] args) {
new FlashyButton();
}
public FlashyButton() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JButton button;
private Color[] colors = new Color[]{Color.RED, Color.YELLOW};
public TestPane() {
button = new JButton("Flash Gorden");
button.setContentAreaFilled(false);
button.setBorderPainted(false);
button.setFocusPainted(false);
button.setOpaque(true);
button.setBackground(Color.YELLOW);
setLayout(new GridBagLayout());
add(button);
Timer timer = new Timer(500, new ActionListener() {
private int counter = 0;
#Override
public void actionPerformed(ActionEvent e) {
counter++;
if (counter % 2 == 0) {
button.setBackground(colors[0]);
} else {
button.setBackground(colors[1]);
}
}
});
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.dispose();
}
}
}
Take a look at Concurrency in Swing and How to Use Swing Timers for more details.
A more complex solution would allow you to use a Thread, but would require to update the UI by using SwingUtilities.invokeLater, which would place an event onto the EDT that would execute a Runnable interface, which you would use to update the UI. This could have synchronisation issues as the Thread you're calling from will have moved on before the actual event is triggered and could cause some dirty updates, unless you control the update process carefully...
I have created ImagePanel which is able to display images from the specified directory -> it sleeps 1 second and loads next image from the java project's directory.
It actually loads next image but it is not displayed(it does not refresh the panel), when it is done with all the files from the directory it shows only the last image from the directory. I would like to make it refresh after loading every image.
import java.awt.Color;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.border.LineBorder;
public class Okno extends JFrame {
JPanel jp;
ImagePanel ImagePanel;
JButton buttonExit;
JButton buttonWyjscie;
public Okno() {
}
public void createGUI() {
setSize(400, 400);
setLayout(new GridLayout());
buttonExit = new JButton("Exit");
buttonWyjscie = new JButton("Wyjscie");
// Sluchacz sluchacz = new Sluchacz();
// buttonExit.addActionListener(sluchacz);
buttonExit.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.exit(0);
}
});
jp = new JPanel();
jp.setBorder(new LineBorder(new Color(40, 120, 80), 4));
ImagePanel = new ImagePanel();
ImagePanel.setBorder(new LineBorder(Color.blue, 4));
jp.add(buttonExit);
add(jp);
add(ImagePanel);
setVisible(true);
slajd();
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
public void slajd() {
try {
File f = new File(".");
File[] tablicaPlikow = f.listFiles();
for (File el : tablicaPlikow) {
String rozszerzenie = el.getName().substring(
el.getName().length() - 3);
if (rozszerzenie.equals("jpg") || rozszerzenie.equals("peg")) {
System.out.println(rozszerzenie);
ImagePanel.setImage(el);
}
repaint();
}
setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new Okno().createGUI();
}
});
}
}
import java.awt.Graphics;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.File;
import javax.imageio.ImageIO;
import javax.swing.JPanel;
public class ImagePanel extends JPanel {
private BufferedImage image;
public ImagePanel() {
}
public ImagePanel(String sciezka) {
setImage(new File(sciezka));
}
public void setImage(File plik) {
try {
image = ImageIO.read(plik);
System.out.println("tutaj");
repaint();
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
}
public void paint(Graphics g) {
if (image != null) {
Image b = image.getScaledInstance(getWidth(), getHeight(),
Image.SCALE_FAST);
g.drawImage(image, 0, 0, getWidth(), getHeight(), null);
}
}
}
Sleeping in the EDT prevents swing from doing the painting, so you see only the last image. Instead of sleeping in the event dispatch thread, use a swing Timer to do repeated tasks:
private final ActionListener timerTask = new ActionListener() {
#Override
public void actionPerformed(final ActionEvent e) {
// Whatever you need to to that
showNextImage();
}
};
Timer timer = new Timer(1000, timerTask);
timer.start();
If loading the images is taking long time, consider using a background task for preloading the next one in memory, without blocking the EDT.
The short answer could be: in method slajd, after call to repaint();, add
Thread.sleep(1000);
However, this is completely contrary to the event-based nature of Swing, and in this particular case doesn't even work because, for efficiency reasons, Swing does not execute repaint() calls immediately. It "collects" and executes them only once after all event processing has concluded. If you include an sleep period (or any other long-running operation) in an event handler (directly or indirectly), repainting will be delayed and the application be extremely unresponsive to the point, as in this case, of not really be working.
What you need to do is in createGUI instantiate a Swing Timer (javax.swing.Timer; do not confuse it with java.util.Timer, or Timer classes in a few other packages) that fires every 1 second instead of calling slajd(). First firing should be immediate, or you could include code to display the first file. The associated listener, which would replace slajd() should keep track of the next file to display. You will most probably want to make this listener a full-fledged class with fields to support this tracking, a pointer to the ImagePanel where to display files, etc.
For more information, read Java's Tutorial on How to Use Swing Timers