PDA

View Full Version : [SOLVED] Java - output all arrives together. Should I be using threads?



tornadof3
November 20th, 2011, 12:38 PM
Hello

I am writing a simple Java program to estimate Pi using Leibniz's series expansion. I have a class that does the actual calculation and a Swing GUI to output results. I have a text area text box and for each iteration of the sequence, it outputs the current value of the fraction that is being evaluated and then at the end of the iteration outputs the estimate for Pi.

I am appending text to this text box during each iteration of the sequence, alas the text all appears together at the very end of the sequence. Likewise, the "Calculating..." label I have made does not actually go red until it has finished and the progress bar does not update until right at the end - not very helpful!! How can I get round this issue?

The class that actually handles the calculation: findpi_interactive.java

import java.text.DecimalFormat;

class findpi_interactive {


public static void main(String[] args) {
iteratePi(true, 999);
// the 'true' assignment is to instruct the method to output to the
// console and not a GUI


}

static double roundToDecimals(double d) {
DecimalFormat twoDForm = new DecimalFormat("#.######");
return Double.valueOf(twoDForm.format(d));
}

static void iteratePi(boolean $output_console, int $n) {
//findpi_gui $gui = new findpi_gui();
String $newoutput=null;

// if $output_console is false, then this will output to the GUI
int $i=0; // a variable to tell the current iteration of the cycle
double $pi_estimate=0; // our final value for Pi
int $cur_numerator=0; // numerator of current iteration
int $cur_denom=0; // denominator of current iteration
double $current_frac=0; // fraction combining num & denom of current iteration
double $error=0; // to hold the % error in our Pi estimate
for ($i=0; $i<=$n; $i++) {
// this is the main iterative loop, which runs for $n cycles
$cur_numerator=(int) Math.pow(-1, $i);
$cur_denom=(2*$i + 1);
$current_frac=((double) $cur_numerator/ (double) $cur_denom);
if ($output_console == true) {
System.out.println($cur_numerator + "/"+ $cur_denom);
} else {
// output to GUI

$newoutput=String.valueOf($cur_numerator) + "/" + String.valueOf($cur_denom);

findpi_gui.newoutput($newoutput, $i);

}
$pi_estimate+=$current_frac;


}
$pi_estimate*=4; // Liebniz's series converges to Pi/4, not Pi.
$error=roundToDecimals(100*($pi_estimate-Math.PI)/(Math.PI));
if ($output_console==true) {
System.out.println("An estimate for Pi is: " + $pi_estimate + " (error ~ " + $error + "%)");
System.exit(0);
} else {

$newoutput="An estimate for Pi is: " + String.valueOf($pi_estimate) + " (error ~ " + String.valueOf($error) + "%)";

findpi_gui.newoutput($newoutput, $n);
findpi_gui.cycle_finished();

}
}
}


The GUI form: findpi_gui.java


import javax.swing.JOptionPane;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;


/* findpi_gui.java
* Created on 16-Oct-2011, 13:02:47
*/

// ** @author pmreid

