JOptionPane using one window for input and output - java

So I'm working on a little project and I'm looking for the base code for how to do this in JOptionPane. I'm still really new to this side of Java. I'm not looking for a whole lot, I just didn't know where to start.
The program should populate the screen with a JOptionPane window. I need it to be modeled like the picture below. The bottom row is a text input from the user and when they hit the enter key, the text should "refresh/clear" and then the middle string area should populate with both the user input and then just below it the result of an if statement according the the code.
for example: The user enters in: "Hello".
Then the text input should refresh and the grey box should do this: "User: Hello."
"Computer: Hello user".
I would really appreciate any and all help on this.

You don't System.out.println() into a gui component. Doesn't work like this. You can write a console program and use JOptionPanes to get user input, but the output would be used in the console program. You would need to create a gui program mimic a console.
Here's a basic layout to start you off
public class Game extends JFrame {
JTextArea jta = new JTextArea(10, 30);
JTextField jtf = new JTextField(30);
public Game(){
add(jtf, BorderLayout.SOUTH);
add(jta, BorderLayour.CENTER);
jta.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
}
});
}
public static void main(String[] args){
SwingUtilities.invokeLater(new Runnable(){
public void run(){
JFrame frame = new Game();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.pack();
frame.setVisible(true);
}
});
}
}
What I would suggest is since this is a very basic program, I would use an array of String commands and iterate through them.
For example:
String[] question = {"Do you want to go to school?",
"Do you want to drive or walk?"};
jta.setText(questions[0]);
Then in your actionPerformed get the answer from the text field. Use an if statement like
if (jtf.getText().equals("yes") {
jta.append(questions[1]);
}
And so on. If you have no idea what I'm talking about, I would really consider using the Swing tutorials I mentioned. There's a lot of info in those tutorials.

Related

Can you make a JPanel randomly generate each time a button is clicked?

Basically just started coding in Java using Eclipse and as my first "serious" project I'm trying to code a simple quiz game. I already set up all the graphics and the answering system but I'm getting stuck at the part where, if you click the right answer, the JPanel restarts with a new question.
I already tried some solutions I've seen online including a do/while method, which proved worthless and calling the main method ( the one where all the code is) which seems impossible.
Here's the method I'm talking about:
public static void main(String []args) throws IOException
{
//here was unnecessary stuff i cut out
ImageIcon image = new ImageIcon (Imagetesting.class.getResource(i+".jpg"));
JLabel label = new JLabel (image);
JFrame f = new JFrame("Quiz");
JLabel x1 = new JLabel(question);
JButton x2 = new JButton(answer1);
//+ other graphic stuff
f.getContentPane().add(MyPanel, "Center"); // Paste MyPanel in center
// of the contentPane
f.setExtendedState(JFrame.MAXIMIZED_BOTH);
f.setVisible(true);
x3.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if(answer2.equals("therightanswer")){
score = score +1;
x1.setText("Right answer Punteggio:"+String.valueOf(punteggio)
// what am I supposed to put here?
);
;
}
}});
So what am I supposed to do? Is it even possible to call the main method to make it restart? Am I doing something wrong?
You can pass the labels into the actionPerformed function to change the values of those objects when the answer is correct.

Swing is very slow with long strings

