Java Swing Program- Input from GUI to Main getting stuck - java

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.

Related

How to check JToggleButton state in a class from another class using IF-statement that have actionPerformed within it?

EDIT : I found my problem but still don't have a clue for why this happen, I'm still not finished Online Lectures from Professor Mehran
Sahami (Stanford), maybe I'll find an answer if I push on on the
lecture videos.
The problem is I remove my other components methods before my button
method for efficient posting space, so I should put my JToggleButton
method after my main JFrame method for it to work, but what if my
other components inherit other class too? Which method should I put first to make all of components works? That I'll found out with
practicing java more.
Thank you #Dan and #SebVb for answers and suggestions, sorry if this
just a beginners mistake :)
I am learning java for a month now and already had simple project for learning but now I have problems with JToggleButton, ItemEvent, and actionPerformed included in If-statement.
I've searching for a week for examples on using actionPerformed within if-statement that have ItemEvent from another class but i can't find a same problem to produce a working result.
I'm trying to make a window scanner that will scan only if toggle button is selected then paint JPanel using buffered image (repaint every 100 millisecond) and disposed it if toggle button is deselected, but I think my approach to do it is wrong. I have one main class and two sub-classes like these:
Main class:
public class WindowScanner {
public static void main(String[] args) {
new Window().setVisible(true);
}
}
Window class:
class Window extends JFrame {
static JToggleButton captureButton = new JToggleButton("CAPTURE");
#SuppressWarnings("Convert2Lambda")
public Window() {
// JFrame looks codes
/** EDIT: these components method should be written after button method
* JPanel looks codes
* JLabel looks codes
* END EDIT
*/
add(captureButton);
// capture button default looks code
ItemListener captureListener = new ItemListener(){
#Override
public void itemStateChanged(ItemEvent captureButtonEvent) {
int captureState = captureButtonEvent.getStateChange();
if(captureState == ItemEvent.SELECTED){
// capture button SELECTED looks code
System.out.println("capture button is selected");
} else if(captureState == ItemEvent.DESELECTED){
// capture button DESELECTED looks code
System.out.println("capture button is deselected");
}
}
}; captureButton.addItemListener(captureListener);
}
}
Scanner class:
public class Scanner extends Window {
private static BufferedImage boardCaptured;
static int delay = 100;
protected BufferedImage boardScanned(){
return boardCaptured;
}
#SuppressWarnings("Convert2Lambda")
public static void Scan() {
if (captureButton.isSelected()) {
ActionListener taskPerformer = new ActionListener() {
#Override
public void actionPerformed(ActionEvent captureEvent) {
try {
// capturing method
} catch (AWTException error) {
// AWTException error method
}
// is this the right place to put JPanel code?
JPanel panel = new JPanel();
boardCaptured = new BufferedImage(500, 500, BufferedImage.TYPE_INT_ARGB);
Graphics2D graphic = boardCaptured.createGraphics();
panel.setSize(500,500);
panel.paint(graphic);
panel.revalidate();
panel.repaint();
}
}; new Timer(delay, taskPerformer).start();
} else {
// this suppose to end capturing if capture button isSelected() == false
}
}
}
So here is my questions:
Do I really have to make Main class separated from Window class?
What the reason?
How to make my if statement in Scan method recognize state of my
JToggleButton from Window class? Is it impossible and I had a wrong
approach to do it?
In Scanner class, i can't make a get/set for my actionPerformed
(Netbeans always checked it as an error), but why I can make one for
BufferdImage?
If I can't get question number 3 happen, how can I make If statement
to stop capturing using Timer.stop()? Or am I in wrong approach again?
Do my JPanel in Scanner class would be produced and make a viewer
for my buffered image?
P.S. I'm sorry it cramped with questions, I tried not to make multiple post, so I make single post with multiple questions. Please notice me if there's answer before, I'm honestly can't find it or had search it with wrong tags.
Here is a simple version of what I think you want to do. This can be edited to include your variables, such as boardCaptured. This code mainly portrays how to get a component from a different class.
Main.java (Contains all the classes in one java file)
import javax.swing.JLabel;
import javax.swing.JToggleButton;
import javax.swing.JFrame;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.util.Random;
import javax.swing.Timer;
class WindowScanner extends JFrame {
private JLabel label;
private JToggleButton captureButton = new JToggleButton("CAPTURE");
WindowScanner() {
super("Fist Window");
setSize(150, 100);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new FlowLayout());
add(captureButton);
setVisible(true);
new Scanner(this);
}
public JToggleButton getCaptureButton() {
return captureButton;
}
}
class Scanner extends JFrame {
private WindowScanner wS;
private int delay = 1000;
private Timer t = new Timer(delay, new taskPerformer());
Scanner(WindowScanner wS) {
super("Second Window");
this.wS = wS;
setBounds(200,0,500,500);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
wS.getCaptureButton().addActionListener(new taskPerformer());
}
private Color randomColor() {
Random rand = new Random();
float r = rand.nextFloat() / 2f ;
float g = rand.nextFloat() / 2f;
float b = rand.nextFloat() / 2f;
Color randomColor = new Color(r, g, b);
return randomColor;
}
private class taskPerformer implements ActionListener {
#Override
public void actionPerformed(ActionEvent captureEvent) {
if(captureEvent.getSource() == wS.getCaptureButton()) {
if (wS.getCaptureButton().isSelected()) {
t.start();
} else {
t.stop();
}
}
if(captureEvent.getSource() == t) {
getContentPane().setBackground(randomColor());
revalidate();
repaint();
}
}
}
}
public class Main {
public static void main (String[] args) {
new WindowScanner();
}
}
This particular piece of code changes the color of the background in the second JFrame to a random color every second using a timer from javax.swing.timer. This code portrays how to get a component, or a variable if you change it, from a different class.
It is mainly these code fragments which allow it.
1
public JToggleButton getCaptureButton() {
return captureButton;
}
This allows other classes to get the component.
2
private WindowScanner wS;
Scanner(WindowScanner wS) {
...
this.wS = wS;
...
}
This makes the current instance of WindowScanner and the instance of WindowScanner declared in Scanner the same instance.
Note: Look into using public getters and setters.
As for your 5 listed questions.
1) Do I really have to make Main class separated from Window class? What the reason?
In most cases yes you do. As SebVb said it is good practice. However you can do something like this if you wish to have them in the same class.
import javax.swing.JToggleButton;
import javax.swing.JFrame;
import java.awt.FlowLayout;
public class Test extends JFrame {
private JToggleButton captureButton = new JToggleButton("CAPTURE");
Test() {
super("Fist Window");
setSize(150, 100);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new FlowLayout());
add(captureButton);
setVisible(true);
}
public JToggleButton getCaptureButton() {
return captureButton;
}
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
Test frame = new Test();
}
});
}
}
2) How to make my if statement in Scan method recognize state of my JToggleButton from Window class? Is it impossible and I had a wrong approach to do it?
You were using the wrong approach to do this. See the code and code fragments I have put above for how to do it correctly. (Using public getters.)
3) In Scanner class, i can't make a get/set for my actionPerformed (Netbeans always checked it as an error), but why I can make one for BufferdImage?
I can't entirely say I'm sure what you are asking but see my code above to see if that helps. If it doesn't leave a comment trying to fully explain what you mean.
4) If I can't get question number 3 happen, how can I make If statement to stop capturing using Timer.stop()? Or am I in wrong approach again?
In my code I show you how this can be related to the JToggleButton. See code fragment below
private class taskPerformer implements ActionListener {
#Override
public void actionPerformed(ActionEvent captureEvent) {
if(captureEvent.getSource() == wS.getCaptureButton()) {
if (wS.getCaptureButton().isSelected()) {
t.start();
} else {
t.stop();
}
}
if(captureEvent.getSource() == t) {
getContentPane().setBackground(randomColor());
revalidate();
repaint();
}
}
}
This code says when the JToggleButton fires an ActionEvent if it is selected then start the timer, t.start(), or if it is not selected stop the timer, t.stop().
5) Do my JPanel in Scanner class would be produced and make a viewer for my buffered image?
Again I'm not entirely sure what you are asking but here is my best guess. You have two options.
1
Put boardCaptured directly on the frame.
paint(graphic);
repaint();
revaildate();
2
Create a JPanel like you did but outside the ActionListener
JPanel panel = new JPanel()
boardCaptured = new BufferedImage(500, 500, BufferedImage.TYPE_INT_ARGB);
Graphics2D graphic = boardCaptured.createGraphics();
panel.setSize(500,500);
panel.paint(graphic);
add(panel);
private class taskPerformer implements ActionListener {
if(captureEvent.getSource() == t) {
panel.paint(graphic);
panel.revalidate();
panel.repaint();
}
}
I think there's an easier way do do what do you want. Taking your questions by order
Having the main class separated from your Window class allow you to re-use your Windows class everywhere you want. It's a good pratice to only init your GUI objects on your main class
Why don't you have your JToggleButton private and a mehtod wwhich will access to his status ? also, with a static field, all your instaces of Windows will share the same JToggleButton.
It's an anonymous class that contains your actionPerformed method. If you want to see it, you have to create an inner class.
I think your approch is wrong. Using a thread, which will launch your repaint with a specific delay is better. If you create a class which extends Runnable, you can check the state of your button and then do the appropriate action
Your JPanel is inside an ActionListener, i've never seen that and i don't think that it can works.
In a shorter version
Put in your Window class your JPanel, BufferedImage and JToggleButton
Create a specific thread to do your repainting when the JToggleButton is selected