public class findpi_gui extends javax.swing.JFrame {
static boolean $firstOutput = true; // a variable to decide whether a new line is required

/** Creates new form findpi_gui */
public findpi_gui() {
initComponents();

this.setLocationRelativeTo(null);

}

/** This method is called from within the constructor to
* initialize the form.
* WARNING: Do NOT modify this code. The content of this method is
* always regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">
private void initComponents() {

jLabelNumberIterations = new javax.swing.JLabel();
jTextNumIterations = new javax.swing.JTextField();
jPiOutput = new javax.swing.JScrollPane();
jTextPiOutput = new javax.swing.JTextArea();
jButtonFindPi = new javax.swing.JButton();
jButtonExit = new javax.swing.JButton();
jButtonRandomText = new javax.swing.JButton();
jLabelWorking = new javax.swing.JLabel();
jProgressBar = new javax.swing.JProgressBar();

setDefaultCloseOperation(javax.swing.WindowConstan ts.EXIT_ON_CLOSE);
setTitle("Find Pi");

jLabelNumberIterations.setText("Number of Iterations:");
jLabelNumberIterations.setName("jLabelNumberIterations"); // NOI18N

jTextNumIterations.setText("999");
jTextNumIterations.setToolTipText("Enter the number of iterations to loop the cycle");
jTextNumIterations.setName("jTextNumIterations"); // NOI18N

jPiOutput.setEnabled(false);
jPiOutput.setName("jPiOutput"); // NOI18N

jTextPiOutput.setColumns(20);
jTextPiOutput.setRows(10);
jTextPiOutput.setWrapStyleWord(true);
jTextPiOutput.setName("jTextPiOutput"); // NOI18N
jPiOutput.setViewportView(jTextPiOutput);

jButtonFindPi.setText("Find Pi");
jButtonFindPi.setName("jButtonFindPi"); // NOI18N
jButtonFindPi.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jButtonFindPiActionPerformed(evt);
}
});

jButtonExit.setText("Exit");
jButtonExit.setName("jButtonExit"); // NOI18N
jButtonExit.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jButtonExitActionPerformed(evt);
}
});

jButtonRandomText.setText("Random Text");
jButtonRandomText.setName("jButtonRandomText"); // NOI18N
jButtonRandomText.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jButtonRandomTextActionPerformed(evt);
}
});

jLabelWorking.setFont(new java.awt.Font("Verdana", 1, 24)); // NOI18N
jLabelWorking.setForeground(new java.awt.Color(240, 23, 23));
jLabelWorking.setText("Calculating...");
jLabelWorking.setEnabled(false);
jLabelWorking.setName("jLabelWorking"); // NOI18N

jProgressBar.setName("jProgressBar"); // NOI18N
jProgressBar.setStringPainted(true);

javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout .Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.G roupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.G roupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addComponent(jButtonRandomText)
.addPreferredGap(javax.swing.LayoutStyle.Component Placement.RELATED, 48, Short.MAX_VALUE)
.addComponent(jLabelNumberIterations)
.addPreferredGap(javax.swing.LayoutStyle.Component Placement.RELATED)
.addComponent(jTextNumIterations, javax.swing.GroupLayout.PREFERRED_SIZE, 104, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(46, 46, 46)
.addComponent(jButtonFindPi)
.addPreferredGap(javax.swing.LayoutStyle.Component Placement.UNRELATED)
.addComponent(jButtonExit))
.addComponent(jPiOutput, javax.swing.GroupLayout.DEFAULT_SIZE, 567, Short.MAX_VALUE))
.addGap(21, 21, 21))
.addGroup(layout.createSequentialGroup()
.addComponent(jLabelWorking)
.addPreferredGap(javax.swing.LayoutStyle.Component Placement.RELATED)
.addComponent(jProgressBar, javax.swing.GroupLayout.PREFERRED_SIZE, 371, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap())))
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout .Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.G roupLayout.Alignment.BASELINE)
.addComponent(jButtonExit)
.addComponent(jButtonFindPi)
.addComponent(jTextNumIterations, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(jLabelNumberIterations)
.addComponent(jButtonRandomText))
.addPreferredGap(javax.swing.LayoutStyle.Component Placement.RELATED, 36, Short.MAX_VALUE)
.addGroup(layout.createParallelGroup(javax.swing.G roupLayout.Alignment.TRAILING, false)
.addComponent(jProgressBar, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(jLabelWorking, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.addGap(18, 18, 18)
.addComponent(jPiOutput, javax.swing.GroupLayout.PREFERRED_SIZE, 204, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(28, 28, 28))
);

pack();
}// </editor-fold>

public static void newoutput(String $newoutput, int $iteration) {
// $newoutput=this.jTextPiOutput.getText() + "\n" + $newoutput;
if ($firstOutput==true) {
jTextPiOutput.setText($newoutput);
$firstOutput=false;
} else {
jTextPiOutput.append("\n " + $newoutput);

}
jProgressBar.setValue($iteration);


}
private void jButtonExitActionPerformed(java.awt.event.ActionEv ent evt) {
System.exit(0);
}

private void jButtonFindPiActionPerformed(java.awt.event.Action Event evt) {
jLabelWorking.setEnabled(true);
int $numIterations=Integer.parseInt(this.jTextNumItera tions.getText());
jProgressBar.setEnabled(true);
jProgressBar.setValue(0);
jProgressBar.setMaximum($numIterations);
$firstOutput=true;
this.jTextPiOutput.setText("");
if ($numIterations > 0) {

this.jButtonFindPi.setEnabled(false);
this.jTextNumIterations.setEnabled(false);

findpi_interactive.iteratePi(false, $numIterations);

} else {
JOptionPane.showMessageDialog(null, "Error with Pi","Number of iterations should be greater than 0", JOptionPane.ERROR_MESSAGE);
}
this.jButtonFindPi.setEnabled(true);
this.jTextNumIterations.setEnabled(true);

}

private void jButtonRandomTextActionPerformed(java.awt.event.Ac tionEvent evt) {
double $random=2566*Math.random();
this.jTextPiOutput.setText("Random text is: " + String.valueOf($random));
}

public static void cycle_finished() {


JOptionPane.showMessageDialog(null,"finished!");
jLabelWorking.setEnabled(false);
jProgressBar.setEnabled(false);


}

public static void main(String args[]) {
try {
// Set System L&F
UIManager.setLookAndFeel(
UIManager.getSystemLookAndFeelClassName());
}
catch (UnsupportedLookAndFeelException e) {
// handle exception
JOptionPane.showMessageDialog(null,"Error setting the look and feel: " + e.getMessage(), "Error",JOptionPane.ERROR_MESSAGE);
}
catch (ClassNotFoundException e) {
// handle exception
JOptionPane.showMessageDialog(null,"Error setting the look and feel: " + e.getMessage(), "Error",JOptionPane.ERROR_MESSAGE);
}
catch (InstantiationException e) {
// handle exception
JOptionPane.showMessageDialog(null,"Error setting the look and feel: " + e.getMessage(), "Error",JOptionPane.ERROR_MESSAGE);
}
catch (IllegalAccessException e) {
// handle exception
JOptionPane.showMessageDialog(null,"Error setting the look and feel: " + e.getMessage(), "Error",JOptionPane.ERROR_MESSAGE);
}
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new findpi_gui().setVisible(true);
}
});
}

// Variables declaration - do not modify
private javax.swing.JButton jButtonExit;
public javax.swing.JButton jButtonFindPi;
private javax.swing.JButton jButtonRandomText;
private javax.swing.JLabel jLabelNumberIterations;
public static javax.swing.JLabel jLabelWorking;
private javax.swing.JScrollPane jPiOutput;
public static javax.swing.JProgressBar jProgressBar;
public javax.swing.JTextField jTextNumIterations;
public static javax.swing.JTextArea jTextPiOutput;
// End of variables declaration

}

ofnuts
November 20th, 2011, 01:21 PM
GUI programs all behavre the same. There is an "Event loop" that monitors events and updates the graphics. But for it to work it must be waiting for events. This means that it shouldn't do any lengthy processing itself.

Technically you start the heavy processing in a thread, and that thread communicates with your Swing event thread using SwingUtilities.invokeLater(). (http://java.sun.com/javase/6/docs/api/javax/swing/SwingUtilities.html)This page explains it fairly well: http://www.kdgregory.com/index.php?page=swing.async (http://www.kdgregory.com/index.php?page=swing.async), (http://java.sun.com/javase/6/docs/api/javax/swing/SwingUtilities.html)otherwise googling for SwingUtilities.invokeLater will lead you to more examples and explanations.
(http://java.sun.com/javase/6/docs/api/javax/swing/SwingUtilities.html)

tornadof3
November 20th, 2011, 02:48 PM
Thanks for this. Reading about that Swing feature got me onto a web page that talked more about threads. The 'intensive' calculation is done by calling the static method iteratePi in the findpi_interactive class. I created a separate thread to handle this and everything now works ok, i.e. modify my code above so that

public void jButtonFindPiActionPerformed() contains:


Thread findpi = new Thread() {
public void run() {
findpi_interactive.iteratePi(false, $numIterations);
};
findpi.start();