Difficulty using threads with Swing - java

package combatframe;
import javax.swing.*;
import java.awt.event.*;
import static java.lang.Math.PI;
import javax.swing.border.*;
public class CombatFrame extends JFrame
{
String[] Ships = {"Battleship", "Cruiser", "Frigate"};
JComboBox attackCombo = new JComboBox(Ships);
JComboBox targetCombo = new JComboBox(Ships);
JButton calculate = new JButton();
JPanel mainPanel = new JPanel();
JPanel outPanel = new JPanel();
JPanel shipPanel = new JPanel();
JTextArea outText = new JTextArea("Results: ", 11, 30);
JTextArea attackStats = new JTextArea("Attacker stats.", 10,10);
JTextArea targetStats = new JTextArea("Target stats.", 10,10);
public static void main(String[] args)
{
CombatFrame combatFrame = new CombatFrame();
}
public CombatFrame()
{
this.setSize(500,500);
this.setTitle("OTG Combat Simulator");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
EventListener c1 = new EventListener();
EventListener c2 = new EventListener();
EventListener calc = new EventListener();
calculate.setText("Calculate!");
Border b1 = BorderFactory.createTitledBorder("Result");
outPanel.setBorder(b1);
Border b2 = BorderFactory.createTitledBorder("Ship Classes");
shipPanel.setBorder(b2);
mainPanel.add(shipPanel);
mainPanel.add(outPanel);
shipPanel.add(attackStats);
shipPanel.add(attackCombo);
shipPanel.add(targetCombo);
shipPanel.add(targetStats);
outPanel.add(calculate);
outPanel.add(outText);
attackCombo.addActionListener(c1);
attackCombo.setSelectedItem("Battleship");
targetCombo.addActionListener(c2);
targetCombo.setSelectedItem("Battleship");
calculate.addActionListener(calc);
this.add(mainPanel);
this.setVisible(true);
}
public double hitProbability(double factor)
{
double hitCount = (double)(Math.random() * 1.001 * factor );
return hitCount;
}
public double Combat(double[] turnArray)
{
double rRange = turnArray[0];
double rAngular = turnArray[1];
double rRadius = turnArray[2];
double damage = turnArray[3];
double appliedDamage = 0;
int[] attHit =
{
0, 0, 0
};
int[] strikes =
{
0, 0, 0, 0, 0
};
double[] damageMod =
{
0,0.4,0.75,1.0,1.25
};
double roll1, roll2, roll3;
roll1 = hitProbability(rRadius);
roll2 = hitProbability(rAngular);
roll3 = hitProbability(rRange);
if (roll1 <=1)
attHit[0]++;
if (roll2 <=1)
attHit[1]++;
if (roll3 <=1)
attHit[2]++;
switch (attHit[0]+attHit[1]+attHit[2])
{
case 3:
double wrecker = Math.random();
if (wrecker >= 0.95 - Math.random())
strikes[4]++;
else
strikes[3]++;
break;
case 2:
strikes[2]++;
break;
case 1:
strikes[1]++;
break;
case 0:
strikes[0]++;
break;
}
for (int x=0; x<5; x++)
{
appliedDamage += strikes[x]*Damage*
(Math.random()+Math.random())*damageMod[x];
}
return appliedDamage;
}
private class EventListener implements ActionListener
{
#Override
public void actionPerformed(ActionEvent e)
{
if (e.getSource()== attackCombo)
{
switch ((String)attackCombo.getSelectedItem())
{
case "Battleship":
Attacker attackBS = new Attacker(50000.0, 400.0, 0.01, 45000.0, 400.0,
300.0, 10.0, 10000.0, 250.0);
break;
case "Cruiser":
Attacker attackCr = new Attacker(25000.0, 125.0, 0.23, 22500.0, 150.0,
600.0, 5.0, 5000.0, 75.0);
break;
case "Frigate":
Attacker attackFr = new Attacker(2500.0, 40.0, 0.365, 1000.0, 39.0,
900.0, 2.0, 1200.0, 25.0);
break;
}
attackStats.setText("Optimal: " + Attacker.Optimal
+ "\n Weapon Sig: " +Attacker.Attack_Signature
+ "\n Tracking: " + Attacker.Tracking
+ "\n Range: " + Attacker.Range
+ "\n Ship Sig: " + Attacker.Target_Signature
+ "\n Velocity: " + Attacker.Orbital
+ "\n Cycle Time" + Attacker.Period
+ "\n Health: " + Attacker.Health
+ "\n Damage: " + Attacker.Damage);
}
if (e.getSource()==targetCombo)
{
switch ((String)targetCombo.getSelectedItem())
{
case "Battleship":
Target targetBS = new Target(50000.0, 400.0, 0.01, 45000.0, 400.0,
300.0, 10.0, 10000.0, 250.0);
break;
case "Cruiser":
Target targetCr = new Target(25000.0, 125.0, 0.23, 22500.0, 150.0,
600.0, 5.0, 5000.0, 75.0);
break;
case "Frigate":
Target targetFr = new Target(2500.0, 40.0, 0.365, 1000.0, 39.0,
900.0, 2.0, 1200.0, 25.0);
break;
}
targetStats.setText("Optimal: " + Target.Optimal
+ "\n Weapon Sig: " + Target.Attack_Signature
+ "\n Tracking: " + Target.Tracking
+ "\n Range: " + Target.Range
+ "\n Ship Sig: " + Target.Target_Signature
+ "\n Velocity: " + Target.Orbital
+ "\n Cycle Time" + Target.Period
+ "\n Health: " + Target.Health
+ "\n Damage: " + Target.Damage);
}
if (e.getSource()==calculate)
{
double[] attackArray =
{//RRange,RAngular,RRadius,Attacker.Damage
0,0,0,0
};
double[] targetArray =
{//RRange,RAngular,RRadius,Target.Damage
0,0,0,0
};
double attackDamage = 0, targetDamage = 0;
for (int i=1; i<1000; i++)
{
if ((i+1)%Attacker.Period==0)
{
double rRange = Attacker.Optimal/Target.Range;
double rAngular =
((Math.sin(PI/4)*(Target.Orbital/Target.Range))
/Attacker.Tracking);
double rRadius = Attacker.Attack_Signature/
Target.Target_Signature;
attackArray[0] = rRange;
attackArray[1] = rAngular;
attackArray[2] = rRadius;
attackArray[3] = Attacker.Damage;
attackDamage += Combat(attackArray);
if (attackDamage >= Target.Health)
{
outText.setText("Attacker wins!");
break;
}
}
if (i%Target.Period==0)
{
double rRange = Target.Optimal/Attacker.Range;
double rAngular =
((Math.sin(PI/4)*(Attacker.Orbital/Attacker.Range))
/Target.Tracking);
double rRadius = Target.Attack_Signature/
Attacker.Target_Signature;
targetArray[0] = rRange;
targetArray[1] = rAngular;
targetArray[2] = rRadius;
targetArray[3] = Target.Damage;
targetDamage += Combat(targetArray);
if (targetDamage >= Attacker.Health)
{
outText.setText("Target wins!");
break;
}
}
}
}
}
}
}
I'm attempting to build a very simple combat simulation frame. It builds 2 combo-boxes and assigns stats based upon the selections therein.
User then hits the calculate button, and out pops the result into the Results field. That all works fine.
I'd like the GUI to append each 'turn' of combat into the Results textfield outText - and I think I could do that easily enough. I'd like it to do so with a slight delay on any turn there is combat - I'd use a Thread.sleep(10) in each if-statement to do with an active turn for each ship.
The only problem with all that is I can't work out how to hold the GUI on one thread - which updates when prompted by the combat calculations after the button is clicked - and the thread handling all the calculations with delays.
I know if I try and run both on the same thread, the GUI will simply freeze up, do all the calculations with applicable delays, then throw out the entire appended lump at once into the results field.
Can anyone offer me any pointers, advice or cyanide? Anything, anything to make the pain go away...
Suspect that SwingWorker would be a good idea, but still can't work out how to actually implement that within my code.
Clarification: My biggest concern is that I can't work out how to implement SwingWorker, or some similar multi-thread process, in order to run the GUI and background calculations in parallel.