GUI components don't load when another method is loaded

I am working on a game that uses a custom console-style GUI to play. I previously asked a question to find out what was causing a NullPointerException. See this link for context.
I now have the error eliminated, but I have a new issue: When I start the game, the GUI only loads the JFrame (EDIT: JPanel not JFrame) but none of the other components. In the original post, the GUI components loaded up normally, but starting the game itself would cause a NullPointerException due to a call to the JTextArea before the GUI dispatch thread was complete.
Here is the current code:
Classic.java
import javax.swing.*;
import java.util.*;
import static javax.swing.SwingUtilities.invokeLater;
public class Classic extends Game{
private static JFrame gui;
private static GUIClassic newContentPane;
...
public void play() {
invokeLater(Classic::startGUI);
invokeLater(Classic::startGame);
}
public static void startGame() {
//Game processes
...
}
private static void startGUI() {
gui = new JFrame("Karma :: Classic Mode");
gui.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
newContentPane = new GUIClassic();
newContentPane.setOpaque(true);
gui.setContentPane(newContentPane);
gui.pack();
gui.setVisible(true);
}
...
}
GUIClassic.java
...
public class GUIClassic extends JPanel implements ActionListener {
private JTextArea output;
private JTextField input;
private boolean inputReady;
private String inputText;
public GUIClassic() {
super();
setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
output = new JTextArea(15, 15);
output.setEditable(false);
JScrollPane outputScroll = new JScrollPane(output);
input = new JTextField("",40);
add(outputScroll);
add(Box.createRigidArea(new Dimension(0,5)));
add(input);
}
}
In the play() method of Classic.java, I tried removing the invokeLater(...) methods, that came up with the same result.
public void play() {
startGUI();
startGame();
}
I also tried moving the call to startGame within startGUI:
public static void startGUI() {
...
startGame();
}
However, removing startGame altogether allows it to start up normally:
public void play() {
invokeLater(Classic::startGUI);
}
I am completely at a loss. I don't understand why loading the GUI without playing the game loads it fine, but beginning the game suddenly makes the components disappear.
Please note: there are no exceptions thrown during runtime.
As a side note, the GUI also does not allow me to close it via the [X] button in the current version, but in the cases where only 'startGUI' is called, the components pop up and the [X] allows you to exit.
Try to invoke the startGame() in a new Thread, because all of the painting stuff is done in the main-thread like that
new Thread(new Runnable() {
#Override
public void run() {
Classic.startGame();
}
}).start();
You never set the size of your Panel.
Try to use the setPreferredSize(Dimension)-method of your GUIClassic before you call the pack()-method of your JFrame.

