0

I have been given an assignment whereby I need to create a tool that analyses a field of text and then outputs a couple of statistics about said body of text via a button click. I seem to have most of the basic framework up but am struggling with getting my two labels that are averageLength and totalWords inside my JPanel and also on getting said JPanel below where I enter my body of text. Any help would be much appreciated. Code is here:

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

public class TextStatisticsPanel extends JPanel
{

//Field for block of text
private JTextArea userText;
//Button to calculate Statistics
private JButton stats;
//Label for where statistics are shown and statistics
private JLabel averageLength, totalWords;

public TextStatisticsPanel(){

//creating the area for user text with wrapped text
userText = new JTextArea();
userText.setWrapStyleWord(true);
userText.setLineWrap(true);

//create button
stats = new JButton("Update Text Statistics");
//Listener for button
stats.addActionListener(new ButtonListener());

//Tilted border creater
JPanel statPanel = new JPanel();
statPanel.setBorder(BorderFactory.createTitledBorder("Text Statistics"));

statPanel.setOpaque(false);



//Create Scroller
JScrollPane scroller = new JScrollPane(userText);
scroller.setPreferredSize(new Dimension (350, 400));
scroller.setBorder(BorderFactory.createTitledBorder  ("Enter the text below"));
//Add the statistics labels
averageLength = new JLabel("The average length of the words: ");
totalWords = new JLabel("The total number of words: ");

//add GUI
add(statPanel);
add(scroller);
add(averageLength);
add(totalWords);
setBackground(new java.awt.Color(202, 225, 255));
setPreferredSize(new Dimension (400, 600));
add(stats);

}

// When button is pressed do :
private class ButtonListener implements ActionListener{

public void actionPerformed(ActionEvent event)
{

    if (event.getSource() == stats){
        //Call statUpdate Method
        statUpdate();

    }
}

// Create statUpdate Method
private void statUpdate() 
{
    //Grab text user inputed
    String text = userText.getText();
    //Split the text by each space to find the number of words
    String[] words = text.split(" ");

    //Calculation for average
    float average = (text.length() - words.length)/words.length;
    //
    averageLength.setText(String.valueOf(average));
    totalWords.setText(String.valueOf(words.length));

    System.out.println(averageLength);
    System.out.println(totalWords);



}
}


}

OK so as to try and use MCVE, this is part of the relevent code however I am still unable to work out the root of the problem.

The code for my second panel is :

JPanel statPanel = new JPanel();
statPanel.setBorder(BorderFactory.createTitledBorder("Text Statistics"));

statPanel.setOpaque(false);

SO as per my understanding this is me creating a second panel among my app. The problem however is that this is being placed in a seemingly random location and is not wrapping around the two labels I wish to be inside this panel and I am unsure how to fix this problem.

The main class code:

import java.awt.*;
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.JFrame;

public class TextStatistics {

public static void main(String[] args) {
    JFrame frame = new JFrame("Text Statistics Tool");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    TextStatisticsPanel panel = new TextStatisticsPanel();
    frame.getContentPane().add(panel);

    frame.pack();
    frame.setVisible(true);
}

}

Providing a visual example to show the code I believe is the problem and the problem I am encountering

Problem Visualised