I built a simple Java program that logs in a JTextArea component.
JTextArea _log = new JTextArea();
_log.setEditable(false);
JScrollPane scrollLog = new JScrollPane(_log);
scrollLog.setPreferredSize(getMaximumSize());
add(scrollLog);
The problem is that logging like this takes 15ms on average:
public void log(String info) {
_log.append(info + "\n");
}
This is far(!) slower than logging using System.out.println. Logging takes more time than the whole running time of the algorithm!
Why is the JTextArea is so slow? Is there a way to improve it?
EDIT 1:
I am using separate thread for the algorithm, and using SwingUtilities.invokeLater to update the log in the UI.
The algorithm tread finish his work after 130ms on average, but the JTextArea finish his appends after 6000ms on avarage.
EDIT 2:
I tried to test this by use setText of string that contains 2500 charaters. In that case the operation took 1000ms on average.
I tried to use another controller then JTextArea and I get same results.
Is it hard for Swing components to deal with large strings? What can I do about it?
EDIT 3:
I just test with this code:
public class Test extends JFrame {
public Test() {
final JTextArea log = new JTextArea();
log.setEditable(false);
log.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
JScrollPane scrollLog = new JScrollPane(log);
scrollLog.setPreferredSize(getMaximumSize());
JButton start = new JButton("Start");
start.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
long start = System.nanoTime();
for (int i = 0; i < 2500; i++) {
log.append("a\n");
}
long end = System.nanoTime();
System.out.println((end - start) / 1000000.0);
}
});
JPanel panel = new JPanel();
panel.setLayout(new GridLayout(2, 1));
panel.add(scrollLog);
panel.add(start);
add(panel);
}
public static void main(String[] args) {
Test frame = new Test();
frame.setSize(600,500);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
The time of that for loop is 1870ms on avarage.
This is the only code that I ran (include the declaration of _log at the top of the question)
A JTextArea is not slow.
Far(!) away from System.out.println.
System.out.println() executes on a separate Thread.
The log takes more time then the hole running time of the algorithm!
So your algorithm is probably executing on the Event Dispatch Thread (EDT) which is the same Thread as the logic that appends text to the text area. So the text area can't repaint itself until the algorithm is finished.
The solution is to use a separate Thread for the long running algorithm.
Or maybe a better choice is to use a SwingWorker so you can run the algorithm and "publish" results to the text area.
Read the section from the Swing tutorial on Concurrency for more information and a working example of a SwingWorker.
Edit:
//log.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
The above line is causing the problem. I get 125 for the first test and 45 when I keep clicking the button.
That property is not needed. The text is still displayed on the left side of the text pane. If you want right aligned text then you need to use a JTextPane and set the attributes of the text pane to be right aligned.
That is why you should always post an MCVE. There is no way we could have guessed from your original question that you were using that method.
Edit2:
Use the alignment feature of a JTextPane:
SimpleAttributeSet center = new SimpleAttributeSet();
StyleConstants.setAlignment(center, StyleConstants.ALIGN_CENTER);
textPane.getStyledDocument().setParagraphAttributes(0, doc.getLength(), center, false);
Now any new text you add to the document should be center aligned. You can change this to right.

Adding entire GUI to JFrame after the fact

Today I have another project that involves the program I have written for work and been working on for well over 6 months. There is a lot of code and classes so I'm going to try to explain the best that I can so you can (hopefully) help me.
Currently my program works by reading a file and allowing a user to make some modifications to that file, then a new file is written. This entire process involves a GUI that is better explained as a series of JOptionPanes, some with imbedded panels.
Here is my goal: Each file uploaded comes with a given number of "batches". My program loops through once for each batch. During a loop each of the relevant JOptionPane GUI's is displayed. When all batches are read the program ends and the file is complete.
I have been asked to add a feature where the entire "project" is inside of a JFrame with a new "upload" button. This would allow the user to run the program multiple times without having to open the JAR over and over again. If they select "Upload" they essentially start the program over.
Here is my main class:
package nachamultifive;
import java.util.ArrayList;
import javax.swing.JFileChooser;
import nachamultifive.Buffered_Reader_Writer.BatchCounter;
import nachamultifive.Buffered_Reader_Writer.FileValidation;
import nachamultifive.Buffered_Reader_Writer.MainWriter;
import nachamultifive.GUIs.FileHandling;
import nachamultifive.GUIs.ReturnBuilderGUI;
public class NachaMain
{
public static JFileChooser saveFile;//The output file save location.
public static JFileChooser uploadFile;//The uploaded NACHA file.
public static int batchTotal;//The total number of batches in the file.
public static ArrayList<String> batchHeaders;//An array of all batch headers.
public static int batchCounter;//The counter that displays the current batch number in sequence.
public static String location;
public static void main(String args[]){
FileHandling fHandling = new FileHandling();//The class that handles upload/save of files.
fHandling.getFile();//Allows the user to upload a file.
fHandling.setDirectory();//Allows the user to choose the save location.
saveFile=fHandling.saveFile;//Sets the file save location as static with the main class.
uploadFile=fHandling.uploadFile;//Sets the uploaded file as static with the main class.
BatchCounter bCounter = new BatchCounter();//The class that handles counting the batches.
bCounter.getBatches();//Counts the total number of batches.
batchTotal=BatchCounter.BatchTotal;//Sets the total number of batches as static with the main class.
batchHeaders=bCounter.batchHeaders;//Sets the batch header array as static with the main class.
MainWriter mWriter = new MainWriter();//The class that handles all writing functions for the new file.
mWriter.writeNacha();//Writes the output file.
location = MainWriter.location;
System.out.println("NachaMain Location=" + location);
FileValidation fValidation = new FileValidation();//The class that handles validating the output ACH file.
fValidation.validateNacha();//Method to validate the ACH file.
ReturnBuilderGUI gui = new ReturnBuilderGUI();//Class used for GUI's.
gui.displayFileOption();//Method used to display the ACH output and error report name.
gui.showSavedErrors();//Method to display the error report.
}
}
Essentially each of the classes calls modifies the input file. Inside of the mWriter class you will see this bit of code:
ReturnBuilderGUI gui = new ReturnBuilderGUI();//The GUI class.
ReturnBuilderGUI.displayGUI();//Calls the GUI to display the initial double list GUI.
Calling that class calls the entire GUI for that loop. (The mWriter class loops for each batch). When the ReturnBuilder class is called this is the basic code layout:
public static void displayGUI(){//Method to display the GUI.
final JButton createReturnButton = new JButton("Create Return");
createReturnButton.addActionListener(new ActionListener(){
public void actionPerformed(final ActionEvent ae){
if(verifyBatch==true){
initialScreenDecisions="NONE";//The user did not choose to add any entry details to the output list.
MainWriter.finishedCounter=true;//The boolean counter to trigger that the return is finished goes to true.
while(MainWriter.entryDetails.size()>0){//Removes all entry details from the input list.
MainWriter.entryDetails.remove(0);
}
while(output.size()>0){//Removes all entry details from the output list..
output.remove(0);
}
JOptionPane.getRootFrame().dispose();
}else {
JOptionPane.showMessageDialog(null, "No batches have been completed!");
}
}
});
final Object[] createR = new Object[] { "Confirm",createReturnButton };
int result = JOptionPane.showOptionDialog(null, getPanel(),"Return Builder", JOptionPane.OK_CANCEL_OPTION,
JOptionPane.PLAIN_MESSAGE, null, createR, "default");
System.out.println(verifyBatch);
//Creates a JOptionPane for the first GUI featuring 7 buttons and 2 lists..
}
The getPanel() method inside of that JOptionsPane calls the panel that has some buttons and lists. Depending on what the user chooses some more JOptionPane's will appear that give the user more options. When they are finished the initial mWriter class will loop again (assuming there are more batches in the input file) and the ReturnBuilder class will be called again restarting the process.
Now, I can't for the life of me figure out a way to make all this happen inside of a JFrame that remains before and after all these other things happen without having to restructure my code.
I don't know if I've given you guys enough information to work with. My only idea right now is that I feel like I need to create a JFrame in the ReturnBuilder class and add the JOptionsPane to it, but then when the ReturnBuilder class is called again later I'm sure the JFrame would just open again and be duplicate.
Any ideas?
It really looks to me that you need to use Cardlayout and flip to the next card at each step. When you are done, reset by flipping to the first card. CardLayout is cyclic, so it will flip to the first card automatically.
public class CardExample extends JFrame {
CardExample() {
JPanel main = new JPanel(new BorderLayout());
CardLayout cl = new CardLayout();
main.setLayout(cl);
for (int i = 0; i < 4; i++)
main.add(new StepPanel(i));
JButton next = new JButton("Next");
next.addActionListener(e -> cl.next(main));
add(main);
add(next, BorderLayout.PAGE_END);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
setVisible(true);
}
class StepPanel extends JPanel {
StepPanel(int i){
add(new JLabel("Step " + i));
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> new CardExample());
}
}
All of this is instead of JOptionPanes, which is usually more comfortable for a step-by-step user interaction (see, for example, installers). Just customize each of what I called StepPanels and at the end you can use a "load and reset" button instead of "next".

How to instantiate an object using a button in Java?

I'm kinda new to Java (I only have experience with Processing) and I have a question.
I'm building a small task planning application in Eclipse. I'm using the MigLayout. Every task has its own row (see image). I use three classes, TaskRowDone, TaskRowBusy and TaskRowToDo.
The app
public HoofdScherm() {
initialize();
drawDone();
drawBusy();
drawToDo();
drawNewBtn();
}
The code above draws the main screen. The code below draws the button labeled "Nieuwe taak" (New task). I want to be able to click this button to make a new task row. I've tried putting the lines TaskRowToDo trtd3 = new TaskRowToDo(5, true, "test", 3); and trtd3.draw() inside the actionPerformed function of the button (as a way of testing), but this doesn't seem to do anything. Does the program only run the functions inside 'HoofdScherm' once and then stop drawing? If this is the case, how do I structure the program so the layout can always be changed? I tried a while(true) loop inside 'HoofdScherm' so it kept looping but of course this crashed the computer.
private void drawNewBtn(){
JButton btnNew = new JButton("Nieuwe taak");
btnNew.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
System.out.println("New task added");
}
});
frmPlanner.getContentPane().add(btnNew, "cell 3 7");
}
I've found out I needed to use revalidate() and repaint() to redraw the frame. Source: Java Swing revalidate() vs repaint()
This works:
panel.invalidate();
//call the method u desire
panel.revalidate();

How to loop back to beginning of program

I am trying to create a while loop with this condition. (a > 1) So basically. every time a is set to greater than 1, it will close the JFrame I created and then start the program over. My problem is that, when I try edit the integer "a" from within an action listener, it doesn't recognize that it has already been declared. This is somewhat difficult to actually describe, so here is my code.
public class TestBox {
public static void main(String[] args) {
int a = 2;
while(a > 1){
a = 0;
JFrame frame = new JFrame("Test Box");
frame.setSize(1200, 800);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setResizable(true);
frame.setLayout(new GridLayout(6, 6, 1, 1));
JPanel panelAOne = new JPanel();
JPanel panelATwo = new JPanel();
JPanel emptySpace = new JPanel();
JLabel labelAOne = new JLabel();
labelAOne.setFont(new Font("Aerial", Font.ITALIC, 21));
labelAOne.setText("Welcome to the Test Box!");
JLabel labelATwo = new JLabel();
labelATwo.setFont(new Font("Aerial", Font.ITALIC, 21));
labelATwo.setText("Where would you like to go?");
JLabel emptyLabel = new JLabel("stuff goes here");
JButton buttonAOne = new JButton("Colors");
panelAOne.add(labelAOne);
panelAOne.add(labelATwo);
panelATwo.add(buttonAOne);
emptySpace.add(emptyLabel);
frame.add(panelAOne);
frame.add(buttonAOne);
frame.add(emptySpace);
frame.setVisible(true);
buttonAOne.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
a = 2;
//If this code is left as is, a GUI will open endlessly until it crashes the computer
}
}
});
}
}
You look to be trying to shoe-horn a linear console program into a Swing event-driven GUI program or visa versa, and you really don't want to do that, and I would recommend that you re-structure your program. Instead, show your GUI, get the input, and re-request the input in the GUI if it's wrong. Don't use a console-type program while loop for this.
So, for instance if the ActionListener finds that the input is not valid, clear the text in the JTextField by calling setText("") on the JTextField, show the user a JOptionPane informing them of the error in input and await another press of the button. But leave the JFrame displayed.
Edit: I don't even see a JTextField in your code, so I'm not sure where the user is supposed to enter input.
Your scheme cannot work, at least not reliably. At the end of the first iteration of your loop, local variable a still has the value set at the top of the loop (0), so the loop exits and the application's main thread dies. The GUI will continue to run, as it does so in a separate thread (the AWT's event-dispatch thread (EDT)), but even if you re-wrote your code so that an analog of variable a could be modified by your ActionListener, nobody who cares would still be paying attention by that point. (Probably. You have multiple threads sharing data without any synchronization, so really the behavior of your program is not well defined.)
GUI programming is fundamentally different from console programming, as Hovercraft Full Of Eels pointed out. It requires a significant mental adjustment to move from one to the other, but the basic paradigm of GUI programming is that everything your program does is a response to an event. Thus, if you want some sort of re-spawning behavior then you should obtain it by registering a listener for the appropriate event, and having it perform the work you want.

Categories

Resources