I will just take a stab at this since I think I understand what you are trying to do. Maybe what you are looking for is to create a new runnable each time the button is clicked:
class Turn implements Runnable {
private final Object source;
private final String attackComboMove;
private final String targetComboMove;
public Turn(Object src, String acm, String tcm) {
source = src;
attackComboMove = acm;
targetComboMove = tcm;
}
#Override public void run() {
// may want to disable GUI buttons here
if (source == attackCombo) {
switch (attackComboMove) {
// ...
// ...
// ...
}
append("attack combo results");
} else if (source == targetCombo) {
switch (targetComboMove) {
// ...
// ...
// ...
}
append("target combo results");
} else if (source == calculate) {
// ...
for (int i = 1; i < 1000; i++) {
// ...
attackDamage += combat(attackArray);
append("combat results: " + attackDamage + " total damage");
if (attackDamage >= target.health) {
append("Attacker wins!");
break;
}
}
}
// and enable the buttons again when combat is over
}
private void append(String text) {
SwingUtilities.invokeLater(new Runnable() {
#Override public void run() {
outTextArea.append("\n" + text);
}
});
try {
Thread.sleep(100);
} catch (InterruptedException e) {}
}
}
It sounds like you just want a scrolling list of the attacks as they happen and that's what this will do for you. IMO it is the right way to do it and ultimately the simplest.
When the user clicks the button you do:
new Thread(new Turn(e.getSource(), (String)attackCombo.getSelectedItem(), (String)targetCombo.getSelectedItem())).start();
(Or make a variable for the new thread if you need to be able to end it early for some reason or alter parameters.)
This pattern can easily be adapted to SwingWorker if it is long-running, just extend SwingWorker and do the same thing but override doInBackground() instead of run().
Otherwise if you are intending to make a real-time simulation and need a background thread you will need to make run/doInBackground be a while(running) loop and use flags and/or some kind of wait/notify scheme when you need to change parameters or calculate a turn.

You could use javax.swing.Timer to schedule something to happen N milliseconds in the future. Note that 10 in Java time usually means 10 milliseconds, which is a nearly imperceptible amount of time. If you want to sleep or delay for a second, use 1000 milliseconds, 10 seconds = 10000 milliseconds, etc. Example:
import java.awt.event.*;
import java.awt.*;
import javax.swing.*;
public class GameFrame extends JFrame
{
private JPanel _contentPane;
private GameFrame _mainFrame;
private JButton _jButton;
private JTextArea _jTextArea;
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
GameFrame frame = new GameFrame();
frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
frame.setSize(640/2, 480/2);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public GameFrame()
{
_mainFrame = this;
_contentPane = new JPanel();
_mainFrame.setContentPane(_contentPane);
_contentPane.setLayout(new BorderLayout());
_jTextArea = new JTextArea();
_contentPane.add(_jTextArea, BorderLayout.CENTER);
_jButton = new JButton("Go");
_jButton.addActionListener(new GoButtonListener());
_contentPane.add(_jButton, BorderLayout.SOUTH);
}
class GoButtonListener implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
_jTextArea.append("Please wait...\n");
Timer timer = new Timer(10000, new LaterListener());
timer.start();
}
}
class LaterListener implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
_jTextArea.append("Ten seconds later!\n");
}
}
}

Related

Why does my JButton action listener execute double the amount of times before?