SkyPlus
  • 105
  • 1
  • 9
  • 1
    1) Use a logical and consistent form of indenting code lines and blocks. The indentation is intended to make the flow of the code easier to follow! 2) A single blank line of white space in source code is all that is *ever* needed. Blank lines after `{` or before `}` are also typically redundant. 3) For better help sooner, post a [MCVE] or [Short, Self Contained, Correct Example](http://www.sscce.org/). – Andrew Thompson Mar 08 '16 at 00:42
  • 1
    `scroller.setPreferredSize(new Dimension (350, 400));` See [Should I avoid the use of set(Preferred|Maximum|Minimum)Size methods in Java Swing?](http://stackoverflow.com/q/7229226/418556) (Yes.) A more useful approach is `userText = new JTextArea(8,40); //suggest a size in columns & rows` – Andrew Thompson Mar 08 '16 at 00:46
  • Thanks for that piece of information. WIll think about that in the future. With regards to my application do you have any idea why my second panel with a border is tiny and sits to the left of my main body text just empty. – SkyPlus Mar 08 '16 at 01:24
  • 1
    *"Thanks for that piece of information."* Uh-huh.. *"WIll think about that in the future. With regards to my application do you have any idea.."* I will think about that in the future - the future after I see the MCVE. – Andrew Thompson Mar 08 '16 at 01:36
  • Hope that my bottom piece is what you meant, and hope you can help me. Thanks. – SkyPlus Mar 08 '16 at 02:00
  • *"Hope that my bottom piece is what you meant .."* To answer that question, ask yourself this one. Can the effect be reproduced by other people using a 'single copy/paste, compile & run' in a new project in whatever IDE they have open? The idea of an MCVE is two parts. 1) Simplify the problem to the simplest it can be - this often reveals the error or at least focuses on the problem code. 2) If that process fails to solve the problem, make it as easy as can be for others to see and reproduce it. – Andrew Thompson Mar 08 '16 at 02:04
  • 1
    BTW - if a person asking questions about code cannot be bothered (or cannot manage) to create an MCVE, most people could not be bothered trying to help. – Andrew Thompson Mar 08 '16 at 02:06
  • I am trying.... I'm a noobie and im trying to replicate the problem but I feel like I need the majority of the code to do so as it is based around how one component is acting around the others around it. The only other thing I feel I could offer to aid is the main class file which I have now attached above. – SkyPlus Mar 08 '16 at 02:18
  • *"The only other thing I feel I could offer.."* It's still not an MCVE. Good luck with it. – Andrew Thompson Mar 08 '16 at 02:24
  • Well as far as I am aware it is complete as the parts there will reproduce the problem I am having and therefore is verifiable too. The minimal part I really wish I could help on but I honestly don't understand how I can take away parts of the code without therefore making it incomplete and not reproducing the same problem I have as like I say I am very new to java. Instead of copying some other works off the web I thought I would try and learn to understand it myself through something like stack overflow.... Also 2:30 am and ive been trying to sort this for hours – SkyPlus Mar 08 '16 at 02:32
  • Added a visual example. I genuinely am trying to cooperate here. – SkyPlus Mar 08 '16 at 02:47

1 Answers1

1

Here's the GUI I put together.

Test Statistics Tool

These are the major changes I made.

  1. I put the JFrame code in a Runnable, so I could start the Swing application with a call to the SwingUtilities invokeLater method. The invokeLater method ensures that the Swing components are created and updated on the Event Dispatch thread. Oracle and I require that everyone starts their Swing applications on the Event Dispatch thread.

  2. I defined several new JPanels in your TextStatisticsPanel class and used two Swing layout managers, BorderLayout and BoxLayout. Study the link in the previous sentence. By study, I mean spend at least two to three weeks of 8 hour days absorbing all of the ins and outs of the Swing layout managers.

  3. I added JTextFields to hold the calculated values. That's what JTextFields are for.

  4. I fixed the integer division in your statUpdate method.

Here's the code. I put everything together in one file so it would be easier to upload. You should put the classes in separate files.

package com.ggl.testing;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;

public class TextStatistics {

