public class Interface extends JFrame
{
public Interface() throws InterruptedException
{
//windowsetup
pan = new MainPanel();
Thread t = new Thread(pan);
t.start();
add(pan);
addKeyListener(pan);
}
public static void main(String[] Args) throws InterruptedException
{
Interface proj = new Interface();
}
}
////////
public class MainPanel extends JPanel implements KeyListener, Runnable
{
public void paint(Graphics g)
{
//painting
}
public void run()
{
while(true)
{
this.repaint();
//other codes
try
{
Thread.sleep(10);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
}
I have a code that looks like this. When I press the run button in Eclipse, sometimes it works properly, but sometimes the there would be nothing in the window at all, nothing painted and keys doesn't work.
I heard that using threads in GUI might cause concurrency problems, and I wonder if this is the case, and what I should do to correct it. Thanks.
stackoverflow.com/questions/369823/java-gui-repaint-problem
I found that this is exactly my case, and the solutions worked.
Though it appears my understanding to Swing is yet too shallow, I must go over the tutorial.
Related
This question already has an answer here:
Loop doesn't see value changed by other thread without a print statement
(1 answer)
Closed 11 months ago.
I have some problem with my code. I want to implement a keylistener. I have a keyHandler class which takes care about keyinput and a while loop in the main class to check if a certain key is pressed or not. I dont understand the behavior of my code. the strange thing is that every thing works when I put the System.out.println("hello") command in front of my if statement. but when i comment it out my programm doesnt realize that i press the key Im checkin in my if statement. I think i could find a workaround. but i would be very glad to understand this strange behavior. why is this happening. Sorry for my bad english. I hope you guys can help me.
public static void main(String[] args) {
boolean running = true;
JFrame window;
KeyHandler k = new KeyHandler();
window = new JFrame();
window.setVisible(true);
window.addKeyListener(k);
while (running) {
//System.out.println("hello");
if (k.isKeyPressed(KeyEvent.VK_W)) {
System.out.println("--------------------------------------------------------------------------");
}
}
}
//here is the KeyHandler class
public class KeyHandler implements KeyListener {
private boolean[] keysPressed = new boolean[128];
#Override
public void keyTyped(KeyEvent e) {
}
#Override
public void keyPressed(KeyEvent e) {
keysPressed[e.getKeyCode()] = true;
System.out.println(e.getKeyChar());
System.out.println(keysPressed[e.getKeyCode()]);
}
#Override
public void keyReleased(KeyEvent e) {
keysPressed[e.getKeyCode()] = false;
System.out.println(e.getKeyChar());
System.out.println(keysPressed[e.getKeyCode()]);
}
public boolean isKeyPressed(int keyCode) {
return keysPressed[keyCode];
}
}
The whole purpose of events and event handling is you don't need a loop to listen for events. Simply start your UI, add the listeners to a list, and allow the listeners to handle the processing.
Create a listener
public interface MyListener extends EventListener {
public void doSomething();
}
Now use it. With this code it just spits out some text when W is pressed, but the listeners could be another component or anything that uses the interface. No need for extra loops.
public class Main {
private EventListenerList listenerList = new EventListenerList();
public Main() {
JFrame frame = new JFrame();
addListener(new MyListener() {
#Override
public void doSomething() {
System.out.println("Hello 1");
}
});
addListener(new MyListener() {
#Override
public void doSomething() {
System.out.println("Hello 2");
}
});
frame.addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_W) {
fireMyEvent();
}
}
});
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public void addListener(MyListener listener) {
listenerList.add(MyListener.class, listener);
}
private void fireMyEvent() {
MyListener[] listeners = listenerList.getListeners(MyListener.class);
if (listeners == null) {
return;
}
for (MyListener listener : listeners) {
listener.doSomething();
}
}
public static void main(String [] args) {
new Main();
}
}
Here's a link that might help. I would not check for a key being pressed by that method. You are creating a resource hog, first off: by having
boolean running = true;
you then enter a while loop,
while (running) {
do x;
}
this can create a spin lock on some systems, this is a very bad practice. As user Lei Yang stated it is really not needed especially with the classes we have today and modern GUI's, your creating an endless loop. One this most certainly is a way to slow down a system, two you really can't continue coding past that point as you have no way to exit the loop. Some IDE's also have a check that won't allow your application to start if you have a loop that is infinite, almost all will at least give you a warning. you should at least if you are looking for a certain key and have to implement it that way do:
while (running) {
//System.out.println("hello");
if (k.isKeyPressed(KeyEvent.VK_W) = "e") {
running = false;
}
}
at least that won't be an endless loop.
(Sorry I can't get a working E.g. of the issue, I need help for my help!)
I'm in the process of creating a custom game engine and have come upon an issue whereby while the game is running - the game stops taking input
I've checked and the program seems to continue running in the background. It also doesn't seem to vary with different machines
(My main device is a Mac Book Pro 2011)
import java.awt.event.*;
import java.io.IOException;
import java.awt.*;
import javax.swing.*;
public class Focus extends JFrame implements KeyListener {
private static final long serialVersionUID = 1L;
char currKey = '\0';
public static void main(String[] args) throws IOException {
SwingUtilities.invokeLater(new Runnable() {public void run() {new UIManager();}});
}
public Focus() throws IOException {
Container contentPane = getContentPane();
contentPane.add(new DrawCanvas());
addKeyListener(this);
setDefaultCloseOperation(EXIT_ON_CLOSE);
pack();
setFocusable(true);
setVisible(true);
}
private class DrawCanvas extends JPanel {
private static final long serialVersionUID = 1L;
public void paintComponent(Graphics pen) {
//Drawloop
if(currKey == 'k') {
//This is the code that randomly stops running
System.out.println("Yo");
}
try {
Thread.sleep(10);
}
catch (InterruptedException e) {
e.printStackTrace();
}
repaint();
}
}
#Override
public void keyTyped(KeyEvent e) {
currKey = e.getKeyChar();
}
#Override
public void keyPressed(KeyEvent e) {
currKey = '\0';
}
#Override
public void keyReleased(KeyEvent e) {
}
}
The code looks right to me (But then, it always does) and the only possible tripping point is that AWT is Instantiated in Main, Run in UIManager and the movement code resides in player though I don't know enough about AWT to know whether this would be the case and relocating the code in a backup lead to a program crash. Any help would be greatly appreciated.
Turns out it was an issue with Java and MacOS key repeats- fixed in a newer Java version
To fix update java
I work on a Java development software with Swing and I have a problem with my code, I want to display an image with the LoadingFrame class, its main work but when I call the constructor and the start() method in my main class, the frame opens but the image doesn't display (I have no Exception).
Why it doesn't work with my main class?
public class LoadingFrame
{
private JFrame frame;
public LoadingFrame()
{
frame = new JFrame();
frame.setSize(800, 600);
frame.setLocationRelativeTo(null);
frame.setUndecorated(true);
frame.setContentPane(new Panneau());
}
public void start()
{
frame.setVisible(true);
}
public void stop()
{
frame.setVisible(false);
}
public static void main(String[] args)
{
LoadingFrame l = new LoadingFrame();
l.start();
try
{
Thread.sleep(3000);
}
catch(Exception e)
{
e.printStackTrace();
}
l.stop();
}
}
public class Panneau extends JPanel
{
public void paintComponent(Graphics g)
{
System.out.println("hello");
try
{
Image img = ImageIO.read(new File("Images/loading.png"));
//g.drawImage(img, 0, 0, this);
//Pour une image de fond
g.drawImage(img, 0, 0, this.getWidth(), this.getHeight(), this);
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
The App class is my main class :
public class App {
//Attributes used to display the application
private JFrame frame;
//Attribute which display a waiting frame
private static LoadingFrame loadingFrame;
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
loadingFrame = new LoadingFrame();
loadingFrame.start();
App window = new App();
loadingFrame.stop();
window.frame.setVisible(true);
}
catch (Exception e)
{
e.printStackTrace();
}
}
});
}
/**
* Create the application.
*/
public App()
{
initialize();
synchronizeScriptReferenceList();
synchronizeTests();
}
[...]
}
I was able to get this to work from App.java. For some reason, using EventQueue isn't cutting it. I tried to use SwingUtilities as well, but that doesn't work either. Finally I tried just get rid of the Thready-stuff in App.main at just straight up running it in the main thread. For some reason, this works when the other approaches do not! Here is my code:
// In the App class:
public static void main(String[] args) {
try {
loadingFrame = new LoadingFrame();
loadingFrame.start();
App window = new App();
loadingFrame.stop();
window.frame.setVisible(true);
}
catch (Exception e)
{
e.printStackTrace();
}
}
When I used this code, I got it to work! (for some reason unknown to me), And here's a bonus rewrite of the Panneau class:
class Panneau extends JPanel
{
Image img;
public Panneau() {
try
{
img = ImageIO.read(new File("Images/loading.png"));
}
catch (IOException e)
{
e.printStackTrace();
}
}
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
g.drawImage(img, 0, 0, getWidth(), getHeight(), this);
}
}
There are two main difference with this class; problems which I addressed. Here they are:
I call super.paintComponent as the very first method in our own paintComponent
I only load the loading image once, in the constructor, and not every single time I want to draw, which moves everything along much smoother. (you don't want the loading screen to be CPU heavy, do you?)
Hopefully, with these improvements, I hope you can make your program work! It worked with me, so I wish the best of luck to you.
P.S. Don't call frame.pack(), that was a mistake on my part. For some reason, I think it doesn't work well with undecorated windows.
I have 2 classes defined below:
public class TextsManager extends Thread {
LinkedList<String> lstOfPendingStr = new LinkedList<String>();
boolean stopLoop = false;
JTextArea txtArea;
public void run()
{
while (!stopLoop)
{
while (!lstOfPendingStr.isEmpty())
{
String tmp = lstOfPendingStr.getFirst();
this.txtArea.append(tmp);
lstOfPendingStr.removeFirst();
}
try {
Thread.sleep(0); // note: I had to force this code
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public void AddNewStr(String newStr)
{
this.lstOfPendingStr.add(newStr);
}
}
And
public class ClientApp {
private JFrame frame;
private JTextField textField;
private JTextArea textArea;
static private TextsManager txtManager;
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
ClientApp window = new ClientApp();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the application.
*/
public ClientApp() {
initialize();
/*
* Client app
*/
txtManager = new TextsManager(textArea);
txtManager.start();
}
/**
* Initialize the contents of the frame.
*/
private void initialize() {
frame = new JFrame();
textArea = new JTextArea();
textField = new JTextField();
textField.addKeyListener(new KeyAdapter() {
#Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER)
{
txtManager.AddNewStr(textField.getText() + "\n");
textField.setText("");
}
}
});
}
}
The program will read User Input from textField, pass it into TextsManager.lstOfPendingStr. Then, on each loop inside TextsManager.run(), it will check for existed members in lstOfPendingStr and output them via txtArea.
The problem is that if I removed the code Thread.sleep(0) inside run(), the run() then apparently stopped working. Despite lstOfPendingStr had been successfully updated with new elements, codes inside the loop while(!lstOfPendingStr.isEmpty()) would not ever to be called.
I put hard codes such as System.out.println or Thread.sleep(0) (as in the provided code) inside the while(!stopLoop), then it worked fine.
Although, I managed to solve the problem by forcing the thread to sleep for a few miliseconds, I want to know the reason behind this issue.
I appreciate your wisdom.
Regard :)
You have a couple of problems.
You are calling methods on lstOfPendingStr from two threads, but initialized it with LinkedList, which is not thread-safe. You should use a thread safe class, LinkedBlockingQueue seems the best options as far as I understood from your code.
Inside the thread you are calling JTextArea#append(). As all AWT/Swing methods, you can not call them from arbitrary threads, but only from the AWT thread. Wrap the call inside an invokeLater block.
The fact that sleep appears to make your code work is just a sign of the concurrency problems.
When the program starts, a new JFrame is created. Once the user clicks the start button a thread is created and started. Part of this threads execution is to validate the data on the form and then execute with that data. Once the data has been validated the thread calls dispose() on the original frame and then creates a new JFrame that acts as a control panel.
There is also an automatic mode of the program that doesn't display any GUI at all, this mode reads data from a configuration file and then starts the execution thread and runs everything but without the control panel.
I want the program to end once the thread completes, but in GUI mode, only if the user has closed the control panel as well.
Is it possible to make the thread wait for the frame to close. I assuming that the frame is run from it's own Thread? or is that not the case.
Thanks.
The answer you chose is a little awkward. Using Thread.sleep(1000) will check for window state every second. It is not a performance issue, but just bad coding style. And you may have a one second response time.
This code is a little bit better.
private static Object lock = new Object();
private static JFrame frame = new JFrame();
/**
* #param args
*/
public static void main(String[] args) {
frame.setSize(300, 300);
frame.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
frame.setVisible(true);
Thread t = new Thread() {
public void run() {
synchronized(lock) {
while (frame.isVisible())
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Working now");
}
}
};
t.start();
frame.addWindowListener(new WindowAdapter() {
#Override
public void windowClosing(WindowEvent arg0) {
synchronized (lock) {
frame.setVisible(false);
lock.notify();
}
}
});
t.join();
}
You can make reference from your thread to the JFrame. Then set the default close operation of JFrame to HIDE_ON_CLOSE. If the JFrame is closed, you can stop the thread.
Example code:
import java.awt.Dimension;
import javax.swing.JFrame;
public class FrameExample extends JFrame {
public FrameExample() {
setSize(new Dimension(100, 100));
setDefaultCloseOperation(HIDE_ON_CLOSE);
setVisible(true);
}
private static class T implements Runnable {
private FrameExample e;
public T(FrameExample e) {
this.e = e;
}
#Override
public void run() {
while (true) {
if (e.isVisible()) {
// do the validation
System.out.println("validation");
try {
Thread.sleep(1000);
} catch (InterruptedException e1) {
break;
}
}
}
}
}
public static void main(String[] args) {
FrameExample frameExample = new FrameExample();
new Thread(new T(frameExample)).start();
}
}
All Swing components, including JFrame, are managed by a single thread, called the Event Dispatch Thread, or EDT. (It's possible to call methods on Swing objects from other threads, but this is usually unsafe, except in a few cases not relevant here.)
You'll probably accomplish what you want here by putting the data validation and execution code in its own object which is otherwise completely unaware of the outside world. Then, call it from one of two other objects: one that manages a GUI, and another that runs in "automatic mode".