Reputation: 145
My project contain two parts: a logical modules and a gui interface. Both send their references to other.
I have an Key Listener when user sent a message. In this listener I call same gui changes before logic and same changes after logic. The problem is both changes will be display at the same time, at the end of execution.
How to force the GUI update in real time?
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Main {
// Init DecisionEngine and GUIApplication
private static final Logic _logic = new Logic();
private static final GUI _gui = new GUI();
public static void main(String[] args) {
// Set DecisionEngine reference in GUIApplication and viceversa
_gui.setLogicReference(_logic);
_logic.setGUIRefecence(_gui);
// User send a message
_gui.textInput.addKeyListener(new KeyListener(){
@Override public void keyPressed(KeyEvent e){
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
_gui.somethingChaged1();// CHANGE 1 GUI
_logic.thinking();// LOGIC PROCESSING (3sec)
_gui.somethingChaged2();// CHANGE 2 GUI
e.consume();// Stopping adding an Enter after message
}
}
@Override public void keyTyped(KeyEvent e) {}
@Override public void keyReleased(KeyEvent e) {}
});
}
private static class Logic {
GUI gui_ref;
public Logic() {}
private void setGUIRefecence(GUI _gui) {gui_ref = _gui;}
private void thinking() {
try {Thread.sleep(3000);} catch (InterruptedException ex) {}
}
}
private static class GUI {
Logic logic_ref;
private JFrame frame;
public JTextArea textInput;
private JLabel isTyping;
public GUI() {
frame = new JFrame();
textInput = new javax.swing.JTextArea(5, 20);
isTyping = new JLabel("Normal mode");
frame.setSize(new Dimension(200,300));
frame.add(textInput, BorderLayout.PAGE_START);
frame.add(isTyping, BorderLayout.CENTER);
frame.revalidate();
frame.repaint();
frame.setVisible(true);
}
private void setLogicReference(Logic _logic) {logic_ref = _logic;}
private void somethingChaged1() {isTyping.setText("loading...");System.out.println("status changed in 'loading...'");}
private void somethingChaged2() {isTyping.setText("is done.");System.out.println("status changed in 'is done.'");}
}
}
Upvotes: 1
Views: 1324
Reputation: 1420
Your problem is right here:
@Override public void keyPressed(KeyEvent e){
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
_gui.somethingChaged1();// CHANGE 1 GUI
_logic.thinking();// LOGIC PROCESSING (3sec)
_gui.somethingChaged2();// CHANGE 2 GUI
e.consume();// Stopping adding an Enter after message
}
}
You are performing long running fuctions in the event dispatch thread. GUIs update in the event dispatch thread so until you exit your listener no GUI changes will be seen. Try this:
@Override public void keyPressed(KeyEvent e){
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
Thread t = new Thread(new Runnable() {
_gui.somethingChaged1();// CHANGE 1 GUI
public void run() {
_logic.thinking();// LOGIC PROCESSING (3sec)
//Changed based on comment from daiscog
SwingUtilities.invokeLater(new Runnable() {
public void run() {
_gui.somethingChaged2();// CHANGE 2 GUI
}
});
}
}, "Logic Code");
t.start();
e.consume();// Stopping adding an Enter after message
}
}
It's also a good practice to name your threads so if you have to debug you can easily find the threads you created.
Upvotes: 0
Reputation: 12057
Just to clarify the comment I made in markbernard's answer, this is how it should be done:
@Override public void keyPressed(KeyEvent e){
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
// Change the GUI from the EDT (current thread):
_gui.somethingChaged1();
// start a new Thread to do the long processing:
Thread t = new Thread(new Runnable() {
public void run() {
_logic.thinking();// LOGIC PROCESSING (3sec)
// when the processing in this new Thread is complete,
// update the GUI again via the EDT:
SwingUtilities.invokeLater(new Runnable(){
public void run() {
_gui.somethingChaged2();
}
});
}
}, "Logic Code");
t.start();
e.consume();// Stopping adding an Enter after message
}
}
Upvotes: 2