Why does using ActionListener break my Swing Applet?

I have an application working in Java Swing, but can't seem to convert to an Applet. Basically there are four buttons displayed on the Applet, and I want one of them to clear the screen and then draw a new interface (I'm going to add stuff for the other buttons, of course, but I got stuck here).
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.JApplet; //import swing applet interface
import java.util.Hashtable;
public class TopMenu extends JApplet{
private JPanel panel = new JPanel();
public void init(){
mainMenu();
}
public void mainMenu() {
// create buttons
panel.setLayout(new GridLayout(2, 2));
JButton collectButton = new JButton("Collect data");
JButton uploadButton = new JButton("Upload");
JButton downloadButton = new JButton("Download");
JButton exploreButton = new JButton("Explore data");
panel.add(collectButton);
panel.add(uploadButton);
panel.add(downloadButton);
panel.add(exploreButton);
collectButton.addActionListener(new ActionListener( ) {
public void actionPerformed(ActionEvent e) {
int numberGFish = 400;
//panel.removeAll();
//panel.add(new JLabel("Just to test"), BorderLayout.CENTER);
//panel.revalidate();
//panel.repaint();
}
});
setContentPane(panel);
}
}
All the code works totally fine until I add the ActionListener, and then the whole thing just goes blank. It compiles okay, but when I test it on my server not even the first menu comes up, it's just a blank panel. I've tried putting loads of really banal things in the ActionListener bit to make sure it's not a problem with them, but as you can see even just declaring a number in there doesn't work.
Originally I actually had the Listener bit call on another method which then did everything else, and that didn't work either, I scrapped that because I thought perhaps that was the problem.
I'm sure this is so simple but I've sunk many hours into it now and I've really just run out of ideas of things to test. And advice gratefully appreciated.
Don't know if it would be a problem or not but all GUI components should be created on the Event Dispatch Thread.
Here is the recommended code structure from the Swing tutorial on How to Make Applets:
public void init() {
//Execute a job on the event-dispatching thread:
//creating this applet's GUI.
try {
javax.swing.SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
createGUI();
}
});
} catch (Exception e) {
System.err.println("createGUI didn't successfully complete");
}
}
private void createGUI() {
JLabel label = new JLabel(
"You are successfully running a Swing applet!");
label.setHorizontalAlignment(JLabel.CENTER);
label.setBorder(BorderFactory.createMatteBorder(1,1,1,1,Color.black));
add(label, BorderLayout.CENTER);
}
I knew it was a dumb question, but I just couldn't figure it out. I've sorted it now.
I was being totally daft and not copying across the TopMenu$1.class file to the server. For some reason it works without this file but not with the ActionListener bit added. So I never thought that could be the problem.
Doh! Thanks all.

