So I've been trying to work with the code in this post for making a console window in a JTextArea. That code seems to be functioning with mine, but I'm running into an odd problem.
My program: I'm basically building a quick and dirty gui for a command line tool I made recently. The only thing that the gui contains is a button that says "Start Automation Engine" and then it has a JTextArea that is supposed to display any text my program sends to System.out.println().
At the moment it displays nothing, though the program itself is running and working (and should be displaying output as a result.) I have noticed that when I click the button on my gui the button stays depressed while the program runs. This has lead me to believe that the JFrame is not updating while the program is running, thus the JTextArea, as it's child, is not updating. This is not so good...
Is there a way to get that JTextArea to update while the program is running in the background?
Here's my code for the JFrame, btw, if you'd like to look at it to get a better idea of what I'm talking about. It was mostly constructed in WindowBuilder in Eclipse. The only thing I did was add a button listener to startAutmoatorEngineButton and then add the last few lines of the initalize() method to set the JTextArea (engineOutput) as the System.out.
public class EngineGUI {
private JFrame frmAutomatorEngine;
private File logPath = new File("<redacted>", "<redacted>");
private File masterFile = new File("<redacted>", "<redacted>");
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
EngineGUI window = new EngineGUI();
window.frmAutomatorEngine.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the application.
*/
public EngineGUI() {
initialize();
}
/**
* Initialize the contents of the frame.
*/
private void initialize() {
frmAutomatorEngine = new JFrame();
frmAutomatorEngine.setType(Type.UTILITY);
frmAutomatorEngine.setResizable(false);
frmAutomatorEngine.setTitle("Automator Engine");
frmAutomatorEngine.setBounds(100, 100, 636, 335);
frmAutomatorEngine.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JMenuBar menuBar = new JMenuBar();
frmAutomatorEngine.setJMenuBar(menuBar);
JMenu mnEngine = new JMenu("Engine");
menuBar.add(mnEngine);
JMenuItem mntmLoadMasterFile = new JMenuItem("Load Master File...");
mnEngine.add(mntmLoadMasterFile);
JMenuItem mntmExit = new JMenuItem("Exit");
mnEngine.add(mntmExit);
frmAutomatorEngine.getContentPane().setLayout(null);
JTextArea engineOutput = new JTextArea();
engineOutput.setBounds(10, 48, 600, 217);
frmAutomatorEngine.getContentPane().add(engineOutput);
JButton startAutomatorEngineButton = new JButton("Start Automator Engine");
startAutomatorEngineButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
MasterFile master = null;
try {
master = new MasterFile(masterFile, logPath);
} catch (FileNotFoundException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
AutomationLoop theLoop = new AutomationLoop(master);
theLoop.startLoop();
}
});
startAutomatorEngineButton.setBounds(441, 11, 169, 23);
frmAutomatorEngine.getContentPane().add(startAutomatorEngineButton);
//Set up textArea to be used as output stream for program
TextAreaOutputStream outputStream = new TextAreaOutputStream(engineOutput);
PrintStream printStream = new PrintStream(outputStream);
System.setOut(printStream);
//System.setErr(printStream);
}
}
It's hard to tell for sure because I don't know what your AutomationLoop and TextAreaOutputStream classes do, but it sounds like a threading problem.
All your Swing code needs to execute in the Event Dispatch Thread. If you have a long running code that is not updating the GUI, then you probably want it running another thread, otherwise the GUI doesn't get a chance to update. From your behavior, it sounds like theLoop.startLoop() is running in the Event Dispatch Thread, and so the GUI never gets a chance to update itself.
Does theLoop.startLoop() start a new thread? If not it probably should; otherwise until that code finishes executing, your GUI will not update.
Related
I have a main class who hold the frame and the JMenu, and another class for the game extending from JPanel. How is possible for when is clicked one option from jMenu create a new game?
At the moment, I have this
class Application {
private static Game game;
private static TheFrame frame;
private static JMenuBar menu;
public Application(){
frame = new TheFrame();
menu = new JMenuBar();
JMenu file = new JMenu("File");
menu.add(file);
frame.setJMenuBar(menu);
JMenuItem newGame = new JMenuItem("New Game");
newGame.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
newGame();
}
});
file.add(newGame);
JMenuItem scores = new JMenuItem("Show Scores");
scores.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
new Score();
}
});
file.add(scores);
JMenuItem exit = new JMenuItem("Exit!");
exit.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.exit(0);
}
});
file.add(exit);
}
private void newGame(){
// frame.getContentPane().removeAll();
frame.getContentPane().setBackground(Color.pink);
game = new Game();
frame.addGameToCanvas(game);
game.runMenu();
}
/**
* Main method
* #param args
*/
public static void main(String args[]) {
Application app = new Application();
}
}
But when I click on new Game, the frame get black and doesn't show anything. I had before this on the command line, and all work fine, but when I try to do this using the menu doesn't work. Any suggestions?
EDIT
the code for frame.addGametoCanva(). What it does, is add the game object to a canvas.
public void addGameToCanvas(Game g){
canvas = g;
add(canvas, BorderLayout.CENTER);
invalidate();
validate();
repaint();
}
And yes, the game object, have a finite loop, for the user inputs (from console)
the game object, have a finite loop, for the user inputs (from console)
The Event-Dispatch thread (EDT) is single threaded - it takes care of painting, event handling, etc... If one attempts to perform a long running tasks on this thread, like waiting for user input from a different source (like the console) the EDT can lock up (in other words an unresponsive UI).
Any long running tasks should be placed inside its own Thread or ran via a SwingWorker. Further, most calls on a non-EDT thread should be dispatched to the EDT using SwingUtilities.invokeLater or SwingUtilities.invokeAndWait (this includes construction of the UI - note the main method operates on a non-EDT thread).
Although, I'm not entirely sure why the mix of UI (eg Console and Swing) - you might consider choosing just a single UI.
I have a Java program where I plan to take input from GUI, and use that input later for processing in main(). I am using Eclipse.
I am sending an HW object(called HWObj) to the GUI JFrame, and checking for a boolean field in the object to continue processing in main().
InputWindow is custom object which extends JPanel implements ActionListener
It contains a reference to the current JFrame(parentFrame). On clicking a JButton in InputWindow, I have written a custom ActionListener which sets the value of HWObj.check to true and disposes the parentFrame. This should cause execution to resume in main().
Code for HW class is as below :
import java.awt.*;
import javax.swing.*;
public class HW {
//globals
boolean check;
public HW() {
//initialisations
check = false;
}
public static void main(String args[]) {
final HW problem = new HW();
try {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
//Create and set up the window.
JFrame frame = new JFrame("Select folders");
frame.setPreferredSize(new Dimension(640, 480));
frame.setResizable(false);
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
InputWindow Directories = new InputWindow(problem, frame);
Directories.setOpaque(true);
frame.add(Directories);
//Display the window.
frame.pack();
frame.setVisible(true);
}
});
} catch(Exception e) {
System.out.println("Exception:"+e.getLocalizedMessage());
}
while(!problem.finish);
//Do processing on problem
System.out.println("Done");
}
}
The Actionlistener in the gui is as follows:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class InputWindow extends JPanel
implements ActionListener {
private static final long serialVersionUID = 4228345704162790878L;
HW problem;
JFrame parentFrame;
//more globals
public InputWindow(HW problem, JFrame parentFrame) {
super();
this.setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
this.parentFrame = parentFrame;
this.problem = problem;
JButton finishButton = new JButton("Finish");
finishButton.setActionCommand("fin");
finishButton.addActionListener(this);
gbc.gridx = 0;
gbc.gridy = 0;
this.add(finishButton, gbc);
//Initialize buttons and text areas and labels
//Code removed for ease of reading
}
public void actionPerformed(ActionEvent e) {
String command = e.getActionCommand();
if(command.equals("fin")) {
//Do a lot of stuff, then
this.removeAll();
parentFrame.dispose();
problem.check = true;
}
}
}
I have checked, and the control to this function comes normally on button click.
Now, I would expect it to return to main, and exit the while loop, and continue processing.
This does not happen. The debugger in eclipse shows only the main thread running, and when I try to pause it, I see that the thread is stuck in the while loop. But if I try to step through, it exits the while loop as expected, and continues. However, it gets remains stuck in the while loop until I manually try to debug it.
What is the problem? Why is it not resuming the main thread as expected?
How do I resolve this issue?
Your problem is to do with how the Java memory model works. The loop in your main thread will be checking a stale value of check.
When you enter the debugger, the memory is forced to be updated, so that's why it starts working at that point.
If you mark your variable as volatile, that will force the JVM to ensure that all threads are using the up-to-date value:
volatile boolean check;
You can read more about volatile and the Java memory model in the documentation.
It looks like you're using a JFrame where you should be using a modal JDialog. If you use the modal JDialog for an input window, you will know exactly when it is "finished" since code flow will resume from the calling code from right after when the dialog was set visible.
Either that or if you are trying to swapviews, then use a CardLayout to swap your view, and use an observer type pattern to listen for change of state.
I am learning to build a GUI. Now I have a button. After I click the button, the program run some computation. I want to redirect the output and add it to the frame I create. How ever, I found out it only updates when I re-size the frame but not automatically. Would anyone point it to me how to do it automatically.
part of the code is
jbtnAlpha.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae){
Runnable runnable = new SVMthread(fnm_train,fnm_test,fnm_result,jfrm);
Thread t = new Thread(runnable);
t.start();
}
});
SVMthread is defined here
public class SVMthread implements Runnable{
private String fnm_train;
private String fnm_test;
private String fnm_result;
private JFrame jfrm;
SVMthread(String fnm_train,String fnm_test,String fnm_result,JFrame jfrm){
this.fnm_train=fnm_train;
this.fnm_test=fnm_test;
this.fnm_result=fnm_result;
this.jfrm=jfrm;
}
public void run(){
try {
jfrm.add(new ResultPanel());
LibSVMTest.SVMrun(fnm_train,fnm_test,fnm_result);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
The ResultPanel is where I redirect the output
public class ResultPanel extends JPanel {
private JTextArea textArea = new JTextArea(15, 30);
private TextAreaOutputStream taOutputStream = new TextAreaOutputStream(
textArea, "Test");
public ResultPanel() {
setLayout(new BorderLayout());
add(new JScrollPane(textArea, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER));
System.setOut(new PrintStream(taOutputStream));
}
}
Never make most Swing calls in a background thread like you're doing. That's grabbing a tiger by its tail -- it will turn around and bite you.
After adding or removing a component from a Swing container, call revalidate() and repaint() on the container to get it to re-layout its components and repaint them.
Edit
Regarding your comment:
I am new to this. Would you explain more what do you mean by background? Sorry if it is a dumb question.
Google: Concurrency in Swing. It will answer your questions.
Try to use only :
frame.revalidate();
I found this code here: how to display console output in java JTextarea one by one in a loop when button action is triggered
and it displays console output in a jtextarea.
I have added this class as action in a jmenuitem. So that it appears when I want and when I run other classes it will show the output there. However when I launch it, it works properly, but if I try and launch another class which will show output in console and accept userinput, the jtextarea which is supposed to show console output at the same time, it freezes. How could I make it so that it stays tuned, despite invoking other classes/frames? Thanks in advance
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class DynamicWrite implements ActionListener
{
JFrame frame = new JFrame("TextArea");
JTextArea tArea = new JTextArea(10,20);
JButton button = new JButton("Click");
JScrollPane pane = new JScrollPane(tArea);
SwingWorker worker;
String s= "Java is an Object Oriented Programming langauge...Java is static typed language...asbfldfjsdj";//some random String
public void prepareAndShowGUI()
{
Container container = frame.getContentPane();
container.add(pane);container.add(button,BorderLayout.NORTH);
tArea.setLineWrap(true);
tArea.setWrapStyleWord(true) ;
button.addActionListener(this);
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
public void actionPerformed(ActionEvent evt)
{
if(evt.getSource()==button)
{
tArea.setText("");
if (worker!=null)
{
worker.cancel(true);
}
worker = new SwingWorker()
{
#Override
protected Integer doInBackground()//Perform the required GUI update here.
{
try
{
for(int i = 0;i<s.length();i++)
{
tArea.append(String.valueOf(s.charAt(i)));
Thread.sleep(5);
}
}catch(Exception ex){}
return 0;
}
};
worker.execute();//Schedules this SwingWorker for execution on a worker thread.
}
}
public static void main(String st[])
{
DynamicWrite dyna = new DynamicWrite();
dyna.prepareAndShowGUI();
}
}
The example you have (apart from from being a bad example as outlined by Hovercraft Full of Eels) has nothing to do with redirecting console output.
If you want to redirect the standard out to the text area, take a look at How to set output stream to TextArea for an example
If you want to redirect the output of some other process, then you can only do this if you've launched the process yourself, there's no (easy way that I know of) to connect to the standard out/in of another running process (that your program didn't start itself directly)
Check out Printing a Java InputStream from a Process for an example
I am creating a popup JFrame that will have a message and yes/no buttons. I am using this method in 2 ways. In 1, the main program calls this method and in the other, this method is called directly after a previous JFrame is closed. This method works when being called form the main program, but when another JFrame calls it, the JFrame created in this method shows up completely blank and the GUI freezes. I cannot exit out of the JFrame, but I can move it. The freezing is a result of the Thread.yield because response is always null, but in what instances will the JFrame fail to be created properly?
Note: response is a static variable. Also when this JFrame is created by another JFrame, the original JFrame does not exit correctly. That JFrame has a JComboBox, and the selected option is frozen on the dropdown. When it does not call this method, it closes properly.
public static String confirmPropertyPurchase(String message)
{
response = null;
final JFrame confirmFrame = new JFrame("Confirm");
confirmFrame.addWindowListener(new WindowAdapter(){
public void windowClosing(WindowEvent ev){
response = "No";
}
public void windowDeactivated(WindowEvent e) {
confirmFrame.requestFocus();
}
});
final JPanel confirmPanel = new JPanel();
final JButton yes = new JButton();
final JButton no = new JButton();
yes.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent arg0){
response = "Yes";
confirmFrame.setVisible(false);
}
});
no.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent arg0){
response = "No";
confirmFrame.setVisible(false);
}
});
final JLabel confirmLabel = new JLabel(" " + message);
yes.setText("Yes");
yes.setPreferredSize(new Dimension(100, 100));
no.setText("No");
no.setPreferredSize(new Dimension(100,100));
confirmFrame.add(confirmLabel, BorderLayout.CENTER);
confirmPanel.add(yes);
confirmPanel.add(no);
confirmFrame.add(confirmPanel, BorderLayout.AFTER_LAST_LINE);
confirmFrame.setPreferredSize(new Dimension(520, 175
));
confirmFrame.pack();
confirmFrame.setVisible(true);
while(response == null)
{
Thread.yield();
}
return response;
}
Again, you shouldn't be using a JFrame as a dialog. In fact your whole bit of code can be replaced with a simple JOptionPane. e.g.,
Component parent = null; // non-null if being called by a GUI
queryString = "Do you want fries with that?";
int intResponse = JOptionPane.showConfirmDialog(parent, queryString,
"Confirm", JOptionPane.YES_NO_OPTION);
myResponse = (intResponse == JOptionPane.YES_OPTION) ? "Yes" : "No";
System.out.println(myResponse);
And this:
while(response == null)
{
Thread.yield();
}
should never be called on the main Swing thread, the EDT or event dispatch thread. The reason the code works when it does is because you're calling this little bit above off of the EDT, but when you call it on the EDT it freezes the EDT and thus the entire GUI. Simply don't do it.
You can't do this, plain and simple. There's only one event thread, and while you're sitting in a loop waiting for somebody to click in your JFrame, you're tying up that thread such that no events can be handled.
Don't try to create your own dialog out of a JFrame -- use JOptionPane or a JDialog, which are designed to handle this situation for you internally.