    public static void main(String[] args) {
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame("Text Statistics Tool");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

                TextStatisticsPanel panel = new TextStatistics().new TextStatisticsPanel();
                frame.add(panel);

                frame.pack();
                frame.setLocationByPlatform(true);
                frame.setVisible(true);
            }
        };
        SwingUtilities.invokeLater(runnable);
    }

    public class TextStatisticsPanel extends JPanel {
        private static final long serialVersionUID = 9049744714586970558L;
        // Field for block of text
        private JTextArea userText;
        // Button to calculate Statistics
        private JButton stats;
        // Label for where statistics are shown and statistics
        private JLabel averageLength, totalWords;
        private JTextField averageLengthField, totalWordsField;

        public TextStatisticsPanel() {

            // creating the area for user text with wrapped text
            userText = new JTextArea();
            userText.setWrapStyleWord(true);
            userText.setLineWrap(true);

            // create button
            stats = new JButton("Update Text Statistics");
            stats.setAlignmentX(JButton.CENTER_ALIGNMENT);
            // Listener for button
            stats.addActionListener(new ButtonListener());

            // Tilted border creater
            JPanel statPanel = new JPanel();
            statPanel.setBorder(BorderFactory
                    .createTitledBorder("Text Statistics"));
            statPanel.setLayout(new BoxLayout(statPanel, BoxLayout.PAGE_AXIS));
            statPanel.setOpaque(false);

            // Create Scroller
            JScrollPane scroller = new JScrollPane(userText);
            scroller.setPreferredSize(new Dimension(350, 400));
            scroller.setBorder(BorderFactory
                    .createTitledBorder("Enter the text below"));

            // Add the statistics labels
            averageLength = new JLabel("The average length of the words: ");
            averageLength.setOpaque(false);

            averageLengthField = new JTextField(10);
            averageLengthField.setEditable(false);
            averageLengthField.setOpaque(false);

            totalWords = new JLabel("The total number of words: ");
            totalWords.setOpaque(false);

            totalWordsField = new JTextField(10);
            totalWordsField.setEditable(false);
            totalWordsField.setOpaque(false);

            // add GUI
            setLayout(new BorderLayout());

            statPanel.add(stats);
            statPanel.add(Box.createVerticalStrut(10));

            JPanel lengthPanel = new JPanel();
            lengthPanel.setOpaque(false);
            lengthPanel.add(averageLength);
            lengthPanel.add(averageLengthField);
            statPanel.add(lengthPanel);
            statPanel.add(Box.createVerticalStrut(10));

            JPanel wordsPanel = new JPanel();
            wordsPanel.setOpaque(false);
            wordsPanel.add(totalWords);
            wordsPanel.add(totalWordsField);
            statPanel.add(wordsPanel);

            add(statPanel, BorderLayout.SOUTH);
            add(scroller, BorderLayout.CENTER);
            setBackground(new java.awt.Color(202, 225, 255));
        }

        // When button is pressed do :
        private class ButtonListener implements ActionListener {

            public void actionPerformed(ActionEvent event) {

                if (event.getSource() == stats) {
                    // Call statUpdate Method
                    statUpdate();
                }
            }

            // Create statUpdate Method
            private void statUpdate() {
                // Grab text user inputed
                String text = userText.getText();
                // Split the text by each space to find the number of words
                String[] words = text.split(" ");

                // Calculation for average
                float average = ((float) text.length() - words.length)
                        / words.length;
                //
                averageLengthField.setText(String.valueOf(average));
                totalWordsField.setText(String.valueOf(words.length));
            }
        }
    }

}
Gilbert Le Blanc
  • 50,182
  • 6
  • 67
  • 111
  • Thank you very much for a very detailed response. I shall be having a look into all of this! Would you mind explaining this line? private static final long serialVersionUID = 9049744714586970558L; – SkyPlus Mar 08 '16 at 12:28
  • @SkyPlus: Swing components like JFrame and JPanel implement the [Serializable interface](http://stackoverflow.com/a/285809/300257). The Serializable interface requires a serialVersionUID when you extend a Swing component. Otherwise, a serialVersionUID is generated for the Serializable interface every time you run the Swing application. Eclipse, the IDE that I use, generates a serialVersionUID for me. – Gilbert Le Blanc Mar 08 '16 at 13:56
  • Thank you. I also seem to get the error " TextStatistics.TextStatisticsPanel cannot be resolved to a type" when creating the class similarly. Could you explain why I get this error and why you have laid it out as such as I haven't seen it like that before – SkyPlus Mar 08 '16 at 16:34
  • @SkyPlus: TextStatisticsPanel is an inner class in my upload, so I have to instantiate the TextStatistics class before I can instantiate the TextStatisticsPanel class. You should remove the new TextStatistics() part once you've separated the classes. – Gilbert Le Blanc Mar 08 '16 at 23:54
  • Okay got you. One final thing, the result I am getting is not the average. Even with the split there it appears to still be counting the spaces I am entering to type the next word. Is there a way around this – SkyPlus Mar 09 '16 at 23:24
  • @SkyPlus: string.trim() removes the preceding and following spaces. You're counting the spaces when computing the average. You need to sum the lengths of the words, rather than use the length of the JTextArea string. Finally, you need to remove punctuation marks before calculating the number of words and average word length. – Gilbert Le Blanc Mar 10 '16 at 00:17