Displaying console output in jtextarea even when other actions occur

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

Swing getHeight() value changing from 0 to 16

I am trying to make a box in Swing that has a label of "user", a text field for the username, and a button "sign in". This is my code
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.io.*;
import java.util.*;
public class Engine
{
JFrame frame;
public void go()
{
setUpGui();
userNameScreen();
}
public void setUpGui()
{
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public void userNameScreen()
{
JPanel background = new JPanel();
frame.getContentPane().add(background);
JLabel labelUserName = new JLabel("User:");
background.add(labelUserName);
System.out.println(labelUserName.getHeight()); // 0
JTextField textFieldUserName = new JTextField();
System.out.println(labelUserName.getHeight()); // 16
textFieldUserName.setPreferredSize(new Dimension(110,labelUserName.getHeight()));
background.add(textFieldUserName);
JButton buttonSignIn = new JButton("Sign In");
background.add(buttonSignIn);
/*
background.add(labelUserName);
background.add(textFieldUserName);
background.add(buttonSignIn);
frame.getContentPane().add(background);
*/
frame.pack();
}
}
My driver class just creates an instance of engine, then runs the method go().
I read that Swing components do not have attributes of height/width until they are added (because that is for the layout manager to decide how much room they have), so it makes sense that in the method userNameScreen(), adding in all components at the end* (commented out here) makes the textFieldUserName variable have no height.
However, you can see in that same method userNameScreen(), I have it do
System.out.println(labelUserName.getHeight());
twice. The first time, it is 0. The second, it is 16. I don't understand why the first time, it would register it as 0. It has already been added to the panel (in the line before), and there doesn't seem to be anything that would change its height between that first println() and the next. So why is the value 0 in the first one, and why does it change to 16 almost immediately after?
*I should note, when I say adding in all the stuff commented out at the end, it also includes removing/commenting out all the same commands done elsewhere in the code.
It is a side effect from not creating/modifying your Swing components on the EDT. Now the EDT is busy doing the layout while you are adding components in another thread.
Your main method should look like:
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new Engine().go();
}
});
}
I'm not sure why this is happening but may be because the addition of the component maybe on a background thread and might not have been updated till the next statement is called and its updated a few millisecs later and appears when you call it second time.

Categories