I'm currently setting up a quiz program right now, where the code will loop 20 times. There are 20 question, 80 answers (4 to each question) and the program will set JButtons and action listeners to listen for the user's answer for each question, then call the original quiz loop again from the correct or incorrect answer.
However, the program I'm having is that the program will display question 1, then question 2, but then question 4, 8, 16 and finally 20 since it cannot double anymore.
I've tried:
Breakpointing the value of the currentQuestion variable, as I believe this is the one which is doubling and causing the problem.
Creating a new method named updateQuestionAndAnswer to ensure that it's not a misclick.
Removing the other three action listeners, which helped me to find that it was whatever action listener that I clicked that was repeatedly being called. E.g if it was on Question 8 and I clicked listener 3, it would call listener 3 another 8 times.
I've enclosed the code below I think will be useful. All variables that you cannot see defined within here, are defined to 0 or "" in a static context outside of methods at the top of my program.
* Defines and places all of the content for the Game page.
* #throws IOException
*/
public void setupGamePanel() throws IOException {
// Game page card constructor
card2 = new JPanel() {
// Make the window the specified sizes
public Dimension getPreferredSize() {
Dimension size = super.getPreferredSize();
size.width += extraWindowWidth;
size.height += extraWindowHeight;
return size;
}
};
// JAVA COMPONENT DEFINITIONS
String[] openingDialogue = new String[]{"<html><span style='font-size:20px;'>We need your help. After Junko Enoshima took over Hope’s Peak Academy, she trapped the entire of Class 78 inside a monstrous killing game! My creator, Chihiro Fujisaki, is one of them.", "<html><span style='font-size:20px;'>I managed to patch into the systems that the Ultimate ******** setup when he fell into despair, but we’ve been hit with a security system implemented by Jin Kirigiri before he…", "<html><span style='font-size:20px;'>Anyway, we need to get a good enough score out of twenty quiz questions in order to get inside the network, where my creator should be waiting for me.", "<html><span style='font-size:20px;'>Let’s do this. Answer the questions as best as you can. Good luck.", ""};
// Score counter
scoreCounter.setText("<html><span style='font-size:30px;'>Score: " + String.valueOf(score) + "</html>");
// Alter ego dialogue text
dialogueText = new JLabel("");
dialogueText.setText("<html><span style='font-size:20px;'>You’re...actually here? You survived. You're inside the first versions of the Neo World Program!</html>");
// Buttons
beginButton = new JButton("BEGIN THE GAME!");
continueButton = new JButton("Continue Dialogue");
// Images
alterEgoImage = ImageIO.read(new File("AlterEgo/alterego.png"));
BufferedImage alterEgoNeutral = ImageIO.read(new File("AlterEgo/neutral.jpeg"));
alterEgoImageLabel = new JLabel(new ImageIcon(alterEgoImage));
// X, Y. Width, Height
alterEgoImageLabel.setBounds(510, 150, 550, 300);
scoreCounter.setBounds(15, 15, 250, 40);
beginButton.setBounds(650, 480, 250, 40);
continueButton.setBounds(650, 480, 250, 40);
dialogueText.setBounds(500, 490, 750, 300);
// Allows for a more dynamic and fluid layout of the page where bounds are specified.
card2.setLayout(null);
// Add the components to the page
card2.add(beginButton);
card2.add(continueButton);
card2.add(dialogueText);
card2.add(scoreCounter);
card2.add(alterEgoImageLabel);
continueButton.setVisible(false);
dialogueText.setVisible(false);
// BEGIN THE QUIZ GAME
// Game begins when the button is clicked
beginButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
beginButton.setVisible(false);
continueButton.setVisible(true);
dialogueText.setVisible(true);
}
});
// Continue the opening dialogue with the continue button, and immediately begin the quiz after the dialogue has finished.
continueButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
dialogueText.setText(openingDialogue[openDialoguePos]);
openDialoguePos++;
if(openDialoguePos == openingDialogue.length) {
continueButton.setVisible(false);
dialogueText.setVisible(false);
// SET THE ALTER EGO IMAGE TO THE NEUTRAL ONE TO BEGIN THE GAME
try {
quizProcess();
} catch(IOException excep) {
// Handle the error here
}
//quizProcess();
}
}
});
} // end setupGamePanel()
/**
* Defines and places all of the content for the leaderboard page.
* #throws IOException
*/
public void setupLeaderboardPanel() throws IOException {
// Leaderboard page card constructor
card3 = new JPanel();
card3.add(new JTextField("TextField", 20));
} // end setupLeaderboardPanel()
/**
* The top level method for the quiz game.
* #throws IOException
*/
public void quizProcess() throws IOException {
// TOP LEVEL METHOD BEGINS
setupQuizUI(answerButtons);
readData(questions, answers);
quizLoop(questions, answers, answerButtons);
}
public void quizLoop(String questions[], String[] answers, JButton[] answerButtons) {
System.out.println("A quiz loop is being initiated");
moveOntoNextQuestion(questions, answers, answerButtons);
//assignData();
checkCorrectQuestion(answerButtons);
}
/**
* Initialise and setup the answer buttons and question text.
*/
public void setupQuizUI(JButton[] answerButtons) {
// Alter ego dialogue text
questionText.setText("<html><span style='font-size:20px;'<</html>");
questionText.setBounds(550, 450, 350, 150);
card2.add(questionText);
//System.out.println("Fifth read: " + currentQuestion + 25);
for(int i = 0; i < answerButtons.length; i++) {
answerButtons[i] = new JButton("Test");
}
// Bound setting (X, Y, Width, Height)
answerButtons[0].setBounds(150, 580, 200, 100);
answerButtons[1].setBounds(550, 580, 200, 100);
answerButtons[2].setBounds(950, 580, 200, 100);
answerButtons[3].setBounds(1350, 580, 200, 100);
// Adding to the JFrame
card2.add(answerButtons[0]);
card2.add(answerButtons[1]);
card2.add(answerButtons[2]);
card2.add(answerButtons[3]);
}
/**
* Read in the data from the database to the arrays.
* #param
*/
public void readData(String[] questions, String[] answers) {
//System.out.println("First read: " + currentQuestion + 5);
questions[0] = "Question 1";
questions[1] = "Question 2";
for(int i = 0; i < questions.length; i++) {
questions[i] = "Question " + (i + 1);
}
for(int i = 0; i < answers.length; i++) {
answers[i] = "Answer" + (i + 1);
}
for(int i = 0; i < correctAnswers.length; i+= 4) {
correctAnswers[i] = 0;
correctAnswers[i + 1] = 1;
correctAnswers[i + 2] = 2;
correctAnswers[i + 3] = 3;
}
/*answers[0] = "Answer 1";
answers[1] = "Answer 2";
answers[2] = "Answer 3";
answers[3] = "Answer 4";
answers[4] = "Answer 5";
answers[5] = "Answer 6";
answers[6] = "Answer 7";
answers[7] = "Answer 8";*/
}
/**
*
* Moves onto the next question and prepares the data for read-in
* Assign the next question to the text field
* Assign the next answers to the buttons
*/
public void moveOntoNextQuestion(String questions[], String[] answers, JButton[] answerButtons) {
//System.out.println("-=-=-=-=-=-=-moveOntoNextQuestion-=-=-=-=-=-=-=-");
String nextQuestion = questions[currentQuestion];
questionText.setText(questions[currentQuestion]);
//System.out.println("The current question is " + questions[currentQuestion]);
//System.out.println("The current answer number is " + currentAnswer);
answerButtons[0].setText(answers[currentAnswer]);
answerButtons[1].setText(answers[currentAnswer + 1]);
answerButtons[2].setText(answers[currentAnswer + 2]);
answerButtons[3].setText(answers[currentAnswer + 3]);
//System.out.println("-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-");
}
/**
* #param answer
* Check if the clicked answer was correct.
*/
public void checkCorrectQuestion(JButton[] answerButtons) {
int correctAnswer = correctAnswers[currentQuestion];
//System.out.println("Current correct answer is " + correctAnswer);
answerButtons[0].addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if(correctAnswer == 0) {
System.out.println("Button 1 is correct!");
score += 10;
updateScore(scoreCounter);
try {
correctAnswerAlterEgo(alterEgoImage, alterEgoImageLabel);
} catch(IOException excep) {
// Handle the error here
}
} else {
System.out.println("Button 1 is wrong!");
score -= 5;
updateScore(scoreCounter);
}
updateQuestionAndAnswer();
//currentQuestion += 1;
//System.out.println("currentQuestion value is " + currentQuestion);
//currentAnswer += 4;
//System.out.println("This is the quizLoop for button 1");
quizLoop(questions, answers, answerButtons);
}
});
**WITHIN HERE ARE THE OTHER THREE ACTION LISTENERS FOR THE BUTTONS. THEY ARE THE EXACT SAME EXCEPT [1], [2] and [3].**
}
public void updateQuestionAndAnswer() {
currentQuestion = currentQuestion + 1;
currentAnswer = currentAnswer + 4;
}
public void updateScore(JLabel scoreCounter) {
scoreCounter.setText("<html><span style='font-size:30px;'>Score: " + String.valueOf(score) + "</html>");
}
public void correctAnswerAlterEgo(BufferedImage alterEgoImage, JLabel alterEgoImageLabel) throws IOException {
System.out.println("Correct answer alter ego method called!");
scoreCounter.setText("<html><span style='font-size:30px;'>TEST: </html>");
// Images
BufferedImage[] happyImages = new BufferedImage[]{ ImageIO.read(new File("AlterEgo/excited.jpeg")), ImageIO.read(new File("AlterEgo/happy.jpeg")), ImageIO.read(new File("AlterEgo/shocked.jpeg"))};
alterEgoImage = ImageIO.read(new File("AlterEgo/class78.jpg"));
alterEgoImageLabel = new JLabel(new ImageIcon(alterEgoImage));
card2.add(alterEgoImageLabel);
card2.remove(alterEgoImageLabel);
}
public void wrongAnswerAlterEgo() {
}```
Thank you for your help,

How to stop list from converting mix of 1s and 0s into all 0s?

I am creating a program to take in 1 or more 8 bit binary sequences and convert them into 12 bit hamming binary sequences. The code works and achieves everything right up until it tries to put the information into the JTextField at which point it converts the 8-bit stream into 1111 1111 and converts the 12 bit stream into 0000 0000 0000 (spacing for easier reading). I have tried stepping through the code and at every point it thinks the list is correct, right until i turn it into a string. Im not sure if im incorrectly using something but hopefully someone can help.
As an example for those who dont understand hamming codes. If you put in "10101010" no spaces, the system should spit out "Your original bit-stream was: 1010 1010 Your new Hamming bit-stream is: 1010 0101 1000"
But instead it will say "Your original bit-stream was: 1111 1111 Your new Hamming bit-stream is: 0000 0000 0000"
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Collections;
#SuppressWarnings("serial")
public class MainProgram extends JPanel {
private static final String INITIAL_TITLE = "Please enter your next 8 bits. "
+ "Do not enter more than 8 bits.\n"
+ "Press Enter when done";
private static final String ARE_YOU_FINISHED = "Are you finished entering streams?";
private static final String CALCULATING = "Just one moment while the code is generated...";
private static final String YES = "YES";
private static final String ENTER = "ENTER";
private static final String NO = "NO";
private static final String CONTINUE = "CONTINUE";
private static int GAP = 12;
private static final int COLUMNS = 35;
private static int TEMP_STREAM = 0;
static int numberOfStreams = 0;
static int counter = 0;
static String OriBuild;
static String NewBuild;
// this is a JTextArea built to look like a JLabel
private JTextArea topTextArea = new JTextArea(2, COLUMNS);
private JTextField dataEntryField = new JTextField(COLUMNS);
private JTextArea dataPrinter = new JTextArea(2,2);
private JButton yesEnterButton = new JButton(ENTER);
private JButton noButton = new JButton(NO);
private JButton contButton = new JButton(CONTINUE);
private boolean enteringData = true;
private boolean dataValidYet = false;
java.util.List<Integer> streamSplit = new ArrayList<>();
java.util.List<Integer> tempEight = new ArrayList<>();
java.util.List<Integer> finalStream = new ArrayList<>();
public MainProgram(){
yesEnterButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
yesEnterButtonActionPerfromed(e);
}
});
noButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
noButtonActionPerfromed(e);
}
});
contButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
contButtonActionPerfromed(e);
}
});
topTextArea.setWrapStyleWord(true);
topTextArea.setLineWrap(true);
topTextArea.setFocusable(false);
topTextArea.setEditable(false);
topTextArea.setOpaque(false);
topTextArea.setText(INITIAL_TITLE);
JPanel innerButtonPanel = new JPanel(new GridLayout(1, 0, GAP, 0));
innerButtonPanel.add(yesEnterButton);
innerButtonPanel.add(contButton);
innerButtonPanel.add(noButton);
contButton.setVisible(false);
noButton.setVisible(false);
JPanel outerButtonPanel = new JPanel();
outerButtonPanel.add(innerButtonPanel);
setBorder(BorderFactory.createEmptyBorder(GAP, GAP, GAP, GAP));
setLayout(new BorderLayout(GAP, GAP));
add(topTextArea, BorderLayout.PAGE_START);
add(dataEntryField, BorderLayout.LINE_END);
dataPrinter.setVisible(false);
add(dataPrinter, BorderLayout.LINE_START);
add(outerButtonPanel, BorderLayout.PAGE_END);
}
protected void noButtonActionPerfromed(ActionEvent e) {
if(!dataValidYet){
enteringData = true;
topTextArea.setText(INITIAL_TITLE);
noButton.setVisible(false);
dataEntryField.setVisible(true);
return;
}
// Pressing no allows more entry
}
private void yesEnterButtonActionPerfromed(ActionEvent e) {
if (enteringData) {
topTextArea.setText(ARE_YOU_FINISHED);
yesEnterButton.setText(YES);
yesEnterButton.setActionCommand(YES);
noButton.setVisible(true);
TEMP_STREAM = checkInput();
enteringData = false;
dataEntryField.setText("");
dataEntryField.setVisible(false);
streamAdd();//This function adds the stream (example: 10101010) to a list creating individual digits of 1,0,1,0,1,0,1,0
return;
}//pressing enter takes the value in box. Pressing yes causes the system to move on
else{
dataValidYet = true;
yesEnterButton.setVisible(false);
noButton.setVisible(false);
logicLaunch();//converts the 8 digit binary into a 12 digit hamming code
contButton.setVisible(true);
}
}
private void contButtonActionPerfromed(ActionEvent e) {
//This groups the 8 original individual digits, that were seperated into a list, back into a string for printing purposes
if(counter < numberOfStreams) {
dataPrinter.setVisible(true);
dataEntryField.setVisible(false);
OriBuild = ("Your original bit-stream was: "
+ streamSplit.get(counter * 0)
+ streamSplit.get(counter * 1)
+ streamSplit.get(counter * 2)
+ streamSplit.get(counter * 3) + " "
+ streamSplit.get(counter * 4)
+ streamSplit.get(counter * 5)
+ streamSplit.get(counter * 6)
+ streamSplit.get(counter * 7));
NewBuild = ("Your new Hamming bit-stream is: "
//This groups the 12 new individual digits, that were seperated into a list, back into a string for printing purposes
+ finalStream.get(counter * 0)
+ finalStream.get(counter * 1)
+ finalStream.get(counter * 2)
+ finalStream.get(counter * 3) + " "
+ finalStream.get(counter * 4)
+ finalStream.get(counter * 5)
+ finalStream.get(counter * 6)
+ finalStream.get(counter * 7) + " "
+ finalStream.get(counter * 8)
+ finalStream.get(counter * 9)
+ finalStream.get(counter * 10)
+ finalStream.get(counter * 11));
System.out.println(OriBuild + " " + NewBuild);
dataPrinter.setText(OriBuild + "\n" + NewBuild);
counter++;
//Prints the strings to the screen so that the user can retrieve wanted information. Then adds 1 to the counter incase more than 1 stream was entered to cycle
} else {
dataPrinter.setText("Program complete. Close and relaunch to re-use");
contButton.setVisible(false);
//Once out of streams program finishes on this informing user that its reached the end of its usefulness and require re-launching.
}
}
private static void createAndShowGui() {
MainProgram mainPanel = new MainProgram();
JFrame frame = new JFrame("HammingCode");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
public int checkInput()
{
String temp1 = dataEntryField.getText();
int temp = Integer.parseInt(temp1);
return temp;
}
public void streamAdd(){
do {
streamSplit.add(TEMP_STREAM % 10);
TEMP_STREAM /= 10;
} while (TEMP_STREAM != 0);
}
public void logicLaunch(){
topTextArea.setText(CALCULATING);
int arrayLength = streamSplit.size();
int bufferLength = 8 - arrayLength % 8;
if (bufferLength != 8)
{
numberOfStreams = arrayLength / 8 + 1;
} else
{
numberOfStreams = arrayLength / 8;
}
int tempStreams = numberOfStreams;
System.out.println(numberOfStreams + "<Streams Buffer>" + bufferLength);
while (bufferLength > 0 && bufferLength != 8)
{
streamSplit.add(0);
bufferLength--;
}
while (tempStreams > 0)
{
for (int i = 0; i < 8; i++)
{
tempEight.add(streamSplit.get(i));
}
if ((tempEight.get(0) + tempEight.get(1) + tempEight.get(3) + tempEight.get(4) + tempEight.get(6)) % 2 == 0)
{
tempEight.add(0, 0);
} else
{
tempEight.add(0, 1);
}
if ((tempEight.get(1) + tempEight.get(3) + tempEight.get(4) + tempEight.get(6) + tempEight.get(7)) % 2 == 0)
{
tempEight.add(1, 0);
} else
{
tempEight.add(1, 1);
}
if ((tempEight.get(3) + tempEight.get(4) + tempEight.get(5) + tempEight.get(9)) % 2 == 0)
{
tempEight.add(3, 0);
} else
{
tempEight.add(3, 1);
}
if ((tempEight.get(7) + tempEight.get(8) + tempEight.get(9) + tempEight.get(10)) % 2 == 0)
{
tempEight.add(7, 0);
} else
{
tempEight.add(7, 1);
}
tempStreams--;
for (int i = 0; i < 12; i++)
{
finalStream.add(tempEight.get(0));
tempEight.remove(0);
}
}
Collections.reverse(streamSplit);
}//Runs all the logic to generate the 12 bit code, this part is working fine. Reverse is for formatting purposes
}
I haven't figured out the code entirely, but it appears your use of counter in contButtonActionPerfromed is incorrect. When counter is 0, it will get element 0 every time; when 1 it will get 0, 1, 2, etc.; when 2 it will get 0, 2, 4, etc. Then there's something about exiting the program after the first time, so maybe you don't go through it a second time (and a third time would likely generate an error attempting to access a non-existent element).
You really ought to be able to debug that method...

for loop for array only processing one element in java?

I can't figure out why whenever I cycle through my array using the for-loop it only produces one element (the first) to console? I'm pretty sure it's a rookie-mistake I'm looking over, so any tips and suggestions would help.
I'm making a program for fun that compares two strings typed in a text field and if they don't exist in the array it produces a JOPtionPane message on the contrary. It's for a battle-hack I may produce in the future for vBulletin forum, but I'm messing around with algorithms before I move to that step. Thanks, guys!
package battleoptionspart1;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.lang.*;
import javax.swing.border.*;
public class BattleOptionsPart1 extends JFrame{
JButton newthread, previewpost;
JRadioButton battle1;
JTextField postcount, oppA, oppB;
JLabel battle2, max;
JPanel panel;
String [] array = {"Bill","Tom","Wendy", "Paula"};
public BattleOptionsPart1 () {
panel = new JPanel();
Toolkit tool = Toolkit.getDefaultToolkit();
Dimension dim = tool.getScreenSize();
this.setSize(500, 500);
this.setTitle("Battle Options");
GridLayout grid = new GridLayout(0,1,2,2);
this.setLayout(grid);
newthread = new JButton("Post New Thread");
previewpost = new JButton("Preview Post");
postcount = new JTextField("", 4);
oppA = new JTextField("",10);
oppB = new JTextField("",10);
battle1 = new JRadioButton();
battle2 = new JLabel("Would you like to start a recorded battle?");
max = new JLabel("Enter max post count user must have to vote");
ListenForButton listen = new ListenForButton();
newthread.addActionListener(listen);
previewpost.addActionListener(listen);
JPanel opponents = new JPanel();
Border oppBorder = BorderFactory.createTitledBorder("Battlers");
opponents.setBorder(oppBorder);
opponents.add(oppA);
opponents.add(oppB);
JPanel battle = new JPanel();
Border battleBorder = BorderFactory.createTitledBorder("Start Battle");
battle.setBorder(battleBorder);
battle.add(battle1);
battle.add(battle2);
JPanel buttons = new JPanel();
Border buttonBorder = BorderFactory.createTitledBorder("Create Thread");
buttons.setBorder(buttonBorder);
buttons.add(newthread);
buttons.add(previewpost);
JPanel restriction = new JPanel();
Border resBorder = BorderFactory.createTitledBorder("Restrictions");
restriction.setBorder(buttonBorder);
restriction.add(postcount);
restriction.add(max);
this.add(opponents);
this.add(battle);
this.add(restriction);
this.add(buttons);
this.add(panel);
int xPos = (dim.width / 2) - (this.getWidth() / 2);
int yPos = (dim.height / 2) - (this.getHeight() / 2);
this.setLocation(xPos,yPos); //places form in the middle
this.setVisible(true); // users can see form
this.setResizable(false); //users can't resize the form
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
private class ListenForButton implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
String compareA = oppA.getText();
String compareB = oppB.getText();
if (e.getSource() == newthread)
{
System.out.println(compareA + "\n" + compareB);
for(int j = 0; j < array.length; j++)
{
System.out.println(array[j]);
if(!compareA.equals(array[j]))
{
JOptionPane.showMessageDialog(null, compareA + " doesn't exist!", "Error Message", JOptionPane.ERROR_MESSAGE);
oppA.requestFocus();
break;
}
if (!compareB.equals(array[j]))
{
JOptionPane.showMessageDialog(null, compareB + " doesn't exist!", "Error Message", JOptionPane.ERROR_MESSAGE);
oppB.requestFocus();
break;
}
else
{
JOptionPane.showMessageDialog(null, "New thread created successfully!", "Success", JOptionPane.INFORMATION_MESSAGE);
break;
}
}
}
else if (e.getSource() == previewpost)
{
System.exit(0);
}
}
}
public static void main(String[] args) {
BattleOptionsPart1 battle = new BattleOptionsPart1();
}
}
In each of the possible options in your loop, you use break, which leaves the loop immediately. If you remove those statements, you'll process each object in the array.
If you want to check if there's a match, you need to go through every element and do your processing after going through the whole array. Here is an example for an array of type int:
boolean contains = false;
for (int i = 0; i < arr.length; i++)
{
if (arr[i] == searchKey)
{
contains = true;
break;
}
}
You're breaking out of the loop. with the break; command after the first array element

Issues: Creating a very accurate Swing Timer

In order to make SwingTimer accurate, I like the logic and example suggested by #Tony Docherty
On CR. Here is the Link.
In order to highlight the given words, again and again, there is always a few microsecond delays. If I have words to highlight say: "hello how are" and the values for each word are (delays): 200,300,400 ms respectively, then the actual time taken by the timer is always more. Say instead of 200 ms, it takes 216 ms. Like this, if I have many words..in the end, the extra delay is noticeable.
I have to highlight each letter say: 'h''e''l''l''0' each should get 200/length(i.e 5) = 40 ms approx. Set the delay after each letter.
My logic is, take the current time say startTime, just before starting the process. Also, calculate the totalDelay which is totalDelay+=delay/.length().
Now check the condition: (startTime+totalDelay-System.currentTime)
if this is -ve, that means the time consumption is more, so skip the letter. Check till there is a positive delay.This means I am adding the timings till now, and overcheck it with the difference in the time taken by the process when it got started.
This may result into skipping to highlight the letters.
But something is wrong. What, it’s difficult for me to make out. It's some problem with the looping thing maybe. I have seen it is entering the loop (to check whether the time is -ve ) just twice. But this should not be the case. And I am also not sure about setting up my next delay. Any ideas?
Here is an SSCCE:
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.lang.reflect.InvocationTargetException;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextPane;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultStyledDocument;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyledDocument;
public class Reminder {
private static final String TEXT = "arey chod chaad ke apnee saleem ki gali anarkali disco chalo";
private static final String[] WORDS = TEXT.split(" ");
private JFrame frame;
private Timer timer;
private StyledDocument doc;
private JTextPane textpane;
private int[] times = new int[100];
private long totalDelay=0,startTime=0;
private int stringIndex = 0;
private int index = 0;
public void startColoring() {
times[0]=100;times[9]=200;times[10]=200;times[11]=200;times[12]=200;
times[1]=400;times[2]=300;times[3]=900;times[4]=1000;times[5]=600;times[6]=200;times[7]=700;times[8]=700;
ActionListener actionListener = new ActionListener() {
#Override
public void actionPerformed(ActionEvent actionEvent)
{
doc.setCharacterAttributes(stringIndex, 1, textpane.getStyle("Red"), true);
stringIndex++;
try {
if (stringIndex >= doc.getLength() || doc.getText(stringIndex, 1).equals(" ")|| doc.getText(stringIndex, 1).equals("\n"))
{
index++;
}
if (index < WORDS.length) {
double delay = times[index];
totalDelay+=delay/WORDS[index].length();
/*Check if there is no -ve delay, and you are running according to the time*/
/*The problem is here I think. It's just entered this twice*/
while(totalDelay+startTime-System.currentTimeMillis()<0)
{
totalDelay+=delay/WORDS[index].length();
stringIndex++;
/*this may result into the end of current word, jump to next word.*/
if (stringIndex >= doc.getLength() || doc.getText(stringIndex, 1).equals(" ") || doc.getText(stringIndex, 1).equals("\n"))
{
index += 1;
totalDelay+=delay/WORDS[index].length();
}
}
timer.setDelay((int)(totalDelay+startTime-System.currentTimeMillis()));
}
else {
timer.stop();
System.err.println("Timer stopped");
}
} catch (BadLocationException e) {
e.printStackTrace();
}
}
};
startTime=System.currentTimeMillis();
timer = new Timer(times[index], actionListener);
timer.setInitialDelay(0);
timer.start();
}
public void initUI() {
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new JPanel();
doc = new DefaultStyledDocument();
textpane = new JTextPane(doc);
textpane.setText(TEXT);
javax.swing.text.Style style = textpane.addStyle("Red", null);
StyleConstants.setForeground(style, Color.RED);
panel.add(textpane);
frame.add(panel);
frame.pack();
frame.setVisible(true);
}
public static void main(String args[]) throws InterruptedException, InvocationTargetException {
SwingUtilities.invokeAndWait(new Runnable() {
#Override
public void run() {
Reminder reminder = new Reminder();
reminder.initUI();
reminder.startColoring();
}
});
}
}
UPDATE:
For better understanding:
The EG given by #Tony Docherty :
Lets take the word "Test" and say it needs to be highlighted for 1 second, therefore each letter is highlighted for 250ms.
Doing things the way you originally, did meant that you set a timer for 250ms for each letter but if each cycle actually took 260ms and lets say the 'e' cycle took 400ms (maybe due to GC or something else using CPU cycles) by the end of the word you would have taken 180ms more than you should have. This error will continue to build for each word until the error is so large highlighting is no longer visually in sync.
The way I am trying, is rather than repeatedly saying this letter needs to be highlighted for x amount of time, calculate the time for each letter relative to the beginning of the sequence ie T = 250, e = 500, s = 750, t = 1000.
So to get the actual time delay you need to add the start time and subtract the current time. To run through the example using the timings I gave above:
StartTime Letter Offset CurrentTime Delay ActualTimeTaken
100000 T 250 100010 240 250
100000 e 500 100260 240 400
100000 s 750 100660 90 100
100000 t 1000 100760 240 250
So you should be able to see now that the timing for each letter is adjusted to take account of any overrun of time from the previous letter. Of course it is possible that a timing overrun is so great that you have to skip highlighting the next letter (or maybe more than 1) but at least I will remaining broadly in sync.
EDITED SSCCE
Update2
In first phase, I take the timings for each word. That is, when the user hits ESC key, the time is stored for a particular word (he does it as the song is played in background.) When the ESC key is pressed, the current word is highlighted and the time spent on the current word is stored in an array. I keep on storing the timings. When the user ends, now I would like to highlight the words as per the set timings. So here, the timing by the user is important. If the timings are fast, so is the highlighting of words or if slow, vice-versa.
New update: progress
The answers below have different logic, but to my surprise, they work more or less the same. A very very weird problem I have found out with all the logic (including mine) is that they seem to work perfectly for few lines, but after that they gain speed, that's also not slowly, but with a huge difference.
Also if you think I should think in a different way, your suggestions are highly appreciated.
I think that to do something like this, you need a Swing Timer that ticks at a constant rate, say 15 msec, as long as it's fast enough to allow the time granularity you require, and then trip the desired behavior inside the timer when the elapsed time is that which you require.
In other words, don't change the Timer's delay at all, but just change the required elapse times according to your need.
You should not have a while (true) loop on the EDT. Let the "while loop" be the Swing Timer itself.
To make your logic more fool proof, you need to check if elapsed time is >= needed time.
Again, don't set the Timer's delay. In other words, don't use it as a timer but rather as a poller. Have it beat every xx msec constantly polling the elapsed time, and then reacting if the elapsed time is >= to your need.
The code I'm suggesting would look something like so:
public void actionPerformed(ActionEvent actionEvent) {
if (index > WORDS.length || stringIndex >= doc.getLength()) {
((Timer)actionEvent.getSource()).stop();
}
currentElapsedTime = calcCurrentElapsedTime();
if (currentElapsedTime >= elapsedTimeForNextChar) {
setNextCharAttrib(stringIndex);
stringIndex++;
if (atNextWord(stringIndex)) {
stringIndex++; // skip whitespace
deltaTimeForEachChar = calcNextCharDeltaForNextWord();
} else {
elapsedTimeForNextChar += deltaTimeForEachChar;
}
}
// else -- we haven't reached the next time to change char attribute yet.
// keep polling.
}
For example, my SSCCE:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.LinkedList;
import java.util.List;
import javax.swing.*;
import javax.swing.text.*;
public class Reminder3 {
private static final String TEXT = "arey chod chaad ke apnee saleem ki gali anarkali disco chalo";
private static final String[] WORDS = TEXT.split(" ");
private static final int[] TIMES = { 100, 400, 300, 900, 1000, 600, 200,
700, 700, 200, 200, 200, 200 };
private static final int POLLING_TIME = 12;
private StyledDocument doc;
private JTextPane textpane;
private JPanel mainPanel = new JPanel();
private List<ReminderWord> reminderWordList = new LinkedList<ReminderWord>();
private Timer timer;
// private int stringIndex = 0;
public Reminder3() {
doc = new DefaultStyledDocument();
textpane = new JTextPane(doc);
textpane.setText(TEXT);
javax.swing.text.Style style = textpane.addStyle("Red", null);
StyleConstants.setForeground(style, Color.RED);
JPanel textPanePanel = new JPanel();
textPanePanel.add(new JScrollPane(textpane));
JButton startBtn = new JButton(new AbstractAction("Start") {
#Override
public void actionPerformed(ActionEvent arg0) {
goThroughWords();
}
});
JPanel btnPanel = new JPanel();
btnPanel.add(startBtn);
mainPanel.setLayout(new BorderLayout());
mainPanel.add(textPanePanel, BorderLayout.CENTER);
mainPanel.add(btnPanel, BorderLayout.SOUTH);
}
public void goThroughWords() {
if (timer != null && timer.isRunning()) {
return;
}
doc = new DefaultStyledDocument();
textpane.setDocument(doc);
textpane.setText(TEXT);
javax.swing.text.Style style = textpane.addStyle("Red", null);
StyleConstants.setForeground(style, Color.RED);
int wordStartTime = 0;
for (int i = 0; i < WORDS.length; i++) {
if (i > 0) {
wordStartTime += TIMES[i - 1];
}
int startIndexPosition = 0; // set this later
ReminderWord reminderWord = new ReminderWord(WORDS[i], TIMES[i],
wordStartTime, startIndexPosition);
reminderWordList.add(reminderWord);
}
int findWordIndex = 0;
for (ReminderWord word : reminderWordList) {
findWordIndex = TEXT.indexOf(word.getWord(), findWordIndex);
word.setStartIndexPosition(findWordIndex);
findWordIndex += word.getWord().length();
}
timer = new Timer(POLLING_TIME, new TimerListener());
timer.start();
}
public JComponent getMainPanel() {
return mainPanel;
}
private void setNextCharAttrib(int textIndex) {
doc.setCharacterAttributes(textIndex, 1,
textpane.getStyle("Red"), true);
}
private class TimerListener implements ActionListener {
private ReminderWord currentWord = null;
private long startTime = System.currentTimeMillis();
#Override
public void actionPerformed(ActionEvent e) {
if (reminderWordList == null) {
((Timer) e.getSource()).stop();
return;
}
if (reminderWordList.isEmpty() && currentWord.atEnd()) {
((Timer) e.getSource()).stop();
return;
}
// if just starting, or if done with current word
if (currentWord == null || currentWord.atEnd()) {
currentWord = reminderWordList.remove(0); // get next word
}
long totalElapsedTime = System.currentTimeMillis() - startTime;
if (totalElapsedTime > (currentWord.getStartElapsedTime() + currentWord
.getIndex() * currentWord.getTimePerChar())) {
setNextCharAttrib(currentWord.getStartIndexPosition() + currentWord.getIndex());
currentWord.increment();
}
}
}
private static void createAndShowGui() {
Reminder3 reminder = new Reminder3();
JFrame frame = new JFrame("Reminder");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(reminder.getMainPanel());
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
class ReminderWord {
private String word;
private int totalTime;
private int timePerChar;
private int startTime;
private int startIndexPosition;
private int index = 0;
public ReminderWord(String word, int totalTime, int startTime,
int startIndexPosition) {
this.word = word;
this.totalTime = totalTime;
this.startTime = startTime;
timePerChar = totalTime / word.length();
this.startIndexPosition = startIndexPosition;
}
public String getWord() {
return word;
}
public int getTotalTime() {
return totalTime;
}
public int getStartElapsedTime() {
return startTime;
}
public int getTimePerChar() {
return timePerChar;
}
public int getStartIndexPosition() {
return startIndexPosition;
}
public int increment() {
index++;
return index;
}
public int getIndex() {
return index;
}
public boolean atEnd() {
return index > word.length();
}
public void setStartIndexPosition(int startIndexPosition) {
this.startIndexPosition = startIndexPosition;
}
#Override
public String toString() {
return "ReminderWord [word=" + word + ", totalTime=" + totalTime
+ ", timePerChar=" + timePerChar + ", startTime=" + startTime
+ ", startIndexPosition=" + startIndexPosition + ", index=" + index
+ "]";
}
}
Okay so I have been looking at the some code (the code I posted in your last question about Karaoke timer)
Using that code I put up some measuring system using System.nanoTime() via System.out.println() which will help us to see what is happening:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JTextPane;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.text.Style;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyledDocument;
public class KaraokeTest {
private int[] timingsArray = {1000, 1000, 9000, 1000, 1000, 1000, 1000, 1000, 1000, 1000};//word/letters timings
private String[] individualWordsToHighlight = {" \nHello\n", " world\n", " Hello", " world", " Hello", " world", " Hello", " world", " Hello", " world"};//each individual word/letters to highlight
private int count = 0;
private final JTextPane jtp = new JTextPane();
private final JButton startButton = new JButton("Start");
private final JFrame frame = new JFrame();
//create Arrays of individual letters and their timings
final ArrayList<String> chars = new ArrayList<>();
final ArrayList<Long> charsTiming = new ArrayList<>();
public KaraokeTest() {
initComponents();
}
private void initComponents() {
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
for (String s : individualWordsToHighlight) {
String tmp = jtp.getText();
jtp.setText(tmp + s);
}
jtp.setEditable(false);
startButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent ae) {
startButton.setEnabled(false);
count = 0;
charsTiming.clear();
chars.clear();
for (String s : individualWordsToHighlight) {
for (int i = 0; i < s.length(); i++) {
chars.add(String.valueOf(s.charAt(i)));
//System.out.println(String.valueOf(s.charAt(i)));
}
}
//calculate each letters timings
for (int x = 0; x < timingsArray.length; x++) {
for (int i = 0; i < individualWordsToHighlight[x].length(); i++) {
individualWordsToHighlight[x] = individualWordsToHighlight[x].replace("\n", " ").replace("\r", " ");//replace line breaks
charsTiming.add((long) (timingsArray[x] / individualWordsToHighlight[x].trim().length()));//dont count spaces
//System.out.println(timingsArray[x] / individualWordsToHighlight[x].length());
}
}
Timer t = new Timer(1, new AbstractAction() {
long startTime = 0;
long acum = 0;
long timeItTookTotal = 0;
long dif = 0, timeItTook = 0, timeToTake = 0;
int delay = 0;
#Override
public void actionPerformed(ActionEvent ae) {
if (count < charsTiming.size()) {
if (count == 0) {
startTime = System.nanoTime();
System.out.println("Started: " + startTime);
}
timeToTake = charsTiming.get(count);
acum += timeToTake;
//highlight the next word
highlightNextWord();
//System.out.println("Acum " + acum);
timeItTook = (acum - ((System.nanoTime() - startTime) / 1000000));
timeItTookTotal += timeItTook;
//System.out.println("Elapsed since start: " + (System.nanoTime() - startTime));
System.out.println("Time the char should take: " + timeToTake);
System.out.println("Time it took: " + timeItTook);
dif = (timeToTake - timeItTook);
System.out.println("Difference: " + dif);
//System.out.println("Difference2 " + (timeToTake - dif));
//calculate start of next letter to highlight less the difference it took between time it took and time it should actually take
delay = (int) (timeToTake - dif);
if (delay < 1) {
delay = 1;
}
//restart timer with new timings
((Timer) ae.getSource()).setInitialDelay((int) timeToTake);//timer is usually faster thus the entire highlighting will be done too fast
//((Timer) ae.getSource()).setInitialDelay(delay);
((Timer) ae.getSource()).restart();
} else {//we are at the end of the array
long timeStopped = System.nanoTime();
System.out.println("Stopped: " + timeStopped);
System.out.println("Time it should take in total: " + acum);
System.out.println("Time it took using accumulator of time taken for each letter: " + timeItTookTotal
+ "\nDifference: " + (acum - timeItTookTotal));
long timeItTookUsingNanoTime = ((timeStopped - startTime) / 1000000);
System.out.println("Time it took using difference (endTime-startTime): " + timeItTookUsingNanoTime
+ "\nDifference: " + (acum - timeItTookUsingNanoTime));
reset();
((Timer) ae.getSource()).stop();//stop the timer
}
count++;//increment counter
}
});
t.setRepeats(false);
t.start();
}
});
frame.add(jtp, BorderLayout.CENTER);
frame.add(startButton, BorderLayout.SOUTH);
frame.pack();
frame.setVisible(true);
}
private void reset() {
startButton.setEnabled(true);
jtp.setText("");
for (String s : individualWordsToHighlight) {
String tmp = jtp.getText();
jtp.setText(tmp + s);
}
JOptionPane.showMessageDialog(frame, "Done");
}
private void highlightNextWord() {
//we still have words to highlight
int sp = 0;
for (int i = 0; i < count + 1; i++) {//get count for number of letters in words (we add 1 because counter is only incrementd after this method is called)
sp += 1;
}
while (chars.get(sp - 1).equals(" ")) {
sp += 1;
count++;
}
//highlight words
Style style = jtp.addStyle("RED", null);
StyleConstants.setForeground(style, Color.RED);
((StyledDocument) jtp.getDocument()).setCharacterAttributes(0, sp, style, true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new KaraokeTest();
}
});
}
}
The output on my PC is:
Started: 10289712615974
Time the char should take: 166
Time it took: 165
Difference 1
...
Time the char should take: 166
Time it took: 155
Difference 11
...
Time the char should take: 166
Time it took: 5
Difference 161
Stopped: 10299835063084
Time it should take in total: 9960
Time it took using accumulator of time taken for each letter: 5542
Difference: 4418
Time it took using difference (endTime-startTime): 10122
Difference: -162
Thus my conclusion is the Swing Timer is actually running faster than we expect as the code in the Timers actionPerformed will not necessarily take as long as the letters expected highlighting time this of course causes an avalanche effect i.e the faster/slower the timer runs the greater/less the difference will become and timers next execution on restart(..) will take be at a different time i.e faster or slower.
in the code do this:
//calculate start of next letter to highlight less the difference it took between time it took and time it should actually take
delay = (int) (timeToTake - dif);
//restart timer with new timings
//((Timer) ae.getSource()).setInitialDelay((int)timeToTake);//timer is usually faster thus the entire highlighting will be done too fast
((Timer) ae.getSource()).setInitialDelay(delay);
((Timer) ae.getSource()).restart();
Produces a more accurate result (maximum latency Ive had is 4ms faster per letter):
Started: 10813491256556
Time the char should take: 166
Time it took: 164
Difference 2
...
Time the char should take: 166
Time it took: 164
Difference 2
...
Time the char should take: 166
Time it took: 162
Difference 4
Stopped: 10823452105363
Time it should take in total: 9960
Time it took using accumulator of time taken for each letter: 9806
Difference: 154
Time it took using difference (endTime-startTime): 9960
Difference: 0
Have you considered java.util.Timer and scheduleAtFixedRate? You will need a little extra work to do stuff on the EDT, but it should fix the issue of accumulated delays.
ScheduledExecutorService tends to be more accurate than Swing's Timer, and it offers the benefit of running more than one thread. In particular, if one tasks gets delayed, it does not affect the starting time of the next tasks (to some extent).
Obviously if the tasks take too long on the EDT, this is going to be your limiting factor.
See below a proposed SSCCE based on yours - I have also slightly refactored the startColoring method and split it in several methods. I have also added some "logging" to get a feedback on the timing of the operations. Don't forget to shutdown the executor when you are done or it might prevent your program from exiting.
Each words starts colouring with a slight delay (between 5 and 20ms on my machine), but the delays are not cumulative. You could actually measure the scheduling overhead and adjust accordingly.
public class Reminder {
private static final String TEXT = "arey chod chaad ke apnee saleem ki gali anarkali disco chalo\n" +
"arey chod chaad ke apnee saleem ki gali anarkali disco chalo\n" +
"arey chod chaad ke apnee saleem ki gali anarkali disco chalo\n" +
"arey chod chaad ke apnee saleem ki gali anarkali disco chalo\n" +
"arey chod chaad ke apnee saleem ki gali anarkali disco chalo\n" +
"arey chod chaad ke apnee saleem ki gali anarkali disco chalo";
private static final String[] WORDS = TEXT.split("\\s+");
private JFrame frame;
private StyledDocument doc;
private JTextPane textpane;
private static final int[] TIMES = {100, 400, 300, 900, 1000, 600, 200, 700, 700, 200, 200,
100, 400, 300, 900, 1000, 600, 200, 700, 700, 200, 200,
100, 400, 300, 900, 1000, 600, 200, 700, 700, 200, 200,
100, 400, 300, 900, 1000, 600, 200, 700, 700, 200, 200,
100, 400, 300, 900, 1000, 600, 200, 700, 700, 200, 200,
100, 400, 300, 900, 1000, 600, 200, 700, 700, 200, 200, 200};
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);
private int currentLetterIndex;
private long start; //for logging
public void startColoring() {
start = System.currentTimeMillis(); //for logging
int startTime = TIMES[0];
for (int i = 0; i < WORDS.length; i++) {
scheduler.schedule(colorWord(i, TIMES[i + 1]), startTime, TimeUnit.MILLISECONDS);
startTime += TIMES[i+1];
}
scheduler.schedule(new Runnable() {
#Override
public void run() {
scheduler.shutdownNow();
}
}, startTime, TimeUnit.MILLISECONDS);
}
//Color the given word, one letter at a time, for the given duration
private Runnable colorWord(final int wordIndex, final int duration) {
final int durationPerLetter = duration / WORDS[wordIndex].length();
final int wordStartIndex = currentLetterIndex;
currentLetterIndex += WORDS[wordIndex].length() + 1;
return new Runnable() {
#Override
public void run() {
System.out.println((System.currentTimeMillis() - start) + " ms - Word: " + WORDS[wordIndex] + " - duration = " + duration + "ms");
for (int i = 0; i < WORDS[wordIndex].length(); i++) {
scheduler.schedule(colorLetter(wordStartIndex + i), i * durationPerLetter, TimeUnit.MILLISECONDS);
}
}
};
}
//Color the letter on the EDT
private Runnable colorLetter(final int letterIndex) {
return new Runnable() {
#Override
public void run() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
System.out.println("\t" + (System.currentTimeMillis() - start) + " ms - letter: " + TEXT.charAt(letterIndex));
doc.setCharacterAttributes(letterIndex, 1, textpane.getStyle("Red"), true);
}
});
}
};
}
public void initUI() {
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new JPanel();
doc = new DefaultStyledDocument();
textpane = new JTextPane(doc);
textpane.setText(TEXT);
javax.swing.text.Style style = textpane.addStyle("Red", null);
StyleConstants.setForeground(style, Color.RED);
panel.add(textpane);
frame.add(panel);
frame.pack();
frame.setVisible(true);
}
public static void main(String args[]) throws InterruptedException, InvocationTargetException {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
Reminder reminder = new Reminder();
reminder.initUI();
reminder.startColoring();
}
});
}
}

Java GPA calculation (how to set unlimited arrays?)

Basically, my problem is that I am not sure on how to set unlimited input arrays from user..
For now, the calculation only goes well if the user enters 6 subject exactly, but not if entered subject is less or more than 6..
Somehow, i want to make the input to be unlimited..
This is what I have so far:
THE MAIN CLASS
import javax.swing.*;
public class CGPAMain extends JFrame
{
public static void main(String[] args)
{
JFrame frame = new JFrame("GPA Calculation");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
MainPage panel = new MainPage();
frame.getContentPane().add(panel);
frame.pack();
frame.setVisible(true);
}
}
THE OTHER CLASS
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class MainPage extends JPanel
{
//declare GUI elements
private JLabel subjectLabel, cHoursLabel, gradeLabel;
private JTextField subject, cHours;
private JButton addSubjectButton, calcGPAButton, clearAllButton;
private JTextArea tArea;
private JComboBox grade;
//declare array to store and collect user input value
String[] subjectArray = new String[6];
String[] gradeArray = new String[6];
int[] cHoursArray = new int[6];
double[] gradeValue = { 4.00, 3.67, 3.33, 3.00, 2.67, 2.33, 2.00, 1.67, 1.33, 1.00, 0.00 };
String[] gradeLetter= { "A", "A-", "B+", "B", "B-", "C+", "C", "D", "D-", "E", "F"};
public MainPage()
{
setLayout (null);
setPreferredSize (new Dimension(500, 500));
setBackground (Color.orange);
//Properties of GUI elements
subjectLabel = new JLabel ("Subject Name: ");
subject = new JTextField (33);
subject.addActionListener (new TempListener());
gradeLabel = new JLabel ("Grade: ");
grade = new JComboBox (gradeLetter);
grade.addActionListener (new TempListener());
cHoursLabel = new JLabel ("Credit Hours: ");
cHours = new JTextField (1);
cHours.addActionListener (new TempListener());
addSubjectButton = new JButton("Add Another Subject");
addSubjectButton.addActionListener(new TempListener());
calcGPAButton = new JButton("Calculate GPA");
calcGPAButton.addActionListener(new TempListener());
clearAllButton = new JButton("Clear All");
clearAllButton.addActionListener(new TempListener());
tArea = new JTextArea(5, 5);
tArea.setEditable(false);
add (subjectLabel);
add (subject);
add (gradeLabel);
add (grade);
add (cHoursLabel);
add (cHours);
add (addSubjectButton);
add (calcGPAButton);
add (clearAllButton);
add (tArea);
//Position of GUI elements
subjectLabel.setBounds (20, 20, 150, 20);
subject.setBounds (120, 20, 350, 20);
gradeLabel.setBounds (20, 50, 50, 20);
grade.setBounds (120, 50, 50, 20);
cHoursLabel.setBounds (20, 80, 100, 20);
cHours.setBounds (120, 80, 50, 20);
addSubjectButton.setBounds (20, 120, 200, 30);
calcGPAButton.setBounds (300, 440, 175, 30);
clearAllButton.setBounds (20, 440, 120, 30);
tArea.setBounds (20, 170, 450, 250);
}
private class TempListener implements ActionListener
{
//---------------------------------------------------------------------------
// Performs the conversion when the enter key is pressed in the text field.
//---------------------------------------------------------------------------
double tCrPoints = 0.00, tCrHours = 0.00, tGPA = 0.00;
String status;
public void actionPerformed(ActionEvent event)
{
if (event.getSource() == addSubjectButton)
{
for (int i=0; i<6; i++)
{
subjectArray[i] = subject.getText();
gradeArray[i] = (String) grade.getSelectedItem();
cHoursArray[i] = Integer.parseInt(cHours.getText());
}
tArea.append (subject.getText() + "\t\t\t" +
grade.getSelectedItem() + "\t" +
cHours.getText() + "\n");
subject.setText("");
cHours.setText("");
}
if (event.getSource() == calcGPAButton)
{
for (int i=0 ; i<gradeArray.length; i++)
{
for (int j=0; j<gradeLetter.length; j++)
{
if(gradeArray[i].equals(gradeLetter[j]))
{
tCrHours += cHoursArray[i];
tCrPoints += gradeValue[j] * cHoursArray[i];
}
}
}
tGPA = tCrPoints/tCrHours;
if (tGPA >= 2)
status = ("Pass");
else
status = ("Fail");
//Output for text area
tArea.setText("Total Credit Points : " + tCrPoints + "\n" +
"Total Credit Hours : " + tCrHours + "\n\n" +
"Grade Point Average (GPA) : " + tGPA + "\n" +
"Status : " + status);
}
if (event.getSource() == clearAllButton)
{
tArea.setText("");
cHours.setText("");
grade.setSelectedIndex(0);
tCrHours = 0.00;
tCrPoints = 0.00;
}
}
}
}
You're looking for ArrayList<E>
You did a good job splitting apart the main() from the GUI and the rest of the application, but I think you should have taken it further to split the GUI from the calculation code, too.
Consider the larger question: Why does a GUI JPanel know about grades and GPAs? The job of a JPanel is to know how to render GUI elements within a box and route input events to the right widget object.
I believe your code would be far more maintainable and definitely more malleable if you split the code to manage the grades from the code to handle the GUI. There's always going to be a bit of a rough edge between telling the GUI to update values into the model vs having the model poll the data values from the GUI, but you can define that interface to suit your needs best: it might be a GPA object that uses SLaks's recommended ArrayList<E> to store individual Subject objects that know the name, grade, dates the course was conducted, etc. (It might just be (name, grade) tuples, since this is short and sweet so far.)
Your GPA object could export an interface: public void addSubject(String className, String grade) and public double getGPA(). Your GUI could call into this interface to add new classes and retrieve the GPA for display.
I realize that this is a much more drastic re-write than you were looking for. And it doesn't even immediately address your concern. But I believe the GPA code, when viewed on its own, would look far more easily manipulated than it currently does buried amongst the GUI code.
You should look at ArrayList, Vector or even LinkedList for the functionality you seek. Here's a tutorial explaining how to use an ArrayList.
The idea is simple: the ArrayList will take care of growing an internal array for storing its elements, and as you keep adding elements to it, it will keep growing automatically to accommodate them. Strictly speaking, the size won't be unlimited, but a maximum of 2^31-1 positions (assuming that you have enough memory) should me more than enough for most practical cases.
EDIT :
For your particular example:
// create an ArrayList
ArrayList<Double> gradeValue = new ArrayList<Double>();
// add elements to the ArrayList
gradeValue.add(4.00);
gradeValue.add(3.67); // etc.
// iterating over the ArrayList
for (int i = 0; i < gradeValue.size(); i++) {
double value = gradeValue.get(i);
// etc.
}
Use a hashmap to contain all your grades, and use array lists to contain your hour and grade earned. Here is a quick rewrite of what you have.
public class MainPage extends JPanel {
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
private JLabel subjectLabel, cHoursLabel, gradeLabel;
private JTextField subject, cHours;
private JButton addSubjectButton, calcGPAButton, clearAllButton;
private JTextArea tArea;
private JComboBox grade;
List<String> gradeArray = new ArrayList<String>();
List<Integer> cHoursArray = new ArrayList<Integer>();
Map<String, Double> grades = new HashMap<String, Double>();
public MainPage() {
this.populateGrades();
setLayout(null);
setPreferredSize (new Dimension(500, 500));
setBackground (Color.orange);
subjectLabel = new JLabel ("Subject Name: ");
subject = new JTextField (33);
subject.addActionListener (new TempListener());
gradeLabel = new JLabel ("Grade: ");
grade = new JComboBox (grades.keySet().toArray());
grade.addActionListener (new TempListener());
// ... THE REST OF YOU CODE ... //
}
private void populateGrades() {
grades.put("A", 4.00);
grades.put("A-", 3.67);
grades.put("B+", 3.67);
grades.put("B", 3.67);
grades.put("B-", 3.67);
grades.put("C+", 3.67);
grades.put("C", 3.67);
grades.put("D", 3.67);
grades.put("D-", 3.67);
grades.put("E", 3.67);
grades.put("F", 3.67);
}
private class TempListener implements ActionListener {
double tCrPoints = 0.00, tCrHours = 0.00, tGPA = 0.00;
String status;
public void actionPerformed(ActionEvent event) {
if (event.getSource() == addSubjectButton) {
// Not sure what you are trying to do here, you are basically setting the 3 arrays identical
// subject.getText() = 1 ==> subjectArray[1,1,1,1,1,1]
// Do not see the point of this at all
/* for (int i=0; i<6; i++) {
subjectArray[i] = subject.getText();
gradeArray[i] = (String) grade.getSelectedItem();
cHoursArray[i] = Integer.parseInt(cHours.getText());
} */
gradeArray.add(grade.getSelectedItem().toString());
cHoursArray.add(Integer.parseInt(cHours.getText()));
tArea.append (subject.getText() + "\t\t\t" + grade.getSelectedItem() + "\t" + cHours.getText() + "\n");
subject.setText("");
cHours.setText("");
}
if (event.getSource() == calcGPAButton) {
for (String grade : gradeArray) {
tCrPoints += grades.get(grade);
}
for (Integer hour : cHoursArray) {
tCrHours += hour;
}
tGPA = tCrPoints/tCrHours;
if (tGPA >= 2) {
status = ("Pass");
} else {
status = ("Fail");
}
tArea.setText("Total Credit Points : " + tCrPoints + "\n" +
"Total Credit Hours : " + tCrHours + "\n\n" +
"Grade Point Average (GPA) : " + tGPA + "\n" +
"Status : " + status);
}
if (event.getSource() == clearAllButton) {
tArea.setText("");
cHours.setText("");
grade.setSelectedIndex(0);
tCrHours = 0.00;
tCrPoints = 0.00;
}
}
}
}

Categories

Resources