简体   繁体   中英

Different Output for Exactly Same Code Using Java Swing and AWT

I am a newbie in swing and awt. I was implementing Huffman Tree in GUI. But in the code I written the output is very strange in Ubuntu(JAVA 8). Sometimes it prints the tree (haphazardly though) sometimes the window remains blank. The function paintComponent is being called for different times for different run. The code is same, no user input but for different run the result is different. Please run the code for at least 5 times to see what I said. Can anyone help me to get out of this problem?
NB You can directly jump to the Huffman class.

import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.Graphics;
import java.io.*;
import java.util.*;

import java.util.LinkedList;
import java.util.PriorityQueue;
import java.util.Queue;

class Node implements Comparable<Node> {
    private  char ch;
    private  int freq;
    private  Node left, right;
    private int x,y ;

    Node(char ch, int freq, Node l, Node r) {
        this.ch = ch;
        this.freq = freq;
        left = l;
        right = r;
    }

    public boolean isLeaf(){
        return (left == null) && (right == null);
    }

    public int compareTo(Node that) {
        return Integer.compare(this.freq,that.freq);
    }

    public Node getLeft(){
        return left;
    }

    public Node getRight(){
        return right;
    }

    public int getFreq(){
        return freq;
    }

    public char getCharacter(){
        return ch;
    }

    public void setPosition(int ht, int wd){
        x=wd; y=ht;
    }

    public int getX(){
        return x;
    }

    public int getY(){
        return y;
    }

    public String getString(){
        if(isLeaf()) return new String((Integer.toString(freq) + ' ' + ch));
        else return new String(Integer.toString(freq));
    }
}

public class Huffman extends JPanel{
    int height=0;
    String str;
    int[] freq = new int[256];
    Node root;
    Queue<Node> q = new LinkedList<Node>();

    static final private int LEFT = 5;
    static final private int RIGHT = 1300;
    static final private int TOP = 5;
    static final private int BOTTOM = 700;
    static final private int RADIUS = 15;

    public Huffman(){
        getStr();
        setFrequency();
        buildTree();
    }

    public void getStr(){
        //Scanner sc = new Scanner(System.in);
        //str = sc.nextLine();
        str = new String("What is happening?");
    }

    public void setFrequency(){
        for(int i=0;i<str.length();i++) freq[str.charAt(i)]++;
    }

    public void buildTree(){
        PriorityQueue<Node> PQueue = new PriorityQueue<Node>();
        for(int i=0;i<256;i++){
            if(freq[i]!=0){
                PQueue.add(new Node((char)i, freq[i], null, null));
            }
        }

        while(PQueue.size()>1){
            Node left, right;
            left = PQueue.poll();
            right = PQueue.poll();
            PQueue.add(new Node('\0',left.getFreq()+right.getFreq(),left,right));
            q.add(left);
            q.add(right);
        }
        root = PQueue.poll();
        q.add(root);
        setCoOrdinates(root,1,1);
    }

    public void setCoOrdinates(Node node, int wd, int ht){
        if(node == null) return;
        height = Math.max(height,ht);
        node.setPosition(wd,ht);
        setCoOrdinates(node.getLeft(),2*wd-1,ht+1);
        setCoOrdinates(node.getRight(),2*wd,ht+1);
    }

    public int getGraphicsX(int x, int y){
        return ((RIGHT-LEFT)/((int)Math.pow(2,y-1)+1))*x + LEFT;
    }

    public int getGraphicsY(int x, int y){
        return ((BOTTOM-TOP)/(height+1))*y + TOP;
    }

    @Override
    public void paintComponent(Graphics g){

        //this '*' is printing for multiple times
        System.out.println("*");

        while(q.isEmpty()==false){

            Node node = q.poll();

            int x = getGraphicsX(node.getX(),node.getY()), y = getGraphicsY(node.getX(),node.getY());

            String str = node.getString();

            g.drawOval(x-RADIUS,y-RADIUS,2*RADIUS,2*RADIUS);
            g.drawString(str,x,y+(RADIUS/2));

            if(node.isLeaf()==false){
                int leftX,leftY,rightX,rightY;

                leftX = getGraphicsX(node.getLeft().getX(), node.getLeft().getY());
                leftY = getGraphicsY(node.getLeft().getX(), node.getLeft().getY());

                rightX = getGraphicsX(node.getRight().getX(), node.getRight().getY());
                rightY = getGraphicsY(node.getRight().getX(), node.getRight().getY());

                g.drawLine(x, y+RADIUS, leftX, leftY-RADIUS);
                g.drawLine(x, y+RADIUS, rightX, rightY-RADIUS);

            }
        }
    }

    public static void main(String[] args) {

        JFrame jFrame = new JFrame();
        jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        jFrame.setSize(RIGHT,BOTTOM);
        Huffman h = new Huffman();

        jFrame.add(h);
        jFrame.setVisible(true);

    }
}
@Override
public void paintComponent(Graphics g){
    ...
    while(q.isEmpty()==false){

        Node node = q.poll();

The problem is that you're modifying the queue inside paintComponent . The UI system can call paintComponent any time that it wants to, for example dragging another window over the panel will cause repaints.

paintComponent should therefore be stateless and not modify the queue.

If your use of poll is absolutely necessary, one simple solution is to copy the queue:

@Override
public void paintComponent(Graphics g){
    Queue<Node> q = new LinkedList<>(this.q);

Seems like you could also just iterate with a for-each loop.


Few other things:

  • Your code in main where you create the GUI needs to be wrapped inside a call to SwingUtilities.invokeLater :

     public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { // create the GUI here } }); } 

    This is because Swing is single-threaded and not thread-safe. See Initial Threads .

  • You should call super.paintComponent , which paints the JPanel (background color, etc.):

     @Override protected void paintComponent(Graphics g) { super.paintComponent(g); 
  • As shown in the previous code snippet, paintComponent is a protected method and there's no reason to make it public .

  • You shouldn't use setSize on a JFrame . If you want the frame to be a fixed size, you should override getPreferredSize() on the JPanel , then call pack() on the frame. The frame will size itself automatically around the panel inside it. (Example shown here .) The size of a JFrame includes eg title bar and borders so using setSize probably also interferes with your painting coordinates.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM