Search code examples
javaswingjframeawtjbutton

Use jbutton to set value then confirm value?


I'm writing a piece of software that quizzes the user on musical notes, though I'll spare the musical details. Since my program is quite large, I wrote a separate program as a proof of concept and to work out my problem.

The simple program should work like this: In the main method, a number is randomly selected (1 or 2). The number is set as the text in the jlabel in the GUI. The user then presses one of the two number jbuttons. The user must then press another jbutton to confirm their selection. The main method compares the question and answer variables. If the numbers are equal, the text in the jlabel changes to "correct". Otherwise it changes to "incorrect". The user must then press a fourth jbutton to get the next question. The method then repeats and the user should be able to do as many exercises as they want.

My Test class which contains my main method

package test;
import java.util.Random;

public class Test {
    protected static int question;
    protected static int answer;
    protected static boolean next = false;
    protected static boolean sure = false;
    protected static boolean exit = false;
    protected static Random random = new Random();
    public static void main(String[] args) {
        GUI.gUI();
        while(!exit){//while
            question = random.nextInt(2) + 1;
            GUI.lblNewLabel.setText("" + question);
            while(!next){//next
                while(!sure) {//sure
                    answer = Other.other();
                }//sure
                sure = false;
                if(question == answer) {
                    GUI.lblNewLabel.setText("correct");
                }
                else {
                    GUI.lblNewLabel.setText("incorrect");
                }
            }//next
            next = false;
        }//exit
    }
}

My GUI

package test;

import java.awt.EventQueue;
import javax.swing.*;
import javax.swing.border.*;
import java.awt.event.*;

public class GUI extends JFrame {
    private static final long serialVersionUID = 1L;
    protected static int answerGUI;

    protected static JLabel lblNewLabel = new JLabel("New label");

    private JPanel contentPane;

    /**
     * Launch the application.
     */
    public static void gUI() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    GUI frame = new GUI();
                    frame.setVisible(true);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    /**
     * Create the frame.
     */
    public GUI() {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setBounds(100, 100, 450, 300);
        contentPane = new JPanel();
        contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
        setContentPane(contentPane);
        contentPane.setLayout(null);
        BTNListener btnlistener = new BTNListener();

        lblNewLabel.setBounds(39, 25, 46, 14);
        contentPane.add(lblNewLabel);

        JButton btn1 = new JButton("No. 1");
        btn1.setBounds(39, 158, 89, 23);
        contentPane.add(btn1);
        btn1.addActionListener(btnlistener);

        JButton btn2 = new JButton("No. 2");
        btn2.setBounds(157, 157, 89, 23);
        contentPane.add(btn2);
        btn2.addActionListener(btnlistener);

        JButton btnAreYouSure = new JButton("Are you sure?");
        btnAreYouSure.setBounds(38, 197, 99, 23);
        contentPane.add(btnAreYouSure);
        btnAreYouSure.addActionListener(btnlistener);

        JButton btnNext = new JButton("Next");
        btnNext.setBounds(159, 197, 89, 23);
        contentPane.add(btnNext);
        btnNext.addActionListener(btnlistener);

        JButton btnExit = new JButton("Exit");
        btnExit.setBounds(269, 198, 89, 23);
        contentPane.add(btnExit);
        btnExit.addActionListener(btnlistener);
    }
    protected class BTNListener implements ActionListener{
        @Override
        public void actionPerformed(ActionEvent e) {
            if(e.getActionCommand() == "No. 1") {
                Other.answer = 1;
            }
            else if(e.getActionCommand() == "No. 2") {
                Other.answer = 2;
            }
            if(e.getActionCommand() == "Are you sure?") {
                Test.sure = true;
            }
            if(e.getActionCommand() == "Next") {
                Test.next = true;
            }
            if(e.getActionCommand() == "Exit") {
                Test.exit = true;
            }
        }
    }
}

My Other class

package test;

public class Other {
    protected static int answer;
    protected static int other() {
        return answer;
    }
}

I decided that I needed another class so the main method has another method to call upon as part of the sequence, though I suppose this method could be written in the GUI.

One of the reasons I've included a confirmation button is because after the first pass, the answer variable in the Other class already has a value, so on the second pass this value is immediately passed to the answer variable in Test and the user isn't able to change the value.

As the program is written right now, the jlabel displays the question value and none of the button presses seem to do anything. I tried using do...while loops instead and it worked for one pass before infinitely looping and not allowing the user to answer.


Solution

  • The code posted has too many issues, so I completely rewrote it. To implement it I used a simplified MVC pattern.

    For convenience and simplicity, the following code can be copy-pasted into one file called MVC_Controller.java, and run. Please note the many comments in the code.

    import java.awt.BorderLayout;
    import java.awt.FlowLayout;
    import java.awt.Label;
    import java.util.Random;
    import javax.swing.ButtonGroup;
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JLabel;
    import javax.swing.JPanel;
    import javax.swing.JTextField;
    import javax.swing.JToggleButton;
    import javax.swing.SwingUtilities;
    
    /*The controller process the user requests.
     * It listeners that are called when the View detects a user interaction.
     * Based on the user action, the Controller calls methods in the View and Model
     * to accomplish the requested action.
     */
    class MVC_Controller{
    
        private MVC_Model model;
        private MVC_View view;
    
        MVC_Controller(MVC_View view,MVC_Model model) {
          this.model=model;
          this.view=view;
          //observe view buttons
          view.getConfirmationButton().addActionListener(e -> confirmationButtonPressed());
          view.getNextButton().addActionListener(e -> setNewQuestion());
          setNewQuestion(); //initialize view with a question
          view.setVisible();
        }
        //respond to confirmation button click
        private void confirmationButtonPressed() {
            model.setAnswer(view.getAnswer());
            if(model.isCorrectAnswer()) {
                view.setInfo("Well done !");
            }else {
                view.setInfo("Wrong answer !");
            }
        }
        //respond to next button click
        private void setNewQuestion() {
            view.clear();
            model.newQuestion();
            view.setInfo("Select button ");
            view.updateFromModel();
        }
    
        /*In practice you would probably want to have this in a
         * Separate Main class
         */
        public static void main(String[] args) {
    
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    MVC_Model model = new MVC_Model();
                    MVC_View  view  = new MVC_View(model);
                    new MVC_Controller(view, model);
                }
            });
        }
    }
    
    /*Model contains the information for the view and information from the view
     * as well as the logic.
     * The model is independent of the user interface.
     */
    class MVC_Model {
    
        private int question,answer = 0;
        private Random rnd = new Random();
    
        MVC_Model() {
            newQuestion();
        }
    
        //set question to 1 or 2.
        void newQuestion() {
            question = (rnd.nextInt(99) > 49)  ? 2 : 1 ;
            answer = 0;
        }
    
        int getQuestion() { return question;}
        int getAnswer()   { return answer;  }
        void setQuestion(int question) { this.question = question; }
        void setAnswer(int answer)     { this.answer = answer;     }
        boolean isCorrectAnswer()    { return question == answer;  }
    }
    
    /*View only contains the user interface part*/
    class MVC_View {
    
        private MVC_Model model;
        private JTextField question = new JTextField(2);
        private JToggleButton button1 = new JToggleButton("1"); //on - off toggle buttons
        private JToggleButton button2 = new JToggleButton("2");
        private JButton confirm = new JButton("Confirm");
        private JButton next = new JButton("Next");
        private JLabel info = new JLabel();
        private JFrame frame;
    
        MVC_View(MVC_Model model){
            this.model = model;
    
            ButtonGroup bGroup = new ButtonGroup();   //add buttons to group so
            bGroup.add(button1);bGroup.add(button2);  //only one can be selected
    
            JPanel topPanel = new JPanel(); //uses FlowLayout by default. Do not use null layouts
            //add components to top panel
            topPanel.add(new Label("Question: ")); //add a label to indicate question
            topPanel.add(question); topPanel.add(button1);  topPanel.add(button2);
            topPanel.add(confirm); topPanel.add(next);
    
            JPanel bottomPanel = new JPanel();
            bottomPanel.setLayout(new FlowLayout(FlowLayout.LEFT, 5, 0)); //set layout manager to left align
            bottomPanel.add(new Label("Information: ")); //add a label to indicate information
            bottomPanel.add(info);
            frame = new JFrame("MVC Model Demo");
            frame.add(topPanel, BorderLayout.NORTH); //uses BorderLayout by default
            frame.add(bottomPanel, BorderLayout.SOUTH); //uses BorderLayout by default
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.pack();
        }
    
        void setInfo(String text)       { info.setText(text); }
        JButton getConfirmationButton() { return confirm; }
        JButton getNextButton()         { return next; }
        int getAnswer() {
            if(button1.isSelected()) {
                return 1;
            }else if (button2.isSelected()){
                return 2;
            }
            return 0; //no button is selected
        }
        //rest all gui
        void clear() {
            button1.setSelected(false);
            button2.setSelected(false);
            question.setText("");
            info.setText("");
        }
    
        //update view from model
        void updateFromModel() {
            question.setText(String.valueOf(model.getQuestion()));
        }
    
        void setVisible() { frame.setVisible(true); }
    }